d3dx9: Improve effect sampler parsing.
[wine.git] / dlls / msi / action.c
blobe4604653c2c64253f44c840b4b41621160a39606
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 WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3193 if (comp->assembly)
3195 const WCHAR prefixW[] = {'<','\\',0};
3196 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3197 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3199 if (keypath)
3201 strcpyW( keypath, prefixW );
3202 strcatW( keypath, comp->assembly->display_name );
3204 return keypath;
3206 return resolve_keypath( package, comp );
3209 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3211 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3212 UINT rc;
3213 MSICOMPONENT *comp;
3214 HKEY hkey;
3216 TRACE("\n");
3218 squash_guid(package->ProductCode,squished_pc);
3219 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3221 msi_set_sourcedir_props(package, FALSE);
3223 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3225 MSIRECORD *uirow;
3226 INSTALLSTATE action;
3228 msi_ui_progress( package, 2, 0, 0, 0 );
3229 if (!comp->ComponentId)
3230 continue;
3232 squash_guid( comp->ComponentId, squished_cc );
3233 msi_free( comp->FullKeypath );
3234 comp->FullKeypath = build_full_keypath( package, comp );
3236 ACTION_RefCountComponent( package, comp );
3238 if (package->need_rollback) action = comp->Installed;
3239 else action = comp->ActionRequest;
3241 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3242 debugstr_w(comp->Component), debugstr_w(squished_cc),
3243 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3245 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3247 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3248 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3249 else
3250 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3252 if (rc != ERROR_SUCCESS)
3253 continue;
3255 if (comp->Attributes & msidbComponentAttributesPermanent)
3257 static const WCHAR szPermKey[] =
3258 { '0','0','0','0','0','0','0','0','0','0','0','0',
3259 '0','0','0','0','0','0','0','0','0','0','0','0',
3260 '0','0','0','0','0','0','0','0',0 };
3262 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3264 if (action == INSTALLSTATE_LOCAL)
3265 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3266 else
3268 MSIFILE *file;
3269 MSIRECORD *row;
3270 LPWSTR ptr, ptr2;
3271 WCHAR source[MAX_PATH];
3272 WCHAR base[MAX_PATH];
3273 LPWSTR sourcepath;
3275 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3276 static const WCHAR query[] = {
3277 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3278 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3279 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3280 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3281 '`','D','i','s','k','I','d','`',0};
3283 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3284 continue;
3286 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3287 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3288 ptr2 = strrchrW(source, '\\') + 1;
3289 msiobj_release(&row->hdr);
3291 lstrcpyW(base, package->PackagePath);
3292 ptr = strrchrW(base, '\\');
3293 *(ptr + 1) = '\0';
3295 sourcepath = msi_resolve_file_source(package, file);
3296 ptr = sourcepath + lstrlenW(base);
3297 lstrcpyW(ptr2, ptr);
3298 msi_free(sourcepath);
3300 msi_reg_set_val_str(hkey, squished_pc, source);
3302 RegCloseKey(hkey);
3304 else if (action == INSTALLSTATE_ABSENT)
3306 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3307 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3308 else
3309 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3312 /* UI stuff */
3313 uirow = MSI_CreateRecord(3);
3314 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3315 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3316 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3317 msi_ui_actiondata( package, szProcessComponents, uirow );
3318 msiobj_release( &uirow->hdr );
3320 return ERROR_SUCCESS;
3323 typedef struct {
3324 CLSID clsid;
3325 LPWSTR source;
3327 LPWSTR path;
3328 ITypeLib *ptLib;
3329 } typelib_struct;
3331 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3332 LPWSTR lpszName, LONG_PTR lParam)
3334 TLIBATTR *attr;
3335 typelib_struct *tl_struct = (typelib_struct*) lParam;
3336 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3337 int sz;
3338 HRESULT res;
3340 if (!IS_INTRESOURCE(lpszName))
3342 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3343 return TRUE;
3346 sz = strlenW(tl_struct->source)+4;
3347 sz *= sizeof(WCHAR);
3349 if ((INT_PTR)lpszName == 1)
3350 tl_struct->path = strdupW(tl_struct->source);
3351 else
3353 tl_struct->path = msi_alloc(sz);
3354 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3357 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3358 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3359 if (FAILED(res))
3361 msi_free(tl_struct->path);
3362 tl_struct->path = NULL;
3364 return TRUE;
3367 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3368 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3370 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3371 return FALSE;
3374 msi_free(tl_struct->path);
3375 tl_struct->path = NULL;
3377 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3378 ITypeLib_Release(tl_struct->ptLib);
3380 return TRUE;
3383 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3385 MSIPACKAGE* package = param;
3386 LPCWSTR component;
3387 MSICOMPONENT *comp;
3388 MSIFILE *file;
3389 typelib_struct tl_struct;
3390 ITypeLib *tlib;
3391 HMODULE module;
3392 HRESULT hr;
3394 component = MSI_RecordGetString(row,3);
3395 comp = msi_get_loaded_component(package,component);
3396 if (!comp)
3397 return ERROR_SUCCESS;
3399 comp->Action = msi_get_component_action( package, comp );
3400 if (comp->Action != INSTALLSTATE_LOCAL)
3402 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3403 return ERROR_SUCCESS;
3406 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3408 TRACE("component has no key path\n");
3409 return ERROR_SUCCESS;
3411 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3413 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3414 if (module)
3416 LPCWSTR guid;
3417 guid = MSI_RecordGetString(row,1);
3418 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3419 tl_struct.source = strdupW( file->TargetPath );
3420 tl_struct.path = NULL;
3422 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3423 (LONG_PTR)&tl_struct);
3425 if (tl_struct.path)
3427 LPCWSTR helpid, help_path = NULL;
3428 HRESULT res;
3430 helpid = MSI_RecordGetString(row,6);
3432 if (helpid) help_path = msi_get_target_folder( package, helpid );
3433 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3435 if (FAILED(res))
3436 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3437 else
3438 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3440 ITypeLib_Release(tl_struct.ptLib);
3441 msi_free(tl_struct.path);
3443 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3445 FreeLibrary(module);
3446 msi_free(tl_struct.source);
3448 else
3450 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3451 if (FAILED(hr))
3453 ERR("Failed to load type library: %08x\n", hr);
3454 return ERROR_INSTALL_FAILURE;
3457 ITypeLib_Release(tlib);
3460 return ERROR_SUCCESS;
3463 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3466 * OK this is a bit confusing.. I am given a _Component key and I believe
3467 * that the file that is being registered as a type library is the "key file
3468 * of that component" which I interpret to mean "The file in the KeyPath of
3469 * that component".
3471 UINT rc;
3472 MSIQUERY * view;
3473 static const WCHAR Query[] =
3474 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3475 '`','T','y','p','e','L','i','b','`',0};
3477 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3478 if (rc != ERROR_SUCCESS)
3479 return ERROR_SUCCESS;
3481 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3482 msiobj_release(&view->hdr);
3483 return rc;
3486 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3488 MSIPACKAGE *package = param;
3489 LPCWSTR component, guid;
3490 MSICOMPONENT *comp;
3491 GUID libid;
3492 UINT version;
3493 LCID language;
3494 SYSKIND syskind;
3495 HRESULT hr;
3497 component = MSI_RecordGetString( row, 3 );
3498 comp = msi_get_loaded_component( package, component );
3499 if (!comp)
3500 return ERROR_SUCCESS;
3502 comp->Action = msi_get_component_action( package, comp );
3503 if (comp->Action != INSTALLSTATE_ABSENT)
3505 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3506 return ERROR_SUCCESS;
3508 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3510 guid = MSI_RecordGetString( row, 1 );
3511 CLSIDFromString( (LPCWSTR)guid, &libid );
3512 version = MSI_RecordGetInteger( row, 4 );
3513 language = MSI_RecordGetInteger( row, 2 );
3515 #ifdef _WIN64
3516 syskind = SYS_WIN64;
3517 #else
3518 syskind = SYS_WIN32;
3519 #endif
3521 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3522 if (FAILED(hr))
3524 WARN("Failed to unregister typelib: %08x\n", hr);
3527 return ERROR_SUCCESS;
3530 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3532 UINT rc;
3533 MSIQUERY *view;
3534 static const WCHAR query[] =
3535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3536 '`','T','y','p','e','L','i','b','`',0};
3538 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3539 if (rc != ERROR_SUCCESS)
3540 return ERROR_SUCCESS;
3542 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3543 msiobj_release( &view->hdr );
3544 return rc;
3547 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3549 static const WCHAR szlnk[] = {'.','l','n','k',0};
3550 LPCWSTR directory, extension, link_folder;
3551 LPWSTR link_file, filename;
3553 directory = MSI_RecordGetString( row, 2 );
3554 link_folder = msi_get_target_folder( package, directory );
3556 /* may be needed because of a bug somewhere else */
3557 msi_create_full_path( link_folder );
3559 filename = msi_dup_record_field( row, 3 );
3560 msi_reduce_to_long_filename( filename );
3562 extension = strchrW( filename, '.' );
3563 if (!extension || strcmpiW( extension, szlnk ))
3565 int len = strlenW( filename );
3566 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3567 memcpy( filename + len, szlnk, sizeof(szlnk) );
3569 link_file = msi_build_directory_name( 2, link_folder, filename );
3570 msi_free( filename );
3572 return link_file;
3575 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3577 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3578 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3579 WCHAR *folder, *dest, *path;
3581 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3582 folder = msi_dup_property( package->db, szWindowsFolder );
3583 else
3585 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3586 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3587 msi_free( appdata );
3589 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3590 msi_create_full_path( dest );
3591 path = msi_build_directory_name( 2, dest, icon_name );
3592 msi_free( folder );
3593 msi_free( dest );
3594 return path;
3597 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3599 MSIPACKAGE *package = param;
3600 LPWSTR link_file, deformated, path;
3601 LPCWSTR component, target;
3602 MSICOMPONENT *comp;
3603 IShellLinkW *sl = NULL;
3604 IPersistFile *pf = NULL;
3605 HRESULT res;
3607 component = MSI_RecordGetString(row, 4);
3608 comp = msi_get_loaded_component(package, component);
3609 if (!comp)
3610 return ERROR_SUCCESS;
3612 comp->Action = msi_get_component_action( package, comp );
3613 if (comp->Action != INSTALLSTATE_LOCAL)
3615 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3616 return ERROR_SUCCESS;
3618 msi_ui_actiondata( package, szCreateShortcuts, row );
3620 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3621 &IID_IShellLinkW, (LPVOID *) &sl );
3623 if (FAILED( res ))
3625 ERR("CLSID_ShellLink not available\n");
3626 goto err;
3629 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3630 if (FAILED( res ))
3632 ERR("QueryInterface(IID_IPersistFile) failed\n");
3633 goto err;
3636 target = MSI_RecordGetString(row, 5);
3637 if (strchrW(target, '['))
3639 deformat_string(package, target, &deformated);
3640 IShellLinkW_SetPath(sl,deformated);
3641 msi_free(deformated);
3643 else
3645 FIXME("poorly handled shortcut format, advertised shortcut\n");
3646 IShellLinkW_SetPath(sl,comp->FullKeypath);
3649 if (!MSI_RecordIsNull(row,6))
3651 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3652 deformat_string(package, arguments, &deformated);
3653 IShellLinkW_SetArguments(sl,deformated);
3654 msi_free(deformated);
3657 if (!MSI_RecordIsNull(row,7))
3659 LPCWSTR description = MSI_RecordGetString(row, 7);
3660 IShellLinkW_SetDescription(sl, description);
3663 if (!MSI_RecordIsNull(row,8))
3664 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3666 if (!MSI_RecordIsNull(row,9))
3668 INT index;
3669 LPCWSTR icon = MSI_RecordGetString(row, 9);
3671 path = msi_build_icon_path(package, icon);
3672 index = MSI_RecordGetInteger(row,10);
3674 /* no value means 0 */
3675 if (index == MSI_NULL_INTEGER)
3676 index = 0;
3678 IShellLinkW_SetIconLocation(sl, path, index);
3679 msi_free(path);
3682 if (!MSI_RecordIsNull(row,11))
3683 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3685 if (!MSI_RecordIsNull(row,12))
3687 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3688 full_path = msi_get_target_folder( package, wkdir );
3689 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3691 link_file = get_link_file(package, row);
3693 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3694 IPersistFile_Save(pf, link_file, FALSE);
3695 msi_free(link_file);
3697 err:
3698 if (pf)
3699 IPersistFile_Release( pf );
3700 if (sl)
3701 IShellLinkW_Release( sl );
3703 return ERROR_SUCCESS;
3706 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3708 UINT rc;
3709 HRESULT res;
3710 MSIQUERY * view;
3711 static const WCHAR Query[] =
3712 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3713 '`','S','h','o','r','t','c','u','t','`',0};
3715 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3716 if (rc != ERROR_SUCCESS)
3717 return ERROR_SUCCESS;
3719 res = CoInitialize( NULL );
3721 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3722 msiobj_release(&view->hdr);
3724 if (SUCCEEDED(res))
3725 CoUninitialize();
3727 return rc;
3730 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3732 MSIPACKAGE *package = param;
3733 LPWSTR link_file;
3734 LPCWSTR component;
3735 MSICOMPONENT *comp;
3737 component = MSI_RecordGetString( row, 4 );
3738 comp = msi_get_loaded_component( package, component );
3739 if (!comp)
3740 return ERROR_SUCCESS;
3742 comp->Action = msi_get_component_action( package, comp );
3743 if (comp->Action != INSTALLSTATE_ABSENT)
3745 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3746 return ERROR_SUCCESS;
3748 msi_ui_actiondata( package, szRemoveShortcuts, row );
3750 link_file = get_link_file( package, row );
3752 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3753 if (!DeleteFileW( link_file ))
3755 WARN("Failed to remove shortcut file %u\n", GetLastError());
3757 msi_free( link_file );
3759 return ERROR_SUCCESS;
3762 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3764 UINT rc;
3765 MSIQUERY *view;
3766 static const WCHAR query[] =
3767 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3768 '`','S','h','o','r','t','c','u','t','`',0};
3770 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3771 if (rc != ERROR_SUCCESS)
3772 return ERROR_SUCCESS;
3774 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3775 msiobj_release( &view->hdr );
3777 return rc;
3780 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3782 MSIPACKAGE* package = param;
3783 HANDLE the_file;
3784 LPWSTR FilePath;
3785 LPCWSTR FileName;
3786 CHAR buffer[1024];
3787 DWORD sz;
3788 UINT rc;
3790 FileName = MSI_RecordGetString(row,1);
3791 if (!FileName)
3793 ERR("Unable to get FileName\n");
3794 return ERROR_SUCCESS;
3797 FilePath = msi_build_icon_path(package, FileName);
3799 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3801 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3802 FILE_ATTRIBUTE_NORMAL, NULL);
3804 if (the_file == INVALID_HANDLE_VALUE)
3806 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3807 msi_free(FilePath);
3808 return ERROR_SUCCESS;
3813 DWORD write;
3814 sz = 1024;
3815 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3816 if (rc != ERROR_SUCCESS)
3818 ERR("Failed to get stream\n");
3819 CloseHandle(the_file);
3820 DeleteFileW(FilePath);
3821 break;
3823 WriteFile(the_file,buffer,sz,&write,NULL);
3824 } while (sz == 1024);
3826 msi_free(FilePath);
3827 CloseHandle(the_file);
3829 return ERROR_SUCCESS;
3832 static UINT msi_publish_icons(MSIPACKAGE *package)
3834 UINT r;
3835 MSIQUERY *view;
3837 static const WCHAR query[]= {
3838 'S','E','L','E','C','T',' ','*',' ',
3839 'F','R','O','M',' ','`','I','c','o','n','`',0};
3841 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3842 if (r == ERROR_SUCCESS)
3844 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3845 msiobj_release(&view->hdr);
3848 return ERROR_SUCCESS;
3851 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3853 UINT r;
3854 HKEY source;
3855 LPWSTR buffer;
3856 MSIMEDIADISK *disk;
3857 MSISOURCELISTINFO *info;
3859 r = RegCreateKeyW(hkey, szSourceList, &source);
3860 if (r != ERROR_SUCCESS)
3861 return r;
3863 RegCloseKey(source);
3865 buffer = strrchrW(package->PackagePath, '\\') + 1;
3866 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3867 package->Context, MSICODE_PRODUCT,
3868 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3869 if (r != ERROR_SUCCESS)
3870 return r;
3872 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3873 package->Context, MSICODE_PRODUCT,
3874 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3875 if (r != ERROR_SUCCESS)
3876 return r;
3878 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3879 package->Context, MSICODE_PRODUCT,
3880 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3881 if (r != ERROR_SUCCESS)
3882 return r;
3884 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3886 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3887 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3888 info->options, info->value);
3889 else
3890 MsiSourceListSetInfoW(package->ProductCode, NULL,
3891 info->context, info->options,
3892 info->property, info->value);
3895 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3897 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3898 disk->context, disk->options,
3899 disk->disk_id, disk->volume_label, disk->disk_prompt);
3902 return ERROR_SUCCESS;
3905 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3907 MSIHANDLE hdb, suminfo;
3908 WCHAR guids[MAX_PATH];
3909 WCHAR packcode[SQUISH_GUID_SIZE];
3910 LPWSTR buffer;
3911 LPWSTR ptr;
3912 DWORD langid;
3913 DWORD size;
3914 UINT r;
3916 static const WCHAR szARPProductIcon[] =
3917 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3918 static const WCHAR szAssignment[] =
3919 {'A','s','s','i','g','n','m','e','n','t',0};
3920 static const WCHAR szAdvertiseFlags[] =
3921 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3922 static const WCHAR szClients[] =
3923 {'C','l','i','e','n','t','s',0};
3924 static const WCHAR szColon[] = {':',0};
3926 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3927 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3928 msi_free(buffer);
3930 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3931 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3933 /* FIXME */
3934 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3936 buffer = msi_dup_property(package->db, szARPProductIcon);
3937 if (buffer)
3939 LPWSTR path = msi_build_icon_path(package, buffer);
3940 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3941 msi_free(path);
3942 msi_free(buffer);
3945 buffer = msi_dup_property(package->db, szProductVersion);
3946 if (buffer)
3948 DWORD verdword = msi_version_str_to_dword(buffer);
3949 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3950 msi_free(buffer);
3953 msi_reg_set_val_dword(hkey, szAssignment, 0);
3954 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3955 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3956 msi_reg_set_val_str(hkey, szClients, szColon);
3958 hdb = alloc_msihandle(&package->db->hdr);
3959 if (!hdb)
3960 return ERROR_NOT_ENOUGH_MEMORY;
3962 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3963 MsiCloseHandle(hdb);
3964 if (r != ERROR_SUCCESS)
3965 goto done;
3967 size = MAX_PATH;
3968 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3969 NULL, guids, &size);
3970 if (r != ERROR_SUCCESS)
3971 goto done;
3973 ptr = strchrW(guids, ';');
3974 if (ptr) *ptr = 0;
3975 squash_guid(guids, packcode);
3976 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3978 done:
3979 MsiCloseHandle(suminfo);
3980 return ERROR_SUCCESS;
3983 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3985 UINT r;
3986 HKEY hkey;
3987 LPWSTR upgrade;
3988 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3990 upgrade = msi_dup_property(package->db, szUpgradeCode);
3991 if (!upgrade)
3992 return ERROR_SUCCESS;
3994 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3995 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3996 else
3997 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3999 if (r != ERROR_SUCCESS)
4001 WARN("failed to open upgrade code key\n");
4002 msi_free(upgrade);
4003 return ERROR_SUCCESS;
4005 squash_guid(package->ProductCode, squashed_pc);
4006 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4007 RegCloseKey(hkey);
4008 msi_free(upgrade);
4009 return ERROR_SUCCESS;
4012 static BOOL msi_check_publish(MSIPACKAGE *package)
4014 MSIFEATURE *feature;
4016 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4018 feature->Action = msi_get_feature_action( package, feature );
4019 if (feature->Action == INSTALLSTATE_LOCAL)
4020 return TRUE;
4023 return FALSE;
4026 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4028 MSIFEATURE *feature;
4030 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4032 feature->Action = msi_get_feature_action( package, feature );
4033 if (feature->Action != INSTALLSTATE_ABSENT)
4034 return FALSE;
4037 return TRUE;
4040 static UINT msi_publish_patches( MSIPACKAGE *package )
4042 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4043 WCHAR patch_squashed[GUID_SIZE];
4044 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4045 LONG res;
4046 MSIPATCHINFO *patch;
4047 UINT r;
4048 WCHAR *p, *all_patches = NULL;
4049 DWORD len = 0;
4051 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4052 if (r != ERROR_SUCCESS)
4053 return ERROR_FUNCTION_FAILED;
4055 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4056 if (res != ERROR_SUCCESS)
4058 r = ERROR_FUNCTION_FAILED;
4059 goto done;
4062 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4063 if (r != ERROR_SUCCESS)
4064 goto done;
4066 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4068 squash_guid( patch->patchcode, patch_squashed );
4069 len += strlenW( patch_squashed ) + 1;
4072 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4073 if (!all_patches)
4074 goto done;
4076 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4078 HKEY patch_key;
4080 squash_guid( patch->patchcode, p );
4081 p += strlenW( p ) + 1;
4083 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4084 (const BYTE *)patch->transforms,
4085 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4086 if (res != ERROR_SUCCESS)
4087 goto done;
4089 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4090 if (r != ERROR_SUCCESS)
4091 goto done;
4093 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4094 (const BYTE *)patch->localfile,
4095 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4096 RegCloseKey( patch_key );
4097 if (res != ERROR_SUCCESS)
4098 goto done;
4100 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4101 if (res != ERROR_SUCCESS)
4102 goto done;
4104 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4105 RegCloseKey( patch_key );
4106 if (res != ERROR_SUCCESS)
4107 goto done;
4110 all_patches[len] = 0;
4111 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4112 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4113 if (res != ERROR_SUCCESS)
4114 goto done;
4116 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4117 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4118 if (res != ERROR_SUCCESS)
4119 r = ERROR_FUNCTION_FAILED;
4121 done:
4122 RegCloseKey( product_patches_key );
4123 RegCloseKey( patches_key );
4124 RegCloseKey( product_key );
4125 msi_free( all_patches );
4126 return r;
4129 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4131 UINT rc;
4132 HKEY hukey = NULL, hudkey = NULL;
4133 MSIRECORD *uirow;
4135 if (!list_empty(&package->patches))
4137 rc = msi_publish_patches(package);
4138 if (rc != ERROR_SUCCESS)
4139 goto end;
4142 /* FIXME: also need to publish if the product is in advertise mode */
4143 if (!msi_check_publish(package))
4144 return ERROR_SUCCESS;
4146 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4147 &hukey, TRUE);
4148 if (rc != ERROR_SUCCESS)
4149 goto end;
4151 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4152 NULL, &hudkey, TRUE);
4153 if (rc != ERROR_SUCCESS)
4154 goto end;
4156 rc = msi_publish_upgrade_code(package);
4157 if (rc != ERROR_SUCCESS)
4158 goto end;
4160 rc = msi_publish_product_properties(package, hukey);
4161 if (rc != ERROR_SUCCESS)
4162 goto end;
4164 rc = msi_publish_sourcelist(package, hukey);
4165 if (rc != ERROR_SUCCESS)
4166 goto end;
4168 rc = msi_publish_icons(package);
4170 end:
4171 uirow = MSI_CreateRecord( 1 );
4172 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4173 msi_ui_actiondata( package, szPublishProduct, uirow );
4174 msiobj_release( &uirow->hdr );
4176 RegCloseKey(hukey);
4177 RegCloseKey(hudkey);
4179 return rc;
4182 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4184 WCHAR *filename, *ptr, *folder, *ret;
4185 const WCHAR *dirprop;
4187 filename = msi_dup_record_field( row, 2 );
4188 if (filename && (ptr = strchrW( filename, '|' )))
4189 ptr++;
4190 else
4191 ptr = filename;
4193 dirprop = MSI_RecordGetString( row, 3 );
4194 if (dirprop)
4196 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4197 if (!folder) folder = msi_dup_property( package->db, dirprop );
4199 else
4200 folder = msi_dup_property( package->db, szWindowsFolder );
4202 if (!folder)
4204 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4205 msi_free( filename );
4206 return NULL;
4209 ret = msi_build_directory_name( 2, folder, ptr );
4211 msi_free( filename );
4212 msi_free( folder );
4213 return ret;
4216 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4218 MSIPACKAGE *package = param;
4219 LPCWSTR component, section, key, value, identifier;
4220 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4221 MSIRECORD * uirow;
4222 INT action;
4223 MSICOMPONENT *comp;
4225 component = MSI_RecordGetString(row, 8);
4226 comp = msi_get_loaded_component(package,component);
4227 if (!comp)
4228 return ERROR_SUCCESS;
4230 comp->Action = msi_get_component_action( package, comp );
4231 if (comp->Action != INSTALLSTATE_LOCAL)
4233 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4234 return ERROR_SUCCESS;
4237 identifier = MSI_RecordGetString(row,1);
4238 section = MSI_RecordGetString(row,4);
4239 key = MSI_RecordGetString(row,5);
4240 value = MSI_RecordGetString(row,6);
4241 action = MSI_RecordGetInteger(row,7);
4243 deformat_string(package,section,&deformated_section);
4244 deformat_string(package,key,&deformated_key);
4245 deformat_string(package,value,&deformated_value);
4247 fullname = get_ini_file_name(package, row);
4249 if (action == 0)
4251 TRACE("Adding value %s to section %s in %s\n",
4252 debugstr_w(deformated_key), debugstr_w(deformated_section),
4253 debugstr_w(fullname));
4254 WritePrivateProfileStringW(deformated_section, deformated_key,
4255 deformated_value, fullname);
4257 else if (action == 1)
4259 WCHAR returned[10];
4260 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4261 returned, 10, fullname);
4262 if (returned[0] == 0)
4264 TRACE("Adding value %s to section %s in %s\n",
4265 debugstr_w(deformated_key), debugstr_w(deformated_section),
4266 debugstr_w(fullname));
4268 WritePrivateProfileStringW(deformated_section, deformated_key,
4269 deformated_value, fullname);
4272 else if (action == 3)
4273 FIXME("Append to existing section not yet implemented\n");
4275 uirow = MSI_CreateRecord(4);
4276 MSI_RecordSetStringW(uirow,1,identifier);
4277 MSI_RecordSetStringW(uirow,2,deformated_section);
4278 MSI_RecordSetStringW(uirow,3,deformated_key);
4279 MSI_RecordSetStringW(uirow,4,deformated_value);
4280 msi_ui_actiondata( package, szWriteIniValues, uirow );
4281 msiobj_release( &uirow->hdr );
4283 msi_free(fullname);
4284 msi_free(deformated_key);
4285 msi_free(deformated_value);
4286 msi_free(deformated_section);
4287 return ERROR_SUCCESS;
4290 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4292 UINT rc;
4293 MSIQUERY * view;
4294 static const WCHAR ExecSeqQuery[] =
4295 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4296 '`','I','n','i','F','i','l','e','`',0};
4298 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4299 if (rc != ERROR_SUCCESS)
4301 TRACE("no IniFile table\n");
4302 return ERROR_SUCCESS;
4305 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4306 msiobj_release(&view->hdr);
4307 return rc;
4310 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4312 MSIPACKAGE *package = param;
4313 LPCWSTR component, section, key, value, identifier;
4314 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4315 MSICOMPONENT *comp;
4316 MSIRECORD *uirow;
4317 INT action;
4319 component = MSI_RecordGetString( row, 8 );
4320 comp = msi_get_loaded_component( package, component );
4321 if (!comp)
4322 return ERROR_SUCCESS;
4324 comp->Action = msi_get_component_action( package, comp );
4325 if (comp->Action != INSTALLSTATE_ABSENT)
4327 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4328 return ERROR_SUCCESS;
4331 identifier = MSI_RecordGetString( row, 1 );
4332 section = MSI_RecordGetString( row, 4 );
4333 key = MSI_RecordGetString( row, 5 );
4334 value = MSI_RecordGetString( row, 6 );
4335 action = MSI_RecordGetInteger( row, 7 );
4337 deformat_string( package, section, &deformated_section );
4338 deformat_string( package, key, &deformated_key );
4339 deformat_string( package, value, &deformated_value );
4341 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4343 filename = get_ini_file_name( package, row );
4345 TRACE("Removing key %s from section %s in %s\n",
4346 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4348 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4350 WARN("Unable to remove key %u\n", GetLastError());
4352 msi_free( filename );
4354 else
4355 FIXME("Unsupported action %d\n", action);
4358 uirow = MSI_CreateRecord( 4 );
4359 MSI_RecordSetStringW( uirow, 1, identifier );
4360 MSI_RecordSetStringW( uirow, 2, deformated_section );
4361 MSI_RecordSetStringW( uirow, 3, deformated_key );
4362 MSI_RecordSetStringW( uirow, 4, deformated_value );
4363 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4364 msiobj_release( &uirow->hdr );
4366 msi_free( deformated_key );
4367 msi_free( deformated_value );
4368 msi_free( deformated_section );
4369 return ERROR_SUCCESS;
4372 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4374 MSIPACKAGE *package = param;
4375 LPCWSTR component, section, key, value, identifier;
4376 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4377 MSICOMPONENT *comp;
4378 MSIRECORD *uirow;
4379 INT action;
4381 component = MSI_RecordGetString( row, 8 );
4382 comp = msi_get_loaded_component( package, component );
4383 if (!comp)
4384 return ERROR_SUCCESS;
4386 comp->Action = msi_get_component_action( package, comp );
4387 if (comp->Action != INSTALLSTATE_LOCAL)
4389 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4390 return ERROR_SUCCESS;
4393 identifier = MSI_RecordGetString( row, 1 );
4394 section = MSI_RecordGetString( row, 4 );
4395 key = MSI_RecordGetString( row, 5 );
4396 value = MSI_RecordGetString( row, 6 );
4397 action = MSI_RecordGetInteger( row, 7 );
4399 deformat_string( package, section, &deformated_section );
4400 deformat_string( package, key, &deformated_key );
4401 deformat_string( package, value, &deformated_value );
4403 if (action == msidbIniFileActionRemoveLine)
4405 filename = get_ini_file_name( package, row );
4407 TRACE("Removing key %s from section %s in %s\n",
4408 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4410 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4412 WARN("Unable to remove key %u\n", GetLastError());
4414 msi_free( filename );
4416 else
4417 FIXME("Unsupported action %d\n", action);
4419 uirow = MSI_CreateRecord( 4 );
4420 MSI_RecordSetStringW( uirow, 1, identifier );
4421 MSI_RecordSetStringW( uirow, 2, deformated_section );
4422 MSI_RecordSetStringW( uirow, 3, deformated_key );
4423 MSI_RecordSetStringW( uirow, 4, deformated_value );
4424 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4425 msiobj_release( &uirow->hdr );
4427 msi_free( deformated_key );
4428 msi_free( deformated_value );
4429 msi_free( deformated_section );
4430 return ERROR_SUCCESS;
4433 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4435 UINT rc;
4436 MSIQUERY *view;
4437 static const WCHAR query[] =
4438 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4439 '`','I','n','i','F','i','l','e','`',0};
4440 static const WCHAR remove_query[] =
4441 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4442 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4444 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4445 if (rc == ERROR_SUCCESS)
4447 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4448 msiobj_release( &view->hdr );
4449 if (rc != ERROR_SUCCESS)
4450 return rc;
4453 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4454 if (rc == ERROR_SUCCESS)
4456 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4457 msiobj_release( &view->hdr );
4458 if (rc != ERROR_SUCCESS)
4459 return rc;
4462 return ERROR_SUCCESS;
4465 static void register_dll( const WCHAR *dll, BOOL unregister )
4467 HMODULE hmod;
4469 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4470 if (hmod)
4472 HRESULT (WINAPI *func_ptr)( void );
4473 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4475 func_ptr = (void *)GetProcAddress( hmod, func );
4476 if (func_ptr)
4478 HRESULT hr = func_ptr();
4479 if (FAILED( hr ))
4480 WARN("failed to register dll 0x%08x\n", hr);
4482 else
4483 WARN("entry point %s not found\n", func);
4484 FreeLibrary( hmod );
4485 return;
4487 WARN("failed to load library %u\n", GetLastError());
4490 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4492 MSIPACKAGE *package = param;
4493 LPCWSTR filename;
4494 MSIFILE *file;
4495 MSIRECORD *uirow;
4497 filename = MSI_RecordGetString(row,1);
4498 file = msi_get_loaded_file( package, filename );
4499 if (!file)
4501 WARN("unable to find file %s\n", debugstr_w(filename));
4502 return ERROR_SUCCESS;
4504 file->Component->Action = msi_get_component_action( package, file->Component );
4505 if (file->Component->Action != INSTALLSTATE_LOCAL)
4507 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4508 return ERROR_SUCCESS;
4511 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4512 register_dll( file->TargetPath, FALSE );
4514 uirow = MSI_CreateRecord( 2 );
4515 MSI_RecordSetStringW( uirow, 1, filename );
4516 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4517 msi_ui_actiondata( package, szSelfRegModules, uirow );
4518 msiobj_release( &uirow->hdr );
4520 return ERROR_SUCCESS;
4523 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4525 UINT rc;
4526 MSIQUERY * view;
4527 static const WCHAR ExecSeqQuery[] =
4528 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4529 '`','S','e','l','f','R','e','g','`',0};
4531 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4532 if (rc != ERROR_SUCCESS)
4534 TRACE("no SelfReg table\n");
4535 return ERROR_SUCCESS;
4538 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4539 msiobj_release(&view->hdr);
4541 return ERROR_SUCCESS;
4544 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4546 MSIPACKAGE *package = param;
4547 LPCWSTR filename;
4548 MSIFILE *file;
4549 MSIRECORD *uirow;
4551 filename = MSI_RecordGetString( row, 1 );
4552 file = msi_get_loaded_file( package, filename );
4553 if (!file)
4555 WARN("unable to find file %s\n", debugstr_w(filename));
4556 return ERROR_SUCCESS;
4558 file->Component->Action = msi_get_component_action( package, file->Component );
4559 if (file->Component->Action != INSTALLSTATE_ABSENT)
4561 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4562 return ERROR_SUCCESS;
4565 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4566 register_dll( file->TargetPath, TRUE );
4568 uirow = MSI_CreateRecord( 2 );
4569 MSI_RecordSetStringW( uirow, 1, filename );
4570 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4571 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4572 msiobj_release( &uirow->hdr );
4574 return ERROR_SUCCESS;
4577 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4579 UINT rc;
4580 MSIQUERY *view;
4581 static const WCHAR query[] =
4582 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4583 '`','S','e','l','f','R','e','g','`',0};
4585 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4586 if (rc != ERROR_SUCCESS)
4588 TRACE("no SelfReg table\n");
4589 return ERROR_SUCCESS;
4592 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4593 msiobj_release( &view->hdr );
4595 return ERROR_SUCCESS;
4598 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4600 MSIFEATURE *feature;
4601 UINT rc;
4602 HKEY hkey = NULL, userdata = NULL;
4604 if (!msi_check_publish(package))
4605 return ERROR_SUCCESS;
4607 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4608 &hkey, TRUE);
4609 if (rc != ERROR_SUCCESS)
4610 goto end;
4612 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4613 &userdata, TRUE);
4614 if (rc != ERROR_SUCCESS)
4615 goto end;
4617 /* here the guids are base 85 encoded */
4618 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4620 ComponentList *cl;
4621 LPWSTR data = NULL;
4622 GUID clsid;
4623 INT size;
4624 BOOL absent = FALSE;
4625 MSIRECORD *uirow;
4627 if (feature->Action != INSTALLSTATE_LOCAL &&
4628 feature->Action != INSTALLSTATE_SOURCE &&
4629 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4631 size = 1;
4632 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4634 size += 21;
4636 if (feature->Feature_Parent)
4637 size += strlenW( feature->Feature_Parent )+2;
4639 data = msi_alloc(size * sizeof(WCHAR));
4641 data[0] = 0;
4642 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4644 MSICOMPONENT* component = cl->component;
4645 WCHAR buf[21];
4647 buf[0] = 0;
4648 if (component->ComponentId)
4650 TRACE("From %s\n",debugstr_w(component->ComponentId));
4651 CLSIDFromString(component->ComponentId, &clsid);
4652 encode_base85_guid(&clsid,buf);
4653 TRACE("to %s\n",debugstr_w(buf));
4654 strcatW(data,buf);
4658 if (feature->Feature_Parent)
4660 static const WCHAR sep[] = {'\2',0};
4661 strcatW(data,sep);
4662 strcatW(data,feature->Feature_Parent);
4665 msi_reg_set_val_str( userdata, feature->Feature, data );
4666 msi_free(data);
4668 size = 0;
4669 if (feature->Feature_Parent)
4670 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4671 if (!absent)
4673 size += sizeof(WCHAR);
4674 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4675 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4677 else
4679 size += 2*sizeof(WCHAR);
4680 data = msi_alloc(size);
4681 data[0] = 0x6;
4682 data[1] = 0;
4683 if (feature->Feature_Parent)
4684 strcpyW( &data[1], feature->Feature_Parent );
4685 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4686 (LPBYTE)data,size);
4687 msi_free(data);
4690 /* the UI chunk */
4691 uirow = MSI_CreateRecord( 1 );
4692 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4693 msi_ui_actiondata( package, szPublishFeatures, uirow );
4694 msiobj_release( &uirow->hdr );
4695 /* FIXME: call msi_ui_progress? */
4698 end:
4699 RegCloseKey(hkey);
4700 RegCloseKey(userdata);
4701 return rc;
4704 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4706 UINT r;
4707 HKEY hkey;
4708 MSIRECORD *uirow;
4710 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4712 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4713 &hkey, FALSE);
4714 if (r == ERROR_SUCCESS)
4716 RegDeleteValueW(hkey, feature->Feature);
4717 RegCloseKey(hkey);
4720 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4721 &hkey, FALSE);
4722 if (r == ERROR_SUCCESS)
4724 RegDeleteValueW(hkey, feature->Feature);
4725 RegCloseKey(hkey);
4728 uirow = MSI_CreateRecord( 1 );
4729 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4730 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4731 msiobj_release( &uirow->hdr );
4733 return ERROR_SUCCESS;
4736 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4738 MSIFEATURE *feature;
4740 if (!msi_check_unpublish(package))
4741 return ERROR_SUCCESS;
4743 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4745 msi_unpublish_feature(package, feature);
4748 return ERROR_SUCCESS;
4751 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4753 SYSTEMTIME systime;
4754 DWORD size, langid;
4755 WCHAR date[9], *val, *buffer;
4756 const WCHAR *prop, *key;
4758 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4759 static const WCHAR modpath_fmt[] =
4760 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4761 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4762 static const WCHAR szModifyPath[] =
4763 {'M','o','d','i','f','y','P','a','t','h',0};
4764 static const WCHAR szUninstallString[] =
4765 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4766 static const WCHAR szEstimatedSize[] =
4767 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4768 static const WCHAR szDisplayVersion[] =
4769 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4770 static const WCHAR szInstallSource[] =
4771 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4772 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4773 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4774 static const WCHAR szAuthorizedCDFPrefix[] =
4775 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4776 static const WCHAR szARPCONTACT[] =
4777 {'A','R','P','C','O','N','T','A','C','T',0};
4778 static const WCHAR szContact[] =
4779 {'C','o','n','t','a','c','t',0};
4780 static const WCHAR szARPCOMMENTS[] =
4781 {'A','R','P','C','O','M','M','E','N','T','S',0};
4782 static const WCHAR szComments[] =
4783 {'C','o','m','m','e','n','t','s',0};
4784 static const WCHAR szProductName[] =
4785 {'P','r','o','d','u','c','t','N','a','m','e',0};
4786 static const WCHAR szDisplayName[] =
4787 {'D','i','s','p','l','a','y','N','a','m','e',0};
4788 static const WCHAR szARPHELPLINK[] =
4789 {'A','R','P','H','E','L','P','L','I','N','K',0};
4790 static const WCHAR szHelpLink[] =
4791 {'H','e','l','p','L','i','n','k',0};
4792 static const WCHAR szARPHELPTELEPHONE[] =
4793 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4794 static const WCHAR szHelpTelephone[] =
4795 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4796 static const WCHAR szARPINSTALLLOCATION[] =
4797 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4798 static const WCHAR szInstallLocation[] =
4799 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4800 static const WCHAR szManufacturer[] =
4801 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4802 static const WCHAR szPublisher[] =
4803 {'P','u','b','l','i','s','h','e','r',0};
4804 static const WCHAR szARPREADME[] =
4805 {'A','R','P','R','E','A','D','M','E',0};
4806 static const WCHAR szReadme[] =
4807 {'R','e','a','d','M','e',0};
4808 static const WCHAR szARPSIZE[] =
4809 {'A','R','P','S','I','Z','E',0};
4810 static const WCHAR szSize[] =
4811 {'S','i','z','e',0};
4812 static const WCHAR szARPURLINFOABOUT[] =
4813 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4814 static const WCHAR szURLInfoAbout[] =
4815 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4816 static const WCHAR szARPURLUPDATEINFO[] =
4817 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4818 static const WCHAR szURLUpdateInfo[] =
4819 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4820 static const WCHAR szARPSYSTEMCOMPONENT[] =
4821 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4822 static const WCHAR szSystemComponent[] =
4823 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4825 static const WCHAR *propval[] = {
4826 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4827 szARPCONTACT, szContact,
4828 szARPCOMMENTS, szComments,
4829 szProductName, szDisplayName,
4830 szARPHELPLINK, szHelpLink,
4831 szARPHELPTELEPHONE, szHelpTelephone,
4832 szARPINSTALLLOCATION, szInstallLocation,
4833 szSourceDir, szInstallSource,
4834 szManufacturer, szPublisher,
4835 szARPREADME, szReadme,
4836 szARPSIZE, szSize,
4837 szARPURLINFOABOUT, szURLInfoAbout,
4838 szARPURLUPDATEINFO, szURLUpdateInfo,
4839 NULL
4841 const WCHAR **p = propval;
4843 while (*p)
4845 prop = *p++;
4846 key = *p++;
4847 val = msi_dup_property(package->db, prop);
4848 msi_reg_set_val_str(hkey, key, val);
4849 msi_free(val);
4852 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4853 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4855 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4857 size = deformat_string(package, modpath_fmt, &buffer);
4858 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4859 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4860 msi_free(buffer);
4862 /* FIXME: Write real Estimated Size when we have it */
4863 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4865 GetLocalTime(&systime);
4866 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4867 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4869 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4870 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4872 buffer = msi_dup_property(package->db, szProductVersion);
4873 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4874 if (buffer)
4876 DWORD verdword = msi_version_str_to_dword(buffer);
4878 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4879 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4880 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4881 msi_free(buffer);
4884 return ERROR_SUCCESS;
4887 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4889 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4890 MSIRECORD *uirow;
4891 LPWSTR upgrade_code;
4892 HKEY hkey, props, upgrade_key;
4893 UINT rc;
4895 /* FIXME: also need to publish if the product is in advertise mode */
4896 if (!msi_check_publish(package))
4897 return ERROR_SUCCESS;
4899 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4900 if (rc != ERROR_SUCCESS)
4901 return rc;
4903 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4904 NULL, &props, TRUE);
4905 if (rc != ERROR_SUCCESS)
4906 goto done;
4908 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4909 msi_free( package->db->localfile );
4910 package->db->localfile = NULL;
4912 rc = msi_publish_install_properties(package, hkey);
4913 if (rc != ERROR_SUCCESS)
4914 goto done;
4916 rc = msi_publish_install_properties(package, props);
4917 if (rc != ERROR_SUCCESS)
4918 goto done;
4920 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4921 if (upgrade_code)
4923 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4924 if (rc == ERROR_SUCCESS)
4926 squash_guid( package->ProductCode, squashed_pc );
4927 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4928 RegCloseKey( upgrade_key );
4930 msi_free( upgrade_code );
4933 done:
4934 uirow = MSI_CreateRecord( 1 );
4935 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4936 msi_ui_actiondata( package, szRegisterProduct, uirow );
4937 msiobj_release( &uirow->hdr );
4939 RegCloseKey(hkey);
4940 return ERROR_SUCCESS;
4943 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4945 return execute_script(package,INSTALL_SCRIPT);
4948 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4950 WCHAR *upgrade, **features;
4951 BOOL full_uninstall = TRUE;
4952 MSIFEATURE *feature;
4953 MSIPATCHINFO *patch;
4955 static const WCHAR szUpgradeCode[] =
4956 {'U','p','g','r','a','d','e','C','o','d','e',0};
4958 features = msi_split_string(remove, ',');
4959 if (!features)
4961 ERR("REMOVE feature list is empty!\n");
4962 return ERROR_FUNCTION_FAILED;
4965 if (!strcmpW( features[0], szAll ))
4966 full_uninstall = TRUE;
4967 else
4969 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4971 if (feature->Action != INSTALLSTATE_ABSENT)
4972 full_uninstall = FALSE;
4975 msi_free(features);
4977 if (!full_uninstall)
4978 return ERROR_SUCCESS;
4980 MSIREG_DeleteProductKey(package->ProductCode);
4981 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4982 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4984 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4985 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4986 MSIREG_DeleteUserProductKey(package->ProductCode);
4987 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4989 upgrade = msi_dup_property(package->db, szUpgradeCode);
4990 if (upgrade)
4992 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4993 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4994 msi_free(upgrade);
4997 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4999 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5002 return ERROR_SUCCESS;
5005 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5007 UINT rc;
5008 WCHAR *remove;
5010 /* turn off scheduling */
5011 package->script->CurrentlyScripting= FALSE;
5013 /* first do the same as an InstallExecute */
5014 rc = ACTION_InstallExecute(package);
5015 if (rc != ERROR_SUCCESS)
5016 return rc;
5018 /* then handle Commit Actions */
5019 rc = execute_script(package,COMMIT_SCRIPT);
5020 if (rc != ERROR_SUCCESS)
5021 return rc;
5023 remove = msi_dup_property(package->db, szRemove);
5024 if (remove)
5025 rc = msi_unpublish_product(package, remove);
5027 msi_free(remove);
5028 return rc;
5031 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5033 static const WCHAR RunOnce[] = {
5034 'S','o','f','t','w','a','r','e','\\',
5035 'M','i','c','r','o','s','o','f','t','\\',
5036 'W','i','n','d','o','w','s','\\',
5037 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5038 'R','u','n','O','n','c','e',0};
5039 static const WCHAR InstallRunOnce[] = {
5040 'S','o','f','t','w','a','r','e','\\',
5041 'M','i','c','r','o','s','o','f','t','\\',
5042 'W','i','n','d','o','w','s','\\',
5043 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5044 'I','n','s','t','a','l','l','e','r','\\',
5045 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5047 static const WCHAR msiexec_fmt[] = {
5048 '%','s',
5049 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5050 '\"','%','s','\"',0};
5051 static const WCHAR install_fmt[] = {
5052 '/','I',' ','\"','%','s','\"',' ',
5053 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5054 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5055 WCHAR buffer[256], sysdir[MAX_PATH];
5056 HKEY hkey;
5057 WCHAR squished_pc[100];
5059 squash_guid(package->ProductCode,squished_pc);
5061 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5062 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5063 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5064 squished_pc);
5066 msi_reg_set_val_str( hkey, squished_pc, buffer );
5067 RegCloseKey(hkey);
5069 TRACE("Reboot command %s\n",debugstr_w(buffer));
5071 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5072 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5074 msi_reg_set_val_str( hkey, squished_pc, buffer );
5075 RegCloseKey(hkey);
5077 return ERROR_INSTALL_SUSPEND;
5080 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5082 static const WCHAR query[] =
5083 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5084 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5085 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5086 MSIRECORD *rec, *row;
5087 DWORD i, size = 0;
5088 va_list va;
5089 const WCHAR *str;
5090 WCHAR *data;
5092 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5094 rec = MSI_CreateRecord( count + 2 );
5095 str = MSI_RecordGetString( row, 1 );
5096 MSI_RecordSetStringW( rec, 0, str );
5097 msiobj_release( &row->hdr );
5098 MSI_RecordSetInteger( rec, 1, error );
5100 va_start( va, count );
5101 for (i = 0; i < count; i++)
5103 str = va_arg( va, const WCHAR *);
5104 MSI_RecordSetStringW( rec, i + 2, str );
5106 va_end( va );
5108 MSI_FormatRecordW( package, rec, NULL, &size );
5109 size++;
5110 data = msi_alloc( size * sizeof(WCHAR) );
5111 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5112 else data[0] = 0;
5113 msiobj_release( &rec->hdr );
5114 return data;
5117 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5119 DWORD attrib;
5120 UINT rc;
5123 * We are currently doing what should be done here in the top level Install
5124 * however for Administrative and uninstalls this step will be needed
5126 if (!package->PackagePath)
5127 return ERROR_SUCCESS;
5129 msi_set_sourcedir_props(package, TRUE);
5131 attrib = GetFileAttributesW(package->db->path);
5132 if (attrib == INVALID_FILE_ATTRIBUTES)
5134 LPWSTR prompt;
5135 LPWSTR msg;
5136 DWORD size = 0;
5138 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5139 package->Context, MSICODE_PRODUCT,
5140 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5141 if (rc == ERROR_MORE_DATA)
5143 prompt = msi_alloc(size * sizeof(WCHAR));
5144 MsiSourceListGetInfoW(package->ProductCode, NULL,
5145 package->Context, MSICODE_PRODUCT,
5146 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5148 else
5149 prompt = strdupW(package->db->path);
5151 msg = msi_build_error_string(package, 1302, 1, prompt);
5152 while(attrib == INVALID_FILE_ATTRIBUTES)
5154 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5155 if (rc == IDCANCEL)
5157 rc = ERROR_INSTALL_USEREXIT;
5158 break;
5160 attrib = GetFileAttributesW(package->db->path);
5162 msi_free(prompt);
5163 rc = ERROR_SUCCESS;
5165 else
5166 return ERROR_SUCCESS;
5168 return rc;
5171 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5173 HKEY hkey = 0;
5174 LPWSTR buffer, productid = NULL;
5175 UINT i, rc = ERROR_SUCCESS;
5176 MSIRECORD *uirow;
5178 static const WCHAR szPropKeys[][80] =
5180 {'P','r','o','d','u','c','t','I','D',0},
5181 {'U','S','E','R','N','A','M','E',0},
5182 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5183 {0},
5186 static const WCHAR szRegKeys[][80] =
5188 {'P','r','o','d','u','c','t','I','D',0},
5189 {'R','e','g','O','w','n','e','r',0},
5190 {'R','e','g','C','o','m','p','a','n','y',0},
5191 {0},
5194 if (msi_check_unpublish(package))
5196 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5197 goto end;
5200 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5201 if (!productid)
5202 goto end;
5204 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5205 NULL, &hkey, TRUE);
5206 if (rc != ERROR_SUCCESS)
5207 goto end;
5209 for( i = 0; szPropKeys[i][0]; i++ )
5211 buffer = msi_dup_property( package->db, szPropKeys[i] );
5212 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5213 msi_free( buffer );
5216 end:
5217 uirow = MSI_CreateRecord( 1 );
5218 MSI_RecordSetStringW( uirow, 1, productid );
5219 msi_ui_actiondata( package, szRegisterUser, uirow );
5220 msiobj_release( &uirow->hdr );
5222 msi_free(productid);
5223 RegCloseKey(hkey);
5224 return rc;
5228 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5230 UINT rc;
5232 package->script->InWhatSequence |= SEQUENCE_EXEC;
5233 rc = ACTION_ProcessExecSequence(package,FALSE);
5234 return rc;
5237 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5239 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5240 WCHAR productid_85[21], component_85[21], *ret;
5241 GUID clsid;
5242 DWORD sz;
5244 /* > is used if there is a component GUID and < if not. */
5246 productid_85[0] = 0;
5247 component_85[0] = 0;
5248 CLSIDFromString( package->ProductCode, &clsid );
5250 encode_base85_guid( &clsid, productid_85 );
5251 if (component)
5253 CLSIDFromString( component->ComponentId, &clsid );
5254 encode_base85_guid( &clsid, component_85 );
5257 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5258 debugstr_w(component_85));
5260 sz = 20 + strlenW( feature ) + 20 + 3;
5261 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5262 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5263 return ret;
5266 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5268 MSIPACKAGE *package = param;
5269 LPCWSTR compgroupid, component, feature, qualifier, text;
5270 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5271 HKEY hkey = NULL;
5272 UINT rc;
5273 MSICOMPONENT *comp;
5274 MSIFEATURE *feat;
5275 DWORD sz;
5276 MSIRECORD *uirow;
5277 int len;
5279 feature = MSI_RecordGetString(rec, 5);
5280 feat = msi_get_loaded_feature(package, feature);
5281 if (!feat)
5282 return ERROR_SUCCESS;
5284 feat->Action = msi_get_feature_action( package, feat );
5285 if (feat->Action != INSTALLSTATE_LOCAL &&
5286 feat->Action != INSTALLSTATE_SOURCE &&
5287 feat->Action != INSTALLSTATE_ADVERTISED)
5289 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5290 return ERROR_SUCCESS;
5293 component = MSI_RecordGetString(rec, 3);
5294 comp = msi_get_loaded_component(package, component);
5295 if (!comp)
5296 return ERROR_SUCCESS;
5298 compgroupid = MSI_RecordGetString(rec,1);
5299 qualifier = MSI_RecordGetString(rec,2);
5301 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5302 if (rc != ERROR_SUCCESS)
5303 goto end;
5305 advertise = msi_create_component_advertise_string( package, comp, feature );
5306 text = MSI_RecordGetString( rec, 4 );
5307 if (text)
5309 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5310 strcpyW( p, advertise );
5311 strcatW( p, text );
5312 msi_free( advertise );
5313 advertise = p;
5315 existing = msi_reg_get_val_str( hkey, qualifier );
5317 sz = strlenW( advertise ) + 1;
5318 if (existing)
5320 for (p = existing; *p; p += len)
5322 len = strlenW( p ) + 1;
5323 if (strcmpW( advertise, p )) sz += len;
5326 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5328 rc = ERROR_OUTOFMEMORY;
5329 goto end;
5331 q = output;
5332 if (existing)
5334 for (p = existing; *p; p += len)
5336 len = strlenW( p ) + 1;
5337 if (strcmpW( advertise, p ))
5339 memcpy( q, p, len * sizeof(WCHAR) );
5340 q += len;
5344 strcpyW( q, advertise );
5345 q[strlenW( q ) + 1] = 0;
5347 msi_reg_set_val_multi_str( hkey, qualifier, output );
5349 end:
5350 RegCloseKey(hkey);
5351 msi_free( output );
5352 msi_free( advertise );
5353 msi_free( existing );
5355 /* the UI chunk */
5356 uirow = MSI_CreateRecord( 2 );
5357 MSI_RecordSetStringW( uirow, 1, compgroupid );
5358 MSI_RecordSetStringW( uirow, 2, qualifier);
5359 msi_ui_actiondata( package, szPublishComponents, uirow );
5360 msiobj_release( &uirow->hdr );
5361 /* FIXME: call ui_progress? */
5363 return rc;
5367 * At present I am ignorning the advertised components part of this and only
5368 * focusing on the qualified component sets
5370 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5372 UINT rc;
5373 MSIQUERY * view;
5374 static const WCHAR ExecSeqQuery[] =
5375 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5376 '`','P','u','b','l','i','s','h',
5377 'C','o','m','p','o','n','e','n','t','`',0};
5379 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5380 if (rc != ERROR_SUCCESS)
5381 return ERROR_SUCCESS;
5383 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5384 msiobj_release(&view->hdr);
5386 return rc;
5389 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5391 static const WCHAR szInstallerComponents[] = {
5392 'S','o','f','t','w','a','r','e','\\',
5393 'M','i','c','r','o','s','o','f','t','\\',
5394 'I','n','s','t','a','l','l','e','r','\\',
5395 'C','o','m','p','o','n','e','n','t','s','\\',0};
5397 MSIPACKAGE *package = param;
5398 LPCWSTR compgroupid, component, feature, qualifier;
5399 MSICOMPONENT *comp;
5400 MSIFEATURE *feat;
5401 MSIRECORD *uirow;
5402 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5403 LONG res;
5405 feature = MSI_RecordGetString( rec, 5 );
5406 feat = msi_get_loaded_feature( package, feature );
5407 if (!feat)
5408 return ERROR_SUCCESS;
5410 feat->Action = msi_get_feature_action( package, feat );
5411 if (feat->Action != INSTALLSTATE_ABSENT)
5413 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5414 return ERROR_SUCCESS;
5417 component = MSI_RecordGetString( rec, 3 );
5418 comp = msi_get_loaded_component( package, component );
5419 if (!comp)
5420 return ERROR_SUCCESS;
5422 compgroupid = MSI_RecordGetString( rec, 1 );
5423 qualifier = MSI_RecordGetString( rec, 2 );
5425 squash_guid( compgroupid, squashed );
5426 strcpyW( keypath, szInstallerComponents );
5427 strcatW( keypath, squashed );
5429 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5430 if (res != ERROR_SUCCESS)
5432 WARN("Unable to delete component key %d\n", res);
5435 uirow = MSI_CreateRecord( 2 );
5436 MSI_RecordSetStringW( uirow, 1, compgroupid );
5437 MSI_RecordSetStringW( uirow, 2, qualifier );
5438 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5439 msiobj_release( &uirow->hdr );
5441 return ERROR_SUCCESS;
5444 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5446 UINT rc;
5447 MSIQUERY *view;
5448 static const WCHAR query[] =
5449 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5450 '`','P','u','b','l','i','s','h',
5451 'C','o','m','p','o','n','e','n','t','`',0};
5453 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5454 if (rc != ERROR_SUCCESS)
5455 return ERROR_SUCCESS;
5457 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5458 msiobj_release( &view->hdr );
5460 return rc;
5463 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5465 static const WCHAR query[] =
5466 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5467 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5468 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5469 MSIPACKAGE *package = param;
5470 MSICOMPONENT *component;
5471 MSIRECORD *row;
5472 MSIFILE *file;
5473 SC_HANDLE hscm = NULL, service = NULL;
5474 LPCWSTR comp, key;
5475 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5476 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5477 DWORD serv_type, start_type, err_control;
5478 SERVICE_DESCRIPTIONW sd = {NULL};
5480 comp = MSI_RecordGetString( rec, 12 );
5481 component = msi_get_loaded_component( package, comp );
5482 if (!component)
5484 WARN("service component not found\n");
5485 goto done;
5487 component->Action = msi_get_component_action( package, component );
5488 if (component->Action != INSTALLSTATE_LOCAL)
5490 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5491 goto done;
5493 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5494 if (!hscm)
5496 ERR("Failed to open the SC Manager!\n");
5497 goto done;
5500 start_type = MSI_RecordGetInteger(rec, 5);
5501 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5502 goto done;
5504 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5505 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5506 serv_type = MSI_RecordGetInteger(rec, 4);
5507 err_control = MSI_RecordGetInteger(rec, 6);
5508 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5509 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5510 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5511 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5512 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5513 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5515 /* fetch the service path */
5516 row = MSI_QueryGetRecord(package->db, query, comp);
5517 if (!row)
5519 ERR("Query failed\n");
5520 goto done;
5522 key = MSI_RecordGetString(row, 6);
5523 file = msi_get_loaded_file(package, key);
5524 msiobj_release(&row->hdr);
5525 if (!file)
5527 ERR("Failed to load the service file\n");
5528 goto done;
5531 if (!args || !args[0]) image_path = file->TargetPath;
5532 else
5534 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5535 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5536 return ERROR_OUTOFMEMORY;
5538 strcpyW(image_path, file->TargetPath);
5539 strcatW(image_path, szSpace);
5540 strcatW(image_path, args);
5542 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5543 start_type, err_control, image_path, load_order,
5544 NULL, depends, serv_name, pass);
5546 if (!service)
5548 if (GetLastError() != ERROR_SERVICE_EXISTS)
5549 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5551 else if (sd.lpDescription)
5553 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5554 WARN("failed to set service description %u\n", GetLastError());
5557 if (image_path != file->TargetPath) msi_free(image_path);
5558 done:
5559 CloseServiceHandle(service);
5560 CloseServiceHandle(hscm);
5561 msi_free(name);
5562 msi_free(disp);
5563 msi_free(sd.lpDescription);
5564 msi_free(load_order);
5565 msi_free(serv_name);
5566 msi_free(pass);
5567 msi_free(depends);
5568 msi_free(args);
5570 return ERROR_SUCCESS;
5573 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5575 UINT rc;
5576 MSIQUERY * view;
5577 static const WCHAR ExecSeqQuery[] =
5578 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5579 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5581 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5582 if (rc != ERROR_SUCCESS)
5583 return ERROR_SUCCESS;
5585 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5586 msiobj_release(&view->hdr);
5588 return rc;
5591 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5592 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5594 LPCWSTR *vector, *temp_vector;
5595 LPWSTR p, q;
5596 DWORD sep_len;
5598 static const WCHAR separator[] = {'[','~',']',0};
5600 *numargs = 0;
5601 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5603 if (!args)
5604 return NULL;
5606 vector = msi_alloc(sizeof(LPWSTR));
5607 if (!vector)
5608 return NULL;
5610 p = args;
5613 (*numargs)++;
5614 vector[*numargs - 1] = p;
5616 if ((q = strstrW(p, separator)))
5618 *q = '\0';
5620 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5621 if (!temp_vector)
5623 msi_free(vector);
5624 return NULL;
5626 vector = temp_vector;
5628 p = q + sep_len;
5630 } while (q);
5632 return vector;
5635 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5637 MSIPACKAGE *package = param;
5638 MSICOMPONENT *comp;
5639 MSIRECORD *uirow;
5640 SC_HANDLE scm = NULL, service = NULL;
5641 LPCWSTR component, *vector = NULL;
5642 LPWSTR name, args, display_name = NULL;
5643 DWORD event, numargs, len;
5644 UINT r = ERROR_FUNCTION_FAILED;
5646 component = MSI_RecordGetString(rec, 6);
5647 comp = msi_get_loaded_component(package, component);
5648 if (!comp)
5649 return ERROR_SUCCESS;
5651 comp->Action = msi_get_component_action( package, comp );
5652 if (comp->Action != INSTALLSTATE_LOCAL)
5654 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5655 return ERROR_SUCCESS;
5658 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5659 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5660 event = MSI_RecordGetInteger(rec, 3);
5662 if (!(event & msidbServiceControlEventStart))
5664 r = ERROR_SUCCESS;
5665 goto done;
5668 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5669 if (!scm)
5671 ERR("Failed to open the service control manager\n");
5672 goto done;
5675 len = 0;
5676 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5677 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5679 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5680 GetServiceDisplayNameW( scm, name, display_name, &len );
5683 service = OpenServiceW(scm, name, SERVICE_START);
5684 if (!service)
5686 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5687 goto done;
5690 vector = msi_service_args_to_vector(args, &numargs);
5692 if (!StartServiceW(service, numargs, vector) &&
5693 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5695 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5696 goto done;
5699 r = ERROR_SUCCESS;
5701 done:
5702 uirow = MSI_CreateRecord( 2 );
5703 MSI_RecordSetStringW( uirow, 1, display_name );
5704 MSI_RecordSetStringW( uirow, 2, name );
5705 msi_ui_actiondata( package, szStartServices, uirow );
5706 msiobj_release( &uirow->hdr );
5708 CloseServiceHandle(service);
5709 CloseServiceHandle(scm);
5711 msi_free(name);
5712 msi_free(args);
5713 msi_free(vector);
5714 msi_free(display_name);
5715 return r;
5718 static UINT ACTION_StartServices( MSIPACKAGE *package )
5720 UINT rc;
5721 MSIQUERY *view;
5723 static const WCHAR query[] = {
5724 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5725 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5727 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5728 if (rc != ERROR_SUCCESS)
5729 return ERROR_SUCCESS;
5731 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5732 msiobj_release(&view->hdr);
5734 return rc;
5737 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5739 DWORD i, needed, count;
5740 ENUM_SERVICE_STATUSW *dependencies;
5741 SERVICE_STATUS ss;
5742 SC_HANDLE depserv;
5744 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5745 0, &needed, &count))
5746 return TRUE;
5748 if (GetLastError() != ERROR_MORE_DATA)
5749 return FALSE;
5751 dependencies = msi_alloc(needed);
5752 if (!dependencies)
5753 return FALSE;
5755 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5756 needed, &needed, &count))
5757 goto error;
5759 for (i = 0; i < count; i++)
5761 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5762 SERVICE_STOP | SERVICE_QUERY_STATUS);
5763 if (!depserv)
5764 goto error;
5766 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5767 goto error;
5770 return TRUE;
5772 error:
5773 msi_free(dependencies);
5774 return FALSE;
5777 static UINT stop_service( LPCWSTR name )
5779 SC_HANDLE scm = NULL, service = NULL;
5780 SERVICE_STATUS status;
5781 SERVICE_STATUS_PROCESS ssp;
5782 DWORD needed;
5784 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5785 if (!scm)
5787 WARN("Failed to open the SCM: %d\n", GetLastError());
5788 goto done;
5791 service = OpenServiceW(scm, name,
5792 SERVICE_STOP |
5793 SERVICE_QUERY_STATUS |
5794 SERVICE_ENUMERATE_DEPENDENTS);
5795 if (!service)
5797 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5798 goto done;
5801 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5802 sizeof(SERVICE_STATUS_PROCESS), &needed))
5804 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5805 goto done;
5808 if (ssp.dwCurrentState == SERVICE_STOPPED)
5809 goto done;
5811 stop_service_dependents(scm, service);
5813 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5814 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5816 done:
5817 CloseServiceHandle(service);
5818 CloseServiceHandle(scm);
5820 return ERROR_SUCCESS;
5823 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5825 MSIPACKAGE *package = param;
5826 MSICOMPONENT *comp;
5827 MSIRECORD *uirow;
5828 LPCWSTR component;
5829 LPWSTR name = NULL, display_name = NULL;
5830 DWORD event, len;
5831 SC_HANDLE scm;
5833 event = MSI_RecordGetInteger( rec, 3 );
5834 if (!(event & msidbServiceControlEventStop))
5835 return ERROR_SUCCESS;
5837 component = MSI_RecordGetString( rec, 6 );
5838 comp = msi_get_loaded_component( package, component );
5839 if (!comp)
5840 return ERROR_SUCCESS;
5842 comp->Action = msi_get_component_action( package, comp );
5843 if (comp->Action != INSTALLSTATE_ABSENT)
5845 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5846 return ERROR_SUCCESS;
5849 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5850 if (!scm)
5852 ERR("Failed to open the service control manager\n");
5853 goto done;
5856 len = 0;
5857 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5858 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5860 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5861 GetServiceDisplayNameW( scm, name, display_name, &len );
5863 CloseServiceHandle( scm );
5865 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5866 stop_service( name );
5868 done:
5869 uirow = MSI_CreateRecord( 2 );
5870 MSI_RecordSetStringW( uirow, 1, display_name );
5871 MSI_RecordSetStringW( uirow, 2, name );
5872 msi_ui_actiondata( package, szStopServices, uirow );
5873 msiobj_release( &uirow->hdr );
5875 msi_free( name );
5876 msi_free( display_name );
5877 return ERROR_SUCCESS;
5880 static UINT ACTION_StopServices( MSIPACKAGE *package )
5882 UINT rc;
5883 MSIQUERY *view;
5885 static const WCHAR query[] = {
5886 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5887 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5889 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5890 if (rc != ERROR_SUCCESS)
5891 return ERROR_SUCCESS;
5893 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5894 msiobj_release(&view->hdr);
5896 return rc;
5899 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5901 MSIPACKAGE *package = param;
5902 MSICOMPONENT *comp;
5903 MSIRECORD *uirow;
5904 LPCWSTR component;
5905 LPWSTR name = NULL, display_name = NULL;
5906 DWORD event, len;
5907 SC_HANDLE scm = NULL, service = NULL;
5909 event = MSI_RecordGetInteger( rec, 3 );
5910 if (!(event & msidbServiceControlEventDelete))
5911 return ERROR_SUCCESS;
5913 component = MSI_RecordGetString(rec, 6);
5914 comp = msi_get_loaded_component(package, component);
5915 if (!comp)
5916 return ERROR_SUCCESS;
5918 comp->Action = msi_get_component_action( package, comp );
5919 if (comp->Action != INSTALLSTATE_ABSENT)
5921 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5922 return ERROR_SUCCESS;
5925 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5926 stop_service( name );
5928 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5929 if (!scm)
5931 WARN("Failed to open the SCM: %d\n", GetLastError());
5932 goto done;
5935 len = 0;
5936 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5937 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5939 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5940 GetServiceDisplayNameW( scm, name, display_name, &len );
5943 service = OpenServiceW( scm, name, DELETE );
5944 if (!service)
5946 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5947 goto done;
5950 if (!DeleteService( service ))
5951 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5953 done:
5954 uirow = MSI_CreateRecord( 2 );
5955 MSI_RecordSetStringW( uirow, 1, display_name );
5956 MSI_RecordSetStringW( uirow, 2, name );
5957 msi_ui_actiondata( package, szDeleteServices, uirow );
5958 msiobj_release( &uirow->hdr );
5960 CloseServiceHandle( service );
5961 CloseServiceHandle( scm );
5962 msi_free( name );
5963 msi_free( display_name );
5965 return ERROR_SUCCESS;
5968 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5970 UINT rc;
5971 MSIQUERY *view;
5973 static const WCHAR query[] = {
5974 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5975 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5977 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5978 if (rc != ERROR_SUCCESS)
5979 return ERROR_SUCCESS;
5981 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5982 msiobj_release( &view->hdr );
5984 return rc;
5987 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5989 MSIPACKAGE *package = param;
5990 LPWSTR driver, driver_path, ptr;
5991 WCHAR outpath[MAX_PATH];
5992 MSIFILE *driver_file = NULL, *setup_file = NULL;
5993 MSICOMPONENT *comp;
5994 MSIRECORD *uirow;
5995 LPCWSTR desc, file_key, component;
5996 DWORD len, usage;
5997 UINT r = ERROR_SUCCESS;
5999 static const WCHAR driver_fmt[] = {
6000 'D','r','i','v','e','r','=','%','s',0};
6001 static const WCHAR setup_fmt[] = {
6002 'S','e','t','u','p','=','%','s',0};
6003 static const WCHAR usage_fmt[] = {
6004 'F','i','l','e','U','s','a','g','e','=','1',0};
6006 component = MSI_RecordGetString( rec, 2 );
6007 comp = msi_get_loaded_component( package, component );
6008 if (!comp)
6009 return ERROR_SUCCESS;
6011 comp->Action = msi_get_component_action( package, comp );
6012 if (comp->Action != INSTALLSTATE_LOCAL)
6014 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6015 return ERROR_SUCCESS;
6017 desc = MSI_RecordGetString(rec, 3);
6019 file_key = MSI_RecordGetString( rec, 4 );
6020 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6022 file_key = MSI_RecordGetString( rec, 5 );
6023 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6025 if (!driver_file)
6027 ERR("ODBC Driver entry not found!\n");
6028 return ERROR_FUNCTION_FAILED;
6031 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6032 if (setup_file)
6033 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6034 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6036 driver = msi_alloc(len * sizeof(WCHAR));
6037 if (!driver)
6038 return ERROR_OUTOFMEMORY;
6040 ptr = driver;
6041 lstrcpyW(ptr, desc);
6042 ptr += lstrlenW(ptr) + 1;
6044 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6045 ptr += len + 1;
6047 if (setup_file)
6049 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6050 ptr += len + 1;
6053 lstrcpyW(ptr, usage_fmt);
6054 ptr += lstrlenW(ptr) + 1;
6055 *ptr = '\0';
6057 driver_path = strdupW(driver_file->TargetPath);
6058 ptr = strrchrW(driver_path, '\\');
6059 if (ptr) *ptr = '\0';
6061 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6062 NULL, ODBC_INSTALL_COMPLETE, &usage))
6064 ERR("Failed to install SQL driver!\n");
6065 r = ERROR_FUNCTION_FAILED;
6068 uirow = MSI_CreateRecord( 5 );
6069 MSI_RecordSetStringW( uirow, 1, desc );
6070 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6071 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6072 msi_ui_actiondata( package, szInstallODBC, uirow );
6073 msiobj_release( &uirow->hdr );
6075 msi_free(driver);
6076 msi_free(driver_path);
6078 return r;
6081 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6083 MSIPACKAGE *package = param;
6084 LPWSTR translator, translator_path, ptr;
6085 WCHAR outpath[MAX_PATH];
6086 MSIFILE *translator_file = NULL, *setup_file = NULL;
6087 MSICOMPONENT *comp;
6088 MSIRECORD *uirow;
6089 LPCWSTR desc, file_key, component;
6090 DWORD len, usage;
6091 UINT r = ERROR_SUCCESS;
6093 static const WCHAR translator_fmt[] = {
6094 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6095 static const WCHAR setup_fmt[] = {
6096 'S','e','t','u','p','=','%','s',0};
6098 component = MSI_RecordGetString( rec, 2 );
6099 comp = msi_get_loaded_component( package, component );
6100 if (!comp)
6101 return ERROR_SUCCESS;
6103 comp->Action = msi_get_component_action( package, comp );
6104 if (comp->Action != INSTALLSTATE_LOCAL)
6106 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6107 return ERROR_SUCCESS;
6109 desc = MSI_RecordGetString(rec, 3);
6111 file_key = MSI_RecordGetString( rec, 4 );
6112 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6114 file_key = MSI_RecordGetString( rec, 5 );
6115 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6117 if (!translator_file)
6119 ERR("ODBC Translator entry not found!\n");
6120 return ERROR_FUNCTION_FAILED;
6123 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6124 if (setup_file)
6125 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6127 translator = msi_alloc(len * sizeof(WCHAR));
6128 if (!translator)
6129 return ERROR_OUTOFMEMORY;
6131 ptr = translator;
6132 lstrcpyW(ptr, desc);
6133 ptr += lstrlenW(ptr) + 1;
6135 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6136 ptr += len + 1;
6138 if (setup_file)
6140 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6141 ptr += len + 1;
6143 *ptr = '\0';
6145 translator_path = strdupW(translator_file->TargetPath);
6146 ptr = strrchrW(translator_path, '\\');
6147 if (ptr) *ptr = '\0';
6149 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6150 NULL, ODBC_INSTALL_COMPLETE, &usage))
6152 ERR("Failed to install SQL translator!\n");
6153 r = ERROR_FUNCTION_FAILED;
6156 uirow = MSI_CreateRecord( 5 );
6157 MSI_RecordSetStringW( uirow, 1, desc );
6158 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6159 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6160 msi_ui_actiondata( package, szInstallODBC, uirow );
6161 msiobj_release( &uirow->hdr );
6163 msi_free(translator);
6164 msi_free(translator_path);
6166 return r;
6169 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6171 MSIPACKAGE *package = param;
6172 MSICOMPONENT *comp;
6173 LPWSTR attrs;
6174 LPCWSTR desc, driver, component;
6175 WORD request = ODBC_ADD_SYS_DSN;
6176 INT registration;
6177 DWORD len;
6178 UINT r = ERROR_SUCCESS;
6179 MSIRECORD *uirow;
6181 static const WCHAR attrs_fmt[] = {
6182 'D','S','N','=','%','s',0 };
6184 component = MSI_RecordGetString( rec, 2 );
6185 comp = msi_get_loaded_component( package, component );
6186 if (!comp)
6187 return ERROR_SUCCESS;
6189 comp->Action = msi_get_component_action( package, comp );
6190 if (comp->Action != INSTALLSTATE_LOCAL)
6192 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6193 return ERROR_SUCCESS;
6196 desc = MSI_RecordGetString(rec, 3);
6197 driver = MSI_RecordGetString(rec, 4);
6198 registration = MSI_RecordGetInteger(rec, 5);
6200 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6201 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6203 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6204 attrs = msi_alloc(len * sizeof(WCHAR));
6205 if (!attrs)
6206 return ERROR_OUTOFMEMORY;
6208 len = sprintfW(attrs, attrs_fmt, desc);
6209 attrs[len + 1] = 0;
6211 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6213 ERR("Failed to install SQL data source!\n");
6214 r = ERROR_FUNCTION_FAILED;
6217 uirow = MSI_CreateRecord( 5 );
6218 MSI_RecordSetStringW( uirow, 1, desc );
6219 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6220 MSI_RecordSetInteger( uirow, 3, request );
6221 msi_ui_actiondata( package, szInstallODBC, uirow );
6222 msiobj_release( &uirow->hdr );
6224 msi_free(attrs);
6226 return r;
6229 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6231 UINT rc;
6232 MSIQUERY *view;
6234 static const WCHAR driver_query[] = {
6235 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6236 'O','D','B','C','D','r','i','v','e','r',0 };
6238 static const WCHAR translator_query[] = {
6239 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6240 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6242 static const WCHAR source_query[] = {
6243 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6244 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6246 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6247 if (rc != ERROR_SUCCESS)
6248 return ERROR_SUCCESS;
6250 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6251 msiobj_release(&view->hdr);
6253 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6254 if (rc != ERROR_SUCCESS)
6255 return ERROR_SUCCESS;
6257 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6258 msiobj_release(&view->hdr);
6260 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6261 if (rc != ERROR_SUCCESS)
6262 return ERROR_SUCCESS;
6264 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6265 msiobj_release(&view->hdr);
6267 return rc;
6270 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6272 MSIPACKAGE *package = param;
6273 MSICOMPONENT *comp;
6274 MSIRECORD *uirow;
6275 DWORD usage;
6276 LPCWSTR desc, component;
6278 component = MSI_RecordGetString( rec, 2 );
6279 comp = msi_get_loaded_component( package, component );
6280 if (!comp)
6281 return ERROR_SUCCESS;
6283 comp->Action = msi_get_component_action( package, comp );
6284 if (comp->Action != INSTALLSTATE_ABSENT)
6286 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6287 return ERROR_SUCCESS;
6290 desc = MSI_RecordGetString( rec, 3 );
6291 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6293 WARN("Failed to remove ODBC driver\n");
6295 else if (!usage)
6297 FIXME("Usage count reached 0\n");
6300 uirow = MSI_CreateRecord( 2 );
6301 MSI_RecordSetStringW( uirow, 1, desc );
6302 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6303 msi_ui_actiondata( package, szRemoveODBC, uirow );
6304 msiobj_release( &uirow->hdr );
6306 return ERROR_SUCCESS;
6309 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6311 MSIPACKAGE *package = param;
6312 MSICOMPONENT *comp;
6313 MSIRECORD *uirow;
6314 DWORD usage;
6315 LPCWSTR desc, component;
6317 component = MSI_RecordGetString( rec, 2 );
6318 comp = msi_get_loaded_component( package, component );
6319 if (!comp)
6320 return ERROR_SUCCESS;
6322 comp->Action = msi_get_component_action( package, comp );
6323 if (comp->Action != INSTALLSTATE_ABSENT)
6325 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6326 return ERROR_SUCCESS;
6329 desc = MSI_RecordGetString( rec, 3 );
6330 if (!SQLRemoveTranslatorW( desc, &usage ))
6332 WARN("Failed to remove ODBC translator\n");
6334 else if (!usage)
6336 FIXME("Usage count reached 0\n");
6339 uirow = MSI_CreateRecord( 2 );
6340 MSI_RecordSetStringW( uirow, 1, desc );
6341 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6342 msi_ui_actiondata( package, szRemoveODBC, uirow );
6343 msiobj_release( &uirow->hdr );
6345 return ERROR_SUCCESS;
6348 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6350 MSIPACKAGE *package = param;
6351 MSICOMPONENT *comp;
6352 MSIRECORD *uirow;
6353 LPWSTR attrs;
6354 LPCWSTR desc, driver, component;
6355 WORD request = ODBC_REMOVE_SYS_DSN;
6356 INT registration;
6357 DWORD len;
6359 static const WCHAR attrs_fmt[] = {
6360 'D','S','N','=','%','s',0 };
6362 component = MSI_RecordGetString( rec, 2 );
6363 comp = msi_get_loaded_component( package, component );
6364 if (!comp)
6365 return ERROR_SUCCESS;
6367 comp->Action = msi_get_component_action( package, comp );
6368 if (comp->Action != INSTALLSTATE_ABSENT)
6370 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6371 return ERROR_SUCCESS;
6374 desc = MSI_RecordGetString( rec, 3 );
6375 driver = MSI_RecordGetString( rec, 4 );
6376 registration = MSI_RecordGetInteger( rec, 5 );
6378 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6379 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6381 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6382 attrs = msi_alloc( len * sizeof(WCHAR) );
6383 if (!attrs)
6384 return ERROR_OUTOFMEMORY;
6386 FIXME("Use ODBCSourceAttribute table\n");
6388 len = sprintfW( attrs, attrs_fmt, desc );
6389 attrs[len + 1] = 0;
6391 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6393 WARN("Failed to remove ODBC data source\n");
6395 msi_free( attrs );
6397 uirow = MSI_CreateRecord( 3 );
6398 MSI_RecordSetStringW( uirow, 1, desc );
6399 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6400 MSI_RecordSetInteger( uirow, 3, request );
6401 msi_ui_actiondata( package, szRemoveODBC, uirow );
6402 msiobj_release( &uirow->hdr );
6404 return ERROR_SUCCESS;
6407 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6409 UINT rc;
6410 MSIQUERY *view;
6412 static const WCHAR driver_query[] = {
6413 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6414 'O','D','B','C','D','r','i','v','e','r',0 };
6416 static const WCHAR translator_query[] = {
6417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6418 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6420 static const WCHAR source_query[] = {
6421 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6422 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6424 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6425 if (rc != ERROR_SUCCESS)
6426 return ERROR_SUCCESS;
6428 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6429 msiobj_release( &view->hdr );
6431 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6432 if (rc != ERROR_SUCCESS)
6433 return ERROR_SUCCESS;
6435 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6436 msiobj_release( &view->hdr );
6438 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6439 if (rc != ERROR_SUCCESS)
6440 return ERROR_SUCCESS;
6442 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6443 msiobj_release( &view->hdr );
6445 return rc;
6448 #define ENV_ACT_SETALWAYS 0x1
6449 #define ENV_ACT_SETABSENT 0x2
6450 #define ENV_ACT_REMOVE 0x4
6451 #define ENV_ACT_REMOVEMATCH 0x8
6453 #define ENV_MOD_MACHINE 0x20000000
6454 #define ENV_MOD_APPEND 0x40000000
6455 #define ENV_MOD_PREFIX 0x80000000
6456 #define ENV_MOD_MASK 0xC0000000
6458 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6460 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6462 LPCWSTR cptr = *name;
6464 static const WCHAR prefix[] = {'[','~',']',0};
6465 static const int prefix_len = 3;
6467 *flags = 0;
6468 while (*cptr)
6470 if (*cptr == '=')
6471 *flags |= ENV_ACT_SETALWAYS;
6472 else if (*cptr == '+')
6473 *flags |= ENV_ACT_SETABSENT;
6474 else if (*cptr == '-')
6475 *flags |= ENV_ACT_REMOVE;
6476 else if (*cptr == '!')
6477 *flags |= ENV_ACT_REMOVEMATCH;
6478 else if (*cptr == '*')
6479 *flags |= ENV_MOD_MACHINE;
6480 else
6481 break;
6483 cptr++;
6484 (*name)++;
6487 if (!*cptr)
6489 ERR("Missing environment variable\n");
6490 return ERROR_FUNCTION_FAILED;
6493 if (*value)
6495 LPCWSTR ptr = *value;
6496 if (!strncmpW(ptr, prefix, prefix_len))
6498 if (ptr[prefix_len] == szSemiColon[0])
6500 *flags |= ENV_MOD_APPEND;
6501 *value += lstrlenW(prefix);
6503 else
6505 *value = NULL;
6508 else if (lstrlenW(*value) >= prefix_len)
6510 ptr += lstrlenW(ptr) - prefix_len;
6511 if (!strcmpW( ptr, prefix ))
6513 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6515 *flags |= ENV_MOD_PREFIX;
6516 /* the "[~]" will be removed by deformat_string */;
6518 else
6520 *value = NULL;
6526 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6527 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6528 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6529 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6531 ERR("Invalid flags: %08x\n", *flags);
6532 return ERROR_FUNCTION_FAILED;
6535 if (!*flags)
6536 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6538 return ERROR_SUCCESS;
6541 static UINT open_env_key( DWORD flags, HKEY *key )
6543 static const WCHAR user_env[] =
6544 {'E','n','v','i','r','o','n','m','e','n','t',0};
6545 static const WCHAR machine_env[] =
6546 {'S','y','s','t','e','m','\\',
6547 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6548 'C','o','n','t','r','o','l','\\',
6549 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6550 'E','n','v','i','r','o','n','m','e','n','t',0};
6551 const WCHAR *env;
6552 HKEY root;
6553 LONG res;
6555 if (flags & ENV_MOD_MACHINE)
6557 env = machine_env;
6558 root = HKEY_LOCAL_MACHINE;
6560 else
6562 env = user_env;
6563 root = HKEY_CURRENT_USER;
6566 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6567 if (res != ERROR_SUCCESS)
6569 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6570 return ERROR_FUNCTION_FAILED;
6573 return ERROR_SUCCESS;
6576 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6578 MSIPACKAGE *package = param;
6579 LPCWSTR name, value, component;
6580 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6581 DWORD flags, type, size;
6582 UINT res;
6583 HKEY env = NULL;
6584 MSICOMPONENT *comp;
6585 MSIRECORD *uirow;
6586 int action = 0;
6588 component = MSI_RecordGetString(rec, 4);
6589 comp = msi_get_loaded_component(package, component);
6590 if (!comp)
6591 return ERROR_SUCCESS;
6593 comp->Action = msi_get_component_action( package, comp );
6594 if (comp->Action != INSTALLSTATE_LOCAL)
6596 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6597 return ERROR_SUCCESS;
6599 name = MSI_RecordGetString(rec, 2);
6600 value = MSI_RecordGetString(rec, 3);
6602 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6604 res = env_parse_flags(&name, &value, &flags);
6605 if (res != ERROR_SUCCESS || !value)
6606 goto done;
6608 if (value && !deformat_string(package, value, &deformatted))
6610 res = ERROR_OUTOFMEMORY;
6611 goto done;
6614 value = deformatted;
6616 res = open_env_key( flags, &env );
6617 if (res != ERROR_SUCCESS)
6618 goto done;
6620 if (flags & ENV_MOD_MACHINE)
6621 action |= 0x20000000;
6623 size = 0;
6624 type = REG_SZ;
6625 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6626 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6627 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6628 goto done;
6630 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6632 action = 0x2;
6634 /* Nothing to do. */
6635 if (!value)
6637 res = ERROR_SUCCESS;
6638 goto done;
6641 /* If we are appending but the string was empty, strip ; */
6642 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6644 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6645 newval = strdupW(value);
6646 if (!newval)
6648 res = ERROR_OUTOFMEMORY;
6649 goto done;
6652 else
6654 action = 0x1;
6656 /* Contrary to MSDN, +-variable to [~];path works */
6657 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6659 res = ERROR_SUCCESS;
6660 goto done;
6663 data = msi_alloc(size);
6664 if (!data)
6666 RegCloseKey(env);
6667 return ERROR_OUTOFMEMORY;
6670 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6671 if (res != ERROR_SUCCESS)
6672 goto done;
6674 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6676 action = 0x4;
6677 res = RegDeleteValueW(env, name);
6678 if (res != ERROR_SUCCESS)
6679 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6680 goto done;
6683 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6684 if (flags & ENV_MOD_MASK)
6686 DWORD mod_size;
6687 int multiplier = 0;
6688 if (flags & ENV_MOD_APPEND) multiplier++;
6689 if (flags & ENV_MOD_PREFIX) multiplier++;
6690 mod_size = lstrlenW(value) * multiplier;
6691 size += mod_size * sizeof(WCHAR);
6694 newval = msi_alloc(size);
6695 ptr = newval;
6696 if (!newval)
6698 res = ERROR_OUTOFMEMORY;
6699 goto done;
6702 if (flags & ENV_MOD_PREFIX)
6704 lstrcpyW(newval, value);
6705 ptr = newval + lstrlenW(value);
6706 action |= 0x80000000;
6709 lstrcpyW(ptr, data);
6711 if (flags & ENV_MOD_APPEND)
6713 lstrcatW(newval, value);
6714 action |= 0x40000000;
6717 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6718 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6719 if (res)
6721 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6724 done:
6725 uirow = MSI_CreateRecord( 3 );
6726 MSI_RecordSetStringW( uirow, 1, name );
6727 MSI_RecordSetStringW( uirow, 2, newval );
6728 MSI_RecordSetInteger( uirow, 3, action );
6729 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6730 msiobj_release( &uirow->hdr );
6732 if (env) RegCloseKey(env);
6733 msi_free(deformatted);
6734 msi_free(data);
6735 msi_free(newval);
6736 return res;
6739 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6741 UINT rc;
6742 MSIQUERY * view;
6743 static const WCHAR ExecSeqQuery[] =
6744 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6745 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6746 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6747 if (rc != ERROR_SUCCESS)
6748 return ERROR_SUCCESS;
6750 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6751 msiobj_release(&view->hdr);
6753 return rc;
6756 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6758 MSIPACKAGE *package = param;
6759 LPCWSTR name, value, component;
6760 LPWSTR deformatted = NULL;
6761 DWORD flags;
6762 HKEY env;
6763 MSICOMPONENT *comp;
6764 MSIRECORD *uirow;
6765 int action = 0;
6766 LONG res;
6767 UINT r;
6769 component = MSI_RecordGetString( rec, 4 );
6770 comp = msi_get_loaded_component( package, component );
6771 if (!comp)
6772 return ERROR_SUCCESS;
6774 comp->Action = msi_get_component_action( package, comp );
6775 if (comp->Action != INSTALLSTATE_ABSENT)
6777 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6778 return ERROR_SUCCESS;
6780 name = MSI_RecordGetString( rec, 2 );
6781 value = MSI_RecordGetString( rec, 3 );
6783 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6785 r = env_parse_flags( &name, &value, &flags );
6786 if (r != ERROR_SUCCESS)
6787 return r;
6789 if (!(flags & ENV_ACT_REMOVE))
6791 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6792 return ERROR_SUCCESS;
6795 if (value && !deformat_string( package, value, &deformatted ))
6796 return ERROR_OUTOFMEMORY;
6798 value = deformatted;
6800 r = open_env_key( flags, &env );
6801 if (r != ERROR_SUCCESS)
6803 r = ERROR_SUCCESS;
6804 goto done;
6807 if (flags & ENV_MOD_MACHINE)
6808 action |= 0x20000000;
6810 TRACE("Removing %s\n", debugstr_w(name));
6812 res = RegDeleteValueW( env, name );
6813 if (res != ERROR_SUCCESS)
6815 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6816 r = ERROR_SUCCESS;
6819 done:
6820 uirow = MSI_CreateRecord( 3 );
6821 MSI_RecordSetStringW( uirow, 1, name );
6822 MSI_RecordSetStringW( uirow, 2, value );
6823 MSI_RecordSetInteger( uirow, 3, action );
6824 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6825 msiobj_release( &uirow->hdr );
6827 if (env) RegCloseKey( env );
6828 msi_free( deformatted );
6829 return r;
6832 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6834 UINT rc;
6835 MSIQUERY *view;
6836 static const WCHAR query[] =
6837 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6838 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6840 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6841 if (rc != ERROR_SUCCESS)
6842 return ERROR_SUCCESS;
6844 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6845 msiobj_release( &view->hdr );
6847 return rc;
6850 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6852 LPWSTR key, template, id;
6853 UINT r = ERROR_SUCCESS;
6855 id = msi_dup_property( package->db, szProductID );
6856 if (id)
6858 msi_free( id );
6859 return ERROR_SUCCESS;
6861 template = msi_dup_property( package->db, szPIDTemplate );
6862 key = msi_dup_property( package->db, szPIDKEY );
6864 if (key && template)
6866 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6867 r = msi_set_property( package->db, szProductID, key );
6869 msi_free( template );
6870 msi_free( key );
6871 return r;
6874 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6876 TRACE("\n");
6877 package->need_reboot = 1;
6878 return ERROR_SUCCESS;
6881 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6883 static const WCHAR szAvailableFreeReg[] =
6884 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6885 MSIRECORD *uirow;
6886 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6888 TRACE("%p %d kilobytes\n", package, space);
6890 uirow = MSI_CreateRecord( 1 );
6891 MSI_RecordSetInteger( uirow, 1, space );
6892 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6893 msiobj_release( &uirow->hdr );
6895 return ERROR_SUCCESS;
6898 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6900 TRACE("%p\n", package);
6902 msi_set_property( package->db, szRollbackDisabled, szOne );
6903 return ERROR_SUCCESS;
6906 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6908 FIXME("%p\n", package);
6909 return ERROR_SUCCESS;
6912 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6914 UINT r, count;
6915 MSIQUERY *view;
6917 static const WCHAR driver_query[] = {
6918 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6919 'O','D','B','C','D','r','i','v','e','r',0 };
6921 static const WCHAR translator_query[] = {
6922 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6923 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6925 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6926 if (r == ERROR_SUCCESS)
6928 count = 0;
6929 r = MSI_IterateRecords( view, &count, NULL, package );
6930 msiobj_release( &view->hdr );
6931 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6934 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6935 if (r == ERROR_SUCCESS)
6937 count = 0;
6938 r = MSI_IterateRecords( view, &count, NULL, package );
6939 msiobj_release( &view->hdr );
6940 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6943 return ERROR_SUCCESS;
6946 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6948 MSIPACKAGE *package = param;
6949 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6950 WCHAR *value;
6952 if ((value = msi_dup_property( package->db, property )))
6954 FIXME("remove %s\n", debugstr_w(value));
6955 msi_free( value );
6957 return ERROR_SUCCESS;
6960 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6962 UINT r;
6963 MSIQUERY *view;
6965 static const WCHAR query[] =
6966 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
6967 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
6969 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6970 if (r == ERROR_SUCCESS)
6972 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6973 msiobj_release( &view->hdr );
6975 return ERROR_SUCCESS;
6978 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6980 MSIPACKAGE *package = param;
6981 int attributes = MSI_RecordGetInteger( rec, 5 );
6983 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6985 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6986 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6987 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6988 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6989 HKEY hkey;
6990 UINT r;
6992 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6994 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6995 if (r != ERROR_SUCCESS)
6996 return ERROR_SUCCESS;
6998 else
7000 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7001 if (r != ERROR_SUCCESS)
7002 return ERROR_SUCCESS;
7004 RegCloseKey( hkey );
7006 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7007 debugstr_w(upgrade_code), debugstr_w(version_min),
7008 debugstr_w(version_max), debugstr_w(language));
7010 return ERROR_SUCCESS;
7013 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7015 UINT r;
7016 MSIQUERY *view;
7017 static const WCHAR query[] =
7018 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7020 if (msi_get_property_int( package->db, szInstalled, 0 ))
7022 TRACE("product is installed, skipping action\n");
7023 return ERROR_SUCCESS;
7025 if (msi_get_property_int( package->db, szPreselected, 0 ))
7027 TRACE("Preselected property is set, not migrating feature states\n");
7028 return ERROR_SUCCESS;
7031 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7032 if (r == ERROR_SUCCESS)
7034 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7035 msiobj_release( &view->hdr );
7037 return ERROR_SUCCESS;
7040 static void bind_image( const char *filename, const char *path )
7042 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7044 WARN("failed to bind image %u\n", GetLastError());
7048 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7050 UINT i;
7051 MSIFILE *file;
7052 MSIPACKAGE *package = param;
7053 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7054 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7055 char *filenameA, *pathA;
7056 WCHAR *pathW, **path_list;
7058 if (!(file = msi_get_loaded_file( package, key )))
7060 WARN("file %s not found\n", debugstr_w(key));
7061 return ERROR_SUCCESS;
7063 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7064 path_list = msi_split_string( paths, ';' );
7065 if (!path_list) bind_image( filenameA, NULL );
7066 else
7068 for (i = 0; path_list[i] && path_list[i][0]; i++)
7070 deformat_string( package, path_list[i], &pathW );
7071 if ((pathA = strdupWtoA( pathW )))
7073 bind_image( filenameA, pathA );
7074 msi_free( pathA );
7076 msi_free( pathW );
7079 msi_free( path_list );
7080 msi_free( filenameA );
7081 return ERROR_SUCCESS;
7084 static UINT ACTION_BindImage( MSIPACKAGE *package )
7086 UINT r;
7087 MSIQUERY *view;
7088 static const WCHAR query[] =
7089 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','B','i','n','d','I','m','a','g','e',0};
7091 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7092 if (r == ERROR_SUCCESS)
7094 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7095 msiobj_release( &view->hdr );
7097 return ERROR_SUCCESS;
7100 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7101 LPCSTR action, LPCWSTR table )
7103 static const WCHAR query[] = {
7104 'S','E','L','E','C','T',' ','*',' ',
7105 'F','R','O','M',' ','`','%','s','`',0 };
7106 MSIQUERY *view = NULL;
7107 DWORD count = 0;
7108 UINT r;
7110 r = MSI_OpenQuery( package->db, &view, query, table );
7111 if (r == ERROR_SUCCESS)
7113 r = MSI_IterateRecords(view, &count, NULL, package);
7114 msiobj_release(&view->hdr);
7117 if (count)
7118 FIXME("%s -> %u ignored %s table values\n",
7119 action, count, debugstr_w(table));
7121 return ERROR_SUCCESS;
7124 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7126 static const WCHAR table[] = {
7127 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7128 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7131 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7133 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7134 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7137 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7139 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7140 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7143 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7145 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7146 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7149 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7151 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7152 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7155 static const struct
7157 const WCHAR *action;
7158 UINT (*handler)(MSIPACKAGE *);
7159 const WCHAR *action_rollback;
7161 StandardActions[] =
7163 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7164 { szAppSearch, ACTION_AppSearch, NULL },
7165 { szBindImage, ACTION_BindImage, NULL },
7166 { szCCPSearch, ACTION_CCPSearch, NULL },
7167 { szCostFinalize, ACTION_CostFinalize, NULL },
7168 { szCostInitialize, ACTION_CostInitialize, NULL },
7169 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7170 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7171 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7172 { szDisableRollback, ACTION_DisableRollback, NULL },
7173 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7174 { szExecuteAction, ACTION_ExecuteAction, NULL },
7175 { szFileCost, ACTION_FileCost, NULL },
7176 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7177 { szForceReboot, ACTION_ForceReboot, NULL },
7178 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7179 { szInstallExecute, ACTION_InstallExecute, NULL },
7180 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7181 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7182 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7183 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7184 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7185 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7186 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7187 { szInstallValidate, ACTION_InstallValidate, NULL },
7188 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7189 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7190 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7191 { szMoveFiles, ACTION_MoveFiles, NULL },
7192 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7193 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7194 { szPatchFiles, ACTION_PatchFiles, NULL },
7195 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7196 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7197 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7198 { szPublishProduct, ACTION_PublishProduct, NULL },
7199 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7200 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7201 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7202 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7203 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7204 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7205 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7206 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7207 { szRegisterUser, ACTION_RegisterUser, NULL },
7208 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7209 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7210 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7211 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7212 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7213 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7214 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7215 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7216 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7217 { szResolveSource, ACTION_ResolveSource, NULL },
7218 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7219 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7220 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7221 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfUnregModules },
7222 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7223 { szStartServices, ACTION_StartServices, szStopServices },
7224 { szStopServices, ACTION_StopServices, szStartServices },
7225 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7226 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7227 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7228 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7229 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7230 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7231 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7232 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7233 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7234 { szValidateProductID, ACTION_ValidateProductID, NULL },
7235 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7236 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7237 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7238 { NULL, NULL, NULL }
7241 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7243 BOOL ret = FALSE;
7244 UINT i;
7246 i = 0;
7247 while (StandardActions[i].action != NULL)
7249 if (!strcmpW( StandardActions[i].action, action ))
7251 ui_actionstart( package, action );
7252 if (StandardActions[i].handler)
7254 ui_actioninfo( package, action, TRUE, 0 );
7255 *rc = StandardActions[i].handler( package );
7256 ui_actioninfo( package, action, FALSE, *rc );
7258 if (StandardActions[i].action_rollback && !package->need_rollback)
7260 TRACE("scheduling rollback action\n");
7261 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7264 else
7266 FIXME("unhandled standard action %s\n", debugstr_w(action));
7267 *rc = ERROR_SUCCESS;
7269 ret = TRUE;
7270 break;
7272 i++;
7274 return ret;
7277 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7279 UINT rc = ERROR_SUCCESS;
7280 BOOL handled;
7282 TRACE("Performing action (%s)\n", debugstr_w(action));
7284 handled = ACTION_HandleStandardAction(package, action, &rc);
7286 if (!handled)
7287 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7289 if (!handled)
7291 WARN("unhandled msi action %s\n", debugstr_w(action));
7292 rc = ERROR_FUNCTION_NOT_CALLED;
7295 return rc;
7298 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7300 UINT rc = ERROR_SUCCESS;
7301 BOOL handled = FALSE;
7303 TRACE("Performing action (%s)\n", debugstr_w(action));
7305 handled = ACTION_HandleStandardAction(package, action, &rc);
7307 if (!handled)
7308 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7310 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7311 handled = TRUE;
7313 if (!handled)
7315 WARN("unhandled msi action %s\n", debugstr_w(action));
7316 rc = ERROR_FUNCTION_NOT_CALLED;
7319 return rc;
7322 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7324 UINT rc = ERROR_SUCCESS;
7325 MSIRECORD *row;
7327 static const WCHAR ExecSeqQuery[] =
7328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7329 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7330 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7331 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7332 static const WCHAR UISeqQuery[] =
7333 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7334 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7335 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7336 ' ', '=',' ','%','i',0};
7338 if (needs_ui_sequence(package))
7339 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7340 else
7341 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7343 if (row)
7345 LPCWSTR action, cond;
7347 TRACE("Running the actions\n");
7349 /* check conditions */
7350 cond = MSI_RecordGetString(row, 2);
7352 /* this is a hack to skip errors in the condition code */
7353 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7355 msiobj_release(&row->hdr);
7356 return ERROR_SUCCESS;
7359 action = MSI_RecordGetString(row, 1);
7360 if (!action)
7362 ERR("failed to fetch action\n");
7363 msiobj_release(&row->hdr);
7364 return ERROR_FUNCTION_FAILED;
7367 if (needs_ui_sequence(package))
7368 rc = ACTION_PerformUIAction(package, action, -1);
7369 else
7370 rc = ACTION_PerformAction(package, action, -1);
7372 msiobj_release(&row->hdr);
7375 return rc;
7378 /****************************************************
7379 * TOP level entry points
7380 *****************************************************/
7382 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7383 LPCWSTR szCommandLine )
7385 UINT rc;
7386 BOOL ui_exists;
7387 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7388 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7389 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7391 msi_set_property( package->db, szAction, szInstall );
7393 package->script->InWhatSequence = SEQUENCE_INSTALL;
7395 if (szPackagePath)
7397 LPWSTR p, dir;
7398 LPCWSTR file;
7400 dir = strdupW(szPackagePath);
7401 p = strrchrW(dir, '\\');
7402 if (p)
7404 *(++p) = 0;
7405 file = szPackagePath + (p - dir);
7407 else
7409 msi_free(dir);
7410 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7411 GetCurrentDirectoryW(MAX_PATH, dir);
7412 lstrcatW(dir, szBackSlash);
7413 file = szPackagePath;
7416 msi_free( package->PackagePath );
7417 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7418 if (!package->PackagePath)
7420 msi_free(dir);
7421 return ERROR_OUTOFMEMORY;
7424 lstrcpyW(package->PackagePath, dir);
7425 lstrcatW(package->PackagePath, file);
7426 msi_free(dir);
7428 msi_set_sourcedir_props(package, FALSE);
7431 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7432 if (rc != ERROR_SUCCESS)
7433 return rc;
7435 msi_apply_transforms( package );
7436 msi_apply_patches( package );
7438 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7440 TRACE("setting reinstall property\n");
7441 msi_set_property( package->db, szReinstall, szAll );
7444 /* properties may have been added by a transform */
7445 msi_clone_properties( package );
7447 msi_parse_command_line( package, szCommandLine, FALSE );
7448 msi_adjust_privilege_properties( package );
7449 msi_set_context( package );
7451 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7453 TRACE("disabling rollback\n");
7454 msi_set_property( package->db, szRollbackDisabled, szOne );
7457 if (needs_ui_sequence( package))
7459 package->script->InWhatSequence |= SEQUENCE_UI;
7460 rc = ACTION_ProcessUISequence(package);
7461 ui_exists = ui_sequence_exists(package);
7462 if (rc == ERROR_SUCCESS || !ui_exists)
7464 package->script->InWhatSequence |= SEQUENCE_EXEC;
7465 rc = ACTION_ProcessExecSequence(package, ui_exists);
7468 else
7469 rc = ACTION_ProcessExecSequence(package, FALSE);
7471 package->script->CurrentlyScripting = FALSE;
7473 /* process the ending type action */
7474 if (rc == ERROR_SUCCESS)
7475 ACTION_PerformActionSequence(package, -1);
7476 else if (rc == ERROR_INSTALL_USEREXIT)
7477 ACTION_PerformActionSequence(package, -2);
7478 else if (rc == ERROR_INSTALL_SUSPEND)
7479 ACTION_PerformActionSequence(package, -4);
7480 else /* failed */
7482 ACTION_PerformActionSequence(package, -3);
7483 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7485 package->need_rollback = TRUE;
7489 /* finish up running custom actions */
7490 ACTION_FinishCustomActions(package);
7492 if (package->need_rollback)
7494 WARN("installation failed, running rollback script\n");
7495 execute_script( package, ROLLBACK_SCRIPT );
7498 if (rc == ERROR_SUCCESS && package->need_reboot)
7499 return ERROR_SUCCESS_REBOOT_REQUIRED;
7501 return rc;