msi: Set the SystemComponent installation property if necessary.
[wine/wine-gecko.git] / dlls / msi / action.c
blob5f42d7cf6e25468377ee46ecbae869b996e0d3a9
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 if (!count) goto done;
226 in_quotes = 1;
227 ignore = 1;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes) count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 if (!count) in_quotes = 0;
238 else in_quotes = 1;
239 len++;
240 break;
242 break;
244 case state_token:
245 switch (*p)
247 case '"':
248 state = state_quote;
249 if (in_quotes) count--;
250 else count++;
251 break;
252 case ' ':
253 state = state_whitespace;
254 if (!count) goto done;
255 in_quotes = 1;
256 len++;
257 break;
258 default:
259 if (!count) in_quotes = 0;
260 else in_quotes = 1;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes) count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = 1;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (!count) in_quotes = 0;
282 else in_quotes = 1;
283 len++;
284 break;
286 break;
288 default: break;
290 if (!ignore) *out++ = *p;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 MSIQUERY *view;
410 UINT rc;
412 static const WCHAR ExecSeqQuery [] =
413 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
414 '`','I','n','s','t','a','l','l',
415 'U','I','S','e','q','u','e','n','c','e','`',
416 ' ','W','H','E','R','E',' ',
417 '`','S','e','q','u','e','n','c','e','`',' ',
418 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
419 '`','S','e','q','u','e','n','c','e','`',0};
421 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
422 if (rc == ERROR_SUCCESS)
424 msiobj_release(&view->hdr);
425 return TRUE;
428 return FALSE;
431 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
433 LPWSTR source, check;
435 if (msi_get_property_int( package->db, szInstalled, 0 ))
437 HKEY hkey;
439 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
440 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
441 RegCloseKey( hkey );
443 else
445 LPWSTR p, db;
446 DWORD len;
448 db = msi_dup_property( package->db, szOriginalDatabase );
449 if (!db)
450 return ERROR_OUTOFMEMORY;
452 p = strrchrW( db, '\\' );
453 if (!p)
455 p = strrchrW( db, '/' );
456 if (!p)
458 msi_free(db);
459 return ERROR_SUCCESS;
463 len = p - db + 2;
464 source = msi_alloc( len * sizeof(WCHAR) );
465 lstrcpynW( source, db, len );
466 msi_free( db );
469 check = msi_dup_property( package->db, szSourceDir );
470 if (!check || replace)
472 UINT r = msi_set_property( package->db, szSourceDir, source );
473 if (r == ERROR_SUCCESS)
474 msi_reset_folders( package, TRUE );
476 msi_free( check );
478 check = msi_dup_property( package->db, szSOURCEDIR );
479 if (!check || replace)
480 msi_set_property( package->db, szSOURCEDIR, source );
482 msi_free( check );
483 msi_free( source );
485 return ERROR_SUCCESS;
488 static BOOL needs_ui_sequence(MSIPACKAGE *package)
490 INT level = msi_get_property_int(package->db, szUILevel, 0);
491 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
494 UINT msi_set_context(MSIPACKAGE *package)
496 int num;
498 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
500 num = msi_get_property_int(package->db, szAllUsers, 0);
501 if (num == 1 || num == 2)
502 package->Context = MSIINSTALLCONTEXT_MACHINE;
504 return ERROR_SUCCESS;
507 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
509 UINT rc;
510 LPCWSTR cond, action;
511 MSIPACKAGE *package = param;
513 action = MSI_RecordGetString(row,1);
514 if (!action)
516 ERR("Error is retrieving action name\n");
517 return ERROR_FUNCTION_FAILED;
520 /* check conditions */
521 cond = MSI_RecordGetString(row,2);
523 /* this is a hack to skip errors in the condition code */
524 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
526 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
527 return ERROR_SUCCESS;
530 if (needs_ui_sequence(package))
531 rc = ACTION_PerformUIAction(package, action, -1);
532 else
533 rc = ACTION_PerformAction(package, action, -1);
535 msi_dialog_check_messages( NULL );
537 if (package->CurrentInstallState != ERROR_SUCCESS)
538 rc = package->CurrentInstallState;
540 if (rc == ERROR_FUNCTION_NOT_CALLED)
541 rc = ERROR_SUCCESS;
543 if (rc != ERROR_SUCCESS)
544 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
546 return rc;
549 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
551 MSIQUERY * view;
552 UINT r;
553 static const WCHAR query[] =
554 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
555 '`','%','s','`',
556 ' ','W','H','E','R','E',' ',
557 '`','S','e','q','u','e','n','c','e','`',' ',
558 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
559 '`','S','e','q','u','e','n','c','e','`',0};
561 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
563 r = MSI_OpenQuery( package->db, &view, query, szTable );
564 if (r == ERROR_SUCCESS)
566 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
567 msiobj_release(&view->hdr);
570 return r;
573 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
575 MSIQUERY * view;
576 UINT rc;
577 static const WCHAR ExecSeqQuery[] =
578 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
579 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
580 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
581 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
582 'O','R','D','E','R',' ', 'B','Y',' ',
583 '`','S','e','q','u','e','n','c','e','`',0 };
584 static const WCHAR IVQuery[] =
585 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
586 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
587 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
588 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
589 ' ','\'', 'I','n','s','t','a','l','l',
590 'V','a','l','i','d','a','t','e','\'', 0};
591 INT seq = 0;
593 if (package->script->ExecuteSequenceRun)
595 TRACE("Execute Sequence already Run\n");
596 return ERROR_SUCCESS;
599 package->script->ExecuteSequenceRun = TRUE;
601 /* get the sequence number */
602 if (UIran)
604 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
605 if( !row )
606 return ERROR_FUNCTION_FAILED;
607 seq = MSI_RecordGetInteger(row,1);
608 msiobj_release(&row->hdr);
611 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
612 if (rc == ERROR_SUCCESS)
614 TRACE("Running the actions\n");
616 msi_set_property(package->db, szSourceDir, NULL);
618 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
619 msiobj_release(&view->hdr);
622 return rc;
625 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
627 MSIQUERY * view;
628 UINT rc;
629 static const WCHAR ExecSeqQuery [] =
630 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
631 '`','I','n','s','t','a','l','l',
632 'U','I','S','e','q','u','e','n','c','e','`',
633 ' ','W','H','E','R','E',' ',
634 '`','S','e','q','u','e','n','c','e','`',' ',
635 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
636 '`','S','e','q','u','e','n','c','e','`',0};
638 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
639 if (rc == ERROR_SUCCESS)
641 TRACE("Running the actions\n");
643 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
644 msiobj_release(&view->hdr);
647 return rc;
650 /********************************************************
651 * ACTION helper functions and functions that perform the actions
652 *******************************************************/
653 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
654 UINT* rc, UINT script, BOOL force )
656 BOOL ret=FALSE;
657 UINT arc;
659 arc = ACTION_CustomAction(package, action, script, force);
661 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
663 *rc = arc;
664 ret = TRUE;
666 return ret;
669 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
671 MSICOMPONENT *comp;
673 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
675 if (!strcmpW( Component, comp->Component )) return comp;
677 return NULL;
680 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
682 MSIFEATURE *feature;
684 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
686 if (!strcmpW( Feature, feature->Feature )) return feature;
688 return NULL;
691 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
693 MSIFILE *file;
695 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
697 if (!strcmpW( key, file->File )) return file;
699 return NULL;
702 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
704 MSIFILEPATCH *patch;
706 /* FIXME: There might be more than one patch */
707 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
709 if (!strcmpW( key, patch->File->File )) return patch;
711 return NULL;
714 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
716 MSIFOLDER *folder;
718 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
720 if (!strcmpW( dir, folder->Directory )) return folder;
722 return NULL;
726 * Recursively create all directories in the path.
727 * shamelessly stolen from setupapi/queue.c
729 BOOL msi_create_full_path( const WCHAR *path )
731 BOOL ret = TRUE;
732 WCHAR *new_path;
733 int len;
735 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
736 strcpyW( new_path, path );
738 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
739 new_path[len - 1] = 0;
741 while (!CreateDirectoryW( new_path, NULL ))
743 WCHAR *slash;
744 DWORD last_error = GetLastError();
745 if (last_error == ERROR_ALREADY_EXISTS) break;
746 if (last_error != ERROR_PATH_NOT_FOUND)
748 ret = FALSE;
749 break;
751 if (!(slash = strrchrW( new_path, '\\' )))
753 ret = FALSE;
754 break;
756 len = slash - new_path;
757 new_path[len] = 0;
758 if (!msi_create_full_path( new_path ))
760 ret = FALSE;
761 break;
763 new_path[len] = '\\';
765 msi_free( new_path );
766 return ret;
769 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
771 MSIRECORD *row;
773 row = MSI_CreateRecord( 4 );
774 MSI_RecordSetInteger( row, 1, a );
775 MSI_RecordSetInteger( row, 2, b );
776 MSI_RecordSetInteger( row, 3, c );
777 MSI_RecordSetInteger( row, 4, d );
778 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
779 msiobj_release( &row->hdr );
781 msi_dialog_check_messages( NULL );
784 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
786 static const WCHAR query[] =
787 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
788 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
789 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
790 WCHAR message[1024];
791 MSIRECORD *row = 0;
792 DWORD size;
794 if (!package->LastAction || strcmpW( package->LastAction, action ))
796 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
798 if (MSI_RecordIsNull( row, 3 ))
800 msiobj_release( &row->hdr );
801 return;
803 /* update the cached action format */
804 msi_free( package->ActionFormat );
805 package->ActionFormat = msi_dup_record_field( row, 3 );
806 msi_free( package->LastAction );
807 package->LastAction = strdupW( action );
808 msiobj_release( &row->hdr );
810 size = 1024;
811 MSI_RecordSetStringW( record, 0, package->ActionFormat );
812 MSI_FormatRecordW( package, record, message, &size );
813 row = MSI_CreateRecord( 1 );
814 MSI_RecordSetStringW( row, 1, message );
815 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
816 msiobj_release( &row->hdr );
819 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
821 if (!comp->Enabled)
823 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
824 return INSTALLSTATE_UNKNOWN;
826 if (package->need_rollback) return comp->Installed;
827 return comp->ActionRequest;
830 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
832 if (package->need_rollback) return feature->Installed;
833 return feature->ActionRequest;
836 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
838 MSIPACKAGE *package = param;
839 LPCWSTR dir, component, full_path;
840 MSIRECORD *uirow;
841 MSIFOLDER *folder;
842 MSICOMPONENT *comp;
844 component = MSI_RecordGetString(row, 2);
845 if (!component)
846 return ERROR_SUCCESS;
848 comp = msi_get_loaded_component(package, component);
849 if (!comp)
850 return ERROR_SUCCESS;
852 comp->Action = msi_get_component_action( package, comp );
853 if (comp->Action != INSTALLSTATE_LOCAL)
855 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
856 return ERROR_SUCCESS;
859 dir = MSI_RecordGetString(row,1);
860 if (!dir)
862 ERR("Unable to get folder id\n");
863 return ERROR_SUCCESS;
866 uirow = MSI_CreateRecord(1);
867 MSI_RecordSetStringW(uirow, 1, dir);
868 msi_ui_actiondata(package, szCreateFolders, uirow);
869 msiobj_release(&uirow->hdr);
871 full_path = msi_get_target_folder( package, dir );
872 if (!full_path)
874 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
875 return ERROR_SUCCESS;
877 TRACE("folder is %s\n", debugstr_w(full_path));
879 folder = msi_get_loaded_folder( package, dir );
880 if (folder->State == 0) msi_create_full_path( full_path );
881 folder->State = 3;
882 return ERROR_SUCCESS;
885 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
887 static const WCHAR query[] =
888 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
889 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
890 UINT rc;
891 MSIQUERY *view;
893 /* create all the empty folders specified in the CreateFolder table */
894 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
895 if (rc != ERROR_SUCCESS)
896 return ERROR_SUCCESS;
898 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
899 msiobj_release(&view->hdr);
901 return rc;
904 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
906 MSIPACKAGE *package = param;
907 LPCWSTR dir, component, full_path;
908 MSIRECORD *uirow;
909 MSIFOLDER *folder;
910 MSICOMPONENT *comp;
912 component = MSI_RecordGetString(row, 2);
913 if (!component)
914 return ERROR_SUCCESS;
916 comp = msi_get_loaded_component(package, component);
917 if (!comp)
918 return ERROR_SUCCESS;
920 comp->Action = msi_get_component_action( package, comp );
921 if (comp->Action != INSTALLSTATE_ABSENT)
923 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
924 return ERROR_SUCCESS;
927 dir = MSI_RecordGetString( row, 1 );
928 if (!dir)
930 ERR("Unable to get folder id\n");
931 return ERROR_SUCCESS;
934 full_path = msi_get_target_folder( package, dir );
935 if (!full_path)
937 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
938 return ERROR_SUCCESS;
940 TRACE("folder is %s\n", debugstr_w(full_path));
942 uirow = MSI_CreateRecord( 1 );
943 MSI_RecordSetStringW( uirow, 1, dir );
944 msi_ui_actiondata( package, szRemoveFolders, uirow );
945 msiobj_release( &uirow->hdr );
947 RemoveDirectoryW( full_path );
948 folder = msi_get_loaded_folder( package, dir );
949 folder->State = 0;
950 return ERROR_SUCCESS;
953 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
955 static const WCHAR query[] =
956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
957 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
959 MSIQUERY *view;
960 UINT rc;
962 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
963 if (rc != ERROR_SUCCESS)
964 return ERROR_SUCCESS;
966 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
967 msiobj_release( &view->hdr );
969 return rc;
972 static UINT load_component( MSIRECORD *row, LPVOID param )
974 MSIPACKAGE *package = param;
975 MSICOMPONENT *comp;
977 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
978 if (!comp)
979 return ERROR_FUNCTION_FAILED;
981 list_add_tail( &package->components, &comp->entry );
983 /* fill in the data */
984 comp->Component = msi_dup_record_field( row, 1 );
986 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
988 comp->ComponentId = msi_dup_record_field( row, 2 );
989 comp->Directory = msi_dup_record_field( row, 3 );
990 comp->Attributes = MSI_RecordGetInteger(row,4);
991 comp->Condition = msi_dup_record_field( row, 5 );
992 comp->KeyPath = msi_dup_record_field( row, 6 );
994 comp->Installed = INSTALLSTATE_UNKNOWN;
995 comp->Action = INSTALLSTATE_UNKNOWN;
996 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
998 comp->assembly = msi_load_assembly( package, comp );
999 return ERROR_SUCCESS;
1002 static UINT load_all_components( MSIPACKAGE *package )
1004 static const WCHAR query[] = {
1005 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1006 '`','C','o','m','p','o','n','e','n','t','`',0 };
1007 MSIQUERY *view;
1008 UINT r;
1010 if (!list_empty(&package->components))
1011 return ERROR_SUCCESS;
1013 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1014 if (r != ERROR_SUCCESS)
1015 return r;
1017 if (!msi_init_assembly_caches( package ))
1019 ERR("can't initialize assembly caches\n");
1020 msiobj_release( &view->hdr );
1021 return ERROR_FUNCTION_FAILED;
1024 r = MSI_IterateRecords(view, NULL, load_component, package);
1025 msiobj_release(&view->hdr);
1027 msi_destroy_assembly_caches( package );
1028 return r;
1031 typedef struct {
1032 MSIPACKAGE *package;
1033 MSIFEATURE *feature;
1034 } _ilfs;
1036 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1038 ComponentList *cl;
1040 cl = msi_alloc( sizeof (*cl) );
1041 if ( !cl )
1042 return ERROR_NOT_ENOUGH_MEMORY;
1043 cl->component = comp;
1044 list_add_tail( &feature->Components, &cl->entry );
1046 return ERROR_SUCCESS;
1049 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1051 FeatureList *fl;
1053 fl = msi_alloc( sizeof(*fl) );
1054 if ( !fl )
1055 return ERROR_NOT_ENOUGH_MEMORY;
1056 fl->feature = child;
1057 list_add_tail( &parent->Children, &fl->entry );
1059 return ERROR_SUCCESS;
1062 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1064 _ilfs* ilfs = param;
1065 LPCWSTR component;
1066 MSICOMPONENT *comp;
1068 component = MSI_RecordGetString(row,1);
1070 /* check to see if the component is already loaded */
1071 comp = msi_get_loaded_component( ilfs->package, component );
1072 if (!comp)
1074 ERR("unknown component %s\n", debugstr_w(component));
1075 return ERROR_FUNCTION_FAILED;
1078 add_feature_component( ilfs->feature, comp );
1079 comp->Enabled = TRUE;
1081 return ERROR_SUCCESS;
1084 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1086 MSIFEATURE *feature;
1088 if ( !name )
1089 return NULL;
1091 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1093 if ( !strcmpW( feature->Feature, name ) )
1094 return feature;
1097 return NULL;
1100 static UINT load_feature(MSIRECORD * row, LPVOID param)
1102 MSIPACKAGE* package = param;
1103 MSIFEATURE* feature;
1104 static const WCHAR Query1[] =
1105 {'S','E','L','E','C','T',' ',
1106 '`','C','o','m','p','o','n','e','n','t','_','`',
1107 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1108 'C','o','m','p','o','n','e','n','t','s','`',' ',
1109 'W','H','E','R','E',' ',
1110 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1111 MSIQUERY * view;
1112 UINT rc;
1113 _ilfs ilfs;
1115 /* fill in the data */
1117 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1118 if (!feature)
1119 return ERROR_NOT_ENOUGH_MEMORY;
1121 list_init( &feature->Children );
1122 list_init( &feature->Components );
1124 feature->Feature = msi_dup_record_field( row, 1 );
1126 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1128 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1129 feature->Title = msi_dup_record_field( row, 3 );
1130 feature->Description = msi_dup_record_field( row, 4 );
1132 if (!MSI_RecordIsNull(row,5))
1133 feature->Display = MSI_RecordGetInteger(row,5);
1135 feature->Level= MSI_RecordGetInteger(row,6);
1136 feature->Directory = msi_dup_record_field( row, 7 );
1137 feature->Attributes = MSI_RecordGetInteger(row,8);
1139 feature->Installed = INSTALLSTATE_UNKNOWN;
1140 feature->Action = INSTALLSTATE_UNKNOWN;
1141 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1143 list_add_tail( &package->features, &feature->entry );
1145 /* load feature components */
1147 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1148 if (rc != ERROR_SUCCESS)
1149 return ERROR_SUCCESS;
1151 ilfs.package = package;
1152 ilfs.feature = feature;
1154 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1155 msiobj_release(&view->hdr);
1157 return ERROR_SUCCESS;
1160 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1162 MSIPACKAGE* package = param;
1163 MSIFEATURE *parent, *child;
1165 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1166 if (!child)
1167 return ERROR_FUNCTION_FAILED;
1169 if (!child->Feature_Parent)
1170 return ERROR_SUCCESS;
1172 parent = find_feature_by_name( package, child->Feature_Parent );
1173 if (!parent)
1174 return ERROR_FUNCTION_FAILED;
1176 add_feature_child( parent, child );
1177 return ERROR_SUCCESS;
1180 static UINT load_all_features( MSIPACKAGE *package )
1182 static const WCHAR query[] = {
1183 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1184 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1185 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1186 MSIQUERY *view;
1187 UINT r;
1189 if (!list_empty(&package->features))
1190 return ERROR_SUCCESS;
1192 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1193 if (r != ERROR_SUCCESS)
1194 return r;
1196 r = MSI_IterateRecords( view, NULL, load_feature, package );
1197 if (r != ERROR_SUCCESS)
1198 return r;
1200 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1201 msiobj_release( &view->hdr );
1203 return r;
1206 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1208 if (!p)
1209 return p;
1210 p = strchrW(p, ch);
1211 if (!p)
1212 return p;
1213 *p = 0;
1214 return p+1;
1217 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1219 static const WCHAR query[] = {
1220 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1221 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1222 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1223 MSIQUERY *view = NULL;
1224 MSIRECORD *row = NULL;
1225 UINT r;
1227 TRACE("%s\n", debugstr_w(file->File));
1229 r = MSI_OpenQuery(package->db, &view, query, file->File);
1230 if (r != ERROR_SUCCESS)
1231 goto done;
1233 r = MSI_ViewExecute(view, NULL);
1234 if (r != ERROR_SUCCESS)
1235 goto done;
1237 r = MSI_ViewFetch(view, &row);
1238 if (r != ERROR_SUCCESS)
1239 goto done;
1241 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1242 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1243 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1244 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1245 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1247 done:
1248 if (view) msiobj_release(&view->hdr);
1249 if (row) msiobj_release(&row->hdr);
1250 return r;
1253 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1255 MSIRECORD *row;
1256 static const WCHAR query[] = {
1257 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1258 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1259 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1261 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1262 if (!row)
1264 WARN("query failed\n");
1265 return ERROR_FUNCTION_FAILED;
1268 file->disk_id = MSI_RecordGetInteger( row, 1 );
1269 msiobj_release( &row->hdr );
1270 return ERROR_SUCCESS;
1273 static UINT load_file(MSIRECORD *row, LPVOID param)
1275 MSIPACKAGE* package = param;
1276 LPCWSTR component;
1277 MSIFILE *file;
1279 /* fill in the data */
1281 file = msi_alloc_zero( sizeof (MSIFILE) );
1282 if (!file)
1283 return ERROR_NOT_ENOUGH_MEMORY;
1285 file->File = msi_dup_record_field( row, 1 );
1287 component = MSI_RecordGetString( row, 2 );
1288 file->Component = msi_get_loaded_component( package, component );
1290 if (!file->Component)
1292 WARN("Component not found: %s\n", debugstr_w(component));
1293 msi_free(file->File);
1294 msi_free(file);
1295 return ERROR_SUCCESS;
1298 file->FileName = msi_dup_record_field( row, 3 );
1299 msi_reduce_to_long_filename( file->FileName );
1301 file->ShortName = msi_dup_record_field( row, 3 );
1302 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1304 file->FileSize = MSI_RecordGetInteger( row, 4 );
1305 file->Version = msi_dup_record_field( row, 5 );
1306 file->Language = msi_dup_record_field( row, 6 );
1307 file->Attributes = MSI_RecordGetInteger( row, 7 );
1308 file->Sequence = MSI_RecordGetInteger( row, 8 );
1310 file->state = msifs_invalid;
1312 /* if the compressed bits are not set in the file attributes,
1313 * then read the information from the package word count property
1315 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1317 file->IsCompressed = FALSE;
1319 else if (file->Attributes &
1320 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1322 file->IsCompressed = TRUE;
1324 else if (file->Attributes & msidbFileAttributesNoncompressed)
1326 file->IsCompressed = FALSE;
1328 else
1330 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1333 load_file_hash(package, file);
1334 load_file_disk_id(package, file);
1336 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1338 list_add_tail( &package->files, &file->entry );
1340 return ERROR_SUCCESS;
1343 static UINT load_all_files(MSIPACKAGE *package)
1345 MSIQUERY * view;
1346 UINT rc;
1347 static const WCHAR Query[] =
1348 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1349 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1350 '`','S','e','q','u','e','n','c','e','`', 0};
1352 if (!list_empty(&package->files))
1353 return ERROR_SUCCESS;
1355 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1356 if (rc != ERROR_SUCCESS)
1357 return ERROR_SUCCESS;
1359 rc = MSI_IterateRecords(view, NULL, load_file, package);
1360 msiobj_release(&view->hdr);
1362 return ERROR_SUCCESS;
1365 static UINT load_media( MSIRECORD *row, LPVOID param )
1367 MSIPACKAGE *package = param;
1368 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1369 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1371 /* FIXME: load external cabinets and directory sources too */
1372 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1373 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1374 return ERROR_SUCCESS;
1377 static UINT load_all_media( MSIPACKAGE *package )
1379 static const WCHAR query[] =
1380 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
1381 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
1382 MSIQUERY *view;
1383 UINT r;
1385 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1386 if (r != ERROR_SUCCESS) return ERROR_SUCCESS;
1388 MSI_IterateRecords( view, NULL, load_media, package );
1389 msiobj_release( &view->hdr );
1390 return ERROR_SUCCESS;
1393 static UINT load_patch(MSIRECORD *row, LPVOID param)
1395 MSIPACKAGE *package = param;
1396 MSIFILEPATCH *patch;
1397 LPWSTR file_key;
1399 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1400 if (!patch)
1401 return ERROR_NOT_ENOUGH_MEMORY;
1403 file_key = msi_dup_record_field( row, 1 );
1404 patch->File = msi_get_loaded_file( package, file_key );
1405 msi_free(file_key);
1407 if( !patch->File )
1409 ERR("Failed to find target for patch in File table\n");
1410 msi_free(patch);
1411 return ERROR_FUNCTION_FAILED;
1414 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1416 /* FIXME: The database should be properly transformed */
1417 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1419 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1420 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1421 patch->IsApplied = FALSE;
1423 /* FIXME:
1424 * Header field - for patch validation.
1425 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1428 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1430 list_add_tail( &package->filepatches, &patch->entry );
1432 return ERROR_SUCCESS;
1435 static UINT load_all_patches(MSIPACKAGE *package)
1437 MSIQUERY *view;
1438 UINT rc;
1439 static const WCHAR Query[] =
1440 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1441 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1442 '`','S','e','q','u','e','n','c','e','`',0};
1444 if (!list_empty(&package->filepatches))
1445 return ERROR_SUCCESS;
1447 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1448 if (rc != ERROR_SUCCESS)
1449 return ERROR_SUCCESS;
1451 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1452 msiobj_release(&view->hdr);
1454 return ERROR_SUCCESS;
1457 static UINT load_folder( MSIRECORD *row, LPVOID param )
1459 MSIPACKAGE *package = param;
1460 static WCHAR szEmpty[] = { 0 };
1461 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1462 MSIFOLDER *folder;
1464 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1465 list_init( &folder->children );
1466 folder->Directory = msi_dup_record_field( row, 1 );
1467 folder->Parent = msi_dup_record_field( row, 2 );
1468 p = msi_dup_record_field(row, 3);
1470 TRACE("%s\n", debugstr_w(folder->Directory));
1472 /* split src and target dir */
1473 tgt_short = p;
1474 src_short = folder_split_path( p, ':' );
1476 /* split the long and short paths */
1477 tgt_long = folder_split_path( tgt_short, '|' );
1478 src_long = folder_split_path( src_short, '|' );
1480 /* check for no-op dirs */
1481 if (tgt_short && !strcmpW( szDot, tgt_short ))
1482 tgt_short = szEmpty;
1483 if (src_short && !strcmpW( szDot, src_short ))
1484 src_short = szEmpty;
1486 if (!tgt_long)
1487 tgt_long = tgt_short;
1489 if (!src_short) {
1490 src_short = tgt_short;
1491 src_long = tgt_long;
1494 if (!src_long)
1495 src_long = src_short;
1497 /* FIXME: use the target short path too */
1498 folder->TargetDefault = strdupW(tgt_long);
1499 folder->SourceShortPath = strdupW(src_short);
1500 folder->SourceLongPath = strdupW(src_long);
1501 msi_free(p);
1503 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1504 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1505 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1507 list_add_tail( &package->folders, &folder->entry );
1508 return ERROR_SUCCESS;
1511 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1513 FolderList *fl;
1515 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1516 fl->folder = child;
1517 list_add_tail( &parent->children, &fl->entry );
1518 return ERROR_SUCCESS;
1521 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1523 MSIPACKAGE *package = param;
1524 MSIFOLDER *parent, *child;
1526 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1527 return ERROR_FUNCTION_FAILED;
1529 if (!child->Parent) return ERROR_SUCCESS;
1531 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1532 return ERROR_FUNCTION_FAILED;
1534 return add_folder_child( parent, child );
1537 static UINT load_all_folders( MSIPACKAGE *package )
1539 static const WCHAR query[] = {
1540 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1541 '`','D','i','r','e','c','t','o','r','y','`',0 };
1542 MSIQUERY *view;
1543 UINT r;
1545 if (!list_empty(&package->folders))
1546 return ERROR_SUCCESS;
1548 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1549 if (r != ERROR_SUCCESS)
1550 return r;
1552 r = MSI_IterateRecords( view, NULL, load_folder, package );
1553 if (r != ERROR_SUCCESS)
1555 msiobj_release( &view->hdr );
1556 return r;
1558 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1559 msiobj_release( &view->hdr );
1560 return r;
1563 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1565 msi_set_property( package->db, szCostingComplete, szZero );
1566 msi_set_property( package->db, szRootDrive, szCRoot );
1568 load_all_folders( package );
1569 load_all_components( package );
1570 load_all_features( package );
1571 load_all_files( package );
1572 load_all_patches( package );
1573 load_all_media( package );
1575 return ERROR_SUCCESS;
1578 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1580 const WCHAR *action = package->script->Actions[script][index];
1581 ui_actionstart( package, action );
1582 TRACE("executing %s\n", debugstr_w(action));
1583 return ACTION_PerformAction( package, action, script );
1586 static UINT execute_script( MSIPACKAGE *package, UINT script )
1588 UINT i, rc = ERROR_SUCCESS;
1590 TRACE("executing script %u\n", script);
1592 if (!package->script)
1594 ERR("no script!\n");
1595 return ERROR_FUNCTION_FAILED;
1597 if (script == ROLLBACK_SCRIPT)
1599 for (i = package->script->ActionCount[script]; i > 0; i--)
1601 rc = execute_script_action( package, script, i - 1 );
1602 if (rc != ERROR_SUCCESS) break;
1605 else
1607 for (i = 0; i < package->script->ActionCount[script]; i++)
1609 rc = execute_script_action( package, script, i );
1610 if (rc != ERROR_SUCCESS) break;
1613 msi_free_action_script(package, script);
1614 return rc;
1617 static UINT ACTION_FileCost(MSIPACKAGE *package)
1619 return ERROR_SUCCESS;
1622 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1624 MSICOMPONENT *comp;
1625 UINT r;
1627 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1629 if (!comp->ComponentId) continue;
1631 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1632 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1633 &comp->Installed );
1634 if (r != ERROR_SUCCESS)
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1637 &comp->Installed );
1638 if (r != ERROR_SUCCESS)
1639 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1640 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1641 &comp->Installed );
1642 if (r != ERROR_SUCCESS)
1643 comp->Installed = INSTALLSTATE_ABSENT;
1647 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1649 MSIFEATURE *feature;
1651 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1653 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1655 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1656 feature->Installed = INSTALLSTATE_ABSENT;
1657 else
1658 feature->Installed = state;
1662 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1664 return (feature->Level > 0 && feature->Level <= level);
1667 static BOOL process_state_property(MSIPACKAGE* package, int level,
1668 LPCWSTR property, INSTALLSTATE state)
1670 LPWSTR override;
1671 MSIFEATURE *feature;
1673 override = msi_dup_property( package->db, property );
1674 if (!override)
1675 return FALSE;
1677 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1679 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1680 continue;
1682 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1684 if (!strcmpiW( override, szAll ))
1686 if (feature->Installed != state)
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1692 else
1694 LPWSTR ptr = override;
1695 LPWSTR ptr2 = strchrW(override,',');
1697 while (ptr)
1699 int len = ptr2 - ptr;
1701 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1702 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1704 if (feature->Installed != state)
1706 feature->Action = state;
1707 feature->ActionRequest = state;
1709 break;
1711 if (ptr2)
1713 ptr=ptr2+1;
1714 ptr2 = strchrW(ptr,',');
1716 else
1717 break;
1721 msi_free(override);
1722 return TRUE;
1725 static BOOL process_overrides( MSIPACKAGE *package, int level )
1727 static const WCHAR szAddLocal[] =
1728 {'A','D','D','L','O','C','A','L',0};
1729 static const WCHAR szAddSource[] =
1730 {'A','D','D','S','O','U','R','C','E',0};
1731 static const WCHAR szAdvertise[] =
1732 {'A','D','V','E','R','T','I','S','E',0};
1733 BOOL ret = FALSE;
1735 /* all these activation/deactivation things happen in order and things
1736 * later on the list override things earlier on the list.
1738 * 0 INSTALLLEVEL processing
1739 * 1 ADDLOCAL
1740 * 2 REMOVE
1741 * 3 ADDSOURCE
1742 * 4 ADDDEFAULT
1743 * 5 REINSTALL
1744 * 6 ADVERTISE
1745 * 7 COMPADDLOCAL
1746 * 8 COMPADDSOURCE
1747 * 9 FILEADDLOCAL
1748 * 10 FILEADDSOURCE
1749 * 11 FILEADDDEFAULT
1751 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1752 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1753 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1754 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1755 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1757 if (ret)
1758 msi_set_property( package->db, szPreselected, szOne );
1760 return ret;
1763 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1765 int level;
1766 MSICOMPONENT* component;
1767 MSIFEATURE *feature;
1769 TRACE("Checking Install Level\n");
1771 level = msi_get_property_int(package->db, szInstallLevel, 1);
1773 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1775 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1777 if (!is_feature_selected( feature, level )) continue;
1779 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1781 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1783 feature->Action = INSTALLSTATE_SOURCE;
1784 feature->ActionRequest = INSTALLSTATE_SOURCE;
1786 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1788 feature->Action = INSTALLSTATE_ADVERTISED;
1789 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1791 else
1793 feature->Action = INSTALLSTATE_LOCAL;
1794 feature->ActionRequest = INSTALLSTATE_LOCAL;
1798 /* disable child features of unselected parent or follow parent */
1799 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1801 FeatureList *fl;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1805 if (!is_feature_selected( feature, level ))
1807 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1808 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1810 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1812 fl->feature->Action = feature->Action;
1813 fl->feature->ActionRequest = feature->ActionRequest;
1818 else /* preselected */
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 if (!is_feature_selected( feature, level )) continue;
1824 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1826 if (feature->Installed == INSTALLSTATE_ABSENT)
1828 feature->Action = INSTALLSTATE_UNKNOWN;
1829 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1831 else
1833 feature->Action = feature->Installed;
1834 feature->ActionRequest = feature->Installed;
1838 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1840 FeatureList *fl;
1842 if (!is_feature_selected( feature, level )) continue;
1844 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1846 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1848 fl->feature->Action = feature->Action;
1849 fl->feature->ActionRequest = feature->ActionRequest;
1855 /* now we want to set component state based based on feature state */
1856 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1858 ComponentList *cl;
1860 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1861 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1862 feature->ActionRequest, feature->Action);
1864 if (!is_feature_selected( feature, level )) continue;
1866 /* features with components that have compressed files are made local */
1867 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1869 if (cl->component->ForceLocalState &&
1870 feature->ActionRequest == INSTALLSTATE_SOURCE)
1872 feature->Action = INSTALLSTATE_LOCAL;
1873 feature->ActionRequest = INSTALLSTATE_LOCAL;
1874 break;
1878 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1880 component = cl->component;
1882 switch (feature->ActionRequest)
1884 case INSTALLSTATE_ABSENT:
1885 component->anyAbsent = 1;
1886 break;
1887 case INSTALLSTATE_ADVERTISED:
1888 component->hasAdvertiseFeature = 1;
1889 break;
1890 case INSTALLSTATE_SOURCE:
1891 component->hasSourceFeature = 1;
1892 break;
1893 case INSTALLSTATE_LOCAL:
1894 component->hasLocalFeature = 1;
1895 break;
1896 case INSTALLSTATE_DEFAULT:
1897 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1898 component->hasAdvertiseFeature = 1;
1899 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1900 component->hasSourceFeature = 1;
1901 else
1902 component->hasLocalFeature = 1;
1903 break;
1904 default:
1905 break;
1910 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1912 /* check if it's local or source */
1913 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1914 (component->hasLocalFeature || component->hasSourceFeature))
1916 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1917 !component->ForceLocalState)
1919 component->Action = INSTALLSTATE_SOURCE;
1920 component->ActionRequest = INSTALLSTATE_SOURCE;
1922 else
1924 component->Action = INSTALLSTATE_LOCAL;
1925 component->ActionRequest = INSTALLSTATE_LOCAL;
1927 continue;
1930 /* if any feature is local, the component must be local too */
1931 if (component->hasLocalFeature)
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1935 continue;
1937 if (component->hasSourceFeature)
1939 component->Action = INSTALLSTATE_SOURCE;
1940 component->ActionRequest = INSTALLSTATE_SOURCE;
1941 continue;
1943 if (component->hasAdvertiseFeature)
1945 component->Action = INSTALLSTATE_ADVERTISED;
1946 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1947 continue;
1949 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1950 if (component->anyAbsent &&
1951 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1976 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1979 return ERROR_SUCCESS;
1982 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1984 MSIPACKAGE *package = param;
1985 LPCWSTR name;
1986 MSIFEATURE *feature;
1988 name = MSI_RecordGetString( row, 1 );
1990 feature = msi_get_loaded_feature( package, name );
1991 if (!feature)
1992 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1993 else
1995 LPCWSTR Condition;
1996 Condition = MSI_RecordGetString(row,3);
1998 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2000 int level = MSI_RecordGetInteger(row,2);
2001 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2002 feature->Level = level;
2005 return ERROR_SUCCESS;
2008 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2010 static const WCHAR name[] = {'\\',0};
2011 VS_FIXEDFILEINFO *ptr, *ret;
2012 LPVOID version;
2013 DWORD versize, handle;
2014 UINT sz;
2016 versize = GetFileVersionInfoSizeW( filename, &handle );
2017 if (!versize)
2018 return NULL;
2020 version = msi_alloc( versize );
2021 if (!version)
2022 return NULL;
2024 GetFileVersionInfoW( filename, 0, versize, version );
2026 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2028 msi_free( version );
2029 return NULL;
2032 ret = msi_alloc( sz );
2033 memcpy( ret, ptr, sz );
2035 msi_free( version );
2036 return ret;
2039 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2041 DWORD ms, ls;
2043 msi_parse_version_string( version, &ms, &ls );
2045 if (fi->dwFileVersionMS > ms) return 1;
2046 else if (fi->dwFileVersionMS < ms) return -1;
2047 else if (fi->dwFileVersionLS > ls) return 1;
2048 else if (fi->dwFileVersionLS < ls) return -1;
2049 return 0;
2052 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2054 DWORD ms1, ms2;
2056 msi_parse_version_string( ver1, &ms1, NULL );
2057 msi_parse_version_string( ver2, &ms2, NULL );
2059 if (ms1 > ms2) return 1;
2060 else if (ms1 < ms2) return -1;
2061 return 0;
2064 DWORD msi_get_disk_file_size( LPCWSTR filename )
2066 HANDLE file;
2067 DWORD size;
2069 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2070 if (file == INVALID_HANDLE_VALUE)
2071 return INVALID_FILE_SIZE;
2073 size = GetFileSize( file, NULL );
2074 TRACE("size is %u\n", size);
2075 CloseHandle( file );
2076 return size;
2079 BOOL msi_file_hash_matches( MSIFILE *file )
2081 UINT r;
2082 MSIFILEHASHINFO hash;
2084 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2085 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2086 if (r != ERROR_SUCCESS)
2087 return FALSE;
2089 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2092 static WCHAR *get_temp_dir( void )
2094 static UINT id;
2095 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2097 GetTempPathW( MAX_PATH, tmp );
2098 for (;;)
2100 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2101 if (CreateDirectoryW( dir, NULL )) break;
2103 return strdupW( dir );
2107 * msi_build_directory_name()
2109 * This function is to save messing round with directory names
2110 * It handles adding backslashes between path segments,
2111 * and can add \ at the end of the directory name if told to.
2113 * It takes a variable number of arguments.
2114 * It always allocates a new string for the result, so make sure
2115 * to free the return value when finished with it.
2117 * The first arg is the number of path segments that follow.
2118 * The arguments following count are a list of path segments.
2119 * A path segment may be NULL.
2121 * Path segments will be added with a \ separating them.
2122 * A \ will not be added after the last segment, however if the
2123 * last segment is NULL, then the last character will be a \
2125 WCHAR *msi_build_directory_name( DWORD count, ... )
2127 DWORD sz = 1, i;
2128 WCHAR *dir;
2129 va_list va;
2131 va_start( va, count );
2132 for (i = 0; i < count; i++)
2134 const WCHAR *str = va_arg( va, const WCHAR * );
2135 if (str) sz += strlenW( str ) + 1;
2137 va_end( va );
2139 dir = msi_alloc( sz * sizeof(WCHAR) );
2140 dir[0] = 0;
2142 va_start( va, count );
2143 for (i = 0; i < count; i++)
2145 const WCHAR *str = va_arg( va, const WCHAR * );
2146 if (!str) continue;
2147 strcatW( dir, str );
2148 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2150 va_end( va );
2151 return dir;
2154 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2156 MSIASSEMBLY *assembly = file->Component->assembly;
2158 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2160 msi_free( file->TargetPath );
2161 if (assembly && !assembly->application)
2163 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2164 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2165 msi_track_tempfile( package, file->TargetPath );
2167 else
2169 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2170 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2173 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2176 static UINT calculate_file_cost( MSIPACKAGE *package )
2178 VS_FIXEDFILEINFO *file_version;
2179 WCHAR *font_version;
2180 MSIFILE *file;
2182 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2184 MSICOMPONENT *comp = file->Component;
2185 DWORD file_size;
2187 if (!comp->Enabled) continue;
2189 if (file->IsCompressed)
2190 comp->ForceLocalState = TRUE;
2192 set_target_path( package, file );
2194 if ((comp->assembly && !comp->assembly->installed) ||
2195 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2197 comp->Cost += file->FileSize;
2198 continue;
2200 file_size = msi_get_disk_file_size( file->TargetPath );
2202 if (file->Version)
2204 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2206 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2208 comp->Cost += file->FileSize - file_size;
2210 msi_free( file_version );
2211 continue;
2213 else if ((font_version = font_version_from_file( file->TargetPath )))
2215 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2217 comp->Cost += file->FileSize - file_size;
2219 msi_free( font_version );
2220 continue;
2223 if (file_size != file->FileSize)
2225 comp->Cost += file->FileSize - file_size;
2228 return ERROR_SUCCESS;
2231 void msi_clean_path( WCHAR *p )
2233 WCHAR *q = p;
2234 int n, len = 0;
2236 while (1)
2238 /* copy until the end of the string or a space */
2239 while (*p != ' ' && (*q = *p))
2241 p++, len++;
2242 /* reduce many backslashes to one */
2243 if (*p != '\\' || *q != '\\')
2244 q++;
2247 /* quit at the end of the string */
2248 if (!*p)
2249 break;
2251 /* count the number of spaces */
2252 n = 0;
2253 while (p[n] == ' ')
2254 n++;
2256 /* if it's leading or trailing space, skip it */
2257 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2258 p += n;
2259 else /* copy n spaces */
2260 while (n && (*q++ = *p++)) n--;
2264 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2266 int len;
2267 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2269 if (!target_dir) return NULL;
2271 len = strlenW( target_dir );
2272 if (target_dir[len - 1] == '\\') return target_dir;
2273 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2275 strcpyW( path, target_dir );
2276 path[len] = '\\';
2277 path[len + 1] = 0;
2279 msi_free( target_dir );
2280 return path;
2283 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2285 FolderList *fl;
2286 MSIFOLDER *folder, *parent, *child;
2287 WCHAR *path;
2289 TRACE("resolving %s\n", debugstr_w(name));
2291 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2293 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2295 if (!load_prop || !(path = get_target_dir_property( package->db )))
2297 path = msi_dup_property( package->db, szRootDrive );
2300 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2302 parent = msi_get_loaded_folder( package, folder->Parent );
2303 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2305 msi_clean_path( path );
2306 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2308 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2309 msi_free( path );
2310 return;
2312 msi_set_property( package->db, folder->Directory, path );
2313 msi_free( folder->ResolvedTarget );
2314 folder->ResolvedTarget = path;
2316 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2318 child = fl->folder;
2319 msi_resolve_target_folder( package, child->Directory, load_prop );
2321 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2324 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2326 static const WCHAR condition_query[] =
2327 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
2328 static const WCHAR szOutOfDiskSpace[] =
2329 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2330 MSICOMPONENT *comp;
2331 UINT rc = ERROR_SUCCESS;
2332 MSIQUERY * view;
2333 LPWSTR level;
2335 TRACE("Building directory properties\n");
2336 msi_resolve_target_folder( package, szTargetDir, TRUE );
2338 TRACE("Evaluating component conditions\n");
2339 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2341 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2343 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2344 comp->Enabled = FALSE;
2346 else
2347 comp->Enabled = TRUE;
2350 /* read components states from the registry */
2351 ACTION_GetComponentInstallStates(package);
2352 ACTION_GetFeatureInstallStates(package);
2354 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2356 TRACE("Evaluating feature conditions\n");
2358 rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
2359 if (rc == ERROR_SUCCESS)
2361 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2362 msiobj_release( &view->hdr );
2366 TRACE("Calculating file cost\n");
2367 calculate_file_cost( package );
2369 msi_set_property( package->db, szCostingComplete, szOne );
2370 /* set default run level if not set */
2371 level = msi_dup_property( package->db, szInstallLevel );
2372 if (!level)
2373 msi_set_property( package->db, szInstallLevel, szOne );
2374 msi_free(level);
2376 /* FIXME: check volume disk space */
2377 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2379 return MSI_SetFeatureStates(package);
2382 /* OK this value is "interpreted" and then formatted based on the
2383 first few characters */
2384 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2385 DWORD *size)
2387 LPSTR data = NULL;
2389 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2391 if (value[1]=='x')
2393 LPWSTR ptr;
2394 CHAR byte[5];
2395 LPWSTR deformated = NULL;
2396 int count;
2398 deformat_string(package, &value[2], &deformated);
2400 /* binary value type */
2401 ptr = deformated;
2402 *type = REG_BINARY;
2403 if (strlenW(ptr)%2)
2404 *size = (strlenW(ptr)/2)+1;
2405 else
2406 *size = strlenW(ptr)/2;
2408 data = msi_alloc(*size);
2410 byte[0] = '0';
2411 byte[1] = 'x';
2412 byte[4] = 0;
2413 count = 0;
2414 /* if uneven pad with a zero in front */
2415 if (strlenW(ptr)%2)
2417 byte[2]= '0';
2418 byte[3]= *ptr;
2419 ptr++;
2420 data[count] = (BYTE)strtol(byte,NULL,0);
2421 count ++;
2422 TRACE("Uneven byte count\n");
2424 while (*ptr)
2426 byte[2]= *ptr;
2427 ptr++;
2428 byte[3]= *ptr;
2429 ptr++;
2430 data[count] = (BYTE)strtol(byte,NULL,0);
2431 count ++;
2433 msi_free(deformated);
2435 TRACE("Data %i bytes(%i)\n",*size,count);
2437 else
2439 LPWSTR deformated;
2440 LPWSTR p;
2441 DWORD d = 0;
2442 deformat_string(package, &value[1], &deformated);
2444 *type=REG_DWORD;
2445 *size = sizeof(DWORD);
2446 data = msi_alloc(*size);
2447 p = deformated;
2448 if (*p == '-')
2449 p++;
2450 while (*p)
2452 if ( (*p < '0') || (*p > '9') )
2453 break;
2454 d *= 10;
2455 d += (*p - '0');
2456 p++;
2458 if (deformated[0] == '-')
2459 d = -d;
2460 *(LPDWORD)data = d;
2461 TRACE("DWORD %i\n",*(LPDWORD)data);
2463 msi_free(deformated);
2466 else
2468 static const WCHAR szMulti[] = {'[','~',']',0};
2469 LPCWSTR ptr;
2470 *type=REG_SZ;
2472 if (value[0]=='#')
2474 if (value[1]=='%')
2476 ptr = &value[2];
2477 *type=REG_EXPAND_SZ;
2479 else
2480 ptr = &value[1];
2482 else
2483 ptr=value;
2485 if (strstrW(value, szMulti))
2486 *type = REG_MULTI_SZ;
2488 /* remove initial delimiter */
2489 if (!strncmpW(value, szMulti, 3))
2490 ptr = value + 3;
2492 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2494 /* add double NULL terminator */
2495 if (*type == REG_MULTI_SZ)
2497 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2498 data = msi_realloc_zero(data, *size);
2501 return data;
2504 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2506 const WCHAR *ret;
2508 switch (root)
2510 case -1:
2511 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2513 *root_key = HKEY_LOCAL_MACHINE;
2514 ret = szHLM;
2516 else
2518 *root_key = HKEY_CURRENT_USER;
2519 ret = szHCU;
2521 break;
2522 case 0:
2523 *root_key = HKEY_CLASSES_ROOT;
2524 ret = szHCR;
2525 break;
2526 case 1:
2527 *root_key = HKEY_CURRENT_USER;
2528 ret = szHCU;
2529 break;
2530 case 2:
2531 *root_key = HKEY_LOCAL_MACHINE;
2532 ret = szHLM;
2533 break;
2534 case 3:
2535 *root_key = HKEY_USERS;
2536 ret = szHU;
2537 break;
2538 default:
2539 ERR("Unknown root %i\n", root);
2540 return NULL;
2543 return ret;
2546 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2548 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2549 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2551 if (is_64bit && package->platform == PLATFORM_INTEL &&
2552 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2554 UINT size;
2555 WCHAR *path_32node;
2557 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2558 if (!(path_32node = msi_alloc( size ))) return NULL;
2560 memcpy( path_32node, path, len * sizeof(WCHAR) );
2561 strcpyW( path_32node + len, szWow6432Node );
2562 strcatW( path_32node, szBackSlash );
2563 strcatW( path_32node, path + len );
2564 return path_32node;
2567 return strdupW( path );
2570 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2572 MSIPACKAGE *package = param;
2573 LPSTR value_data = NULL;
2574 HKEY root_key, hkey;
2575 DWORD type,size;
2576 LPWSTR deformated, uikey, keypath;
2577 LPCWSTR szRoot, component, name, key, value;
2578 MSICOMPONENT *comp;
2579 MSIRECORD * uirow;
2580 INT root;
2581 BOOL check_first = FALSE;
2582 UINT rc;
2584 msi_ui_progress( package, 2, 0, 0, 0 );
2586 component = MSI_RecordGetString(row, 6);
2587 comp = msi_get_loaded_component(package,component);
2588 if (!comp)
2589 return ERROR_SUCCESS;
2591 comp->Action = msi_get_component_action( package, comp );
2592 if (comp->Action != INSTALLSTATE_LOCAL)
2594 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2595 return ERROR_SUCCESS;
2598 name = MSI_RecordGetString(row, 4);
2599 if( MSI_RecordIsNull(row,5) && name )
2601 /* null values can have special meanings */
2602 if (name[0]=='-' && name[1] == 0)
2603 return ERROR_SUCCESS;
2604 else if ((name[0]=='+' && name[1] == 0) ||
2605 (name[0] == '*' && name[1] == 0))
2606 name = NULL;
2607 check_first = TRUE;
2610 root = MSI_RecordGetInteger(row,2);
2611 key = MSI_RecordGetString(row, 3);
2613 szRoot = get_root_key( package, root, &root_key );
2614 if (!szRoot)
2615 return ERROR_SUCCESS;
2617 deformat_string(package, key , &deformated);
2618 size = strlenW(deformated) + strlenW(szRoot) + 1;
2619 uikey = msi_alloc(size*sizeof(WCHAR));
2620 strcpyW(uikey,szRoot);
2621 strcatW(uikey,deformated);
2623 keypath = get_keypath( package, root_key, deformated );
2624 msi_free( deformated );
2625 if (RegCreateKeyW( root_key, keypath, &hkey ))
2627 ERR("Could not create key %s\n", debugstr_w(keypath));
2628 msi_free(uikey);
2629 msi_free(keypath);
2630 return ERROR_SUCCESS;
2633 value = MSI_RecordGetString(row,5);
2634 if (value)
2635 value_data = parse_value(package, value, &type, &size);
2636 else
2638 value_data = (LPSTR)strdupW(szEmpty);
2639 size = sizeof(szEmpty);
2640 type = REG_SZ;
2643 deformat_string(package, name, &deformated);
2645 if (!check_first)
2647 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2648 debugstr_w(uikey));
2649 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2651 else
2653 DWORD sz = 0;
2654 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2655 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2657 TRACE("value %s of %s checked already exists\n",
2658 debugstr_w(deformated), debugstr_w(uikey));
2660 else
2662 TRACE("Checked and setting value %s of %s\n",
2663 debugstr_w(deformated), debugstr_w(uikey));
2664 if (deformated || size)
2665 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2668 RegCloseKey(hkey);
2670 uirow = MSI_CreateRecord(3);
2671 MSI_RecordSetStringW(uirow,2,deformated);
2672 MSI_RecordSetStringW(uirow,1,uikey);
2673 if (type == REG_SZ || type == REG_EXPAND_SZ)
2674 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2675 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2676 msiobj_release( &uirow->hdr );
2678 msi_free(value_data);
2679 msi_free(deformated);
2680 msi_free(uikey);
2681 msi_free(keypath);
2683 return ERROR_SUCCESS;
2686 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2688 UINT rc;
2689 MSIQUERY * view;
2690 static const WCHAR ExecSeqQuery[] =
2691 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2692 '`','R','e','g','i','s','t','r','y','`',0 };
2694 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2695 if (rc != ERROR_SUCCESS)
2696 return ERROR_SUCCESS;
2698 /* increment progress bar each time action data is sent */
2699 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2701 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2702 msiobj_release(&view->hdr);
2703 return rc;
2706 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2708 LONG res;
2709 HKEY hkey;
2710 DWORD num_subkeys, num_values;
2712 if (delete_key)
2714 if ((res = RegDeleteTreeW( hkey_root, key )))
2716 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2718 return;
2721 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2723 if ((res = RegDeleteValueW( hkey, value )))
2725 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2727 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2728 NULL, NULL, NULL, NULL );
2729 RegCloseKey( hkey );
2730 if (!res && !num_subkeys && !num_values)
2732 TRACE("Removing empty key %s\n", debugstr_w(key));
2733 RegDeleteKeyW( hkey_root, key );
2735 return;
2737 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2741 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2743 MSIPACKAGE *package = param;
2744 LPCWSTR component, name, key_str, root_key_str;
2745 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2746 MSICOMPONENT *comp;
2747 MSIRECORD *uirow;
2748 BOOL delete_key = FALSE;
2749 HKEY hkey_root;
2750 UINT size;
2751 INT root;
2753 msi_ui_progress( package, 2, 0, 0, 0 );
2755 component = MSI_RecordGetString( row, 6 );
2756 comp = msi_get_loaded_component( package, component );
2757 if (!comp)
2758 return ERROR_SUCCESS;
2760 comp->Action = msi_get_component_action( package, comp );
2761 if (comp->Action != INSTALLSTATE_ABSENT)
2763 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2764 return ERROR_SUCCESS;
2767 name = MSI_RecordGetString( row, 4 );
2768 if (MSI_RecordIsNull( row, 5 ) && name )
2770 if (name[0] == '+' && !name[1])
2771 return ERROR_SUCCESS;
2772 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2774 delete_key = TRUE;
2775 name = NULL;
2779 root = MSI_RecordGetInteger( row, 2 );
2780 key_str = MSI_RecordGetString( row, 3 );
2782 root_key_str = get_root_key( package, root, &hkey_root );
2783 if (!root_key_str)
2784 return ERROR_SUCCESS;
2786 deformat_string( package, key_str, &deformated_key );
2787 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2788 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2789 strcpyW( ui_key_str, root_key_str );
2790 strcatW( ui_key_str, deformated_key );
2792 deformat_string( package, name, &deformated_name );
2794 keypath = get_keypath( package, hkey_root, deformated_key );
2795 msi_free( deformated_key );
2796 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2797 msi_free( keypath );
2799 uirow = MSI_CreateRecord( 2 );
2800 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2801 MSI_RecordSetStringW( uirow, 2, deformated_name );
2803 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2804 msiobj_release( &uirow->hdr );
2806 msi_free( ui_key_str );
2807 msi_free( deformated_name );
2808 return ERROR_SUCCESS;
2811 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2813 MSIPACKAGE *package = param;
2814 LPCWSTR component, name, key_str, root_key_str;
2815 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2816 MSICOMPONENT *comp;
2817 MSIRECORD *uirow;
2818 BOOL delete_key = FALSE;
2819 HKEY hkey_root;
2820 UINT size;
2821 INT root;
2823 msi_ui_progress( package, 2, 0, 0, 0 );
2825 component = MSI_RecordGetString( row, 5 );
2826 comp = msi_get_loaded_component( package, component );
2827 if (!comp)
2828 return ERROR_SUCCESS;
2830 comp->Action = msi_get_component_action( package, comp );
2831 if (comp->Action != INSTALLSTATE_LOCAL)
2833 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2834 return ERROR_SUCCESS;
2837 if ((name = MSI_RecordGetString( row, 4 )))
2839 if (name[0] == '-' && !name[1])
2841 delete_key = TRUE;
2842 name = NULL;
2846 root = MSI_RecordGetInteger( row, 2 );
2847 key_str = MSI_RecordGetString( row, 3 );
2849 root_key_str = get_root_key( package, root, &hkey_root );
2850 if (!root_key_str)
2851 return ERROR_SUCCESS;
2853 deformat_string( package, key_str, &deformated_key );
2854 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2855 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2856 strcpyW( ui_key_str, root_key_str );
2857 strcatW( ui_key_str, deformated_key );
2859 deformat_string( package, name, &deformated_name );
2861 keypath = get_keypath( package, hkey_root, deformated_key );
2862 msi_free( deformated_key );
2863 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2864 msi_free( keypath );
2866 uirow = MSI_CreateRecord( 2 );
2867 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2868 MSI_RecordSetStringW( uirow, 2, deformated_name );
2870 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2871 msiobj_release( &uirow->hdr );
2873 msi_free( ui_key_str );
2874 msi_free( deformated_name );
2875 return ERROR_SUCCESS;
2878 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2880 UINT rc;
2881 MSIQUERY *view;
2882 static const WCHAR registry_query[] =
2883 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2884 '`','R','e','g','i','s','t','r','y','`',0 };
2885 static const WCHAR remove_registry_query[] =
2886 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2887 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2889 /* increment progress bar each time action data is sent */
2890 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2892 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2893 if (rc == ERROR_SUCCESS)
2895 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2896 msiobj_release( &view->hdr );
2897 if (rc != ERROR_SUCCESS)
2898 return rc;
2901 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2902 if (rc == ERROR_SUCCESS)
2904 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2905 msiobj_release( &view->hdr );
2906 if (rc != ERROR_SUCCESS)
2907 return rc;
2910 return ERROR_SUCCESS;
2913 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2915 package->script->CurrentlyScripting = TRUE;
2917 return ERROR_SUCCESS;
2921 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2923 MSICOMPONENT *comp;
2924 DWORD progress = 0;
2925 DWORD total = 0;
2926 static const WCHAR q1[]=
2927 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2928 '`','R','e','g','i','s','t','r','y','`',0};
2929 UINT rc;
2930 MSIQUERY * view;
2931 MSIFEATURE *feature;
2932 MSIFILE *file;
2934 TRACE("InstallValidate\n");
2936 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2937 if (rc == ERROR_SUCCESS)
2939 MSI_IterateRecords( view, &progress, NULL, package );
2940 msiobj_release( &view->hdr );
2941 total += progress * REG_PROGRESS_VALUE;
2944 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2945 total += COMPONENT_PROGRESS_VALUE;
2947 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2948 total += file->FileSize;
2950 msi_ui_progress( package, 0, total, 0, 0 );
2952 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2954 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2955 debugstr_w(feature->Feature), feature->Installed,
2956 feature->ActionRequest, feature->Action);
2959 return ERROR_SUCCESS;
2962 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2964 MSIPACKAGE* package = param;
2965 LPCWSTR cond = NULL;
2966 LPCWSTR message = NULL;
2967 UINT r;
2969 static const WCHAR title[]=
2970 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2972 cond = MSI_RecordGetString(row,1);
2974 r = MSI_EvaluateConditionW(package,cond);
2975 if (r == MSICONDITION_FALSE)
2977 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2979 LPWSTR deformated;
2980 message = MSI_RecordGetString(row,2);
2981 deformat_string(package,message,&deformated);
2982 MessageBoxW(NULL,deformated,title,MB_OK);
2983 msi_free(deformated);
2986 return ERROR_INSTALL_FAILURE;
2989 return ERROR_SUCCESS;
2992 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2994 UINT rc;
2995 MSIQUERY * view = NULL;
2996 static const WCHAR ExecSeqQuery[] =
2997 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2998 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3000 TRACE("Checking launch conditions\n");
3002 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3003 if (rc != ERROR_SUCCESS)
3004 return ERROR_SUCCESS;
3006 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3007 msiobj_release(&view->hdr);
3009 return rc;
3012 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3015 if (!cmp->KeyPath)
3016 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3018 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3020 MSIRECORD * row = 0;
3021 UINT root,len;
3022 LPWSTR deformated,buffer,deformated_name;
3023 LPCWSTR key,name;
3024 static const WCHAR ExecSeqQuery[] =
3025 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3026 '`','R','e','g','i','s','t','r','y','`',' ',
3027 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3028 ' ','=',' ' ,'\'','%','s','\'',0 };
3029 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3030 static const WCHAR fmt2[]=
3031 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3033 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3034 if (!row)
3035 return NULL;
3037 root = MSI_RecordGetInteger(row,2);
3038 key = MSI_RecordGetString(row, 3);
3039 name = MSI_RecordGetString(row, 4);
3040 deformat_string(package, key , &deformated);
3041 deformat_string(package, name, &deformated_name);
3043 len = strlenW(deformated) + 6;
3044 if (deformated_name)
3045 len+=strlenW(deformated_name);
3047 buffer = msi_alloc( len *sizeof(WCHAR));
3049 if (deformated_name)
3050 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3051 else
3052 sprintfW(buffer,fmt,root,deformated);
3054 msi_free(deformated);
3055 msi_free(deformated_name);
3056 msiobj_release(&row->hdr);
3058 return buffer;
3060 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3062 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3063 return NULL;
3065 else
3067 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3069 if (file)
3070 return strdupW( file->TargetPath );
3072 return NULL;
3075 static HKEY openSharedDLLsKey(void)
3077 HKEY hkey=0;
3078 static const WCHAR path[] =
3079 {'S','o','f','t','w','a','r','e','\\',
3080 'M','i','c','r','o','s','o','f','t','\\',
3081 'W','i','n','d','o','w','s','\\',
3082 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3083 'S','h','a','r','e','d','D','L','L','s',0};
3085 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3086 return hkey;
3089 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3091 HKEY hkey;
3092 DWORD count=0;
3093 DWORD type;
3094 DWORD sz = sizeof(count);
3095 DWORD rc;
3097 hkey = openSharedDLLsKey();
3098 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3099 if (rc != ERROR_SUCCESS)
3100 count = 0;
3101 RegCloseKey(hkey);
3102 return count;
3105 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3107 HKEY hkey;
3109 hkey = openSharedDLLsKey();
3110 if (count > 0)
3111 msi_reg_set_val_dword( hkey, path, count );
3112 else
3113 RegDeleteValueW(hkey,path);
3114 RegCloseKey(hkey);
3115 return count;
3118 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3120 MSIFEATURE *feature;
3121 INT count = 0;
3122 BOOL write = FALSE;
3124 /* only refcount DLLs */
3125 if (comp->KeyPath == NULL ||
3126 comp->assembly ||
3127 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3128 comp->Attributes & msidbComponentAttributesODBCDataSource)
3129 write = FALSE;
3130 else
3132 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3133 write = (count > 0);
3135 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3136 write = TRUE;
3139 /* increment counts */
3140 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3142 ComponentList *cl;
3144 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3145 continue;
3147 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3149 if ( cl->component == comp )
3150 count++;
3154 /* decrement counts */
3155 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3157 ComponentList *cl;
3159 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3160 continue;
3162 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3164 if ( cl->component == comp )
3165 count--;
3169 /* ref count all the files in the component */
3170 if (write)
3172 MSIFILE *file;
3174 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3176 if (file->Component == comp)
3177 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3181 /* add a count for permanent */
3182 if (comp->Attributes & msidbComponentAttributesPermanent)
3183 count ++;
3185 comp->RefCount = count;
3187 if (write)
3188 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3191 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3193 WCHAR squished_pc[GUID_SIZE];
3194 WCHAR squished_cc[GUID_SIZE];
3195 UINT rc;
3196 MSICOMPONENT *comp;
3197 HKEY hkey;
3199 TRACE("\n");
3201 squash_guid(package->ProductCode,squished_pc);
3202 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3204 msi_set_sourcedir_props(package, FALSE);
3206 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3208 MSIRECORD * uirow;
3210 msi_ui_progress( package, 2, 0, 0, 0 );
3211 if (!comp->ComponentId)
3212 continue;
3214 squash_guid(comp->ComponentId,squished_cc);
3216 msi_free(comp->FullKeypath);
3217 if (comp->assembly)
3219 const WCHAR prefixW[] = {'<','\\',0};
3220 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3222 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3223 if (comp->FullKeypath)
3225 strcpyW( comp->FullKeypath, prefixW );
3226 strcatW( comp->FullKeypath, comp->assembly->display_name );
3229 else comp->FullKeypath = resolve_keypath( package, comp );
3231 ACTION_RefCountComponent( package, comp );
3233 comp->Action = msi_get_component_action( package, comp );
3234 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3235 debugstr_w(comp->Component),
3236 debugstr_w(squished_cc),
3237 debugstr_w(comp->FullKeypath),
3238 comp->RefCount,
3239 comp->Action);
3241 if (comp->Action == INSTALLSTATE_LOCAL ||
3242 comp->Action == INSTALLSTATE_SOURCE)
3244 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3245 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3246 else
3247 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3249 if (rc != ERROR_SUCCESS)
3250 continue;
3252 if (comp->Attributes & msidbComponentAttributesPermanent)
3254 static const WCHAR szPermKey[] =
3255 { '0','0','0','0','0','0','0','0','0','0','0','0',
3256 '0','0','0','0','0','0','0','0','0','0','0','0',
3257 '0','0','0','0','0','0','0','0',0 };
3259 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3262 if (comp->Action == INSTALLSTATE_LOCAL)
3263 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3264 else
3266 MSIFILE *file;
3267 MSIRECORD *row;
3268 LPWSTR ptr, ptr2;
3269 WCHAR source[MAX_PATH];
3270 WCHAR base[MAX_PATH];
3271 LPWSTR sourcepath;
3273 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3274 static const WCHAR query[] = {
3275 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3276 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3277 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3278 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3279 '`','D','i','s','k','I','d','`',0};
3281 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3282 continue;
3284 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3285 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3286 ptr2 = strrchrW(source, '\\') + 1;
3287 msiobj_release(&row->hdr);
3289 lstrcpyW(base, package->PackagePath);
3290 ptr = strrchrW(base, '\\');
3291 *(ptr + 1) = '\0';
3293 sourcepath = msi_resolve_file_source(package, file);
3294 ptr = sourcepath + lstrlenW(base);
3295 lstrcpyW(ptr2, ptr);
3296 msi_free(sourcepath);
3298 msi_reg_set_val_str(hkey, squished_pc, source);
3300 RegCloseKey(hkey);
3302 else if (comp->Action == INSTALLSTATE_ABSENT)
3304 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3305 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3306 else
3307 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3310 /* UI stuff */
3311 uirow = MSI_CreateRecord(3);
3312 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3313 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3314 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3315 msi_ui_actiondata( package, szProcessComponents, uirow );
3316 msiobj_release( &uirow->hdr );
3318 return ERROR_SUCCESS;
3321 typedef struct {
3322 CLSID clsid;
3323 LPWSTR source;
3325 LPWSTR path;
3326 ITypeLib *ptLib;
3327 } typelib_struct;
3329 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3330 LPWSTR lpszName, LONG_PTR lParam)
3332 TLIBATTR *attr;
3333 typelib_struct *tl_struct = (typelib_struct*) lParam;
3334 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3335 int sz;
3336 HRESULT res;
3338 if (!IS_INTRESOURCE(lpszName))
3340 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3341 return TRUE;
3344 sz = strlenW(tl_struct->source)+4;
3345 sz *= sizeof(WCHAR);
3347 if ((INT_PTR)lpszName == 1)
3348 tl_struct->path = strdupW(tl_struct->source);
3349 else
3351 tl_struct->path = msi_alloc(sz);
3352 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3355 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3356 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3357 if (FAILED(res))
3359 msi_free(tl_struct->path);
3360 tl_struct->path = NULL;
3362 return TRUE;
3365 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3366 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3368 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3369 return FALSE;
3372 msi_free(tl_struct->path);
3373 tl_struct->path = NULL;
3375 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3376 ITypeLib_Release(tl_struct->ptLib);
3378 return TRUE;
3381 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3383 MSIPACKAGE* package = param;
3384 LPCWSTR component;
3385 MSICOMPONENT *comp;
3386 MSIFILE *file;
3387 typelib_struct tl_struct;
3388 ITypeLib *tlib;
3389 HMODULE module;
3390 HRESULT hr;
3392 component = MSI_RecordGetString(row,3);
3393 comp = msi_get_loaded_component(package,component);
3394 if (!comp)
3395 return ERROR_SUCCESS;
3397 comp->Action = msi_get_component_action( package, comp );
3398 if (comp->Action != INSTALLSTATE_LOCAL)
3400 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3401 return ERROR_SUCCESS;
3404 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3406 TRACE("component has no key path\n");
3407 return ERROR_SUCCESS;
3409 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3411 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3412 if (module)
3414 LPCWSTR guid;
3415 guid = MSI_RecordGetString(row,1);
3416 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3417 tl_struct.source = strdupW( file->TargetPath );
3418 tl_struct.path = NULL;
3420 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3421 (LONG_PTR)&tl_struct);
3423 if (tl_struct.path)
3425 LPCWSTR helpid, help_path = NULL;
3426 HRESULT res;
3428 helpid = MSI_RecordGetString(row,6);
3430 if (helpid) help_path = msi_get_target_folder( package, helpid );
3431 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3433 if (FAILED(res))
3434 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3435 else
3436 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3438 ITypeLib_Release(tl_struct.ptLib);
3439 msi_free(tl_struct.path);
3441 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3443 FreeLibrary(module);
3444 msi_free(tl_struct.source);
3446 else
3448 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3449 if (FAILED(hr))
3451 ERR("Failed to load type library: %08x\n", hr);
3452 return ERROR_INSTALL_FAILURE;
3455 ITypeLib_Release(tlib);
3458 return ERROR_SUCCESS;
3461 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3464 * OK this is a bit confusing.. I am given a _Component key and I believe
3465 * that the file that is being registered as a type library is the "key file
3466 * of that component" which I interpret to mean "The file in the KeyPath of
3467 * that component".
3469 UINT rc;
3470 MSIQUERY * view;
3471 static const WCHAR Query[] =
3472 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3473 '`','T','y','p','e','L','i','b','`',0};
3475 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3476 if (rc != ERROR_SUCCESS)
3477 return ERROR_SUCCESS;
3479 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3480 msiobj_release(&view->hdr);
3481 return rc;
3484 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3486 MSIPACKAGE *package = param;
3487 LPCWSTR component, guid;
3488 MSICOMPONENT *comp;
3489 GUID libid;
3490 UINT version;
3491 LCID language;
3492 SYSKIND syskind;
3493 HRESULT hr;
3495 component = MSI_RecordGetString( row, 3 );
3496 comp = msi_get_loaded_component( package, component );
3497 if (!comp)
3498 return ERROR_SUCCESS;
3500 comp->Action = msi_get_component_action( package, comp );
3501 if (comp->Action != INSTALLSTATE_ABSENT)
3503 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3504 return ERROR_SUCCESS;
3506 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3508 guid = MSI_RecordGetString( row, 1 );
3509 CLSIDFromString( (LPCWSTR)guid, &libid );
3510 version = MSI_RecordGetInteger( row, 4 );
3511 language = MSI_RecordGetInteger( row, 2 );
3513 #ifdef _WIN64
3514 syskind = SYS_WIN64;
3515 #else
3516 syskind = SYS_WIN32;
3517 #endif
3519 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3520 if (FAILED(hr))
3522 WARN("Failed to unregister typelib: %08x\n", hr);
3525 return ERROR_SUCCESS;
3528 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3530 UINT rc;
3531 MSIQUERY *view;
3532 static const WCHAR query[] =
3533 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3534 '`','T','y','p','e','L','i','b','`',0};
3536 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3537 if (rc != ERROR_SUCCESS)
3538 return ERROR_SUCCESS;
3540 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3541 msiobj_release( &view->hdr );
3542 return rc;
3545 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3547 static const WCHAR szlnk[] = {'.','l','n','k',0};
3548 LPCWSTR directory, extension, link_folder;
3549 LPWSTR link_file, filename;
3551 directory = MSI_RecordGetString( row, 2 );
3552 link_folder = msi_get_target_folder( package, directory );
3554 /* may be needed because of a bug somewhere else */
3555 msi_create_full_path( link_folder );
3557 filename = msi_dup_record_field( row, 3 );
3558 msi_reduce_to_long_filename( filename );
3560 extension = strchrW( filename, '.' );
3561 if (!extension || strcmpiW( extension, szlnk ))
3563 int len = strlenW( filename );
3564 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3565 memcpy( filename + len, szlnk, sizeof(szlnk) );
3567 link_file = msi_build_directory_name( 2, link_folder, filename );
3568 msi_free( filename );
3570 return link_file;
3573 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3575 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3576 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3577 WCHAR *folder, *dest, *path;
3579 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3580 folder = msi_dup_property( package->db, szWindowsFolder );
3581 else
3583 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3584 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3585 msi_free( appdata );
3587 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3588 msi_create_full_path( dest );
3589 path = msi_build_directory_name( 2, dest, icon_name );
3590 msi_free( folder );
3591 msi_free( dest );
3592 return path;
3595 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3597 MSIPACKAGE *package = param;
3598 LPWSTR link_file, deformated, path;
3599 LPCWSTR component, target;
3600 MSICOMPONENT *comp;
3601 IShellLinkW *sl = NULL;
3602 IPersistFile *pf = NULL;
3603 HRESULT res;
3605 component = MSI_RecordGetString(row, 4);
3606 comp = msi_get_loaded_component(package, component);
3607 if (!comp)
3608 return ERROR_SUCCESS;
3610 comp->Action = msi_get_component_action( package, comp );
3611 if (comp->Action != INSTALLSTATE_LOCAL)
3613 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3614 return ERROR_SUCCESS;
3616 msi_ui_actiondata( package, szCreateShortcuts, row );
3618 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3619 &IID_IShellLinkW, (LPVOID *) &sl );
3621 if (FAILED( res ))
3623 ERR("CLSID_ShellLink not available\n");
3624 goto err;
3627 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3628 if (FAILED( res ))
3630 ERR("QueryInterface(IID_IPersistFile) failed\n");
3631 goto err;
3634 target = MSI_RecordGetString(row, 5);
3635 if (strchrW(target, '['))
3637 deformat_string(package, target, &deformated);
3638 IShellLinkW_SetPath(sl,deformated);
3639 msi_free(deformated);
3641 else
3643 FIXME("poorly handled shortcut format, advertised shortcut\n");
3644 IShellLinkW_SetPath(sl,comp->FullKeypath);
3647 if (!MSI_RecordIsNull(row,6))
3649 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3650 deformat_string(package, arguments, &deformated);
3651 IShellLinkW_SetArguments(sl,deformated);
3652 msi_free(deformated);
3655 if (!MSI_RecordIsNull(row,7))
3657 LPCWSTR description = MSI_RecordGetString(row, 7);
3658 IShellLinkW_SetDescription(sl, description);
3661 if (!MSI_RecordIsNull(row,8))
3662 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3664 if (!MSI_RecordIsNull(row,9))
3666 INT index;
3667 LPCWSTR icon = MSI_RecordGetString(row, 9);
3669 path = msi_build_icon_path(package, icon);
3670 index = MSI_RecordGetInteger(row,10);
3672 /* no value means 0 */
3673 if (index == MSI_NULL_INTEGER)
3674 index = 0;
3676 IShellLinkW_SetIconLocation(sl, path, index);
3677 msi_free(path);
3680 if (!MSI_RecordIsNull(row,11))
3681 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3683 if (!MSI_RecordIsNull(row,12))
3685 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3686 full_path = msi_get_target_folder( package, wkdir );
3687 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3689 link_file = get_link_file(package, row);
3691 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3692 IPersistFile_Save(pf, link_file, FALSE);
3693 msi_free(link_file);
3695 err:
3696 if (pf)
3697 IPersistFile_Release( pf );
3698 if (sl)
3699 IShellLinkW_Release( sl );
3701 return ERROR_SUCCESS;
3704 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3706 UINT rc;
3707 HRESULT res;
3708 MSIQUERY * view;
3709 static const WCHAR Query[] =
3710 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3711 '`','S','h','o','r','t','c','u','t','`',0};
3713 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3714 if (rc != ERROR_SUCCESS)
3715 return ERROR_SUCCESS;
3717 res = CoInitialize( NULL );
3719 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3720 msiobj_release(&view->hdr);
3722 if (SUCCEEDED(res))
3723 CoUninitialize();
3725 return rc;
3728 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3730 MSIPACKAGE *package = param;
3731 LPWSTR link_file;
3732 LPCWSTR component;
3733 MSICOMPONENT *comp;
3735 component = MSI_RecordGetString( row, 4 );
3736 comp = msi_get_loaded_component( package, component );
3737 if (!comp)
3738 return ERROR_SUCCESS;
3740 comp->Action = msi_get_component_action( package, comp );
3741 if (comp->Action != INSTALLSTATE_ABSENT)
3743 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3744 return ERROR_SUCCESS;
3746 msi_ui_actiondata( package, szRemoveShortcuts, row );
3748 link_file = get_link_file( package, row );
3750 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3751 if (!DeleteFileW( link_file ))
3753 WARN("Failed to remove shortcut file %u\n", GetLastError());
3755 msi_free( link_file );
3757 return ERROR_SUCCESS;
3760 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3762 UINT rc;
3763 MSIQUERY *view;
3764 static const WCHAR query[] =
3765 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3766 '`','S','h','o','r','t','c','u','t','`',0};
3768 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3769 if (rc != ERROR_SUCCESS)
3770 return ERROR_SUCCESS;
3772 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3773 msiobj_release( &view->hdr );
3775 return rc;
3778 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3780 MSIPACKAGE* package = param;
3781 HANDLE the_file;
3782 LPWSTR FilePath;
3783 LPCWSTR FileName;
3784 CHAR buffer[1024];
3785 DWORD sz;
3786 UINT rc;
3788 FileName = MSI_RecordGetString(row,1);
3789 if (!FileName)
3791 ERR("Unable to get FileName\n");
3792 return ERROR_SUCCESS;
3795 FilePath = msi_build_icon_path(package, FileName);
3797 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3799 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3800 FILE_ATTRIBUTE_NORMAL, NULL);
3802 if (the_file == INVALID_HANDLE_VALUE)
3804 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3805 msi_free(FilePath);
3806 return ERROR_SUCCESS;
3811 DWORD write;
3812 sz = 1024;
3813 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3814 if (rc != ERROR_SUCCESS)
3816 ERR("Failed to get stream\n");
3817 CloseHandle(the_file);
3818 DeleteFileW(FilePath);
3819 break;
3821 WriteFile(the_file,buffer,sz,&write,NULL);
3822 } while (sz == 1024);
3824 msi_free(FilePath);
3825 CloseHandle(the_file);
3827 return ERROR_SUCCESS;
3830 static UINT msi_publish_icons(MSIPACKAGE *package)
3832 UINT r;
3833 MSIQUERY *view;
3835 static const WCHAR query[]= {
3836 'S','E','L','E','C','T',' ','*',' ',
3837 'F','R','O','M',' ','`','I','c','o','n','`',0};
3839 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3840 if (r == ERROR_SUCCESS)
3842 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3843 msiobj_release(&view->hdr);
3846 return ERROR_SUCCESS;
3849 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3851 UINT r;
3852 HKEY source;
3853 LPWSTR buffer;
3854 MSIMEDIADISK *disk;
3855 MSISOURCELISTINFO *info;
3857 r = RegCreateKeyW(hkey, szSourceList, &source);
3858 if (r != ERROR_SUCCESS)
3859 return r;
3861 RegCloseKey(source);
3863 buffer = strrchrW(package->PackagePath, '\\') + 1;
3864 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3865 package->Context, MSICODE_PRODUCT,
3866 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3867 if (r != ERROR_SUCCESS)
3868 return r;
3870 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3871 package->Context, MSICODE_PRODUCT,
3872 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3873 if (r != ERROR_SUCCESS)
3874 return r;
3876 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3877 package->Context, MSICODE_PRODUCT,
3878 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3879 if (r != ERROR_SUCCESS)
3880 return r;
3882 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3884 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3885 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3886 info->options, info->value);
3887 else
3888 MsiSourceListSetInfoW(package->ProductCode, NULL,
3889 info->context, info->options,
3890 info->property, info->value);
3893 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3895 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3896 disk->context, disk->options,
3897 disk->disk_id, disk->volume_label, disk->disk_prompt);
3900 return ERROR_SUCCESS;
3903 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3905 MSIHANDLE hdb, suminfo;
3906 WCHAR guids[MAX_PATH];
3907 WCHAR packcode[SQUISH_GUID_SIZE];
3908 LPWSTR buffer;
3909 LPWSTR ptr;
3910 DWORD langid;
3911 DWORD size;
3912 UINT r;
3914 static const WCHAR szARPProductIcon[] =
3915 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3916 static const WCHAR szAssignment[] =
3917 {'A','s','s','i','g','n','m','e','n','t',0};
3918 static const WCHAR szAdvertiseFlags[] =
3919 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3920 static const WCHAR szClients[] =
3921 {'C','l','i','e','n','t','s',0};
3922 static const WCHAR szColon[] = {':',0};
3924 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3925 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3926 msi_free(buffer);
3928 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3929 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3931 /* FIXME */
3932 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3934 buffer = msi_dup_property(package->db, szARPProductIcon);
3935 if (buffer)
3937 LPWSTR path = msi_build_icon_path(package, buffer);
3938 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3939 msi_free(path);
3940 msi_free(buffer);
3943 buffer = msi_dup_property(package->db, szProductVersion);
3944 if (buffer)
3946 DWORD verdword = msi_version_str_to_dword(buffer);
3947 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3948 msi_free(buffer);
3951 msi_reg_set_val_dword(hkey, szAssignment, 0);
3952 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3953 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3954 msi_reg_set_val_str(hkey, szClients, szColon);
3956 hdb = alloc_msihandle(&package->db->hdr);
3957 if (!hdb)
3958 return ERROR_NOT_ENOUGH_MEMORY;
3960 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3961 MsiCloseHandle(hdb);
3962 if (r != ERROR_SUCCESS)
3963 goto done;
3965 size = MAX_PATH;
3966 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3967 NULL, guids, &size);
3968 if (r != ERROR_SUCCESS)
3969 goto done;
3971 ptr = strchrW(guids, ';');
3972 if (ptr) *ptr = 0;
3973 squash_guid(guids, packcode);
3974 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3976 done:
3977 MsiCloseHandle(suminfo);
3978 return ERROR_SUCCESS;
3981 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3983 UINT r;
3984 HKEY hkey;
3985 LPWSTR upgrade;
3986 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3988 upgrade = msi_dup_property(package->db, szUpgradeCode);
3989 if (!upgrade)
3990 return ERROR_SUCCESS;
3992 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3993 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3994 else
3995 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3997 if (r != ERROR_SUCCESS)
3999 WARN("failed to open upgrade code key\n");
4000 msi_free(upgrade);
4001 return ERROR_SUCCESS;
4003 squash_guid(package->ProductCode, squashed_pc);
4004 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4005 RegCloseKey(hkey);
4006 msi_free(upgrade);
4007 return ERROR_SUCCESS;
4010 static BOOL msi_check_publish(MSIPACKAGE *package)
4012 MSIFEATURE *feature;
4014 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4016 feature->Action = msi_get_feature_action( package, feature );
4017 if (feature->Action == INSTALLSTATE_LOCAL)
4018 return TRUE;
4021 return FALSE;
4024 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4026 MSIFEATURE *feature;
4028 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4030 feature->Action = msi_get_feature_action( package, feature );
4031 if (feature->Action != INSTALLSTATE_ABSENT)
4032 return FALSE;
4035 return TRUE;
4038 static UINT msi_publish_patches( MSIPACKAGE *package )
4040 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4041 WCHAR patch_squashed[GUID_SIZE];
4042 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4043 LONG res;
4044 MSIPATCHINFO *patch;
4045 UINT r;
4046 WCHAR *p, *all_patches = NULL;
4047 DWORD len = 0;
4049 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4050 if (r != ERROR_SUCCESS)
4051 return ERROR_FUNCTION_FAILED;
4053 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4054 if (res != ERROR_SUCCESS)
4056 r = ERROR_FUNCTION_FAILED;
4057 goto done;
4060 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4061 if (r != ERROR_SUCCESS)
4062 goto done;
4064 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4066 squash_guid( patch->patchcode, patch_squashed );
4067 len += strlenW( patch_squashed ) + 1;
4070 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4071 if (!all_patches)
4072 goto done;
4074 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4076 HKEY patch_key;
4078 squash_guid( patch->patchcode, p );
4079 p += strlenW( p ) + 1;
4081 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4082 (const BYTE *)patch->transforms,
4083 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4084 if (res != ERROR_SUCCESS)
4085 goto done;
4087 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4088 if (r != ERROR_SUCCESS)
4089 goto done;
4091 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4092 (const BYTE *)patch->localfile,
4093 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4094 RegCloseKey( patch_key );
4095 if (res != ERROR_SUCCESS)
4096 goto done;
4098 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4099 if (res != ERROR_SUCCESS)
4100 goto done;
4102 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4103 RegCloseKey( patch_key );
4104 if (res != ERROR_SUCCESS)
4105 goto done;
4108 all_patches[len] = 0;
4109 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4110 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4111 if (res != ERROR_SUCCESS)
4112 goto done;
4114 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4115 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4116 if (res != ERROR_SUCCESS)
4117 r = ERROR_FUNCTION_FAILED;
4119 done:
4120 RegCloseKey( product_patches_key );
4121 RegCloseKey( patches_key );
4122 RegCloseKey( product_key );
4123 msi_free( all_patches );
4124 return r;
4127 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4129 UINT rc;
4130 HKEY hukey = NULL, hudkey = NULL;
4131 MSIRECORD *uirow;
4133 if (!list_empty(&package->patches))
4135 rc = msi_publish_patches(package);
4136 if (rc != ERROR_SUCCESS)
4137 goto end;
4140 /* FIXME: also need to publish if the product is in advertise mode */
4141 if (!msi_check_publish(package))
4142 return ERROR_SUCCESS;
4144 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4145 &hukey, TRUE);
4146 if (rc != ERROR_SUCCESS)
4147 goto end;
4149 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4150 NULL, &hudkey, TRUE);
4151 if (rc != ERROR_SUCCESS)
4152 goto end;
4154 rc = msi_publish_upgrade_code(package);
4155 if (rc != ERROR_SUCCESS)
4156 goto end;
4158 rc = msi_publish_product_properties(package, hukey);
4159 if (rc != ERROR_SUCCESS)
4160 goto end;
4162 rc = msi_publish_sourcelist(package, hukey);
4163 if (rc != ERROR_SUCCESS)
4164 goto end;
4166 rc = msi_publish_icons(package);
4168 end:
4169 uirow = MSI_CreateRecord( 1 );
4170 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4171 msi_ui_actiondata( package, szPublishProduct, uirow );
4172 msiobj_release( &uirow->hdr );
4174 RegCloseKey(hukey);
4175 RegCloseKey(hudkey);
4177 return rc;
4180 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4182 WCHAR *filename, *ptr, *folder, *ret;
4183 const WCHAR *dirprop;
4185 filename = msi_dup_record_field( row, 2 );
4186 if (filename && (ptr = strchrW( filename, '|' )))
4187 ptr++;
4188 else
4189 ptr = filename;
4191 dirprop = MSI_RecordGetString( row, 3 );
4192 if (dirprop)
4194 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4195 if (!folder) folder = msi_dup_property( package->db, dirprop );
4197 else
4198 folder = msi_dup_property( package->db, szWindowsFolder );
4200 if (!folder)
4202 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4203 msi_free( filename );
4204 return NULL;
4207 ret = msi_build_directory_name( 2, folder, ptr );
4209 msi_free( filename );
4210 msi_free( folder );
4211 return ret;
4214 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4216 MSIPACKAGE *package = param;
4217 LPCWSTR component, section, key, value, identifier;
4218 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4219 MSIRECORD * uirow;
4220 INT action;
4221 MSICOMPONENT *comp;
4223 component = MSI_RecordGetString(row, 8);
4224 comp = msi_get_loaded_component(package,component);
4225 if (!comp)
4226 return ERROR_SUCCESS;
4228 comp->Action = msi_get_component_action( package, comp );
4229 if (comp->Action != INSTALLSTATE_LOCAL)
4231 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4232 return ERROR_SUCCESS;
4235 identifier = MSI_RecordGetString(row,1);
4236 section = MSI_RecordGetString(row,4);
4237 key = MSI_RecordGetString(row,5);
4238 value = MSI_RecordGetString(row,6);
4239 action = MSI_RecordGetInteger(row,7);
4241 deformat_string(package,section,&deformated_section);
4242 deformat_string(package,key,&deformated_key);
4243 deformat_string(package,value,&deformated_value);
4245 fullname = get_ini_file_name(package, row);
4247 if (action == 0)
4249 TRACE("Adding value %s to section %s in %s\n",
4250 debugstr_w(deformated_key), debugstr_w(deformated_section),
4251 debugstr_w(fullname));
4252 WritePrivateProfileStringW(deformated_section, deformated_key,
4253 deformated_value, fullname);
4255 else if (action == 1)
4257 WCHAR returned[10];
4258 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4259 returned, 10, fullname);
4260 if (returned[0] == 0)
4262 TRACE("Adding value %s to section %s in %s\n",
4263 debugstr_w(deformated_key), debugstr_w(deformated_section),
4264 debugstr_w(fullname));
4266 WritePrivateProfileStringW(deformated_section, deformated_key,
4267 deformated_value, fullname);
4270 else if (action == 3)
4271 FIXME("Append to existing section not yet implemented\n");
4273 uirow = MSI_CreateRecord(4);
4274 MSI_RecordSetStringW(uirow,1,identifier);
4275 MSI_RecordSetStringW(uirow,2,deformated_section);
4276 MSI_RecordSetStringW(uirow,3,deformated_key);
4277 MSI_RecordSetStringW(uirow,4,deformated_value);
4278 msi_ui_actiondata( package, szWriteIniValues, uirow );
4279 msiobj_release( &uirow->hdr );
4281 msi_free(fullname);
4282 msi_free(deformated_key);
4283 msi_free(deformated_value);
4284 msi_free(deformated_section);
4285 return ERROR_SUCCESS;
4288 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4290 UINT rc;
4291 MSIQUERY * view;
4292 static const WCHAR ExecSeqQuery[] =
4293 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4294 '`','I','n','i','F','i','l','e','`',0};
4296 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4297 if (rc != ERROR_SUCCESS)
4299 TRACE("no IniFile table\n");
4300 return ERROR_SUCCESS;
4303 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4304 msiobj_release(&view->hdr);
4305 return rc;
4308 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4310 MSIPACKAGE *package = param;
4311 LPCWSTR component, section, key, value, identifier;
4312 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4313 MSICOMPONENT *comp;
4314 MSIRECORD *uirow;
4315 INT action;
4317 component = MSI_RecordGetString( row, 8 );
4318 comp = msi_get_loaded_component( package, component );
4319 if (!comp)
4320 return ERROR_SUCCESS;
4322 comp->Action = msi_get_component_action( package, comp );
4323 if (comp->Action != INSTALLSTATE_ABSENT)
4325 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4326 return ERROR_SUCCESS;
4329 identifier = MSI_RecordGetString( row, 1 );
4330 section = MSI_RecordGetString( row, 4 );
4331 key = MSI_RecordGetString( row, 5 );
4332 value = MSI_RecordGetString( row, 6 );
4333 action = MSI_RecordGetInteger( row, 7 );
4335 deformat_string( package, section, &deformated_section );
4336 deformat_string( package, key, &deformated_key );
4337 deformat_string( package, value, &deformated_value );
4339 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4341 filename = get_ini_file_name( package, row );
4343 TRACE("Removing key %s from section %s in %s\n",
4344 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4346 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4348 WARN("Unable to remove key %u\n", GetLastError());
4350 msi_free( filename );
4352 else
4353 FIXME("Unsupported action %d\n", action);
4356 uirow = MSI_CreateRecord( 4 );
4357 MSI_RecordSetStringW( uirow, 1, identifier );
4358 MSI_RecordSetStringW( uirow, 2, deformated_section );
4359 MSI_RecordSetStringW( uirow, 3, deformated_key );
4360 MSI_RecordSetStringW( uirow, 4, deformated_value );
4361 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4362 msiobj_release( &uirow->hdr );
4364 msi_free( deformated_key );
4365 msi_free( deformated_value );
4366 msi_free( deformated_section );
4367 return ERROR_SUCCESS;
4370 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4372 MSIPACKAGE *package = param;
4373 LPCWSTR component, section, key, value, identifier;
4374 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4375 MSICOMPONENT *comp;
4376 MSIRECORD *uirow;
4377 INT action;
4379 component = MSI_RecordGetString( row, 8 );
4380 comp = msi_get_loaded_component( package, component );
4381 if (!comp)
4382 return ERROR_SUCCESS;
4384 comp->Action = msi_get_component_action( package, comp );
4385 if (comp->Action != INSTALLSTATE_LOCAL)
4387 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4388 return ERROR_SUCCESS;
4391 identifier = MSI_RecordGetString( row, 1 );
4392 section = MSI_RecordGetString( row, 4 );
4393 key = MSI_RecordGetString( row, 5 );
4394 value = MSI_RecordGetString( row, 6 );
4395 action = MSI_RecordGetInteger( row, 7 );
4397 deformat_string( package, section, &deformated_section );
4398 deformat_string( package, key, &deformated_key );
4399 deformat_string( package, value, &deformated_value );
4401 if (action == msidbIniFileActionRemoveLine)
4403 filename = get_ini_file_name( package, row );
4405 TRACE("Removing key %s from section %s in %s\n",
4406 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4408 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4410 WARN("Unable to remove key %u\n", GetLastError());
4412 msi_free( filename );
4414 else
4415 FIXME("Unsupported action %d\n", action);
4417 uirow = MSI_CreateRecord( 4 );
4418 MSI_RecordSetStringW( uirow, 1, identifier );
4419 MSI_RecordSetStringW( uirow, 2, deformated_section );
4420 MSI_RecordSetStringW( uirow, 3, deformated_key );
4421 MSI_RecordSetStringW( uirow, 4, deformated_value );
4422 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4423 msiobj_release( &uirow->hdr );
4425 msi_free( deformated_key );
4426 msi_free( deformated_value );
4427 msi_free( deformated_section );
4428 return ERROR_SUCCESS;
4431 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4433 UINT rc;
4434 MSIQUERY *view;
4435 static const WCHAR query[] =
4436 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4437 '`','I','n','i','F','i','l','e','`',0};
4438 static const WCHAR remove_query[] =
4439 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4440 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4442 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4443 if (rc == ERROR_SUCCESS)
4445 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4446 msiobj_release( &view->hdr );
4447 if (rc != ERROR_SUCCESS)
4448 return rc;
4451 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4452 if (rc == ERROR_SUCCESS)
4454 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4455 msiobj_release( &view->hdr );
4456 if (rc != ERROR_SUCCESS)
4457 return rc;
4460 return ERROR_SUCCESS;
4463 static void register_dll( const WCHAR *dll, BOOL unregister )
4465 HMODULE hmod;
4467 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4468 if (hmod)
4470 HRESULT (WINAPI *func_ptr)( void );
4471 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4473 func_ptr = (void *)GetProcAddress( hmod, func );
4474 if (func_ptr)
4476 HRESULT hr = func_ptr();
4477 if (FAILED( hr ))
4478 WARN("failed to register dll 0x%08x\n", hr);
4480 else
4481 WARN("entry point %s not found\n", func);
4482 FreeLibrary( hmod );
4483 return;
4485 WARN("failed to load library %u\n", GetLastError());
4488 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4490 MSIPACKAGE *package = param;
4491 LPCWSTR filename;
4492 MSIFILE *file;
4493 MSIRECORD *uirow;
4495 filename = MSI_RecordGetString(row,1);
4496 file = msi_get_loaded_file( package, filename );
4497 if (!file)
4499 WARN("unable to find file %s\n", debugstr_w(filename));
4500 return ERROR_SUCCESS;
4502 file->Component->Action = msi_get_component_action( package, file->Component );
4503 if (file->Component->Action != INSTALLSTATE_LOCAL)
4505 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4506 return ERROR_SUCCESS;
4509 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4510 register_dll( file->TargetPath, FALSE );
4512 uirow = MSI_CreateRecord( 2 );
4513 MSI_RecordSetStringW( uirow, 1, filename );
4514 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4515 msi_ui_actiondata( package, szSelfRegModules, uirow );
4516 msiobj_release( &uirow->hdr );
4518 return ERROR_SUCCESS;
4521 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4523 UINT rc;
4524 MSIQUERY * view;
4525 static const WCHAR ExecSeqQuery[] =
4526 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4527 '`','S','e','l','f','R','e','g','`',0};
4529 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4530 if (rc != ERROR_SUCCESS)
4532 TRACE("no SelfReg table\n");
4533 return ERROR_SUCCESS;
4536 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4537 msiobj_release(&view->hdr);
4539 return ERROR_SUCCESS;
4542 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4544 MSIPACKAGE *package = param;
4545 LPCWSTR filename;
4546 MSIFILE *file;
4547 MSIRECORD *uirow;
4549 filename = MSI_RecordGetString( row, 1 );
4550 file = msi_get_loaded_file( package, filename );
4551 if (!file)
4553 WARN("unable to find file %s\n", debugstr_w(filename));
4554 return ERROR_SUCCESS;
4556 file->Component->Action = msi_get_component_action( package, file->Component );
4557 if (file->Component->Action != INSTALLSTATE_ABSENT)
4559 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4560 return ERROR_SUCCESS;
4563 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4564 register_dll( file->TargetPath, TRUE );
4566 uirow = MSI_CreateRecord( 2 );
4567 MSI_RecordSetStringW( uirow, 1, filename );
4568 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4569 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4570 msiobj_release( &uirow->hdr );
4572 return ERROR_SUCCESS;
4575 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4577 UINT rc;
4578 MSIQUERY *view;
4579 static const WCHAR query[] =
4580 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4581 '`','S','e','l','f','R','e','g','`',0};
4583 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4584 if (rc != ERROR_SUCCESS)
4586 TRACE("no SelfReg table\n");
4587 return ERROR_SUCCESS;
4590 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4591 msiobj_release( &view->hdr );
4593 return ERROR_SUCCESS;
4596 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4598 MSIFEATURE *feature;
4599 UINT rc;
4600 HKEY hkey = NULL, userdata = NULL;
4602 if (!msi_check_publish(package))
4603 return ERROR_SUCCESS;
4605 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4606 &hkey, TRUE);
4607 if (rc != ERROR_SUCCESS)
4608 goto end;
4610 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4611 &userdata, TRUE);
4612 if (rc != ERROR_SUCCESS)
4613 goto end;
4615 /* here the guids are base 85 encoded */
4616 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4618 ComponentList *cl;
4619 LPWSTR data = NULL;
4620 GUID clsid;
4621 INT size;
4622 BOOL absent = FALSE;
4623 MSIRECORD *uirow;
4625 if (feature->Action != INSTALLSTATE_LOCAL &&
4626 feature->Action != INSTALLSTATE_SOURCE &&
4627 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4629 size = 1;
4630 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4632 size += 21;
4634 if (feature->Feature_Parent)
4635 size += strlenW( feature->Feature_Parent )+2;
4637 data = msi_alloc(size * sizeof(WCHAR));
4639 data[0] = 0;
4640 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4642 MSICOMPONENT* component = cl->component;
4643 WCHAR buf[21];
4645 buf[0] = 0;
4646 if (component->ComponentId)
4648 TRACE("From %s\n",debugstr_w(component->ComponentId));
4649 CLSIDFromString(component->ComponentId, &clsid);
4650 encode_base85_guid(&clsid,buf);
4651 TRACE("to %s\n",debugstr_w(buf));
4652 strcatW(data,buf);
4656 if (feature->Feature_Parent)
4658 static const WCHAR sep[] = {'\2',0};
4659 strcatW(data,sep);
4660 strcatW(data,feature->Feature_Parent);
4663 msi_reg_set_val_str( userdata, feature->Feature, data );
4664 msi_free(data);
4666 size = 0;
4667 if (feature->Feature_Parent)
4668 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4669 if (!absent)
4671 size += sizeof(WCHAR);
4672 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4673 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4675 else
4677 size += 2*sizeof(WCHAR);
4678 data = msi_alloc(size);
4679 data[0] = 0x6;
4680 data[1] = 0;
4681 if (feature->Feature_Parent)
4682 strcpyW( &data[1], feature->Feature_Parent );
4683 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4684 (LPBYTE)data,size);
4685 msi_free(data);
4688 /* the UI chunk */
4689 uirow = MSI_CreateRecord( 1 );
4690 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4691 msi_ui_actiondata( package, szPublishFeatures, uirow );
4692 msiobj_release( &uirow->hdr );
4693 /* FIXME: call msi_ui_progress? */
4696 end:
4697 RegCloseKey(hkey);
4698 RegCloseKey(userdata);
4699 return rc;
4702 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4704 UINT r;
4705 HKEY hkey;
4706 MSIRECORD *uirow;
4708 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4710 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4711 &hkey, FALSE);
4712 if (r == ERROR_SUCCESS)
4714 RegDeleteValueW(hkey, feature->Feature);
4715 RegCloseKey(hkey);
4718 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4719 &hkey, FALSE);
4720 if (r == ERROR_SUCCESS)
4722 RegDeleteValueW(hkey, feature->Feature);
4723 RegCloseKey(hkey);
4726 uirow = MSI_CreateRecord( 1 );
4727 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4728 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4729 msiobj_release( &uirow->hdr );
4731 return ERROR_SUCCESS;
4734 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4736 MSIFEATURE *feature;
4738 if (!msi_check_unpublish(package))
4739 return ERROR_SUCCESS;
4741 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4743 msi_unpublish_feature(package, feature);
4746 return ERROR_SUCCESS;
4749 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4751 SYSTEMTIME systime;
4752 DWORD size, langid;
4753 WCHAR date[9], *val, *buffer;
4754 const WCHAR *prop, *key;
4756 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4757 static const WCHAR modpath_fmt[] =
4758 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4759 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4760 static const WCHAR szModifyPath[] =
4761 {'M','o','d','i','f','y','P','a','t','h',0};
4762 static const WCHAR szUninstallString[] =
4763 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4764 static const WCHAR szEstimatedSize[] =
4765 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4766 static const WCHAR szDisplayVersion[] =
4767 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4768 static const WCHAR szInstallSource[] =
4769 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4770 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4771 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4772 static const WCHAR szAuthorizedCDFPrefix[] =
4773 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4774 static const WCHAR szARPCONTACT[] =
4775 {'A','R','P','C','O','N','T','A','C','T',0};
4776 static const WCHAR szContact[] =
4777 {'C','o','n','t','a','c','t',0};
4778 static const WCHAR szARPCOMMENTS[] =
4779 {'A','R','P','C','O','M','M','E','N','T','S',0};
4780 static const WCHAR szComments[] =
4781 {'C','o','m','m','e','n','t','s',0};
4782 static const WCHAR szProductName[] =
4783 {'P','r','o','d','u','c','t','N','a','m','e',0};
4784 static const WCHAR szDisplayName[] =
4785 {'D','i','s','p','l','a','y','N','a','m','e',0};
4786 static const WCHAR szARPHELPLINK[] =
4787 {'A','R','P','H','E','L','P','L','I','N','K',0};
4788 static const WCHAR szHelpLink[] =
4789 {'H','e','l','p','L','i','n','k',0};
4790 static const WCHAR szARPHELPTELEPHONE[] =
4791 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4792 static const WCHAR szHelpTelephone[] =
4793 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4794 static const WCHAR szARPINSTALLLOCATION[] =
4795 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4796 static const WCHAR szInstallLocation[] =
4797 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4798 static const WCHAR szManufacturer[] =
4799 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4800 static const WCHAR szPublisher[] =
4801 {'P','u','b','l','i','s','h','e','r',0};
4802 static const WCHAR szARPREADME[] =
4803 {'A','R','P','R','E','A','D','M','E',0};
4804 static const WCHAR szReadme[] =
4805 {'R','e','a','d','M','e',0};
4806 static const WCHAR szARPSIZE[] =
4807 {'A','R','P','S','I','Z','E',0};
4808 static const WCHAR szSize[] =
4809 {'S','i','z','e',0};
4810 static const WCHAR szARPURLINFOABOUT[] =
4811 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4812 static const WCHAR szURLInfoAbout[] =
4813 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4814 static const WCHAR szARPURLUPDATEINFO[] =
4815 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4816 static const WCHAR szURLUpdateInfo[] =
4817 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4818 static const WCHAR szARPSYSTEMCOMPONENT[] =
4819 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4820 static const WCHAR szSystemComponent[] =
4821 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4823 static const WCHAR *propval[] = {
4824 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4825 szARPCONTACT, szContact,
4826 szARPCOMMENTS, szComments,
4827 szProductName, szDisplayName,
4828 szARPHELPLINK, szHelpLink,
4829 szARPHELPTELEPHONE, szHelpTelephone,
4830 szARPINSTALLLOCATION, szInstallLocation,
4831 szSourceDir, szInstallSource,
4832 szManufacturer, szPublisher,
4833 szARPREADME, szReadme,
4834 szARPSIZE, szSize,
4835 szARPURLINFOABOUT, szURLInfoAbout,
4836 szARPURLUPDATEINFO, szURLUpdateInfo,
4837 NULL
4839 const WCHAR **p = propval;
4841 while (*p)
4843 prop = *p++;
4844 key = *p++;
4845 val = msi_dup_property(package->db, prop);
4846 msi_reg_set_val_str(hkey, key, val);
4847 msi_free(val);
4850 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4851 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4853 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4855 size = deformat_string(package, modpath_fmt, &buffer);
4856 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4857 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4858 msi_free(buffer);
4860 /* FIXME: Write real Estimated Size when we have it */
4861 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4863 GetLocalTime(&systime);
4864 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4865 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4867 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4868 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4870 buffer = msi_dup_property(package->db, szProductVersion);
4871 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4872 if (buffer)
4874 DWORD verdword = msi_version_str_to_dword(buffer);
4876 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4877 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4878 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4879 msi_free(buffer);
4882 return ERROR_SUCCESS;
4885 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4887 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4888 MSIRECORD *uirow;
4889 LPWSTR upgrade_code;
4890 HKEY hkey, props, upgrade_key;
4891 UINT rc;
4893 /* FIXME: also need to publish if the product is in advertise mode */
4894 if (!msi_check_publish(package))
4895 return ERROR_SUCCESS;
4897 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4898 if (rc != ERROR_SUCCESS)
4899 return rc;
4901 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4902 NULL, &props, TRUE);
4903 if (rc != ERROR_SUCCESS)
4904 goto done;
4906 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4907 msi_free( package->db->localfile );
4908 package->db->localfile = NULL;
4910 rc = msi_publish_install_properties(package, hkey);
4911 if (rc != ERROR_SUCCESS)
4912 goto done;
4914 rc = msi_publish_install_properties(package, props);
4915 if (rc != ERROR_SUCCESS)
4916 goto done;
4918 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4919 if (upgrade_code)
4921 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4922 if (rc == ERROR_SUCCESS)
4924 squash_guid( package->ProductCode, squashed_pc );
4925 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4926 RegCloseKey( upgrade_key );
4928 msi_free( upgrade_code );
4931 done:
4932 uirow = MSI_CreateRecord( 1 );
4933 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4934 msi_ui_actiondata( package, szRegisterProduct, uirow );
4935 msiobj_release( &uirow->hdr );
4937 RegCloseKey(hkey);
4938 return ERROR_SUCCESS;
4941 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4943 return execute_script(package,INSTALL_SCRIPT);
4946 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4948 WCHAR *upgrade, **features;
4949 BOOL full_uninstall = TRUE;
4950 MSIFEATURE *feature;
4951 MSIPATCHINFO *patch;
4953 static const WCHAR szUpgradeCode[] =
4954 {'U','p','g','r','a','d','e','C','o','d','e',0};
4956 features = msi_split_string(remove, ',');
4957 if (!features)
4959 ERR("REMOVE feature list is empty!\n");
4960 return ERROR_FUNCTION_FAILED;
4963 if (!strcmpW( features[0], szAll ))
4964 full_uninstall = TRUE;
4965 else
4967 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4969 if (feature->Action != INSTALLSTATE_ABSENT)
4970 full_uninstall = FALSE;
4973 msi_free(features);
4975 if (!full_uninstall)
4976 return ERROR_SUCCESS;
4978 MSIREG_DeleteProductKey(package->ProductCode);
4979 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4980 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4982 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4983 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4984 MSIREG_DeleteUserProductKey(package->ProductCode);
4985 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4987 upgrade = msi_dup_property(package->db, szUpgradeCode);
4988 if (upgrade)
4990 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4991 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4992 msi_free(upgrade);
4995 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4997 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5000 return ERROR_SUCCESS;
5003 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5005 UINT rc;
5006 WCHAR *remove;
5008 /* turn off scheduling */
5009 package->script->CurrentlyScripting= FALSE;
5011 /* first do the same as an InstallExecute */
5012 rc = ACTION_InstallExecute(package);
5013 if (rc != ERROR_SUCCESS)
5014 return rc;
5016 /* then handle Commit Actions */
5017 rc = execute_script(package,COMMIT_SCRIPT);
5018 if (rc != ERROR_SUCCESS)
5019 return rc;
5021 remove = msi_dup_property(package->db, szRemove);
5022 if (remove)
5023 rc = msi_unpublish_product(package, remove);
5025 msi_free(remove);
5026 return rc;
5029 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5031 static const WCHAR RunOnce[] = {
5032 'S','o','f','t','w','a','r','e','\\',
5033 'M','i','c','r','o','s','o','f','t','\\',
5034 'W','i','n','d','o','w','s','\\',
5035 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5036 'R','u','n','O','n','c','e',0};
5037 static const WCHAR InstallRunOnce[] = {
5038 'S','o','f','t','w','a','r','e','\\',
5039 'M','i','c','r','o','s','o','f','t','\\',
5040 'W','i','n','d','o','w','s','\\',
5041 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5042 'I','n','s','t','a','l','l','e','r','\\',
5043 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5045 static const WCHAR msiexec_fmt[] = {
5046 '%','s',
5047 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5048 '\"','%','s','\"',0};
5049 static const WCHAR install_fmt[] = {
5050 '/','I',' ','\"','%','s','\"',' ',
5051 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5052 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5053 WCHAR buffer[256], sysdir[MAX_PATH];
5054 HKEY hkey;
5055 WCHAR squished_pc[100];
5057 squash_guid(package->ProductCode,squished_pc);
5059 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5060 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5061 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5062 squished_pc);
5064 msi_reg_set_val_str( hkey, squished_pc, buffer );
5065 RegCloseKey(hkey);
5067 TRACE("Reboot command %s\n",debugstr_w(buffer));
5069 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5070 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5072 msi_reg_set_val_str( hkey, squished_pc, buffer );
5073 RegCloseKey(hkey);
5075 return ERROR_INSTALL_SUSPEND;
5078 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5080 static const WCHAR query[] =
5081 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5082 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5083 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5084 MSIRECORD *rec, *row;
5085 DWORD i, size = 0;
5086 va_list va;
5087 const WCHAR *str;
5088 WCHAR *data;
5090 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5092 rec = MSI_CreateRecord( count + 2 );
5093 str = MSI_RecordGetString( row, 1 );
5094 MSI_RecordSetStringW( rec, 0, str );
5095 msiobj_release( &row->hdr );
5096 MSI_RecordSetInteger( rec, 1, error );
5098 va_start( va, count );
5099 for (i = 0; i < count; i++)
5101 str = va_arg( va, const WCHAR *);
5102 MSI_RecordSetStringW( rec, i + 2, str );
5104 va_end( va );
5106 MSI_FormatRecordW( package, rec, NULL, &size );
5107 size++;
5108 data = msi_alloc( size * sizeof(WCHAR) );
5109 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5110 else data[0] = 0;
5111 msiobj_release( &rec->hdr );
5112 return data;
5115 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5117 DWORD attrib;
5118 UINT rc;
5121 * We are currently doing what should be done here in the top level Install
5122 * however for Administrative and uninstalls this step will be needed
5124 if (!package->PackagePath)
5125 return ERROR_SUCCESS;
5127 msi_set_sourcedir_props(package, TRUE);
5129 attrib = GetFileAttributesW(package->db->path);
5130 if (attrib == INVALID_FILE_ATTRIBUTES)
5132 LPWSTR prompt;
5133 LPWSTR msg;
5134 DWORD size = 0;
5136 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5137 package->Context, MSICODE_PRODUCT,
5138 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5139 if (rc == ERROR_MORE_DATA)
5141 prompt = msi_alloc(size * sizeof(WCHAR));
5142 MsiSourceListGetInfoW(package->ProductCode, NULL,
5143 package->Context, MSICODE_PRODUCT,
5144 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5146 else
5147 prompt = strdupW(package->db->path);
5149 msg = msi_build_error_string(package, 1302, 1, prompt);
5150 while(attrib == INVALID_FILE_ATTRIBUTES)
5152 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5153 if (rc == IDCANCEL)
5155 rc = ERROR_INSTALL_USEREXIT;
5156 break;
5158 attrib = GetFileAttributesW(package->db->path);
5160 msi_free(prompt);
5161 rc = ERROR_SUCCESS;
5163 else
5164 return ERROR_SUCCESS;
5166 return rc;
5169 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5171 HKEY hkey = 0;
5172 LPWSTR buffer, productid = NULL;
5173 UINT i, rc = ERROR_SUCCESS;
5174 MSIRECORD *uirow;
5176 static const WCHAR szPropKeys[][80] =
5178 {'P','r','o','d','u','c','t','I','D',0},
5179 {'U','S','E','R','N','A','M','E',0},
5180 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5181 {0},
5184 static const WCHAR szRegKeys[][80] =
5186 {'P','r','o','d','u','c','t','I','D',0},
5187 {'R','e','g','O','w','n','e','r',0},
5188 {'R','e','g','C','o','m','p','a','n','y',0},
5189 {0},
5192 if (msi_check_unpublish(package))
5194 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5195 goto end;
5198 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5199 if (!productid)
5200 goto end;
5202 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5203 NULL, &hkey, TRUE);
5204 if (rc != ERROR_SUCCESS)
5205 goto end;
5207 for( i = 0; szPropKeys[i][0]; i++ )
5209 buffer = msi_dup_property( package->db, szPropKeys[i] );
5210 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5211 msi_free( buffer );
5214 end:
5215 uirow = MSI_CreateRecord( 1 );
5216 MSI_RecordSetStringW( uirow, 1, productid );
5217 msi_ui_actiondata( package, szRegisterUser, uirow );
5218 msiobj_release( &uirow->hdr );
5220 msi_free(productid);
5221 RegCloseKey(hkey);
5222 return rc;
5226 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5228 UINT rc;
5230 package->script->InWhatSequence |= SEQUENCE_EXEC;
5231 rc = ACTION_ProcessExecSequence(package,FALSE);
5232 return rc;
5235 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5237 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5238 WCHAR productid_85[21], component_85[21], *ret;
5239 GUID clsid;
5240 DWORD sz;
5242 /* > is used if there is a component GUID and < if not. */
5244 productid_85[0] = 0;
5245 component_85[0] = 0;
5246 CLSIDFromString( package->ProductCode, &clsid );
5248 encode_base85_guid( &clsid, productid_85 );
5249 if (component)
5251 CLSIDFromString( component->ComponentId, &clsid );
5252 encode_base85_guid( &clsid, component_85 );
5255 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5256 debugstr_w(component_85));
5258 sz = 20 + strlenW( feature ) + 20 + 3;
5259 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5260 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5261 return ret;
5264 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5266 MSIPACKAGE *package = param;
5267 LPCWSTR compgroupid, component, feature, qualifier, text;
5268 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5269 HKEY hkey = NULL;
5270 UINT rc;
5271 MSICOMPONENT *comp;
5272 MSIFEATURE *feat;
5273 DWORD sz;
5274 MSIRECORD *uirow;
5275 int len;
5277 feature = MSI_RecordGetString(rec, 5);
5278 feat = msi_get_loaded_feature(package, feature);
5279 if (!feat)
5280 return ERROR_SUCCESS;
5282 feat->Action = msi_get_feature_action( package, feat );
5283 if (feat->Action != INSTALLSTATE_LOCAL &&
5284 feat->Action != INSTALLSTATE_SOURCE &&
5285 feat->Action != INSTALLSTATE_ADVERTISED)
5287 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5288 return ERROR_SUCCESS;
5291 component = MSI_RecordGetString(rec, 3);
5292 comp = msi_get_loaded_component(package, component);
5293 if (!comp)
5294 return ERROR_SUCCESS;
5296 compgroupid = MSI_RecordGetString(rec,1);
5297 qualifier = MSI_RecordGetString(rec,2);
5299 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5300 if (rc != ERROR_SUCCESS)
5301 goto end;
5303 advertise = msi_create_component_advertise_string( package, comp, feature );
5304 text = MSI_RecordGetString( rec, 4 );
5305 if (text)
5307 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5308 strcpyW( p, advertise );
5309 strcatW( p, text );
5310 msi_free( advertise );
5311 advertise = p;
5313 existing = msi_reg_get_val_str( hkey, qualifier );
5315 sz = strlenW( advertise ) + 1;
5316 if (existing)
5318 for (p = existing; *p; p += len)
5320 len = strlenW( p ) + 1;
5321 if (strcmpW( advertise, p )) sz += len;
5324 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5326 rc = ERROR_OUTOFMEMORY;
5327 goto end;
5329 q = output;
5330 if (existing)
5332 for (p = existing; *p; p += len)
5334 len = strlenW( p ) + 1;
5335 if (strcmpW( advertise, p ))
5337 memcpy( q, p, len * sizeof(WCHAR) );
5338 q += len;
5342 strcpyW( q, advertise );
5343 q[strlenW( q ) + 1] = 0;
5345 msi_reg_set_val_multi_str( hkey, qualifier, output );
5347 end:
5348 RegCloseKey(hkey);
5349 msi_free( output );
5350 msi_free( advertise );
5351 msi_free( existing );
5353 /* the UI chunk */
5354 uirow = MSI_CreateRecord( 2 );
5355 MSI_RecordSetStringW( uirow, 1, compgroupid );
5356 MSI_RecordSetStringW( uirow, 2, qualifier);
5357 msi_ui_actiondata( package, szPublishComponents, uirow );
5358 msiobj_release( &uirow->hdr );
5359 /* FIXME: call ui_progress? */
5361 return rc;
5365 * At present I am ignorning the advertised components part of this and only
5366 * focusing on the qualified component sets
5368 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5370 UINT rc;
5371 MSIQUERY * view;
5372 static const WCHAR ExecSeqQuery[] =
5373 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5374 '`','P','u','b','l','i','s','h',
5375 'C','o','m','p','o','n','e','n','t','`',0};
5377 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5378 if (rc != ERROR_SUCCESS)
5379 return ERROR_SUCCESS;
5381 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5382 msiobj_release(&view->hdr);
5384 return rc;
5387 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5389 static const WCHAR szInstallerComponents[] = {
5390 'S','o','f','t','w','a','r','e','\\',
5391 'M','i','c','r','o','s','o','f','t','\\',
5392 'I','n','s','t','a','l','l','e','r','\\',
5393 'C','o','m','p','o','n','e','n','t','s','\\',0};
5395 MSIPACKAGE *package = param;
5396 LPCWSTR compgroupid, component, feature, qualifier;
5397 MSICOMPONENT *comp;
5398 MSIFEATURE *feat;
5399 MSIRECORD *uirow;
5400 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5401 LONG res;
5403 feature = MSI_RecordGetString( rec, 5 );
5404 feat = msi_get_loaded_feature( package, feature );
5405 if (!feat)
5406 return ERROR_SUCCESS;
5408 feat->Action = msi_get_feature_action( package, feat );
5409 if (feat->Action != INSTALLSTATE_ABSENT)
5411 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5412 return ERROR_SUCCESS;
5415 component = MSI_RecordGetString( rec, 3 );
5416 comp = msi_get_loaded_component( package, component );
5417 if (!comp)
5418 return ERROR_SUCCESS;
5420 compgroupid = MSI_RecordGetString( rec, 1 );
5421 qualifier = MSI_RecordGetString( rec, 2 );
5423 squash_guid( compgroupid, squashed );
5424 strcpyW( keypath, szInstallerComponents );
5425 strcatW( keypath, squashed );
5427 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5428 if (res != ERROR_SUCCESS)
5430 WARN("Unable to delete component key %d\n", res);
5433 uirow = MSI_CreateRecord( 2 );
5434 MSI_RecordSetStringW( uirow, 1, compgroupid );
5435 MSI_RecordSetStringW( uirow, 2, qualifier );
5436 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5437 msiobj_release( &uirow->hdr );
5439 return ERROR_SUCCESS;
5442 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5444 UINT rc;
5445 MSIQUERY *view;
5446 static const WCHAR query[] =
5447 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5448 '`','P','u','b','l','i','s','h',
5449 'C','o','m','p','o','n','e','n','t','`',0};
5451 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5452 if (rc != ERROR_SUCCESS)
5453 return ERROR_SUCCESS;
5455 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5456 msiobj_release( &view->hdr );
5458 return rc;
5461 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5463 static const WCHAR query[] =
5464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5465 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5466 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5467 MSIPACKAGE *package = param;
5468 MSICOMPONENT *component;
5469 MSIRECORD *row;
5470 MSIFILE *file;
5471 SC_HANDLE hscm = NULL, service = NULL;
5472 LPCWSTR comp, key;
5473 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5474 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5475 DWORD serv_type, start_type, err_control;
5476 SERVICE_DESCRIPTIONW sd = {NULL};
5478 comp = MSI_RecordGetString( rec, 12 );
5479 component = msi_get_loaded_component( package, comp );
5480 if (!component)
5482 WARN("service component not found\n");
5483 goto done;
5485 component->Action = msi_get_component_action( package, component );
5486 if (component->Action != INSTALLSTATE_LOCAL)
5488 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5489 goto done;
5491 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5492 if (!hscm)
5494 ERR("Failed to open the SC Manager!\n");
5495 goto done;
5498 start_type = MSI_RecordGetInteger(rec, 5);
5499 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5500 goto done;
5502 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5503 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5504 serv_type = MSI_RecordGetInteger(rec, 4);
5505 err_control = MSI_RecordGetInteger(rec, 6);
5506 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5507 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5508 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5509 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5510 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5511 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5513 /* fetch the service path */
5514 row = MSI_QueryGetRecord(package->db, query, comp);
5515 if (!row)
5517 ERR("Query failed\n");
5518 goto done;
5520 key = MSI_RecordGetString(row, 6);
5521 file = msi_get_loaded_file(package, key);
5522 msiobj_release(&row->hdr);
5523 if (!file)
5525 ERR("Failed to load the service file\n");
5526 goto done;
5529 if (!args || !args[0]) image_path = file->TargetPath;
5530 else
5532 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5533 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5534 return ERROR_OUTOFMEMORY;
5536 strcpyW(image_path, file->TargetPath);
5537 strcatW(image_path, szSpace);
5538 strcatW(image_path, args);
5540 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5541 start_type, err_control, image_path, load_order,
5542 NULL, depends, serv_name, pass);
5544 if (!service)
5546 if (GetLastError() != ERROR_SERVICE_EXISTS)
5547 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5549 else if (sd.lpDescription)
5551 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5552 WARN("failed to set service description %u\n", GetLastError());
5555 if (image_path != file->TargetPath) msi_free(image_path);
5556 done:
5557 CloseServiceHandle(service);
5558 CloseServiceHandle(hscm);
5559 msi_free(name);
5560 msi_free(disp);
5561 msi_free(sd.lpDescription);
5562 msi_free(load_order);
5563 msi_free(serv_name);
5564 msi_free(pass);
5565 msi_free(depends);
5566 msi_free(args);
5568 return ERROR_SUCCESS;
5571 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5573 UINT rc;
5574 MSIQUERY * view;
5575 static const WCHAR ExecSeqQuery[] =
5576 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5577 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5579 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5580 if (rc != ERROR_SUCCESS)
5581 return ERROR_SUCCESS;
5583 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5584 msiobj_release(&view->hdr);
5586 return rc;
5589 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5590 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5592 LPCWSTR *vector, *temp_vector;
5593 LPWSTR p, q;
5594 DWORD sep_len;
5596 static const WCHAR separator[] = {'[','~',']',0};
5598 *numargs = 0;
5599 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5601 if (!args)
5602 return NULL;
5604 vector = msi_alloc(sizeof(LPWSTR));
5605 if (!vector)
5606 return NULL;
5608 p = args;
5611 (*numargs)++;
5612 vector[*numargs - 1] = p;
5614 if ((q = strstrW(p, separator)))
5616 *q = '\0';
5618 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5619 if (!temp_vector)
5621 msi_free(vector);
5622 return NULL;
5624 vector = temp_vector;
5626 p = q + sep_len;
5628 } while (q);
5630 return vector;
5633 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5635 MSIPACKAGE *package = param;
5636 MSICOMPONENT *comp;
5637 MSIRECORD *uirow;
5638 SC_HANDLE scm = NULL, service = NULL;
5639 LPCWSTR component, *vector = NULL;
5640 LPWSTR name, args, display_name = NULL;
5641 DWORD event, numargs, len;
5642 UINT r = ERROR_FUNCTION_FAILED;
5644 component = MSI_RecordGetString(rec, 6);
5645 comp = msi_get_loaded_component(package, component);
5646 if (!comp)
5647 return ERROR_SUCCESS;
5649 comp->Action = msi_get_component_action( package, comp );
5650 if (comp->Action != INSTALLSTATE_LOCAL)
5652 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5653 return ERROR_SUCCESS;
5656 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5657 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5658 event = MSI_RecordGetInteger(rec, 3);
5660 if (!(event & msidbServiceControlEventStart))
5662 r = ERROR_SUCCESS;
5663 goto done;
5666 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5667 if (!scm)
5669 ERR("Failed to open the service control manager\n");
5670 goto done;
5673 len = 0;
5674 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5675 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5677 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5678 GetServiceDisplayNameW( scm, name, display_name, &len );
5681 service = OpenServiceW(scm, name, SERVICE_START);
5682 if (!service)
5684 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5685 goto done;
5688 vector = msi_service_args_to_vector(args, &numargs);
5690 if (!StartServiceW(service, numargs, vector) &&
5691 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5693 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5694 goto done;
5697 r = ERROR_SUCCESS;
5699 done:
5700 uirow = MSI_CreateRecord( 2 );
5701 MSI_RecordSetStringW( uirow, 1, display_name );
5702 MSI_RecordSetStringW( uirow, 2, name );
5703 msi_ui_actiondata( package, szStartServices, uirow );
5704 msiobj_release( &uirow->hdr );
5706 CloseServiceHandle(service);
5707 CloseServiceHandle(scm);
5709 msi_free(name);
5710 msi_free(args);
5711 msi_free(vector);
5712 msi_free(display_name);
5713 return r;
5716 static UINT ACTION_StartServices( MSIPACKAGE *package )
5718 UINT rc;
5719 MSIQUERY *view;
5721 static const WCHAR query[] = {
5722 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5723 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5725 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5726 if (rc != ERROR_SUCCESS)
5727 return ERROR_SUCCESS;
5729 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5730 msiobj_release(&view->hdr);
5732 return rc;
5735 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5737 DWORD i, needed, count;
5738 ENUM_SERVICE_STATUSW *dependencies;
5739 SERVICE_STATUS ss;
5740 SC_HANDLE depserv;
5742 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5743 0, &needed, &count))
5744 return TRUE;
5746 if (GetLastError() != ERROR_MORE_DATA)
5747 return FALSE;
5749 dependencies = msi_alloc(needed);
5750 if (!dependencies)
5751 return FALSE;
5753 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5754 needed, &needed, &count))
5755 goto error;
5757 for (i = 0; i < count; i++)
5759 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5760 SERVICE_STOP | SERVICE_QUERY_STATUS);
5761 if (!depserv)
5762 goto error;
5764 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5765 goto error;
5768 return TRUE;
5770 error:
5771 msi_free(dependencies);
5772 return FALSE;
5775 static UINT stop_service( LPCWSTR name )
5777 SC_HANDLE scm = NULL, service = NULL;
5778 SERVICE_STATUS status;
5779 SERVICE_STATUS_PROCESS ssp;
5780 DWORD needed;
5782 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5783 if (!scm)
5785 WARN("Failed to open the SCM: %d\n", GetLastError());
5786 goto done;
5789 service = OpenServiceW(scm, name,
5790 SERVICE_STOP |
5791 SERVICE_QUERY_STATUS |
5792 SERVICE_ENUMERATE_DEPENDENTS);
5793 if (!service)
5795 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5796 goto done;
5799 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5800 sizeof(SERVICE_STATUS_PROCESS), &needed))
5802 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5803 goto done;
5806 if (ssp.dwCurrentState == SERVICE_STOPPED)
5807 goto done;
5809 stop_service_dependents(scm, service);
5811 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5812 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5814 done:
5815 CloseServiceHandle(service);
5816 CloseServiceHandle(scm);
5818 return ERROR_SUCCESS;
5821 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5823 MSIPACKAGE *package = param;
5824 MSICOMPONENT *comp;
5825 MSIRECORD *uirow;
5826 LPCWSTR component;
5827 LPWSTR name = NULL, display_name = NULL;
5828 DWORD event, len;
5829 SC_HANDLE scm;
5831 event = MSI_RecordGetInteger( rec, 3 );
5832 if (!(event & msidbServiceControlEventStop))
5833 return ERROR_SUCCESS;
5835 component = MSI_RecordGetString( rec, 6 );
5836 comp = msi_get_loaded_component( package, component );
5837 if (!comp)
5838 return ERROR_SUCCESS;
5840 comp->Action = msi_get_component_action( package, comp );
5841 if (comp->Action != INSTALLSTATE_ABSENT)
5843 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5844 return ERROR_SUCCESS;
5847 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5848 if (!scm)
5850 ERR("Failed to open the service control manager\n");
5851 goto done;
5854 len = 0;
5855 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5856 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5858 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5859 GetServiceDisplayNameW( scm, name, display_name, &len );
5861 CloseServiceHandle( scm );
5863 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5864 stop_service( name );
5866 done:
5867 uirow = MSI_CreateRecord( 2 );
5868 MSI_RecordSetStringW( uirow, 1, display_name );
5869 MSI_RecordSetStringW( uirow, 2, name );
5870 msi_ui_actiondata( package, szStopServices, uirow );
5871 msiobj_release( &uirow->hdr );
5873 msi_free( name );
5874 msi_free( display_name );
5875 return ERROR_SUCCESS;
5878 static UINT ACTION_StopServices( MSIPACKAGE *package )
5880 UINT rc;
5881 MSIQUERY *view;
5883 static const WCHAR query[] = {
5884 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5885 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5887 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5888 if (rc != ERROR_SUCCESS)
5889 return ERROR_SUCCESS;
5891 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5892 msiobj_release(&view->hdr);
5894 return rc;
5897 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5899 MSIPACKAGE *package = param;
5900 MSICOMPONENT *comp;
5901 MSIRECORD *uirow;
5902 LPCWSTR component;
5903 LPWSTR name = NULL, display_name = NULL;
5904 DWORD event, len;
5905 SC_HANDLE scm = NULL, service = NULL;
5907 event = MSI_RecordGetInteger( rec, 3 );
5908 if (!(event & msidbServiceControlEventDelete))
5909 return ERROR_SUCCESS;
5911 component = MSI_RecordGetString(rec, 6);
5912 comp = msi_get_loaded_component(package, component);
5913 if (!comp)
5914 return ERROR_SUCCESS;
5916 comp->Action = msi_get_component_action( package, comp );
5917 if (comp->Action != INSTALLSTATE_ABSENT)
5919 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5920 return ERROR_SUCCESS;
5923 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5924 stop_service( name );
5926 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5927 if (!scm)
5929 WARN("Failed to open the SCM: %d\n", GetLastError());
5930 goto done;
5933 len = 0;
5934 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5935 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5937 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5938 GetServiceDisplayNameW( scm, name, display_name, &len );
5941 service = OpenServiceW( scm, name, DELETE );
5942 if (!service)
5944 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5945 goto done;
5948 if (!DeleteService( service ))
5949 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5951 done:
5952 uirow = MSI_CreateRecord( 2 );
5953 MSI_RecordSetStringW( uirow, 1, display_name );
5954 MSI_RecordSetStringW( uirow, 2, name );
5955 msi_ui_actiondata( package, szDeleteServices, uirow );
5956 msiobj_release( &uirow->hdr );
5958 CloseServiceHandle( service );
5959 CloseServiceHandle( scm );
5960 msi_free( name );
5961 msi_free( display_name );
5963 return ERROR_SUCCESS;
5966 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5968 UINT rc;
5969 MSIQUERY *view;
5971 static const WCHAR query[] = {
5972 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5973 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5975 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5976 if (rc != ERROR_SUCCESS)
5977 return ERROR_SUCCESS;
5979 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5980 msiobj_release( &view->hdr );
5982 return rc;
5985 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5987 MSIPACKAGE *package = param;
5988 LPWSTR driver, driver_path, ptr;
5989 WCHAR outpath[MAX_PATH];
5990 MSIFILE *driver_file = NULL, *setup_file = NULL;
5991 MSICOMPONENT *comp;
5992 MSIRECORD *uirow;
5993 LPCWSTR desc, file_key, component;
5994 DWORD len, usage;
5995 UINT r = ERROR_SUCCESS;
5997 static const WCHAR driver_fmt[] = {
5998 'D','r','i','v','e','r','=','%','s',0};
5999 static const WCHAR setup_fmt[] = {
6000 'S','e','t','u','p','=','%','s',0};
6001 static const WCHAR usage_fmt[] = {
6002 'F','i','l','e','U','s','a','g','e','=','1',0};
6004 component = MSI_RecordGetString( rec, 2 );
6005 comp = msi_get_loaded_component( package, component );
6006 if (!comp)
6007 return ERROR_SUCCESS;
6009 comp->Action = msi_get_component_action( package, comp );
6010 if (comp->Action != INSTALLSTATE_LOCAL)
6012 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6013 return ERROR_SUCCESS;
6015 desc = MSI_RecordGetString(rec, 3);
6017 file_key = MSI_RecordGetString( rec, 4 );
6018 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6020 file_key = MSI_RecordGetString( rec, 5 );
6021 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6023 if (!driver_file)
6025 ERR("ODBC Driver entry not found!\n");
6026 return ERROR_FUNCTION_FAILED;
6029 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6030 if (setup_file)
6031 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6032 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6034 driver = msi_alloc(len * sizeof(WCHAR));
6035 if (!driver)
6036 return ERROR_OUTOFMEMORY;
6038 ptr = driver;
6039 lstrcpyW(ptr, desc);
6040 ptr += lstrlenW(ptr) + 1;
6042 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6043 ptr += len + 1;
6045 if (setup_file)
6047 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6048 ptr += len + 1;
6051 lstrcpyW(ptr, usage_fmt);
6052 ptr += lstrlenW(ptr) + 1;
6053 *ptr = '\0';
6055 driver_path = strdupW(driver_file->TargetPath);
6056 ptr = strrchrW(driver_path, '\\');
6057 if (ptr) *ptr = '\0';
6059 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6060 NULL, ODBC_INSTALL_COMPLETE, &usage))
6062 ERR("Failed to install SQL driver!\n");
6063 r = ERROR_FUNCTION_FAILED;
6066 uirow = MSI_CreateRecord( 5 );
6067 MSI_RecordSetStringW( uirow, 1, desc );
6068 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6069 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6070 msi_ui_actiondata( package, szInstallODBC, uirow );
6071 msiobj_release( &uirow->hdr );
6073 msi_free(driver);
6074 msi_free(driver_path);
6076 return r;
6079 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6081 MSIPACKAGE *package = param;
6082 LPWSTR translator, translator_path, ptr;
6083 WCHAR outpath[MAX_PATH];
6084 MSIFILE *translator_file = NULL, *setup_file = NULL;
6085 MSICOMPONENT *comp;
6086 MSIRECORD *uirow;
6087 LPCWSTR desc, file_key, component;
6088 DWORD len, usage;
6089 UINT r = ERROR_SUCCESS;
6091 static const WCHAR translator_fmt[] = {
6092 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6093 static const WCHAR setup_fmt[] = {
6094 'S','e','t','u','p','=','%','s',0};
6096 component = MSI_RecordGetString( rec, 2 );
6097 comp = msi_get_loaded_component( package, component );
6098 if (!comp)
6099 return ERROR_SUCCESS;
6101 comp->Action = msi_get_component_action( package, comp );
6102 if (comp->Action != INSTALLSTATE_LOCAL)
6104 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6105 return ERROR_SUCCESS;
6107 desc = MSI_RecordGetString(rec, 3);
6109 file_key = MSI_RecordGetString( rec, 4 );
6110 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6112 file_key = MSI_RecordGetString( rec, 5 );
6113 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6115 if (!translator_file)
6117 ERR("ODBC Translator entry not found!\n");
6118 return ERROR_FUNCTION_FAILED;
6121 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6122 if (setup_file)
6123 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6125 translator = msi_alloc(len * sizeof(WCHAR));
6126 if (!translator)
6127 return ERROR_OUTOFMEMORY;
6129 ptr = translator;
6130 lstrcpyW(ptr, desc);
6131 ptr += lstrlenW(ptr) + 1;
6133 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6134 ptr += len + 1;
6136 if (setup_file)
6138 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6139 ptr += len + 1;
6141 *ptr = '\0';
6143 translator_path = strdupW(translator_file->TargetPath);
6144 ptr = strrchrW(translator_path, '\\');
6145 if (ptr) *ptr = '\0';
6147 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6148 NULL, ODBC_INSTALL_COMPLETE, &usage))
6150 ERR("Failed to install SQL translator!\n");
6151 r = ERROR_FUNCTION_FAILED;
6154 uirow = MSI_CreateRecord( 5 );
6155 MSI_RecordSetStringW( uirow, 1, desc );
6156 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6157 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6158 msi_ui_actiondata( package, szInstallODBC, uirow );
6159 msiobj_release( &uirow->hdr );
6161 msi_free(translator);
6162 msi_free(translator_path);
6164 return r;
6167 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6169 MSIPACKAGE *package = param;
6170 MSICOMPONENT *comp;
6171 LPWSTR attrs;
6172 LPCWSTR desc, driver, component;
6173 WORD request = ODBC_ADD_SYS_DSN;
6174 INT registration;
6175 DWORD len;
6176 UINT r = ERROR_SUCCESS;
6177 MSIRECORD *uirow;
6179 static const WCHAR attrs_fmt[] = {
6180 'D','S','N','=','%','s',0 };
6182 component = MSI_RecordGetString( rec, 2 );
6183 comp = msi_get_loaded_component( package, component );
6184 if (!comp)
6185 return ERROR_SUCCESS;
6187 comp->Action = msi_get_component_action( package, comp );
6188 if (comp->Action != INSTALLSTATE_LOCAL)
6190 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6191 return ERROR_SUCCESS;
6194 desc = MSI_RecordGetString(rec, 3);
6195 driver = MSI_RecordGetString(rec, 4);
6196 registration = MSI_RecordGetInteger(rec, 5);
6198 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6199 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6201 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6202 attrs = msi_alloc(len * sizeof(WCHAR));
6203 if (!attrs)
6204 return ERROR_OUTOFMEMORY;
6206 len = sprintfW(attrs, attrs_fmt, desc);
6207 attrs[len + 1] = 0;
6209 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6211 ERR("Failed to install SQL data source!\n");
6212 r = ERROR_FUNCTION_FAILED;
6215 uirow = MSI_CreateRecord( 5 );
6216 MSI_RecordSetStringW( uirow, 1, desc );
6217 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6218 MSI_RecordSetInteger( uirow, 3, request );
6219 msi_ui_actiondata( package, szInstallODBC, uirow );
6220 msiobj_release( &uirow->hdr );
6222 msi_free(attrs);
6224 return r;
6227 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6229 UINT rc;
6230 MSIQUERY *view;
6232 static const WCHAR driver_query[] = {
6233 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6234 'O','D','B','C','D','r','i','v','e','r',0 };
6236 static const WCHAR translator_query[] = {
6237 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6238 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6240 static const WCHAR source_query[] = {
6241 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6242 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6244 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6245 if (rc != ERROR_SUCCESS)
6246 return ERROR_SUCCESS;
6248 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6249 msiobj_release(&view->hdr);
6251 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6252 if (rc != ERROR_SUCCESS)
6253 return ERROR_SUCCESS;
6255 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6256 msiobj_release(&view->hdr);
6258 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6259 if (rc != ERROR_SUCCESS)
6260 return ERROR_SUCCESS;
6262 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6263 msiobj_release(&view->hdr);
6265 return rc;
6268 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6270 MSIPACKAGE *package = param;
6271 MSICOMPONENT *comp;
6272 MSIRECORD *uirow;
6273 DWORD usage;
6274 LPCWSTR desc, component;
6276 component = MSI_RecordGetString( rec, 2 );
6277 comp = msi_get_loaded_component( package, component );
6278 if (!comp)
6279 return ERROR_SUCCESS;
6281 comp->Action = msi_get_component_action( package, comp );
6282 if (comp->Action != INSTALLSTATE_ABSENT)
6284 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6285 return ERROR_SUCCESS;
6288 desc = MSI_RecordGetString( rec, 3 );
6289 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6291 WARN("Failed to remove ODBC driver\n");
6293 else if (!usage)
6295 FIXME("Usage count reached 0\n");
6298 uirow = MSI_CreateRecord( 2 );
6299 MSI_RecordSetStringW( uirow, 1, desc );
6300 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6301 msi_ui_actiondata( package, szRemoveODBC, uirow );
6302 msiobj_release( &uirow->hdr );
6304 return ERROR_SUCCESS;
6307 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6309 MSIPACKAGE *package = param;
6310 MSICOMPONENT *comp;
6311 MSIRECORD *uirow;
6312 DWORD usage;
6313 LPCWSTR desc, component;
6315 component = MSI_RecordGetString( rec, 2 );
6316 comp = msi_get_loaded_component( package, component );
6317 if (!comp)
6318 return ERROR_SUCCESS;
6320 comp->Action = msi_get_component_action( package, comp );
6321 if (comp->Action != INSTALLSTATE_ABSENT)
6323 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6324 return ERROR_SUCCESS;
6327 desc = MSI_RecordGetString( rec, 3 );
6328 if (!SQLRemoveTranslatorW( desc, &usage ))
6330 WARN("Failed to remove ODBC translator\n");
6332 else if (!usage)
6334 FIXME("Usage count reached 0\n");
6337 uirow = MSI_CreateRecord( 2 );
6338 MSI_RecordSetStringW( uirow, 1, desc );
6339 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6340 msi_ui_actiondata( package, szRemoveODBC, uirow );
6341 msiobj_release( &uirow->hdr );
6343 return ERROR_SUCCESS;
6346 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6348 MSIPACKAGE *package = param;
6349 MSICOMPONENT *comp;
6350 MSIRECORD *uirow;
6351 LPWSTR attrs;
6352 LPCWSTR desc, driver, component;
6353 WORD request = ODBC_REMOVE_SYS_DSN;
6354 INT registration;
6355 DWORD len;
6357 static const WCHAR attrs_fmt[] = {
6358 'D','S','N','=','%','s',0 };
6360 component = MSI_RecordGetString( rec, 2 );
6361 comp = msi_get_loaded_component( package, component );
6362 if (!comp)
6363 return ERROR_SUCCESS;
6365 comp->Action = msi_get_component_action( package, comp );
6366 if (comp->Action != INSTALLSTATE_ABSENT)
6368 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6369 return ERROR_SUCCESS;
6372 desc = MSI_RecordGetString( rec, 3 );
6373 driver = MSI_RecordGetString( rec, 4 );
6374 registration = MSI_RecordGetInteger( rec, 5 );
6376 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6377 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6379 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6380 attrs = msi_alloc( len * sizeof(WCHAR) );
6381 if (!attrs)
6382 return ERROR_OUTOFMEMORY;
6384 FIXME("Use ODBCSourceAttribute table\n");
6386 len = sprintfW( attrs, attrs_fmt, desc );
6387 attrs[len + 1] = 0;
6389 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6391 WARN("Failed to remove ODBC data source\n");
6393 msi_free( attrs );
6395 uirow = MSI_CreateRecord( 3 );
6396 MSI_RecordSetStringW( uirow, 1, desc );
6397 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6398 MSI_RecordSetInteger( uirow, 3, request );
6399 msi_ui_actiondata( package, szRemoveODBC, uirow );
6400 msiobj_release( &uirow->hdr );
6402 return ERROR_SUCCESS;
6405 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6407 UINT rc;
6408 MSIQUERY *view;
6410 static const WCHAR driver_query[] = {
6411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6412 'O','D','B','C','D','r','i','v','e','r',0 };
6414 static const WCHAR translator_query[] = {
6415 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6416 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6418 static const WCHAR source_query[] = {
6419 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6420 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6422 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6423 if (rc != ERROR_SUCCESS)
6424 return ERROR_SUCCESS;
6426 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6427 msiobj_release( &view->hdr );
6429 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6430 if (rc != ERROR_SUCCESS)
6431 return ERROR_SUCCESS;
6433 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6434 msiobj_release( &view->hdr );
6436 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6437 if (rc != ERROR_SUCCESS)
6438 return ERROR_SUCCESS;
6440 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6441 msiobj_release( &view->hdr );
6443 return rc;
6446 #define ENV_ACT_SETALWAYS 0x1
6447 #define ENV_ACT_SETABSENT 0x2
6448 #define ENV_ACT_REMOVE 0x4
6449 #define ENV_ACT_REMOVEMATCH 0x8
6451 #define ENV_MOD_MACHINE 0x20000000
6452 #define ENV_MOD_APPEND 0x40000000
6453 #define ENV_MOD_PREFIX 0x80000000
6454 #define ENV_MOD_MASK 0xC0000000
6456 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6458 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6460 LPCWSTR cptr = *name;
6462 static const WCHAR prefix[] = {'[','~',']',0};
6463 static const int prefix_len = 3;
6465 *flags = 0;
6466 while (*cptr)
6468 if (*cptr == '=')
6469 *flags |= ENV_ACT_SETALWAYS;
6470 else if (*cptr == '+')
6471 *flags |= ENV_ACT_SETABSENT;
6472 else if (*cptr == '-')
6473 *flags |= ENV_ACT_REMOVE;
6474 else if (*cptr == '!')
6475 *flags |= ENV_ACT_REMOVEMATCH;
6476 else if (*cptr == '*')
6477 *flags |= ENV_MOD_MACHINE;
6478 else
6479 break;
6481 cptr++;
6482 (*name)++;
6485 if (!*cptr)
6487 ERR("Missing environment variable\n");
6488 return ERROR_FUNCTION_FAILED;
6491 if (*value)
6493 LPCWSTR ptr = *value;
6494 if (!strncmpW(ptr, prefix, prefix_len))
6496 if (ptr[prefix_len] == szSemiColon[0])
6498 *flags |= ENV_MOD_APPEND;
6499 *value += lstrlenW(prefix);
6501 else
6503 *value = NULL;
6506 else if (lstrlenW(*value) >= prefix_len)
6508 ptr += lstrlenW(ptr) - prefix_len;
6509 if (!strcmpW( ptr, prefix ))
6511 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6513 *flags |= ENV_MOD_PREFIX;
6514 /* the "[~]" will be removed by deformat_string */;
6516 else
6518 *value = NULL;
6524 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6525 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6526 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6527 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6529 ERR("Invalid flags: %08x\n", *flags);
6530 return ERROR_FUNCTION_FAILED;
6533 if (!*flags)
6534 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6536 return ERROR_SUCCESS;
6539 static UINT open_env_key( DWORD flags, HKEY *key )
6541 static const WCHAR user_env[] =
6542 {'E','n','v','i','r','o','n','m','e','n','t',0};
6543 static const WCHAR machine_env[] =
6544 {'S','y','s','t','e','m','\\',
6545 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6546 'C','o','n','t','r','o','l','\\',
6547 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6548 'E','n','v','i','r','o','n','m','e','n','t',0};
6549 const WCHAR *env;
6550 HKEY root;
6551 LONG res;
6553 if (flags & ENV_MOD_MACHINE)
6555 env = machine_env;
6556 root = HKEY_LOCAL_MACHINE;
6558 else
6560 env = user_env;
6561 root = HKEY_CURRENT_USER;
6564 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6565 if (res != ERROR_SUCCESS)
6567 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6568 return ERROR_FUNCTION_FAILED;
6571 return ERROR_SUCCESS;
6574 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6576 MSIPACKAGE *package = param;
6577 LPCWSTR name, value, component;
6578 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6579 DWORD flags, type, size;
6580 UINT res;
6581 HKEY env = NULL;
6582 MSICOMPONENT *comp;
6583 MSIRECORD *uirow;
6584 int action = 0;
6586 component = MSI_RecordGetString(rec, 4);
6587 comp = msi_get_loaded_component(package, component);
6588 if (!comp)
6589 return ERROR_SUCCESS;
6591 comp->Action = msi_get_component_action( package, comp );
6592 if (comp->Action != INSTALLSTATE_LOCAL)
6594 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6595 return ERROR_SUCCESS;
6597 name = MSI_RecordGetString(rec, 2);
6598 value = MSI_RecordGetString(rec, 3);
6600 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6602 res = env_parse_flags(&name, &value, &flags);
6603 if (res != ERROR_SUCCESS || !value)
6604 goto done;
6606 if (value && !deformat_string(package, value, &deformatted))
6608 res = ERROR_OUTOFMEMORY;
6609 goto done;
6612 value = deformatted;
6614 res = open_env_key( flags, &env );
6615 if (res != ERROR_SUCCESS)
6616 goto done;
6618 if (flags & ENV_MOD_MACHINE)
6619 action |= 0x20000000;
6621 size = 0;
6622 type = REG_SZ;
6623 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6624 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6625 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6626 goto done;
6628 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6630 action = 0x2;
6632 /* Nothing to do. */
6633 if (!value)
6635 res = ERROR_SUCCESS;
6636 goto done;
6639 /* If we are appending but the string was empty, strip ; */
6640 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6642 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6643 newval = strdupW(value);
6644 if (!newval)
6646 res = ERROR_OUTOFMEMORY;
6647 goto done;
6650 else
6652 action = 0x1;
6654 /* Contrary to MSDN, +-variable to [~];path works */
6655 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6657 res = ERROR_SUCCESS;
6658 goto done;
6661 data = msi_alloc(size);
6662 if (!data)
6664 RegCloseKey(env);
6665 return ERROR_OUTOFMEMORY;
6668 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6669 if (res != ERROR_SUCCESS)
6670 goto done;
6672 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6674 action = 0x4;
6675 res = RegDeleteValueW(env, name);
6676 if (res != ERROR_SUCCESS)
6677 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6678 goto done;
6681 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6682 if (flags & ENV_MOD_MASK)
6684 DWORD mod_size;
6685 int multiplier = 0;
6686 if (flags & ENV_MOD_APPEND) multiplier++;
6687 if (flags & ENV_MOD_PREFIX) multiplier++;
6688 mod_size = lstrlenW(value) * multiplier;
6689 size += mod_size * sizeof(WCHAR);
6692 newval = msi_alloc(size);
6693 ptr = newval;
6694 if (!newval)
6696 res = ERROR_OUTOFMEMORY;
6697 goto done;
6700 if (flags & ENV_MOD_PREFIX)
6702 lstrcpyW(newval, value);
6703 ptr = newval + lstrlenW(value);
6704 action |= 0x80000000;
6707 lstrcpyW(ptr, data);
6709 if (flags & ENV_MOD_APPEND)
6711 lstrcatW(newval, value);
6712 action |= 0x40000000;
6715 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6716 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6717 if (res)
6719 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6722 done:
6723 uirow = MSI_CreateRecord( 3 );
6724 MSI_RecordSetStringW( uirow, 1, name );
6725 MSI_RecordSetStringW( uirow, 2, newval );
6726 MSI_RecordSetInteger( uirow, 3, action );
6727 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6728 msiobj_release( &uirow->hdr );
6730 if (env) RegCloseKey(env);
6731 msi_free(deformatted);
6732 msi_free(data);
6733 msi_free(newval);
6734 return res;
6737 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6739 UINT rc;
6740 MSIQUERY * view;
6741 static const WCHAR ExecSeqQuery[] =
6742 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6743 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6744 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6745 if (rc != ERROR_SUCCESS)
6746 return ERROR_SUCCESS;
6748 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6749 msiobj_release(&view->hdr);
6751 return rc;
6754 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6756 MSIPACKAGE *package = param;
6757 LPCWSTR name, value, component;
6758 LPWSTR deformatted = NULL;
6759 DWORD flags;
6760 HKEY env;
6761 MSICOMPONENT *comp;
6762 MSIRECORD *uirow;
6763 int action = 0;
6764 LONG res;
6765 UINT r;
6767 component = MSI_RecordGetString( rec, 4 );
6768 comp = msi_get_loaded_component( package, component );
6769 if (!comp)
6770 return ERROR_SUCCESS;
6772 comp->Action = msi_get_component_action( package, comp );
6773 if (comp->Action != INSTALLSTATE_ABSENT)
6775 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6776 return ERROR_SUCCESS;
6778 name = MSI_RecordGetString( rec, 2 );
6779 value = MSI_RecordGetString( rec, 3 );
6781 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6783 r = env_parse_flags( &name, &value, &flags );
6784 if (r != ERROR_SUCCESS)
6785 return r;
6787 if (!(flags & ENV_ACT_REMOVE))
6789 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6790 return ERROR_SUCCESS;
6793 if (value && !deformat_string( package, value, &deformatted ))
6794 return ERROR_OUTOFMEMORY;
6796 value = deformatted;
6798 r = open_env_key( flags, &env );
6799 if (r != ERROR_SUCCESS)
6801 r = ERROR_SUCCESS;
6802 goto done;
6805 if (flags & ENV_MOD_MACHINE)
6806 action |= 0x20000000;
6808 TRACE("Removing %s\n", debugstr_w(name));
6810 res = RegDeleteValueW( env, name );
6811 if (res != ERROR_SUCCESS)
6813 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6814 r = ERROR_SUCCESS;
6817 done:
6818 uirow = MSI_CreateRecord( 3 );
6819 MSI_RecordSetStringW( uirow, 1, name );
6820 MSI_RecordSetStringW( uirow, 2, value );
6821 MSI_RecordSetInteger( uirow, 3, action );
6822 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6823 msiobj_release( &uirow->hdr );
6825 if (env) RegCloseKey( env );
6826 msi_free( deformatted );
6827 return r;
6830 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6832 UINT rc;
6833 MSIQUERY *view;
6834 static const WCHAR query[] =
6835 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6836 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6838 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6839 if (rc != ERROR_SUCCESS)
6840 return ERROR_SUCCESS;
6842 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6843 msiobj_release( &view->hdr );
6845 return rc;
6848 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6850 LPWSTR key, template, id;
6851 UINT r = ERROR_SUCCESS;
6853 id = msi_dup_property( package->db, szProductID );
6854 if (id)
6856 msi_free( id );
6857 return ERROR_SUCCESS;
6859 template = msi_dup_property( package->db, szPIDTemplate );
6860 key = msi_dup_property( package->db, szPIDKEY );
6862 if (key && template)
6864 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6865 r = msi_set_property( package->db, szProductID, key );
6867 msi_free( template );
6868 msi_free( key );
6869 return r;
6872 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6874 TRACE("\n");
6875 package->need_reboot = 1;
6876 return ERROR_SUCCESS;
6879 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6881 static const WCHAR szAvailableFreeReg[] =
6882 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6883 MSIRECORD *uirow;
6884 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6886 TRACE("%p %d kilobytes\n", package, space);
6888 uirow = MSI_CreateRecord( 1 );
6889 MSI_RecordSetInteger( uirow, 1, space );
6890 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6891 msiobj_release( &uirow->hdr );
6893 return ERROR_SUCCESS;
6896 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6898 TRACE("%p\n", package);
6900 msi_set_property( package->db, szRollbackDisabled, szOne );
6901 return ERROR_SUCCESS;
6904 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6906 FIXME("%p\n", package);
6907 return ERROR_SUCCESS;
6910 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6912 UINT r, count;
6913 MSIQUERY *view;
6915 static const WCHAR driver_query[] = {
6916 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6917 'O','D','B','C','D','r','i','v','e','r',0 };
6919 static const WCHAR translator_query[] = {
6920 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6921 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6923 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6924 if (r == ERROR_SUCCESS)
6926 count = 0;
6927 r = MSI_IterateRecords( view, &count, NULL, package );
6928 msiobj_release( &view->hdr );
6929 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6932 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6933 if (r == ERROR_SUCCESS)
6935 count = 0;
6936 r = MSI_IterateRecords( view, &count, NULL, package );
6937 msiobj_release( &view->hdr );
6938 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6941 return ERROR_SUCCESS;
6944 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6946 MSIPACKAGE *package = param;
6947 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6948 WCHAR *value;
6950 if ((value = msi_dup_property( package->db, property )))
6952 FIXME("remove %s\n", debugstr_w(value));
6953 msi_free( value );
6955 return ERROR_SUCCESS;
6958 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6960 UINT r;
6961 MSIQUERY *view;
6963 static const WCHAR query[] =
6964 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
6965 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
6967 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6968 if (r == ERROR_SUCCESS)
6970 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6971 msiobj_release( &view->hdr );
6973 return ERROR_SUCCESS;
6976 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6978 MSIPACKAGE *package = param;
6979 int attributes = MSI_RecordGetInteger( rec, 5 );
6981 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6983 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6984 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6985 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6986 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6987 HKEY hkey;
6988 UINT r;
6990 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6992 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6993 if (r != ERROR_SUCCESS)
6994 return ERROR_SUCCESS;
6996 else
6998 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6999 if (r != ERROR_SUCCESS)
7000 return ERROR_SUCCESS;
7002 RegCloseKey( hkey );
7004 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7005 debugstr_w(upgrade_code), debugstr_w(version_min),
7006 debugstr_w(version_max), debugstr_w(language));
7008 return ERROR_SUCCESS;
7011 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7013 UINT r;
7014 MSIQUERY *view;
7015 static const WCHAR query[] =
7016 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7018 if (msi_get_property_int( package->db, szInstalled, 0 ))
7020 TRACE("product is installed, skipping action\n");
7021 return ERROR_SUCCESS;
7023 if (msi_get_property_int( package->db, szPreselected, 0 ))
7025 TRACE("Preselected property is set, not migrating feature states\n");
7026 return ERROR_SUCCESS;
7029 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7030 if (r == ERROR_SUCCESS)
7032 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7033 msiobj_release( &view->hdr );
7035 return ERROR_SUCCESS;
7038 static void bind_image( const char *filename, const char *path )
7040 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7042 WARN("failed to bind image %u\n", GetLastError());
7046 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7048 UINT i;
7049 MSIFILE *file;
7050 MSIPACKAGE *package = param;
7051 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7052 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7053 char *filenameA, *pathA;
7054 WCHAR *pathW, **path_list;
7056 if (!(file = msi_get_loaded_file( package, key )))
7058 WARN("file %s not found\n", debugstr_w(key));
7059 return ERROR_SUCCESS;
7061 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7062 path_list = msi_split_string( paths, ';' );
7063 if (!path_list) bind_image( filenameA, NULL );
7064 else
7066 for (i = 0; path_list[i] && path_list[i][0]; i++)
7068 deformat_string( package, path_list[i], &pathW );
7069 if ((pathA = strdupWtoA( pathW )))
7071 bind_image( filenameA, pathA );
7072 msi_free( pathA );
7074 msi_free( pathW );
7077 msi_free( path_list );
7078 msi_free( filenameA );
7079 return ERROR_SUCCESS;
7082 static UINT ACTION_BindImage( MSIPACKAGE *package )
7084 UINT r;
7085 MSIQUERY *view;
7086 static const WCHAR query[] =
7087 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','B','i','n','d','I','m','a','g','e',0};
7089 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7090 if (r == ERROR_SUCCESS)
7092 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7093 msiobj_release( &view->hdr );
7095 return ERROR_SUCCESS;
7098 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7099 LPCSTR action, LPCWSTR table )
7101 static const WCHAR query[] = {
7102 'S','E','L','E','C','T',' ','*',' ',
7103 'F','R','O','M',' ','`','%','s','`',0 };
7104 MSIQUERY *view = NULL;
7105 DWORD count = 0;
7106 UINT r;
7108 r = MSI_OpenQuery( package->db, &view, query, table );
7109 if (r == ERROR_SUCCESS)
7111 r = MSI_IterateRecords(view, &count, NULL, package);
7112 msiobj_release(&view->hdr);
7115 if (count)
7116 FIXME("%s -> %u ignored %s table values\n",
7117 action, count, debugstr_w(table));
7119 return ERROR_SUCCESS;
7122 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7124 static const WCHAR table[] = {
7125 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7126 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7129 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7131 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7132 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7135 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7137 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7138 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7141 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7143 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7144 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7147 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7149 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7150 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7153 static const struct
7155 const WCHAR *action;
7156 UINT (*handler)(MSIPACKAGE *);
7157 const WCHAR *action_rollback;
7159 StandardActions[] =
7161 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7162 { szAppSearch, ACTION_AppSearch, NULL },
7163 { szBindImage, ACTION_BindImage, NULL },
7164 { szCCPSearch, ACTION_CCPSearch, NULL },
7165 { szCostFinalize, ACTION_CostFinalize, NULL },
7166 { szCostInitialize, ACTION_CostInitialize, NULL },
7167 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7168 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7169 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7170 { szDisableRollback, ACTION_DisableRollback, NULL },
7171 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7172 { szExecuteAction, ACTION_ExecuteAction, NULL },
7173 { szFileCost, ACTION_FileCost, NULL },
7174 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7175 { szForceReboot, ACTION_ForceReboot, NULL },
7176 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7177 { szInstallExecute, ACTION_InstallExecute, NULL },
7178 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7179 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7180 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7181 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7182 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7183 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7184 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7185 { szInstallValidate, ACTION_InstallValidate, NULL },
7186 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7187 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7188 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7189 { szMoveFiles, ACTION_MoveFiles, NULL },
7190 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7191 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7192 { szPatchFiles, ACTION_PatchFiles, NULL },
7193 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7194 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7195 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7196 { szPublishProduct, ACTION_PublishProduct, NULL },
7197 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7198 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7199 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7200 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7201 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7202 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7203 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7204 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7205 { szRegisterUser, ACTION_RegisterUser, NULL },
7206 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7207 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7208 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7209 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7210 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7211 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7212 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7213 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7214 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7215 { szResolveSource, ACTION_ResolveSource, NULL },
7216 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7217 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7218 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7219 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfUnregModules },
7220 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7221 { szStartServices, ACTION_StartServices, szStopServices },
7222 { szStopServices, ACTION_StopServices, szStartServices },
7223 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7224 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7225 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7226 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7227 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7228 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7229 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7230 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7231 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7232 { szValidateProductID, ACTION_ValidateProductID, NULL },
7233 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7234 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7235 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7236 { NULL, NULL, NULL }
7239 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7241 BOOL ret = FALSE;
7242 UINT i;
7244 i = 0;
7245 while (StandardActions[i].action != NULL)
7247 if (!strcmpW( StandardActions[i].action, action ))
7249 ui_actionstart( package, action );
7250 if (StandardActions[i].handler)
7252 ui_actioninfo( package, action, TRUE, 0 );
7253 *rc = StandardActions[i].handler( package );
7254 ui_actioninfo( package, action, FALSE, *rc );
7256 if (StandardActions[i].action_rollback && !package->need_rollback)
7258 TRACE("scheduling rollback action\n");
7259 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7262 else
7264 FIXME("unhandled standard action %s\n", debugstr_w(action));
7265 *rc = ERROR_SUCCESS;
7267 ret = TRUE;
7268 break;
7270 i++;
7272 return ret;
7275 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7277 UINT rc = ERROR_SUCCESS;
7278 BOOL handled;
7280 TRACE("Performing action (%s)\n", debugstr_w(action));
7282 handled = ACTION_HandleStandardAction(package, action, &rc);
7284 if (!handled)
7285 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7287 if (!handled)
7289 WARN("unhandled msi action %s\n", debugstr_w(action));
7290 rc = ERROR_FUNCTION_NOT_CALLED;
7293 return rc;
7296 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7298 UINT rc = ERROR_SUCCESS;
7299 BOOL handled = FALSE;
7301 TRACE("Performing action (%s)\n", debugstr_w(action));
7303 handled = ACTION_HandleStandardAction(package, action, &rc);
7305 if (!handled)
7306 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7308 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7309 handled = TRUE;
7311 if (!handled)
7313 WARN("unhandled msi action %s\n", debugstr_w(action));
7314 rc = ERROR_FUNCTION_NOT_CALLED;
7317 return rc;
7320 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7322 UINT rc = ERROR_SUCCESS;
7323 MSIRECORD *row;
7325 static const WCHAR ExecSeqQuery[] =
7326 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7327 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7328 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7329 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7330 static const WCHAR UISeqQuery[] =
7331 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7332 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7333 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7334 ' ', '=',' ','%','i',0};
7336 if (needs_ui_sequence(package))
7337 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7338 else
7339 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7341 if (row)
7343 LPCWSTR action, cond;
7345 TRACE("Running the actions\n");
7347 /* check conditions */
7348 cond = MSI_RecordGetString(row, 2);
7350 /* this is a hack to skip errors in the condition code */
7351 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7353 msiobj_release(&row->hdr);
7354 return ERROR_SUCCESS;
7357 action = MSI_RecordGetString(row, 1);
7358 if (!action)
7360 ERR("failed to fetch action\n");
7361 msiobj_release(&row->hdr);
7362 return ERROR_FUNCTION_FAILED;
7365 if (needs_ui_sequence(package))
7366 rc = ACTION_PerformUIAction(package, action, -1);
7367 else
7368 rc = ACTION_PerformAction(package, action, -1);
7370 msiobj_release(&row->hdr);
7373 return rc;
7376 /****************************************************
7377 * TOP level entry points
7378 *****************************************************/
7380 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7381 LPCWSTR szCommandLine )
7383 UINT rc;
7384 BOOL ui_exists;
7385 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7386 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7387 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7389 msi_set_property( package->db, szAction, szInstall );
7391 package->script->InWhatSequence = SEQUENCE_INSTALL;
7393 if (szPackagePath)
7395 LPWSTR p, dir;
7396 LPCWSTR file;
7398 dir = strdupW(szPackagePath);
7399 p = strrchrW(dir, '\\');
7400 if (p)
7402 *(++p) = 0;
7403 file = szPackagePath + (p - dir);
7405 else
7407 msi_free(dir);
7408 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7409 GetCurrentDirectoryW(MAX_PATH, dir);
7410 lstrcatW(dir, szBackSlash);
7411 file = szPackagePath;
7414 msi_free( package->PackagePath );
7415 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7416 if (!package->PackagePath)
7418 msi_free(dir);
7419 return ERROR_OUTOFMEMORY;
7422 lstrcpyW(package->PackagePath, dir);
7423 lstrcatW(package->PackagePath, file);
7424 msi_free(dir);
7426 msi_set_sourcedir_props(package, FALSE);
7429 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7430 if (rc != ERROR_SUCCESS)
7431 return rc;
7433 msi_apply_transforms( package );
7434 msi_apply_patches( package );
7436 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7438 TRACE("setting reinstall property\n");
7439 msi_set_property( package->db, szReinstall, szAll );
7442 /* properties may have been added by a transform */
7443 msi_clone_properties( package );
7445 msi_parse_command_line( package, szCommandLine, FALSE );
7446 msi_adjust_privilege_properties( package );
7447 msi_set_context( package );
7449 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7451 TRACE("disabling rollback\n");
7452 msi_set_property( package->db, szRollbackDisabled, szOne );
7455 if (needs_ui_sequence( package))
7457 package->script->InWhatSequence |= SEQUENCE_UI;
7458 rc = ACTION_ProcessUISequence(package);
7459 ui_exists = ui_sequence_exists(package);
7460 if (rc == ERROR_SUCCESS || !ui_exists)
7462 package->script->InWhatSequence |= SEQUENCE_EXEC;
7463 rc = ACTION_ProcessExecSequence(package, ui_exists);
7466 else
7467 rc = ACTION_ProcessExecSequence(package, FALSE);
7469 package->script->CurrentlyScripting = FALSE;
7471 /* process the ending type action */
7472 if (rc == ERROR_SUCCESS)
7473 ACTION_PerformActionSequence(package, -1);
7474 else if (rc == ERROR_INSTALL_USEREXIT)
7475 ACTION_PerformActionSequence(package, -2);
7476 else if (rc == ERROR_INSTALL_SUSPEND)
7477 ACTION_PerformActionSequence(package, -4);
7478 else /* failed */
7480 ACTION_PerformActionSequence(package, -3);
7481 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7483 package->need_rollback = TRUE;
7487 /* finish up running custom actions */
7488 ACTION_FinishCustomActions(package);
7490 if (package->need_rollback)
7492 WARN("installation failed, running rollback script\n");
7493 execute_script( package, ROLLBACK_SCRIPT );
7496 if (rc == ERROR_SUCCESS && package->need_reboot)
7497 return ERROR_SUCCESS_REBOOT_REQUIRED;
7499 return rc;