msi: Implement and test MsiGetFeatureInfo.
[wine/multimedia.git] / dlls / msi / action.c
blob437c8d9a38b82962ec2c5ea841fa97cedc4529c1
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 UINT msi_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 UINT load_feature(MSIRECORD * row, LPVOID param)
1086 MSIPACKAGE* package = param;
1087 MSIFEATURE* feature;
1088 static const WCHAR Query1[] =
1089 {'S','E','L','E','C','T',' ',
1090 '`','C','o','m','p','o','n','e','n','t','_','`',
1091 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1092 'C','o','m','p','o','n','e','n','t','s','`',' ',
1093 'W','H','E','R','E',' ',
1094 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1095 MSIQUERY * view;
1096 UINT rc;
1097 _ilfs ilfs;
1099 /* fill in the data */
1101 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1102 if (!feature)
1103 return ERROR_NOT_ENOUGH_MEMORY;
1105 list_init( &feature->Children );
1106 list_init( &feature->Components );
1108 feature->Feature = msi_dup_record_field( row, 1 );
1110 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1112 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1113 feature->Title = msi_dup_record_field( row, 3 );
1114 feature->Description = msi_dup_record_field( row, 4 );
1116 if (!MSI_RecordIsNull(row,5))
1117 feature->Display = MSI_RecordGetInteger(row,5);
1119 feature->Level= MSI_RecordGetInteger(row,6);
1120 feature->Directory = msi_dup_record_field( row, 7 );
1121 feature->Attributes = MSI_RecordGetInteger(row,8);
1123 feature->Installed = INSTALLSTATE_UNKNOWN;
1124 feature->Action = INSTALLSTATE_UNKNOWN;
1125 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1127 list_add_tail( &package->features, &feature->entry );
1129 /* load feature components */
1131 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1132 if (rc != ERROR_SUCCESS)
1133 return ERROR_SUCCESS;
1135 ilfs.package = package;
1136 ilfs.feature = feature;
1138 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1139 msiobj_release(&view->hdr);
1141 return ERROR_SUCCESS;
1144 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1146 MSIPACKAGE *package = param;
1147 MSIFEATURE *parent, *child;
1149 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1150 if (!child)
1151 return ERROR_FUNCTION_FAILED;
1153 if (!child->Feature_Parent)
1154 return ERROR_SUCCESS;
1156 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1157 if (!parent)
1158 return ERROR_FUNCTION_FAILED;
1160 add_feature_child( parent, child );
1161 return ERROR_SUCCESS;
1164 UINT msi_load_all_features( MSIPACKAGE *package )
1166 static const WCHAR query[] = {
1167 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1168 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1169 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1170 MSIQUERY *view;
1171 UINT r;
1173 if (!list_empty(&package->features))
1174 return ERROR_SUCCESS;
1176 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1177 if (r != ERROR_SUCCESS)
1178 return r;
1180 r = MSI_IterateRecords( view, NULL, load_feature, package );
1181 if (r != ERROR_SUCCESS)
1182 return r;
1184 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1185 msiobj_release( &view->hdr );
1187 return r;
1190 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1192 if (!p)
1193 return p;
1194 p = strchrW(p, ch);
1195 if (!p)
1196 return p;
1197 *p = 0;
1198 return p+1;
1201 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1205 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1206 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1207 MSIQUERY *view = NULL;
1208 MSIRECORD *row = NULL;
1209 UINT r;
1211 TRACE("%s\n", debugstr_w(file->File));
1213 r = MSI_OpenQuery(package->db, &view, query, file->File);
1214 if (r != ERROR_SUCCESS)
1215 goto done;
1217 r = MSI_ViewExecute(view, NULL);
1218 if (r != ERROR_SUCCESS)
1219 goto done;
1221 r = MSI_ViewFetch(view, &row);
1222 if (r != ERROR_SUCCESS)
1223 goto done;
1225 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1226 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1227 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1228 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1229 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1231 done:
1232 if (view) msiobj_release(&view->hdr);
1233 if (row) msiobj_release(&row->hdr);
1234 return r;
1237 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1239 MSIRECORD *row;
1240 static const WCHAR query[] = {
1241 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1242 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1243 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1245 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1246 if (!row)
1248 WARN("query failed\n");
1249 return ERROR_FUNCTION_FAILED;
1252 file->disk_id = MSI_RecordGetInteger( row, 1 );
1253 msiobj_release( &row->hdr );
1254 return ERROR_SUCCESS;
1257 static UINT load_file(MSIRECORD *row, LPVOID param)
1259 MSIPACKAGE* package = param;
1260 LPCWSTR component;
1261 MSIFILE *file;
1263 /* fill in the data */
1265 file = msi_alloc_zero( sizeof (MSIFILE) );
1266 if (!file)
1267 return ERROR_NOT_ENOUGH_MEMORY;
1269 file->File = msi_dup_record_field( row, 1 );
1271 component = MSI_RecordGetString( row, 2 );
1272 file->Component = msi_get_loaded_component( package, component );
1274 if (!file->Component)
1276 WARN("Component not found: %s\n", debugstr_w(component));
1277 msi_free(file->File);
1278 msi_free(file);
1279 return ERROR_SUCCESS;
1282 file->FileName = msi_dup_record_field( row, 3 );
1283 msi_reduce_to_long_filename( file->FileName );
1285 file->ShortName = msi_dup_record_field( row, 3 );
1286 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1288 file->FileSize = MSI_RecordGetInteger( row, 4 );
1289 file->Version = msi_dup_record_field( row, 5 );
1290 file->Language = msi_dup_record_field( row, 6 );
1291 file->Attributes = MSI_RecordGetInteger( row, 7 );
1292 file->Sequence = MSI_RecordGetInteger( row, 8 );
1294 file->state = msifs_invalid;
1296 /* if the compressed bits are not set in the file attributes,
1297 * then read the information from the package word count property
1299 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1301 file->IsCompressed = FALSE;
1303 else if (file->Attributes &
1304 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1306 file->IsCompressed = TRUE;
1308 else if (file->Attributes & msidbFileAttributesNoncompressed)
1310 file->IsCompressed = FALSE;
1312 else
1314 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1317 load_file_hash(package, file);
1318 load_file_disk_id(package, file);
1320 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1322 list_add_tail( &package->files, &file->entry );
1324 return ERROR_SUCCESS;
1327 static UINT load_all_files(MSIPACKAGE *package)
1329 MSIQUERY * view;
1330 UINT rc;
1331 static const WCHAR Query[] =
1332 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1333 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1334 '`','S','e','q','u','e','n','c','e','`', 0};
1336 if (!list_empty(&package->files))
1337 return ERROR_SUCCESS;
1339 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1340 if (rc != ERROR_SUCCESS)
1341 return ERROR_SUCCESS;
1343 rc = MSI_IterateRecords(view, NULL, load_file, package);
1344 msiobj_release(&view->hdr);
1346 return ERROR_SUCCESS;
1349 static UINT load_media( MSIRECORD *row, LPVOID param )
1351 MSIPACKAGE *package = param;
1352 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1353 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1355 /* FIXME: load external cabinets and directory sources too */
1356 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1357 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1358 return ERROR_SUCCESS;
1361 static UINT load_all_media( MSIPACKAGE *package )
1363 static const WCHAR query[] =
1364 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
1365 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
1366 MSIQUERY *view;
1367 UINT r;
1369 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1370 if (r != ERROR_SUCCESS) return ERROR_SUCCESS;
1372 MSI_IterateRecords( view, NULL, load_media, package );
1373 msiobj_release( &view->hdr );
1374 return ERROR_SUCCESS;
1377 static UINT load_patch(MSIRECORD *row, LPVOID param)
1379 MSIPACKAGE *package = param;
1380 MSIFILEPATCH *patch;
1381 LPWSTR file_key;
1383 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1384 if (!patch)
1385 return ERROR_NOT_ENOUGH_MEMORY;
1387 file_key = msi_dup_record_field( row, 1 );
1388 patch->File = msi_get_loaded_file( package, file_key );
1389 msi_free(file_key);
1391 if( !patch->File )
1393 ERR("Failed to find target for patch in File table\n");
1394 msi_free(patch);
1395 return ERROR_FUNCTION_FAILED;
1398 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1400 /* FIXME: The database should be properly transformed */
1401 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1403 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1404 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1405 patch->IsApplied = FALSE;
1407 /* FIXME:
1408 * Header field - for patch validation.
1409 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1412 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1414 list_add_tail( &package->filepatches, &patch->entry );
1416 return ERROR_SUCCESS;
1419 static UINT load_all_patches(MSIPACKAGE *package)
1421 MSIQUERY *view;
1422 UINT rc;
1423 static const WCHAR Query[] =
1424 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1425 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1426 '`','S','e','q','u','e','n','c','e','`',0};
1428 if (!list_empty(&package->filepatches))
1429 return ERROR_SUCCESS;
1431 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1432 if (rc != ERROR_SUCCESS)
1433 return ERROR_SUCCESS;
1435 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1436 msiobj_release(&view->hdr);
1438 return ERROR_SUCCESS;
1441 static UINT load_folder( MSIRECORD *row, LPVOID param )
1443 MSIPACKAGE *package = param;
1444 static WCHAR szEmpty[] = { 0 };
1445 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1446 MSIFOLDER *folder;
1448 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1449 list_init( &folder->children );
1450 folder->Directory = msi_dup_record_field( row, 1 );
1451 folder->Parent = msi_dup_record_field( row, 2 );
1452 p = msi_dup_record_field(row, 3);
1454 TRACE("%s\n", debugstr_w(folder->Directory));
1456 /* split src and target dir */
1457 tgt_short = p;
1458 src_short = folder_split_path( p, ':' );
1460 /* split the long and short paths */
1461 tgt_long = folder_split_path( tgt_short, '|' );
1462 src_long = folder_split_path( src_short, '|' );
1464 /* check for no-op dirs */
1465 if (tgt_short && !strcmpW( szDot, tgt_short ))
1466 tgt_short = szEmpty;
1467 if (src_short && !strcmpW( szDot, src_short ))
1468 src_short = szEmpty;
1470 if (!tgt_long)
1471 tgt_long = tgt_short;
1473 if (!src_short) {
1474 src_short = tgt_short;
1475 src_long = tgt_long;
1478 if (!src_long)
1479 src_long = src_short;
1481 /* FIXME: use the target short path too */
1482 folder->TargetDefault = strdupW(tgt_long);
1483 folder->SourceShortPath = strdupW(src_short);
1484 folder->SourceLongPath = strdupW(src_long);
1485 msi_free(p);
1487 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1488 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1489 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1491 list_add_tail( &package->folders, &folder->entry );
1492 return ERROR_SUCCESS;
1495 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1497 FolderList *fl;
1499 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1500 fl->folder = child;
1501 list_add_tail( &parent->children, &fl->entry );
1502 return ERROR_SUCCESS;
1505 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1507 MSIPACKAGE *package = param;
1508 MSIFOLDER *parent, *child;
1510 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1511 return ERROR_FUNCTION_FAILED;
1513 if (!child->Parent) return ERROR_SUCCESS;
1515 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1516 return ERROR_FUNCTION_FAILED;
1518 return add_folder_child( parent, child );
1521 static UINT load_all_folders( MSIPACKAGE *package )
1523 static const WCHAR query[] = {
1524 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1525 '`','D','i','r','e','c','t','o','r','y','`',0 };
1526 MSIQUERY *view;
1527 UINT r;
1529 if (!list_empty(&package->folders))
1530 return ERROR_SUCCESS;
1532 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1533 if (r != ERROR_SUCCESS)
1534 return r;
1536 r = MSI_IterateRecords( view, NULL, load_folder, package );
1537 if (r != ERROR_SUCCESS)
1539 msiobj_release( &view->hdr );
1540 return r;
1542 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1543 msiobj_release( &view->hdr );
1544 return r;
1547 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1549 msi_set_property( package->db, szCostingComplete, szZero );
1550 msi_set_property( package->db, szRootDrive, szCRoot );
1552 load_all_folders( package );
1553 msi_load_all_components( package );
1554 msi_load_all_features( package );
1555 load_all_files( package );
1556 load_all_patches( package );
1557 load_all_media( package );
1559 return ERROR_SUCCESS;
1562 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1564 const WCHAR *action = package->script->Actions[script][index];
1565 ui_actionstart( package, action );
1566 TRACE("executing %s\n", debugstr_w(action));
1567 return ACTION_PerformAction( package, action, script );
1570 static UINT execute_script( MSIPACKAGE *package, UINT script )
1572 UINT i, rc = ERROR_SUCCESS;
1574 TRACE("executing script %u\n", script);
1576 if (!package->script)
1578 ERR("no script!\n");
1579 return ERROR_FUNCTION_FAILED;
1581 if (script == ROLLBACK_SCRIPT)
1583 for (i = package->script->ActionCount[script]; i > 0; i--)
1585 rc = execute_script_action( package, script, i - 1 );
1586 if (rc != ERROR_SUCCESS) break;
1589 else
1591 for (i = 0; i < package->script->ActionCount[script]; i++)
1593 rc = execute_script_action( package, script, i );
1594 if (rc != ERROR_SUCCESS) break;
1597 msi_free_action_script(package, script);
1598 return rc;
1601 static UINT ACTION_FileCost(MSIPACKAGE *package)
1603 return ERROR_SUCCESS;
1606 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1608 MSICOMPONENT *comp;
1609 UINT r;
1611 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1613 if (!comp->ComponentId) continue;
1615 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1616 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1617 &comp->Installed );
1618 if (r != ERROR_SUCCESS)
1619 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1620 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1621 &comp->Installed );
1622 if (r != ERROR_SUCCESS)
1623 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1624 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1625 &comp->Installed );
1626 if (r != ERROR_SUCCESS)
1627 comp->Installed = INSTALLSTATE_ABSENT;
1631 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1633 MSIFEATURE *feature;
1635 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1637 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1639 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1640 feature->Installed = INSTALLSTATE_ABSENT;
1641 else
1642 feature->Installed = state;
1646 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1648 return (feature->Level > 0 && feature->Level <= level);
1651 static BOOL process_state_property(MSIPACKAGE* package, int level,
1652 LPCWSTR property, INSTALLSTATE state)
1654 LPWSTR override;
1655 MSIFEATURE *feature;
1657 override = msi_dup_property( package->db, property );
1658 if (!override)
1659 return FALSE;
1661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1663 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1664 continue;
1666 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1668 if (!strcmpiW( override, szAll ))
1670 if (feature->Installed != state)
1672 feature->Action = state;
1673 feature->ActionRequest = state;
1676 else
1678 LPWSTR ptr = override;
1679 LPWSTR ptr2 = strchrW(override,',');
1681 while (ptr)
1683 int len = ptr2 - ptr;
1685 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1686 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1688 if (feature->Installed != state)
1690 feature->Action = state;
1691 feature->ActionRequest = state;
1693 break;
1695 if (ptr2)
1697 ptr=ptr2+1;
1698 ptr2 = strchrW(ptr,',');
1700 else
1701 break;
1705 msi_free(override);
1706 return TRUE;
1709 static BOOL process_overrides( MSIPACKAGE *package, int level )
1711 static const WCHAR szAddLocal[] =
1712 {'A','D','D','L','O','C','A','L',0};
1713 static const WCHAR szAddSource[] =
1714 {'A','D','D','S','O','U','R','C','E',0};
1715 static const WCHAR szAdvertise[] =
1716 {'A','D','V','E','R','T','I','S','E',0};
1717 BOOL ret = FALSE;
1719 /* all these activation/deactivation things happen in order and things
1720 * later on the list override things earlier on the list.
1722 * 0 INSTALLLEVEL processing
1723 * 1 ADDLOCAL
1724 * 2 REMOVE
1725 * 3 ADDSOURCE
1726 * 4 ADDDEFAULT
1727 * 5 REINSTALL
1728 * 6 ADVERTISE
1729 * 7 COMPADDLOCAL
1730 * 8 COMPADDSOURCE
1731 * 9 FILEADDLOCAL
1732 * 10 FILEADDSOURCE
1733 * 11 FILEADDDEFAULT
1735 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1736 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1737 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1738 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1739 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1741 if (ret)
1742 msi_set_property( package->db, szPreselected, szOne );
1744 return ret;
1747 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1749 int level;
1750 MSICOMPONENT* component;
1751 MSIFEATURE *feature;
1753 TRACE("Checking Install Level\n");
1755 level = msi_get_property_int(package->db, szInstallLevel, 1);
1757 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1759 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1761 if (!is_feature_selected( feature, level )) continue;
1763 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1765 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1767 feature->Action = INSTALLSTATE_SOURCE;
1768 feature->ActionRequest = INSTALLSTATE_SOURCE;
1770 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1772 feature->Action = INSTALLSTATE_ADVERTISED;
1773 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1775 else
1777 feature->Action = INSTALLSTATE_LOCAL;
1778 feature->ActionRequest = INSTALLSTATE_LOCAL;
1782 /* disable child features of unselected parent or follow parent */
1783 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1785 FeatureList *fl;
1787 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1789 if (!is_feature_selected( feature, level ))
1791 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1792 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1794 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1796 fl->feature->Action = feature->Action;
1797 fl->feature->ActionRequest = feature->ActionRequest;
1802 else /* preselected */
1804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1806 if (!is_feature_selected( feature, level )) continue;
1808 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1810 if (feature->Installed == INSTALLSTATE_ABSENT)
1812 feature->Action = INSTALLSTATE_UNKNOWN;
1813 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1815 else
1817 feature->Action = feature->Installed;
1818 feature->ActionRequest = feature->Installed;
1822 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1824 FeatureList *fl;
1826 if (!is_feature_selected( feature, level )) continue;
1828 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1830 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1832 fl->feature->Action = feature->Action;
1833 fl->feature->ActionRequest = feature->ActionRequest;
1839 /* now we want to set component state based based on feature state */
1840 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1842 ComponentList *cl;
1844 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1845 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1846 feature->ActionRequest, feature->Action);
1848 if (!is_feature_selected( feature, level )) continue;
1850 /* features with components that have compressed files are made local */
1851 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1853 if (cl->component->ForceLocalState &&
1854 feature->ActionRequest == INSTALLSTATE_SOURCE)
1856 feature->Action = INSTALLSTATE_LOCAL;
1857 feature->ActionRequest = INSTALLSTATE_LOCAL;
1858 break;
1862 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1864 component = cl->component;
1866 switch (feature->ActionRequest)
1868 case INSTALLSTATE_ABSENT:
1869 component->anyAbsent = 1;
1870 break;
1871 case INSTALLSTATE_ADVERTISED:
1872 component->hasAdvertiseFeature = 1;
1873 break;
1874 case INSTALLSTATE_SOURCE:
1875 component->hasSourceFeature = 1;
1876 break;
1877 case INSTALLSTATE_LOCAL:
1878 component->hasLocalFeature = 1;
1879 break;
1880 case INSTALLSTATE_DEFAULT:
1881 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1882 component->hasAdvertiseFeature = 1;
1883 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1884 component->hasSourceFeature = 1;
1885 else
1886 component->hasLocalFeature = 1;
1887 break;
1888 default:
1889 break;
1894 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1896 /* check if it's local or source */
1897 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1898 (component->hasLocalFeature || component->hasSourceFeature))
1900 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1901 !component->ForceLocalState)
1903 component->Action = INSTALLSTATE_SOURCE;
1904 component->ActionRequest = INSTALLSTATE_SOURCE;
1906 else
1908 component->Action = INSTALLSTATE_LOCAL;
1909 component->ActionRequest = INSTALLSTATE_LOCAL;
1911 continue;
1914 /* if any feature is local, the component must be local too */
1915 if (component->hasLocalFeature)
1917 component->Action = INSTALLSTATE_LOCAL;
1918 component->ActionRequest = INSTALLSTATE_LOCAL;
1919 continue;
1921 if (component->hasSourceFeature)
1923 component->Action = INSTALLSTATE_SOURCE;
1924 component->ActionRequest = INSTALLSTATE_SOURCE;
1925 continue;
1927 if (component->hasAdvertiseFeature)
1929 component->Action = INSTALLSTATE_ADVERTISED;
1930 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1931 continue;
1933 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1934 if (component->anyAbsent &&
1935 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1937 component->Action = INSTALLSTATE_ABSENT;
1938 component->ActionRequest = INSTALLSTATE_ABSENT;
1942 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1944 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1946 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1951 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1952 component->Installed == INSTALLSTATE_SOURCE &&
1953 component->hasSourceFeature)
1955 component->Action = INSTALLSTATE_UNKNOWN;
1956 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1959 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1960 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1963 return ERROR_SUCCESS;
1966 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1968 MSIPACKAGE *package = param;
1969 LPCWSTR name;
1970 MSIFEATURE *feature;
1972 name = MSI_RecordGetString( row, 1 );
1974 feature = msi_get_loaded_feature( package, name );
1975 if (!feature)
1976 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1977 else
1979 LPCWSTR Condition;
1980 Condition = MSI_RecordGetString(row,3);
1982 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1984 int level = MSI_RecordGetInteger(row,2);
1985 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1986 feature->Level = level;
1989 return ERROR_SUCCESS;
1992 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1994 static const WCHAR name[] = {'\\',0};
1995 VS_FIXEDFILEINFO *ptr, *ret;
1996 LPVOID version;
1997 DWORD versize, handle;
1998 UINT sz;
2000 versize = GetFileVersionInfoSizeW( filename, &handle );
2001 if (!versize)
2002 return NULL;
2004 version = msi_alloc( versize );
2005 if (!version)
2006 return NULL;
2008 GetFileVersionInfoW( filename, 0, versize, version );
2010 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2012 msi_free( version );
2013 return NULL;
2016 ret = msi_alloc( sz );
2017 memcpy( ret, ptr, sz );
2019 msi_free( version );
2020 return ret;
2023 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2025 DWORD ms, ls;
2027 msi_parse_version_string( version, &ms, &ls );
2029 if (fi->dwFileVersionMS > ms) return 1;
2030 else if (fi->dwFileVersionMS < ms) return -1;
2031 else if (fi->dwFileVersionLS > ls) return 1;
2032 else if (fi->dwFileVersionLS < ls) return -1;
2033 return 0;
2036 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2038 DWORD ms1, ms2;
2040 msi_parse_version_string( ver1, &ms1, NULL );
2041 msi_parse_version_string( ver2, &ms2, NULL );
2043 if (ms1 > ms2) return 1;
2044 else if (ms1 < ms2) return -1;
2045 return 0;
2048 DWORD msi_get_disk_file_size( LPCWSTR filename )
2050 HANDLE file;
2051 DWORD size;
2053 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2054 if (file == INVALID_HANDLE_VALUE)
2055 return INVALID_FILE_SIZE;
2057 size = GetFileSize( file, NULL );
2058 TRACE("size is %u\n", size);
2059 CloseHandle( file );
2060 return size;
2063 BOOL msi_file_hash_matches( MSIFILE *file )
2065 UINT r;
2066 MSIFILEHASHINFO hash;
2068 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2069 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2070 if (r != ERROR_SUCCESS)
2071 return FALSE;
2073 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2076 static WCHAR *get_temp_dir( void )
2078 static UINT id;
2079 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2081 GetTempPathW( MAX_PATH, tmp );
2082 for (;;)
2084 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2085 if (CreateDirectoryW( dir, NULL )) break;
2087 return strdupW( dir );
2091 * msi_build_directory_name()
2093 * This function is to save messing round with directory names
2094 * It handles adding backslashes between path segments,
2095 * and can add \ at the end of the directory name if told to.
2097 * It takes a variable number of arguments.
2098 * It always allocates a new string for the result, so make sure
2099 * to free the return value when finished with it.
2101 * The first arg is the number of path segments that follow.
2102 * The arguments following count are a list of path segments.
2103 * A path segment may be NULL.
2105 * Path segments will be added with a \ separating them.
2106 * A \ will not be added after the last segment, however if the
2107 * last segment is NULL, then the last character will be a \
2109 WCHAR *msi_build_directory_name( DWORD count, ... )
2111 DWORD sz = 1, i;
2112 WCHAR *dir;
2113 va_list va;
2115 va_start( va, count );
2116 for (i = 0; i < count; i++)
2118 const WCHAR *str = va_arg( va, const WCHAR * );
2119 if (str) sz += strlenW( str ) + 1;
2121 va_end( va );
2123 dir = msi_alloc( sz * sizeof(WCHAR) );
2124 dir[0] = 0;
2126 va_start( va, count );
2127 for (i = 0; i < count; i++)
2129 const WCHAR *str = va_arg( va, const WCHAR * );
2130 if (!str) continue;
2131 strcatW( dir, str );
2132 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2134 va_end( va );
2135 return dir;
2138 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2140 MSIASSEMBLY *assembly = file->Component->assembly;
2142 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2144 msi_free( file->TargetPath );
2145 if (assembly && !assembly->application)
2147 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2148 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2149 msi_track_tempfile( package, file->TargetPath );
2151 else
2153 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2154 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2157 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2160 static UINT calculate_file_cost( MSIPACKAGE *package )
2162 VS_FIXEDFILEINFO *file_version;
2163 WCHAR *font_version;
2164 MSIFILE *file;
2166 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2168 MSICOMPONENT *comp = file->Component;
2169 DWORD file_size;
2171 if (!comp->Enabled) continue;
2173 if (file->IsCompressed)
2174 comp->ForceLocalState = TRUE;
2176 set_target_path( package, file );
2178 if ((comp->assembly && !comp->assembly->installed) ||
2179 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2181 comp->Cost += file->FileSize;
2182 continue;
2184 file_size = msi_get_disk_file_size( file->TargetPath );
2186 if (file->Version)
2188 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2190 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2192 comp->Cost += file->FileSize - file_size;
2194 msi_free( file_version );
2195 continue;
2197 else if ((font_version = font_version_from_file( file->TargetPath )))
2199 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2201 comp->Cost += file->FileSize - file_size;
2203 msi_free( font_version );
2204 continue;
2207 if (file_size != file->FileSize)
2209 comp->Cost += file->FileSize - file_size;
2212 return ERROR_SUCCESS;
2215 void msi_clean_path( WCHAR *p )
2217 WCHAR *q = p;
2218 int n, len = 0;
2220 while (1)
2222 /* copy until the end of the string or a space */
2223 while (*p != ' ' && (*q = *p))
2225 p++, len++;
2226 /* reduce many backslashes to one */
2227 if (*p != '\\' || *q != '\\')
2228 q++;
2231 /* quit at the end of the string */
2232 if (!*p)
2233 break;
2235 /* count the number of spaces */
2236 n = 0;
2237 while (p[n] == ' ')
2238 n++;
2240 /* if it's leading or trailing space, skip it */
2241 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2242 p += n;
2243 else /* copy n spaces */
2244 while (n && (*q++ = *p++)) n--;
2248 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2250 int len;
2251 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2253 if (!target_dir) return NULL;
2255 len = strlenW( target_dir );
2256 if (target_dir[len - 1] == '\\') return target_dir;
2257 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2259 strcpyW( path, target_dir );
2260 path[len] = '\\';
2261 path[len + 1] = 0;
2263 msi_free( target_dir );
2264 return path;
2267 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2269 FolderList *fl;
2270 MSIFOLDER *folder, *parent, *child;
2271 WCHAR *path;
2273 TRACE("resolving %s\n", debugstr_w(name));
2275 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2277 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2279 if (!load_prop || !(path = get_target_dir_property( package->db )))
2281 path = msi_dup_property( package->db, szRootDrive );
2284 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2286 parent = msi_get_loaded_folder( package, folder->Parent );
2287 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2289 msi_clean_path( path );
2290 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2292 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2293 msi_free( path );
2294 return;
2296 msi_set_property( package->db, folder->Directory, path );
2297 msi_free( folder->ResolvedTarget );
2298 folder->ResolvedTarget = path;
2300 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2302 child = fl->folder;
2303 msi_resolve_target_folder( package, child->Directory, load_prop );
2305 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2308 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2310 static const WCHAR condition_query[] =
2311 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
2312 static const WCHAR szOutOfDiskSpace[] =
2313 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2314 MSICOMPONENT *comp;
2315 UINT rc = ERROR_SUCCESS;
2316 MSIQUERY * view;
2317 LPWSTR level;
2319 TRACE("Building directory properties\n");
2320 msi_resolve_target_folder( package, szTargetDir, TRUE );
2322 TRACE("Evaluating component conditions\n");
2323 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2325 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2327 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2328 comp->Enabled = FALSE;
2330 else
2331 comp->Enabled = TRUE;
2334 /* read components states from the registry */
2335 ACTION_GetComponentInstallStates(package);
2336 ACTION_GetFeatureInstallStates(package);
2338 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2340 TRACE("Evaluating feature conditions\n");
2342 rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
2343 if (rc == ERROR_SUCCESS)
2345 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2346 msiobj_release( &view->hdr );
2350 TRACE("Calculating file cost\n");
2351 calculate_file_cost( package );
2353 msi_set_property( package->db, szCostingComplete, szOne );
2354 /* set default run level if not set */
2355 level = msi_dup_property( package->db, szInstallLevel );
2356 if (!level)
2357 msi_set_property( package->db, szInstallLevel, szOne );
2358 msi_free(level);
2360 /* FIXME: check volume disk space */
2361 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2363 return MSI_SetFeatureStates(package);
2366 /* OK this value is "interpreted" and then formatted based on the
2367 first few characters */
2368 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2369 DWORD *size)
2371 LPSTR data = NULL;
2373 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2375 if (value[1]=='x')
2377 LPWSTR ptr;
2378 CHAR byte[5];
2379 LPWSTR deformated = NULL;
2380 int count;
2382 deformat_string(package, &value[2], &deformated);
2384 /* binary value type */
2385 ptr = deformated;
2386 *type = REG_BINARY;
2387 if (strlenW(ptr)%2)
2388 *size = (strlenW(ptr)/2)+1;
2389 else
2390 *size = strlenW(ptr)/2;
2392 data = msi_alloc(*size);
2394 byte[0] = '0';
2395 byte[1] = 'x';
2396 byte[4] = 0;
2397 count = 0;
2398 /* if uneven pad with a zero in front */
2399 if (strlenW(ptr)%2)
2401 byte[2]= '0';
2402 byte[3]= *ptr;
2403 ptr++;
2404 data[count] = (BYTE)strtol(byte,NULL,0);
2405 count ++;
2406 TRACE("Uneven byte count\n");
2408 while (*ptr)
2410 byte[2]= *ptr;
2411 ptr++;
2412 byte[3]= *ptr;
2413 ptr++;
2414 data[count] = (BYTE)strtol(byte,NULL,0);
2415 count ++;
2417 msi_free(deformated);
2419 TRACE("Data %i bytes(%i)\n",*size,count);
2421 else
2423 LPWSTR deformated;
2424 LPWSTR p;
2425 DWORD d = 0;
2426 deformat_string(package, &value[1], &deformated);
2428 *type=REG_DWORD;
2429 *size = sizeof(DWORD);
2430 data = msi_alloc(*size);
2431 p = deformated;
2432 if (*p == '-')
2433 p++;
2434 while (*p)
2436 if ( (*p < '0') || (*p > '9') )
2437 break;
2438 d *= 10;
2439 d += (*p - '0');
2440 p++;
2442 if (deformated[0] == '-')
2443 d = -d;
2444 *(LPDWORD)data = d;
2445 TRACE("DWORD %i\n",*(LPDWORD)data);
2447 msi_free(deformated);
2450 else
2452 static const WCHAR szMulti[] = {'[','~',']',0};
2453 LPCWSTR ptr;
2454 *type=REG_SZ;
2456 if (value[0]=='#')
2458 if (value[1]=='%')
2460 ptr = &value[2];
2461 *type=REG_EXPAND_SZ;
2463 else
2464 ptr = &value[1];
2466 else
2467 ptr=value;
2469 if (strstrW(value, szMulti))
2470 *type = REG_MULTI_SZ;
2472 /* remove initial delimiter */
2473 if (!strncmpW(value, szMulti, 3))
2474 ptr = value + 3;
2476 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2478 /* add double NULL terminator */
2479 if (*type == REG_MULTI_SZ)
2481 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2482 data = msi_realloc_zero(data, *size);
2485 return data;
2488 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2490 const WCHAR *ret;
2492 switch (root)
2494 case -1:
2495 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2497 *root_key = HKEY_LOCAL_MACHINE;
2498 ret = szHLM;
2500 else
2502 *root_key = HKEY_CURRENT_USER;
2503 ret = szHCU;
2505 break;
2506 case 0:
2507 *root_key = HKEY_CLASSES_ROOT;
2508 ret = szHCR;
2509 break;
2510 case 1:
2511 *root_key = HKEY_CURRENT_USER;
2512 ret = szHCU;
2513 break;
2514 case 2:
2515 *root_key = HKEY_LOCAL_MACHINE;
2516 ret = szHLM;
2517 break;
2518 case 3:
2519 *root_key = HKEY_USERS;
2520 ret = szHU;
2521 break;
2522 default:
2523 ERR("Unknown root %i\n", root);
2524 return NULL;
2527 return ret;
2530 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2532 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2533 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2535 if (is_64bit && package->platform == PLATFORM_INTEL &&
2536 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2538 UINT size;
2539 WCHAR *path_32node;
2541 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2542 if (!(path_32node = msi_alloc( size ))) return NULL;
2544 memcpy( path_32node, path, len * sizeof(WCHAR) );
2545 strcpyW( path_32node + len, szWow6432Node );
2546 strcatW( path_32node, szBackSlash );
2547 strcatW( path_32node, path + len );
2548 return path_32node;
2551 return strdupW( path );
2554 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2556 MSIPACKAGE *package = param;
2557 LPSTR value_data = NULL;
2558 HKEY root_key, hkey;
2559 DWORD type,size;
2560 LPWSTR deformated, uikey, keypath;
2561 LPCWSTR szRoot, component, name, key, value;
2562 MSICOMPONENT *comp;
2563 MSIRECORD * uirow;
2564 INT root;
2565 BOOL check_first = FALSE;
2566 UINT rc;
2568 msi_ui_progress( package, 2, 0, 0, 0 );
2570 component = MSI_RecordGetString(row, 6);
2571 comp = msi_get_loaded_component(package,component);
2572 if (!comp)
2573 return ERROR_SUCCESS;
2575 comp->Action = msi_get_component_action( package, comp );
2576 if (comp->Action != INSTALLSTATE_LOCAL)
2578 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2579 return ERROR_SUCCESS;
2582 name = MSI_RecordGetString(row, 4);
2583 if( MSI_RecordIsNull(row,5) && name )
2585 /* null values can have special meanings */
2586 if (name[0]=='-' && name[1] == 0)
2587 return ERROR_SUCCESS;
2588 else if ((name[0]=='+' && name[1] == 0) ||
2589 (name[0] == '*' && name[1] == 0))
2590 name = NULL;
2591 check_first = TRUE;
2594 root = MSI_RecordGetInteger(row,2);
2595 key = MSI_RecordGetString(row, 3);
2597 szRoot = get_root_key( package, root, &root_key );
2598 if (!szRoot)
2599 return ERROR_SUCCESS;
2601 deformat_string(package, key , &deformated);
2602 size = strlenW(deformated) + strlenW(szRoot) + 1;
2603 uikey = msi_alloc(size*sizeof(WCHAR));
2604 strcpyW(uikey,szRoot);
2605 strcatW(uikey,deformated);
2607 keypath = get_keypath( package, root_key, deformated );
2608 msi_free( deformated );
2609 if (RegCreateKeyW( root_key, keypath, &hkey ))
2611 ERR("Could not create key %s\n", debugstr_w(keypath));
2612 msi_free(uikey);
2613 msi_free(keypath);
2614 return ERROR_SUCCESS;
2617 value = MSI_RecordGetString(row,5);
2618 if (value)
2619 value_data = parse_value(package, value, &type, &size);
2620 else
2622 value_data = (LPSTR)strdupW(szEmpty);
2623 size = sizeof(szEmpty);
2624 type = REG_SZ;
2627 deformat_string(package, name, &deformated);
2629 if (!check_first)
2631 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2632 debugstr_w(uikey));
2633 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2635 else
2637 DWORD sz = 0;
2638 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2639 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2641 TRACE("value %s of %s checked already exists\n",
2642 debugstr_w(deformated), debugstr_w(uikey));
2644 else
2646 TRACE("Checked and setting value %s of %s\n",
2647 debugstr_w(deformated), debugstr_w(uikey));
2648 if (deformated || size)
2649 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2652 RegCloseKey(hkey);
2654 uirow = MSI_CreateRecord(3);
2655 MSI_RecordSetStringW(uirow,2,deformated);
2656 MSI_RecordSetStringW(uirow,1,uikey);
2657 if (type == REG_SZ || type == REG_EXPAND_SZ)
2658 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2659 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2660 msiobj_release( &uirow->hdr );
2662 msi_free(value_data);
2663 msi_free(deformated);
2664 msi_free(uikey);
2665 msi_free(keypath);
2667 return ERROR_SUCCESS;
2670 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2672 UINT rc;
2673 MSIQUERY * view;
2674 static const WCHAR ExecSeqQuery[] =
2675 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2676 '`','R','e','g','i','s','t','r','y','`',0 };
2678 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2679 if (rc != ERROR_SUCCESS)
2680 return ERROR_SUCCESS;
2682 /* increment progress bar each time action data is sent */
2683 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2685 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2686 msiobj_release(&view->hdr);
2687 return rc;
2690 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2692 LONG res;
2693 HKEY hkey;
2694 DWORD num_subkeys, num_values;
2696 if (delete_key)
2698 if ((res = RegDeleteTreeW( hkey_root, key )))
2700 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2702 return;
2705 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2707 if ((res = RegDeleteValueW( hkey, value )))
2709 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2711 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2712 NULL, NULL, NULL, NULL );
2713 RegCloseKey( hkey );
2714 if (!res && !num_subkeys && !num_values)
2716 TRACE("Removing empty key %s\n", debugstr_w(key));
2717 RegDeleteKeyW( hkey_root, key );
2719 return;
2721 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2725 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2727 MSIPACKAGE *package = param;
2728 LPCWSTR component, name, key_str, root_key_str;
2729 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2730 MSICOMPONENT *comp;
2731 MSIRECORD *uirow;
2732 BOOL delete_key = FALSE;
2733 HKEY hkey_root;
2734 UINT size;
2735 INT root;
2737 msi_ui_progress( package, 2, 0, 0, 0 );
2739 component = MSI_RecordGetString( row, 6 );
2740 comp = msi_get_loaded_component( package, component );
2741 if (!comp)
2742 return ERROR_SUCCESS;
2744 comp->Action = msi_get_component_action( package, comp );
2745 if (comp->Action != INSTALLSTATE_ABSENT)
2747 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2748 return ERROR_SUCCESS;
2751 name = MSI_RecordGetString( row, 4 );
2752 if (MSI_RecordIsNull( row, 5 ) && name )
2754 if (name[0] == '+' && !name[1])
2755 return ERROR_SUCCESS;
2756 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2758 delete_key = TRUE;
2759 name = NULL;
2763 root = MSI_RecordGetInteger( row, 2 );
2764 key_str = MSI_RecordGetString( row, 3 );
2766 root_key_str = get_root_key( package, root, &hkey_root );
2767 if (!root_key_str)
2768 return ERROR_SUCCESS;
2770 deformat_string( package, key_str, &deformated_key );
2771 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2772 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2773 strcpyW( ui_key_str, root_key_str );
2774 strcatW( ui_key_str, deformated_key );
2776 deformat_string( package, name, &deformated_name );
2778 keypath = get_keypath( package, hkey_root, deformated_key );
2779 msi_free( deformated_key );
2780 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2781 msi_free( keypath );
2783 uirow = MSI_CreateRecord( 2 );
2784 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2785 MSI_RecordSetStringW( uirow, 2, deformated_name );
2787 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2788 msiobj_release( &uirow->hdr );
2790 msi_free( ui_key_str );
2791 msi_free( deformated_name );
2792 return ERROR_SUCCESS;
2795 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2797 MSIPACKAGE *package = param;
2798 LPCWSTR component, name, key_str, root_key_str;
2799 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2800 MSICOMPONENT *comp;
2801 MSIRECORD *uirow;
2802 BOOL delete_key = FALSE;
2803 HKEY hkey_root;
2804 UINT size;
2805 INT root;
2807 msi_ui_progress( package, 2, 0, 0, 0 );
2809 component = MSI_RecordGetString( row, 5 );
2810 comp = msi_get_loaded_component( package, component );
2811 if (!comp)
2812 return ERROR_SUCCESS;
2814 comp->Action = msi_get_component_action( package, comp );
2815 if (comp->Action != INSTALLSTATE_LOCAL)
2817 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2818 return ERROR_SUCCESS;
2821 if ((name = MSI_RecordGetString( row, 4 )))
2823 if (name[0] == '-' && !name[1])
2825 delete_key = TRUE;
2826 name = NULL;
2830 root = MSI_RecordGetInteger( row, 2 );
2831 key_str = MSI_RecordGetString( row, 3 );
2833 root_key_str = get_root_key( package, root, &hkey_root );
2834 if (!root_key_str)
2835 return ERROR_SUCCESS;
2837 deformat_string( package, key_str, &deformated_key );
2838 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2839 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2840 strcpyW( ui_key_str, root_key_str );
2841 strcatW( ui_key_str, deformated_key );
2843 deformat_string( package, name, &deformated_name );
2845 keypath = get_keypath( package, hkey_root, deformated_key );
2846 msi_free( deformated_key );
2847 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2848 msi_free( keypath );
2850 uirow = MSI_CreateRecord( 2 );
2851 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2852 MSI_RecordSetStringW( uirow, 2, deformated_name );
2854 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2855 msiobj_release( &uirow->hdr );
2857 msi_free( ui_key_str );
2858 msi_free( deformated_name );
2859 return ERROR_SUCCESS;
2862 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2864 UINT rc;
2865 MSIQUERY *view;
2866 static const WCHAR registry_query[] =
2867 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2868 '`','R','e','g','i','s','t','r','y','`',0 };
2869 static const WCHAR remove_registry_query[] =
2870 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2871 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2873 /* increment progress bar each time action data is sent */
2874 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2876 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2877 if (rc == ERROR_SUCCESS)
2879 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2880 msiobj_release( &view->hdr );
2881 if (rc != ERROR_SUCCESS)
2882 return rc;
2885 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2886 if (rc == ERROR_SUCCESS)
2888 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2889 msiobj_release( &view->hdr );
2890 if (rc != ERROR_SUCCESS)
2891 return rc;
2894 return ERROR_SUCCESS;
2897 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2899 package->script->CurrentlyScripting = TRUE;
2901 return ERROR_SUCCESS;
2905 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2907 MSICOMPONENT *comp;
2908 DWORD progress = 0;
2909 DWORD total = 0;
2910 static const WCHAR q1[]=
2911 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2912 '`','R','e','g','i','s','t','r','y','`',0};
2913 UINT rc;
2914 MSIQUERY * view;
2915 MSIFEATURE *feature;
2916 MSIFILE *file;
2918 TRACE("InstallValidate\n");
2920 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2921 if (rc == ERROR_SUCCESS)
2923 MSI_IterateRecords( view, &progress, NULL, package );
2924 msiobj_release( &view->hdr );
2925 total += progress * REG_PROGRESS_VALUE;
2928 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2929 total += COMPONENT_PROGRESS_VALUE;
2931 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2932 total += file->FileSize;
2934 msi_ui_progress( package, 0, total, 0, 0 );
2936 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2938 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2939 debugstr_w(feature->Feature), feature->Installed,
2940 feature->ActionRequest, feature->Action);
2943 return ERROR_SUCCESS;
2946 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2948 MSIPACKAGE* package = param;
2949 LPCWSTR cond = NULL;
2950 LPCWSTR message = NULL;
2951 UINT r;
2953 static const WCHAR title[]=
2954 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2956 cond = MSI_RecordGetString(row,1);
2958 r = MSI_EvaluateConditionW(package,cond);
2959 if (r == MSICONDITION_FALSE)
2961 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2963 LPWSTR deformated;
2964 message = MSI_RecordGetString(row,2);
2965 deformat_string(package,message,&deformated);
2966 MessageBoxW(NULL,deformated,title,MB_OK);
2967 msi_free(deformated);
2970 return ERROR_INSTALL_FAILURE;
2973 return ERROR_SUCCESS;
2976 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2978 UINT rc;
2979 MSIQUERY * view = NULL;
2980 static const WCHAR ExecSeqQuery[] =
2981 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2982 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2984 TRACE("Checking launch conditions\n");
2986 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2987 if (rc != ERROR_SUCCESS)
2988 return ERROR_SUCCESS;
2990 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2991 msiobj_release(&view->hdr);
2993 return rc;
2996 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2999 if (!cmp->KeyPath)
3000 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3002 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3004 MSIRECORD * row = 0;
3005 UINT root,len;
3006 LPWSTR deformated,buffer,deformated_name;
3007 LPCWSTR key,name;
3008 static const WCHAR ExecSeqQuery[] =
3009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3010 '`','R','e','g','i','s','t','r','y','`',' ',
3011 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3012 ' ','=',' ' ,'\'','%','s','\'',0 };
3013 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3014 static const WCHAR fmt2[]=
3015 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3017 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3018 if (!row)
3019 return NULL;
3021 root = MSI_RecordGetInteger(row,2);
3022 key = MSI_RecordGetString(row, 3);
3023 name = MSI_RecordGetString(row, 4);
3024 deformat_string(package, key , &deformated);
3025 deformat_string(package, name, &deformated_name);
3027 len = strlenW(deformated) + 6;
3028 if (deformated_name)
3029 len+=strlenW(deformated_name);
3031 buffer = msi_alloc( len *sizeof(WCHAR));
3033 if (deformated_name)
3034 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3035 else
3036 sprintfW(buffer,fmt,root,deformated);
3038 msi_free(deformated);
3039 msi_free(deformated_name);
3040 msiobj_release(&row->hdr);
3042 return buffer;
3044 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3046 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3047 return NULL;
3049 else
3051 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3053 if (file)
3054 return strdupW( file->TargetPath );
3056 return NULL;
3059 static HKEY openSharedDLLsKey(void)
3061 HKEY hkey=0;
3062 static const WCHAR path[] =
3063 {'S','o','f','t','w','a','r','e','\\',
3064 'M','i','c','r','o','s','o','f','t','\\',
3065 'W','i','n','d','o','w','s','\\',
3066 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3067 'S','h','a','r','e','d','D','L','L','s',0};
3069 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3070 return hkey;
3073 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3075 HKEY hkey;
3076 DWORD count=0;
3077 DWORD type;
3078 DWORD sz = sizeof(count);
3079 DWORD rc;
3081 hkey = openSharedDLLsKey();
3082 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3083 if (rc != ERROR_SUCCESS)
3084 count = 0;
3085 RegCloseKey(hkey);
3086 return count;
3089 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3091 HKEY hkey;
3093 hkey = openSharedDLLsKey();
3094 if (count > 0)
3095 msi_reg_set_val_dword( hkey, path, count );
3096 else
3097 RegDeleteValueW(hkey,path);
3098 RegCloseKey(hkey);
3099 return count;
3102 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3104 MSIFEATURE *feature;
3105 INT count = 0;
3106 BOOL write = FALSE;
3108 /* only refcount DLLs */
3109 if (comp->KeyPath == NULL ||
3110 comp->assembly ||
3111 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3112 comp->Attributes & msidbComponentAttributesODBCDataSource)
3113 write = FALSE;
3114 else
3116 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3117 write = (count > 0);
3119 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3120 write = TRUE;
3123 /* increment counts */
3124 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3126 ComponentList *cl;
3128 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3129 continue;
3131 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3133 if ( cl->component == comp )
3134 count++;
3138 /* decrement counts */
3139 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3141 ComponentList *cl;
3143 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3144 continue;
3146 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3148 if ( cl->component == comp )
3149 count--;
3153 /* ref count all the files in the component */
3154 if (write)
3156 MSIFILE *file;
3158 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3160 if (file->Component == comp)
3161 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3165 /* add a count for permanent */
3166 if (comp->Attributes & msidbComponentAttributesPermanent)
3167 count ++;
3169 comp->RefCount = count;
3171 if (write)
3172 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3175 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3177 if (comp->assembly)
3179 const WCHAR prefixW[] = {'<','\\',0};
3180 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3181 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3183 if (keypath)
3185 strcpyW( keypath, prefixW );
3186 strcatW( keypath, comp->assembly->display_name );
3188 return keypath;
3190 return resolve_keypath( package, comp );
3193 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3195 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3196 UINT rc;
3197 MSICOMPONENT *comp;
3198 HKEY hkey;
3200 TRACE("\n");
3202 squash_guid(package->ProductCode,squished_pc);
3203 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3205 msi_set_sourcedir_props(package, FALSE);
3207 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3209 MSIRECORD *uirow;
3210 INSTALLSTATE action;
3212 msi_ui_progress( package, 2, 0, 0, 0 );
3213 if (!comp->ComponentId)
3214 continue;
3216 squash_guid( comp->ComponentId, squished_cc );
3217 msi_free( comp->FullKeypath );
3218 comp->FullKeypath = build_full_keypath( package, comp );
3220 ACTION_RefCountComponent( package, comp );
3222 if (package->need_rollback) action = comp->Installed;
3223 else action = comp->ActionRequest;
3225 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3226 debugstr_w(comp->Component), debugstr_w(squished_cc),
3227 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3229 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3231 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3232 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3233 else
3234 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3236 if (rc != ERROR_SUCCESS)
3237 continue;
3239 if (comp->Attributes & msidbComponentAttributesPermanent)
3241 static const WCHAR szPermKey[] =
3242 { '0','0','0','0','0','0','0','0','0','0','0','0',
3243 '0','0','0','0','0','0','0','0','0','0','0','0',
3244 '0','0','0','0','0','0','0','0',0 };
3246 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3248 if (action == INSTALLSTATE_LOCAL)
3249 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3250 else
3252 MSIFILE *file;
3253 MSIRECORD *row;
3254 LPWSTR ptr, ptr2;
3255 WCHAR source[MAX_PATH];
3256 WCHAR base[MAX_PATH];
3257 LPWSTR sourcepath;
3259 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3260 static const WCHAR query[] = {
3261 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3262 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3263 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3264 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3265 '`','D','i','s','k','I','d','`',0};
3267 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3268 continue;
3270 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3271 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3272 ptr2 = strrchrW(source, '\\') + 1;
3273 msiobj_release(&row->hdr);
3275 lstrcpyW(base, package->PackagePath);
3276 ptr = strrchrW(base, '\\');
3277 *(ptr + 1) = '\0';
3279 sourcepath = msi_resolve_file_source(package, file);
3280 ptr = sourcepath + lstrlenW(base);
3281 lstrcpyW(ptr2, ptr);
3282 msi_free(sourcepath);
3284 msi_reg_set_val_str(hkey, squished_pc, source);
3286 RegCloseKey(hkey);
3288 else if (action == INSTALLSTATE_ABSENT)
3290 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3291 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3292 else
3293 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3296 /* UI stuff */
3297 uirow = MSI_CreateRecord(3);
3298 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3299 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3300 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3301 msi_ui_actiondata( package, szProcessComponents, uirow );
3302 msiobj_release( &uirow->hdr );
3304 return ERROR_SUCCESS;
3307 typedef struct {
3308 CLSID clsid;
3309 LPWSTR source;
3311 LPWSTR path;
3312 ITypeLib *ptLib;
3313 } typelib_struct;
3315 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3316 LPWSTR lpszName, LONG_PTR lParam)
3318 TLIBATTR *attr;
3319 typelib_struct *tl_struct = (typelib_struct*) lParam;
3320 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3321 int sz;
3322 HRESULT res;
3324 if (!IS_INTRESOURCE(lpszName))
3326 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3327 return TRUE;
3330 sz = strlenW(tl_struct->source)+4;
3331 sz *= sizeof(WCHAR);
3333 if ((INT_PTR)lpszName == 1)
3334 tl_struct->path = strdupW(tl_struct->source);
3335 else
3337 tl_struct->path = msi_alloc(sz);
3338 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3341 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3342 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3343 if (FAILED(res))
3345 msi_free(tl_struct->path);
3346 tl_struct->path = NULL;
3348 return TRUE;
3351 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3352 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3354 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3355 return FALSE;
3358 msi_free(tl_struct->path);
3359 tl_struct->path = NULL;
3361 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3362 ITypeLib_Release(tl_struct->ptLib);
3364 return TRUE;
3367 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3369 MSIPACKAGE* package = param;
3370 LPCWSTR component;
3371 MSICOMPONENT *comp;
3372 MSIFILE *file;
3373 typelib_struct tl_struct;
3374 ITypeLib *tlib;
3375 HMODULE module;
3376 HRESULT hr;
3378 component = MSI_RecordGetString(row,3);
3379 comp = msi_get_loaded_component(package,component);
3380 if (!comp)
3381 return ERROR_SUCCESS;
3383 comp->Action = msi_get_component_action( package, comp );
3384 if (comp->Action != INSTALLSTATE_LOCAL)
3386 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3387 return ERROR_SUCCESS;
3390 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3392 TRACE("component has no key path\n");
3393 return ERROR_SUCCESS;
3395 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3397 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3398 if (module)
3400 LPCWSTR guid;
3401 guid = MSI_RecordGetString(row,1);
3402 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3403 tl_struct.source = strdupW( file->TargetPath );
3404 tl_struct.path = NULL;
3406 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3407 (LONG_PTR)&tl_struct);
3409 if (tl_struct.path)
3411 LPCWSTR helpid, help_path = NULL;
3412 HRESULT res;
3414 helpid = MSI_RecordGetString(row,6);
3416 if (helpid) help_path = msi_get_target_folder( package, helpid );
3417 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3419 if (FAILED(res))
3420 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3421 else
3422 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3424 ITypeLib_Release(tl_struct.ptLib);
3425 msi_free(tl_struct.path);
3427 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3429 FreeLibrary(module);
3430 msi_free(tl_struct.source);
3432 else
3434 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3435 if (FAILED(hr))
3437 ERR("Failed to load type library: %08x\n", hr);
3438 return ERROR_INSTALL_FAILURE;
3441 ITypeLib_Release(tlib);
3444 return ERROR_SUCCESS;
3447 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3450 * OK this is a bit confusing.. I am given a _Component key and I believe
3451 * that the file that is being registered as a type library is the "key file
3452 * of that component" which I interpret to mean "The file in the KeyPath of
3453 * that component".
3455 UINT rc;
3456 MSIQUERY * view;
3457 static const WCHAR Query[] =
3458 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3459 '`','T','y','p','e','L','i','b','`',0};
3461 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3462 if (rc != ERROR_SUCCESS)
3463 return ERROR_SUCCESS;
3465 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3466 msiobj_release(&view->hdr);
3467 return rc;
3470 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3472 MSIPACKAGE *package = param;
3473 LPCWSTR component, guid;
3474 MSICOMPONENT *comp;
3475 GUID libid;
3476 UINT version;
3477 LCID language;
3478 SYSKIND syskind;
3479 HRESULT hr;
3481 component = MSI_RecordGetString( row, 3 );
3482 comp = msi_get_loaded_component( package, component );
3483 if (!comp)
3484 return ERROR_SUCCESS;
3486 comp->Action = msi_get_component_action( package, comp );
3487 if (comp->Action != INSTALLSTATE_ABSENT)
3489 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3490 return ERROR_SUCCESS;
3492 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3494 guid = MSI_RecordGetString( row, 1 );
3495 CLSIDFromString( (LPCWSTR)guid, &libid );
3496 version = MSI_RecordGetInteger( row, 4 );
3497 language = MSI_RecordGetInteger( row, 2 );
3499 #ifdef _WIN64
3500 syskind = SYS_WIN64;
3501 #else
3502 syskind = SYS_WIN32;
3503 #endif
3505 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3506 if (FAILED(hr))
3508 WARN("Failed to unregister typelib: %08x\n", hr);
3511 return ERROR_SUCCESS;
3514 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3516 UINT rc;
3517 MSIQUERY *view;
3518 static const WCHAR query[] =
3519 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3520 '`','T','y','p','e','L','i','b','`',0};
3522 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3523 if (rc != ERROR_SUCCESS)
3524 return ERROR_SUCCESS;
3526 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3527 msiobj_release( &view->hdr );
3528 return rc;
3531 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3533 static const WCHAR szlnk[] = {'.','l','n','k',0};
3534 LPCWSTR directory, extension, link_folder;
3535 LPWSTR link_file, filename;
3537 directory = MSI_RecordGetString( row, 2 );
3538 link_folder = msi_get_target_folder( package, directory );
3540 /* may be needed because of a bug somewhere else */
3541 msi_create_full_path( link_folder );
3543 filename = msi_dup_record_field( row, 3 );
3544 msi_reduce_to_long_filename( filename );
3546 extension = strchrW( filename, '.' );
3547 if (!extension || strcmpiW( extension, szlnk ))
3549 int len = strlenW( filename );
3550 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3551 memcpy( filename + len, szlnk, sizeof(szlnk) );
3553 link_file = msi_build_directory_name( 2, link_folder, filename );
3554 msi_free( filename );
3556 return link_file;
3559 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3561 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3562 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3563 WCHAR *folder, *dest, *path;
3565 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3566 folder = msi_dup_property( package->db, szWindowsFolder );
3567 else
3569 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3570 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3571 msi_free( appdata );
3573 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3574 msi_create_full_path( dest );
3575 path = msi_build_directory_name( 2, dest, icon_name );
3576 msi_free( folder );
3577 msi_free( dest );
3578 return path;
3581 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3583 MSIPACKAGE *package = param;
3584 LPWSTR link_file, deformated, path;
3585 LPCWSTR component, target;
3586 MSICOMPONENT *comp;
3587 IShellLinkW *sl = NULL;
3588 IPersistFile *pf = NULL;
3589 HRESULT res;
3591 component = MSI_RecordGetString(row, 4);
3592 comp = msi_get_loaded_component(package, component);
3593 if (!comp)
3594 return ERROR_SUCCESS;
3596 comp->Action = msi_get_component_action( package, comp );
3597 if (comp->Action != INSTALLSTATE_LOCAL)
3599 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3600 return ERROR_SUCCESS;
3602 msi_ui_actiondata( package, szCreateShortcuts, row );
3604 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3605 &IID_IShellLinkW, (LPVOID *) &sl );
3607 if (FAILED( res ))
3609 ERR("CLSID_ShellLink not available\n");
3610 goto err;
3613 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3614 if (FAILED( res ))
3616 ERR("QueryInterface(IID_IPersistFile) failed\n");
3617 goto err;
3620 target = MSI_RecordGetString(row, 5);
3621 if (strchrW(target, '['))
3623 deformat_string(package, target, &deformated);
3624 IShellLinkW_SetPath(sl,deformated);
3625 msi_free(deformated);
3627 else
3629 FIXME("poorly handled shortcut format, advertised shortcut\n");
3630 IShellLinkW_SetPath(sl,comp->FullKeypath);
3633 if (!MSI_RecordIsNull(row,6))
3635 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3636 deformat_string(package, arguments, &deformated);
3637 IShellLinkW_SetArguments(sl,deformated);
3638 msi_free(deformated);
3641 if (!MSI_RecordIsNull(row,7))
3643 LPCWSTR description = MSI_RecordGetString(row, 7);
3644 IShellLinkW_SetDescription(sl, description);
3647 if (!MSI_RecordIsNull(row,8))
3648 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3650 if (!MSI_RecordIsNull(row,9))
3652 INT index;
3653 LPCWSTR icon = MSI_RecordGetString(row, 9);
3655 path = msi_build_icon_path(package, icon);
3656 index = MSI_RecordGetInteger(row,10);
3658 /* no value means 0 */
3659 if (index == MSI_NULL_INTEGER)
3660 index = 0;
3662 IShellLinkW_SetIconLocation(sl, path, index);
3663 msi_free(path);
3666 if (!MSI_RecordIsNull(row,11))
3667 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3669 if (!MSI_RecordIsNull(row,12))
3671 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3672 full_path = msi_get_target_folder( package, wkdir );
3673 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3675 link_file = get_link_file(package, row);
3677 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3678 IPersistFile_Save(pf, link_file, FALSE);
3679 msi_free(link_file);
3681 err:
3682 if (pf)
3683 IPersistFile_Release( pf );
3684 if (sl)
3685 IShellLinkW_Release( sl );
3687 return ERROR_SUCCESS;
3690 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3692 UINT rc;
3693 HRESULT res;
3694 MSIQUERY * view;
3695 static const WCHAR Query[] =
3696 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3697 '`','S','h','o','r','t','c','u','t','`',0};
3699 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3700 if (rc != ERROR_SUCCESS)
3701 return ERROR_SUCCESS;
3703 res = CoInitialize( NULL );
3705 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3706 msiobj_release(&view->hdr);
3708 if (SUCCEEDED(res))
3709 CoUninitialize();
3711 return rc;
3714 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3716 MSIPACKAGE *package = param;
3717 LPWSTR link_file;
3718 LPCWSTR component;
3719 MSICOMPONENT *comp;
3721 component = MSI_RecordGetString( row, 4 );
3722 comp = msi_get_loaded_component( package, component );
3723 if (!comp)
3724 return ERROR_SUCCESS;
3726 comp->Action = msi_get_component_action( package, comp );
3727 if (comp->Action != INSTALLSTATE_ABSENT)
3729 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3730 return ERROR_SUCCESS;
3732 msi_ui_actiondata( package, szRemoveShortcuts, row );
3734 link_file = get_link_file( package, row );
3736 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3737 if (!DeleteFileW( link_file ))
3739 WARN("Failed to remove shortcut file %u\n", GetLastError());
3741 msi_free( link_file );
3743 return ERROR_SUCCESS;
3746 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3748 UINT rc;
3749 MSIQUERY *view;
3750 static const WCHAR query[] =
3751 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3752 '`','S','h','o','r','t','c','u','t','`',0};
3754 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3755 if (rc != ERROR_SUCCESS)
3756 return ERROR_SUCCESS;
3758 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3759 msiobj_release( &view->hdr );
3761 return rc;
3764 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3766 MSIPACKAGE* package = param;
3767 HANDLE the_file;
3768 LPWSTR FilePath;
3769 LPCWSTR FileName;
3770 CHAR buffer[1024];
3771 DWORD sz;
3772 UINT rc;
3774 FileName = MSI_RecordGetString(row,1);
3775 if (!FileName)
3777 ERR("Unable to get FileName\n");
3778 return ERROR_SUCCESS;
3781 FilePath = msi_build_icon_path(package, FileName);
3783 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3785 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3786 FILE_ATTRIBUTE_NORMAL, NULL);
3788 if (the_file == INVALID_HANDLE_VALUE)
3790 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3791 msi_free(FilePath);
3792 return ERROR_SUCCESS;
3797 DWORD write;
3798 sz = 1024;
3799 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3800 if (rc != ERROR_SUCCESS)
3802 ERR("Failed to get stream\n");
3803 CloseHandle(the_file);
3804 DeleteFileW(FilePath);
3805 break;
3807 WriteFile(the_file,buffer,sz,&write,NULL);
3808 } while (sz == 1024);
3810 msi_free(FilePath);
3811 CloseHandle(the_file);
3813 return ERROR_SUCCESS;
3816 static UINT msi_publish_icons(MSIPACKAGE *package)
3818 UINT r;
3819 MSIQUERY *view;
3821 static const WCHAR query[]= {
3822 'S','E','L','E','C','T',' ','*',' ',
3823 'F','R','O','M',' ','`','I','c','o','n','`',0};
3825 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3826 if (r == ERROR_SUCCESS)
3828 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3829 msiobj_release(&view->hdr);
3832 return ERROR_SUCCESS;
3835 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3837 UINT r;
3838 HKEY source;
3839 LPWSTR buffer;
3840 MSIMEDIADISK *disk;
3841 MSISOURCELISTINFO *info;
3843 r = RegCreateKeyW(hkey, szSourceList, &source);
3844 if (r != ERROR_SUCCESS)
3845 return r;
3847 RegCloseKey(source);
3849 buffer = strrchrW(package->PackagePath, '\\') + 1;
3850 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3851 package->Context, MSICODE_PRODUCT,
3852 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3853 if (r != ERROR_SUCCESS)
3854 return r;
3856 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3857 package->Context, MSICODE_PRODUCT,
3858 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3859 if (r != ERROR_SUCCESS)
3860 return r;
3862 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3863 package->Context, MSICODE_PRODUCT,
3864 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3865 if (r != ERROR_SUCCESS)
3866 return r;
3868 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3870 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3871 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3872 info->options, info->value);
3873 else
3874 MsiSourceListSetInfoW(package->ProductCode, NULL,
3875 info->context, info->options,
3876 info->property, info->value);
3879 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3881 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3882 disk->context, disk->options,
3883 disk->disk_id, disk->volume_label, disk->disk_prompt);
3886 return ERROR_SUCCESS;
3889 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3891 MSIHANDLE hdb, suminfo;
3892 WCHAR guids[MAX_PATH];
3893 WCHAR packcode[SQUISH_GUID_SIZE];
3894 LPWSTR buffer;
3895 LPWSTR ptr;
3896 DWORD langid;
3897 DWORD size;
3898 UINT r;
3900 static const WCHAR szARPProductIcon[] =
3901 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3902 static const WCHAR szAssignment[] =
3903 {'A','s','s','i','g','n','m','e','n','t',0};
3904 static const WCHAR szAdvertiseFlags[] =
3905 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3906 static const WCHAR szClients[] =
3907 {'C','l','i','e','n','t','s',0};
3908 static const WCHAR szColon[] = {':',0};
3910 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3911 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3912 msi_free(buffer);
3914 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3915 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3917 /* FIXME */
3918 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3920 buffer = msi_dup_property(package->db, szARPProductIcon);
3921 if (buffer)
3923 LPWSTR path = msi_build_icon_path(package, buffer);
3924 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3925 msi_free(path);
3926 msi_free(buffer);
3929 buffer = msi_dup_property(package->db, szProductVersion);
3930 if (buffer)
3932 DWORD verdword = msi_version_str_to_dword(buffer);
3933 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3934 msi_free(buffer);
3937 msi_reg_set_val_dword(hkey, szAssignment, 0);
3938 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3939 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3940 msi_reg_set_val_str(hkey, szClients, szColon);
3942 hdb = alloc_msihandle(&package->db->hdr);
3943 if (!hdb)
3944 return ERROR_NOT_ENOUGH_MEMORY;
3946 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3947 MsiCloseHandle(hdb);
3948 if (r != ERROR_SUCCESS)
3949 goto done;
3951 size = MAX_PATH;
3952 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3953 NULL, guids, &size);
3954 if (r != ERROR_SUCCESS)
3955 goto done;
3957 ptr = strchrW(guids, ';');
3958 if (ptr) *ptr = 0;
3959 squash_guid(guids, packcode);
3960 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3962 done:
3963 MsiCloseHandle(suminfo);
3964 return ERROR_SUCCESS;
3967 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3969 UINT r;
3970 HKEY hkey;
3971 LPWSTR upgrade;
3972 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3974 upgrade = msi_dup_property(package->db, szUpgradeCode);
3975 if (!upgrade)
3976 return ERROR_SUCCESS;
3978 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3979 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3980 else
3981 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3983 if (r != ERROR_SUCCESS)
3985 WARN("failed to open upgrade code key\n");
3986 msi_free(upgrade);
3987 return ERROR_SUCCESS;
3989 squash_guid(package->ProductCode, squashed_pc);
3990 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3991 RegCloseKey(hkey);
3992 msi_free(upgrade);
3993 return ERROR_SUCCESS;
3996 static BOOL msi_check_publish(MSIPACKAGE *package)
3998 MSIFEATURE *feature;
4000 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4002 feature->Action = msi_get_feature_action( package, feature );
4003 if (feature->Action == INSTALLSTATE_LOCAL)
4004 return TRUE;
4007 return FALSE;
4010 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4012 MSIFEATURE *feature;
4014 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4016 feature->Action = msi_get_feature_action( package, feature );
4017 if (feature->Action != INSTALLSTATE_ABSENT)
4018 return FALSE;
4021 return TRUE;
4024 static UINT msi_publish_patches( MSIPACKAGE *package )
4026 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4027 WCHAR patch_squashed[GUID_SIZE];
4028 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4029 LONG res;
4030 MSIPATCHINFO *patch;
4031 UINT r;
4032 WCHAR *p, *all_patches = NULL;
4033 DWORD len = 0;
4035 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4036 if (r != ERROR_SUCCESS)
4037 return ERROR_FUNCTION_FAILED;
4039 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4040 if (res != ERROR_SUCCESS)
4042 r = ERROR_FUNCTION_FAILED;
4043 goto done;
4046 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4047 if (r != ERROR_SUCCESS)
4048 goto done;
4050 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4052 squash_guid( patch->patchcode, patch_squashed );
4053 len += strlenW( patch_squashed ) + 1;
4056 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4057 if (!all_patches)
4058 goto done;
4060 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4062 HKEY patch_key;
4064 squash_guid( patch->patchcode, p );
4065 p += strlenW( p ) + 1;
4067 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4068 (const BYTE *)patch->transforms,
4069 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4070 if (res != ERROR_SUCCESS)
4071 goto done;
4073 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4074 if (r != ERROR_SUCCESS)
4075 goto done;
4077 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4078 (const BYTE *)patch->localfile,
4079 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4080 RegCloseKey( patch_key );
4081 if (res != ERROR_SUCCESS)
4082 goto done;
4084 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4085 if (res != ERROR_SUCCESS)
4086 goto done;
4088 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4089 RegCloseKey( patch_key );
4090 if (res != ERROR_SUCCESS)
4091 goto done;
4094 all_patches[len] = 0;
4095 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4096 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4097 if (res != ERROR_SUCCESS)
4098 goto done;
4100 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4101 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4102 if (res != ERROR_SUCCESS)
4103 r = ERROR_FUNCTION_FAILED;
4105 done:
4106 RegCloseKey( product_patches_key );
4107 RegCloseKey( patches_key );
4108 RegCloseKey( product_key );
4109 msi_free( all_patches );
4110 return r;
4113 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4115 UINT rc;
4116 HKEY hukey = NULL, hudkey = NULL;
4117 MSIRECORD *uirow;
4119 if (!list_empty(&package->patches))
4121 rc = msi_publish_patches(package);
4122 if (rc != ERROR_SUCCESS)
4123 goto end;
4126 /* FIXME: also need to publish if the product is in advertise mode */
4127 if (!msi_check_publish(package))
4128 return ERROR_SUCCESS;
4130 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4131 &hukey, TRUE);
4132 if (rc != ERROR_SUCCESS)
4133 goto end;
4135 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4136 NULL, &hudkey, TRUE);
4137 if (rc != ERROR_SUCCESS)
4138 goto end;
4140 rc = msi_publish_upgrade_code(package);
4141 if (rc != ERROR_SUCCESS)
4142 goto end;
4144 rc = msi_publish_product_properties(package, hukey);
4145 if (rc != ERROR_SUCCESS)
4146 goto end;
4148 rc = msi_publish_sourcelist(package, hukey);
4149 if (rc != ERROR_SUCCESS)
4150 goto end;
4152 rc = msi_publish_icons(package);
4154 end:
4155 uirow = MSI_CreateRecord( 1 );
4156 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4157 msi_ui_actiondata( package, szPublishProduct, uirow );
4158 msiobj_release( &uirow->hdr );
4160 RegCloseKey(hukey);
4161 RegCloseKey(hudkey);
4163 return rc;
4166 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4168 WCHAR *filename, *ptr, *folder, *ret;
4169 const WCHAR *dirprop;
4171 filename = msi_dup_record_field( row, 2 );
4172 if (filename && (ptr = strchrW( filename, '|' )))
4173 ptr++;
4174 else
4175 ptr = filename;
4177 dirprop = MSI_RecordGetString( row, 3 );
4178 if (dirprop)
4180 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4181 if (!folder) folder = msi_dup_property( package->db, dirprop );
4183 else
4184 folder = msi_dup_property( package->db, szWindowsFolder );
4186 if (!folder)
4188 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4189 msi_free( filename );
4190 return NULL;
4193 ret = msi_build_directory_name( 2, folder, ptr );
4195 msi_free( filename );
4196 msi_free( folder );
4197 return ret;
4200 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4202 MSIPACKAGE *package = param;
4203 LPCWSTR component, section, key, value, identifier;
4204 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4205 MSIRECORD * uirow;
4206 INT action;
4207 MSICOMPONENT *comp;
4209 component = MSI_RecordGetString(row, 8);
4210 comp = msi_get_loaded_component(package,component);
4211 if (!comp)
4212 return ERROR_SUCCESS;
4214 comp->Action = msi_get_component_action( package, comp );
4215 if (comp->Action != INSTALLSTATE_LOCAL)
4217 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4218 return ERROR_SUCCESS;
4221 identifier = MSI_RecordGetString(row,1);
4222 section = MSI_RecordGetString(row,4);
4223 key = MSI_RecordGetString(row,5);
4224 value = MSI_RecordGetString(row,6);
4225 action = MSI_RecordGetInteger(row,7);
4227 deformat_string(package,section,&deformated_section);
4228 deformat_string(package,key,&deformated_key);
4229 deformat_string(package,value,&deformated_value);
4231 fullname = get_ini_file_name(package, row);
4233 if (action == 0)
4235 TRACE("Adding value %s to section %s in %s\n",
4236 debugstr_w(deformated_key), debugstr_w(deformated_section),
4237 debugstr_w(fullname));
4238 WritePrivateProfileStringW(deformated_section, deformated_key,
4239 deformated_value, fullname);
4241 else if (action == 1)
4243 WCHAR returned[10];
4244 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4245 returned, 10, fullname);
4246 if (returned[0] == 0)
4248 TRACE("Adding value %s to section %s in %s\n",
4249 debugstr_w(deformated_key), debugstr_w(deformated_section),
4250 debugstr_w(fullname));
4252 WritePrivateProfileStringW(deformated_section, deformated_key,
4253 deformated_value, fullname);
4256 else if (action == 3)
4257 FIXME("Append to existing section not yet implemented\n");
4259 uirow = MSI_CreateRecord(4);
4260 MSI_RecordSetStringW(uirow,1,identifier);
4261 MSI_RecordSetStringW(uirow,2,deformated_section);
4262 MSI_RecordSetStringW(uirow,3,deformated_key);
4263 MSI_RecordSetStringW(uirow,4,deformated_value);
4264 msi_ui_actiondata( package, szWriteIniValues, uirow );
4265 msiobj_release( &uirow->hdr );
4267 msi_free(fullname);
4268 msi_free(deformated_key);
4269 msi_free(deformated_value);
4270 msi_free(deformated_section);
4271 return ERROR_SUCCESS;
4274 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4276 UINT rc;
4277 MSIQUERY * view;
4278 static const WCHAR ExecSeqQuery[] =
4279 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4280 '`','I','n','i','F','i','l','e','`',0};
4282 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4283 if (rc != ERROR_SUCCESS)
4285 TRACE("no IniFile table\n");
4286 return ERROR_SUCCESS;
4289 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4290 msiobj_release(&view->hdr);
4291 return rc;
4294 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4296 MSIPACKAGE *package = param;
4297 LPCWSTR component, section, key, value, identifier;
4298 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4299 MSICOMPONENT *comp;
4300 MSIRECORD *uirow;
4301 INT action;
4303 component = MSI_RecordGetString( row, 8 );
4304 comp = msi_get_loaded_component( package, component );
4305 if (!comp)
4306 return ERROR_SUCCESS;
4308 comp->Action = msi_get_component_action( package, comp );
4309 if (comp->Action != INSTALLSTATE_ABSENT)
4311 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4312 return ERROR_SUCCESS;
4315 identifier = MSI_RecordGetString( row, 1 );
4316 section = MSI_RecordGetString( row, 4 );
4317 key = MSI_RecordGetString( row, 5 );
4318 value = MSI_RecordGetString( row, 6 );
4319 action = MSI_RecordGetInteger( row, 7 );
4321 deformat_string( package, section, &deformated_section );
4322 deformat_string( package, key, &deformated_key );
4323 deformat_string( package, value, &deformated_value );
4325 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4327 filename = get_ini_file_name( package, row );
4329 TRACE("Removing key %s from section %s in %s\n",
4330 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4332 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4334 WARN("Unable to remove key %u\n", GetLastError());
4336 msi_free( filename );
4338 else
4339 FIXME("Unsupported action %d\n", action);
4342 uirow = MSI_CreateRecord( 4 );
4343 MSI_RecordSetStringW( uirow, 1, identifier );
4344 MSI_RecordSetStringW( uirow, 2, deformated_section );
4345 MSI_RecordSetStringW( uirow, 3, deformated_key );
4346 MSI_RecordSetStringW( uirow, 4, deformated_value );
4347 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4348 msiobj_release( &uirow->hdr );
4350 msi_free( deformated_key );
4351 msi_free( deformated_value );
4352 msi_free( deformated_section );
4353 return ERROR_SUCCESS;
4356 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4358 MSIPACKAGE *package = param;
4359 LPCWSTR component, section, key, value, identifier;
4360 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4361 MSICOMPONENT *comp;
4362 MSIRECORD *uirow;
4363 INT action;
4365 component = MSI_RecordGetString( row, 8 );
4366 comp = msi_get_loaded_component( package, component );
4367 if (!comp)
4368 return ERROR_SUCCESS;
4370 comp->Action = msi_get_component_action( package, comp );
4371 if (comp->Action != INSTALLSTATE_LOCAL)
4373 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4374 return ERROR_SUCCESS;
4377 identifier = MSI_RecordGetString( row, 1 );
4378 section = MSI_RecordGetString( row, 4 );
4379 key = MSI_RecordGetString( row, 5 );
4380 value = MSI_RecordGetString( row, 6 );
4381 action = MSI_RecordGetInteger( row, 7 );
4383 deformat_string( package, section, &deformated_section );
4384 deformat_string( package, key, &deformated_key );
4385 deformat_string( package, value, &deformated_value );
4387 if (action == msidbIniFileActionRemoveLine)
4389 filename = get_ini_file_name( package, row );
4391 TRACE("Removing key %s from section %s in %s\n",
4392 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4394 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4396 WARN("Unable to remove key %u\n", GetLastError());
4398 msi_free( filename );
4400 else
4401 FIXME("Unsupported action %d\n", action);
4403 uirow = MSI_CreateRecord( 4 );
4404 MSI_RecordSetStringW( uirow, 1, identifier );
4405 MSI_RecordSetStringW( uirow, 2, deformated_section );
4406 MSI_RecordSetStringW( uirow, 3, deformated_key );
4407 MSI_RecordSetStringW( uirow, 4, deformated_value );
4408 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4409 msiobj_release( &uirow->hdr );
4411 msi_free( deformated_key );
4412 msi_free( deformated_value );
4413 msi_free( deformated_section );
4414 return ERROR_SUCCESS;
4417 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4419 UINT rc;
4420 MSIQUERY *view;
4421 static const WCHAR query[] =
4422 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4423 '`','I','n','i','F','i','l','e','`',0};
4424 static const WCHAR remove_query[] =
4425 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4426 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4428 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4429 if (rc == ERROR_SUCCESS)
4431 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4432 msiobj_release( &view->hdr );
4433 if (rc != ERROR_SUCCESS)
4434 return rc;
4437 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4438 if (rc == ERROR_SUCCESS)
4440 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4441 msiobj_release( &view->hdr );
4442 if (rc != ERROR_SUCCESS)
4443 return rc;
4446 return ERROR_SUCCESS;
4449 static void register_dll( const WCHAR *dll, BOOL unregister )
4451 HMODULE hmod;
4453 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4454 if (hmod)
4456 HRESULT (WINAPI *func_ptr)( void );
4457 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4459 func_ptr = (void *)GetProcAddress( hmod, func );
4460 if (func_ptr)
4462 HRESULT hr = func_ptr();
4463 if (FAILED( hr ))
4464 WARN("failed to register dll 0x%08x\n", hr);
4466 else
4467 WARN("entry point %s not found\n", func);
4468 FreeLibrary( hmod );
4469 return;
4471 WARN("failed to load library %u\n", GetLastError());
4474 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4476 MSIPACKAGE *package = param;
4477 LPCWSTR filename;
4478 MSIFILE *file;
4479 MSIRECORD *uirow;
4481 filename = MSI_RecordGetString(row,1);
4482 file = msi_get_loaded_file( package, filename );
4483 if (!file)
4485 WARN("unable to find file %s\n", debugstr_w(filename));
4486 return ERROR_SUCCESS;
4488 file->Component->Action = msi_get_component_action( package, file->Component );
4489 if (file->Component->Action != INSTALLSTATE_LOCAL)
4491 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4492 return ERROR_SUCCESS;
4495 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4496 register_dll( file->TargetPath, FALSE );
4498 uirow = MSI_CreateRecord( 2 );
4499 MSI_RecordSetStringW( uirow, 1, filename );
4500 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4501 msi_ui_actiondata( package, szSelfRegModules, uirow );
4502 msiobj_release( &uirow->hdr );
4504 return ERROR_SUCCESS;
4507 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4509 UINT rc;
4510 MSIQUERY * view;
4511 static const WCHAR ExecSeqQuery[] =
4512 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4513 '`','S','e','l','f','R','e','g','`',0};
4515 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4516 if (rc != ERROR_SUCCESS)
4518 TRACE("no SelfReg table\n");
4519 return ERROR_SUCCESS;
4522 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4523 msiobj_release(&view->hdr);
4525 return ERROR_SUCCESS;
4528 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4530 MSIPACKAGE *package = param;
4531 LPCWSTR filename;
4532 MSIFILE *file;
4533 MSIRECORD *uirow;
4535 filename = MSI_RecordGetString( row, 1 );
4536 file = msi_get_loaded_file( package, filename );
4537 if (!file)
4539 WARN("unable to find file %s\n", debugstr_w(filename));
4540 return ERROR_SUCCESS;
4542 file->Component->Action = msi_get_component_action( package, file->Component );
4543 if (file->Component->Action != INSTALLSTATE_ABSENT)
4545 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4546 return ERROR_SUCCESS;
4549 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4550 register_dll( file->TargetPath, TRUE );
4552 uirow = MSI_CreateRecord( 2 );
4553 MSI_RecordSetStringW( uirow, 1, filename );
4554 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4555 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4556 msiobj_release( &uirow->hdr );
4558 return ERROR_SUCCESS;
4561 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4563 UINT rc;
4564 MSIQUERY *view;
4565 static const WCHAR query[] =
4566 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4567 '`','S','e','l','f','R','e','g','`',0};
4569 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4570 if (rc != ERROR_SUCCESS)
4572 TRACE("no SelfReg table\n");
4573 return ERROR_SUCCESS;
4576 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4577 msiobj_release( &view->hdr );
4579 return ERROR_SUCCESS;
4582 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4584 MSIFEATURE *feature;
4585 UINT rc;
4586 HKEY hkey = NULL, userdata = NULL;
4588 if (!msi_check_publish(package))
4589 return ERROR_SUCCESS;
4591 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4592 &hkey, TRUE);
4593 if (rc != ERROR_SUCCESS)
4594 goto end;
4596 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4597 &userdata, TRUE);
4598 if (rc != ERROR_SUCCESS)
4599 goto end;
4601 /* here the guids are base 85 encoded */
4602 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4604 ComponentList *cl;
4605 LPWSTR data = NULL;
4606 GUID clsid;
4607 INT size;
4608 BOOL absent = FALSE;
4609 MSIRECORD *uirow;
4611 if (feature->Action != INSTALLSTATE_LOCAL &&
4612 feature->Action != INSTALLSTATE_SOURCE &&
4613 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4615 size = 1;
4616 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4618 size += 21;
4620 if (feature->Feature_Parent)
4621 size += strlenW( feature->Feature_Parent )+2;
4623 data = msi_alloc(size * sizeof(WCHAR));
4625 data[0] = 0;
4626 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4628 MSICOMPONENT* component = cl->component;
4629 WCHAR buf[21];
4631 buf[0] = 0;
4632 if (component->ComponentId)
4634 TRACE("From %s\n",debugstr_w(component->ComponentId));
4635 CLSIDFromString(component->ComponentId, &clsid);
4636 encode_base85_guid(&clsid,buf);
4637 TRACE("to %s\n",debugstr_w(buf));
4638 strcatW(data,buf);
4642 if (feature->Feature_Parent)
4644 static const WCHAR sep[] = {'\2',0};
4645 strcatW(data,sep);
4646 strcatW(data,feature->Feature_Parent);
4649 msi_reg_set_val_str( userdata, feature->Feature, data );
4650 msi_free(data);
4652 size = 0;
4653 if (feature->Feature_Parent)
4654 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4655 if (!absent)
4657 size += sizeof(WCHAR);
4658 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4659 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4661 else
4663 size += 2*sizeof(WCHAR);
4664 data = msi_alloc(size);
4665 data[0] = 0x6;
4666 data[1] = 0;
4667 if (feature->Feature_Parent)
4668 strcpyW( &data[1], feature->Feature_Parent );
4669 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4670 (LPBYTE)data,size);
4671 msi_free(data);
4674 /* the UI chunk */
4675 uirow = MSI_CreateRecord( 1 );
4676 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4677 msi_ui_actiondata( package, szPublishFeatures, uirow );
4678 msiobj_release( &uirow->hdr );
4679 /* FIXME: call msi_ui_progress? */
4682 end:
4683 RegCloseKey(hkey);
4684 RegCloseKey(userdata);
4685 return rc;
4688 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4690 UINT r;
4691 HKEY hkey;
4692 MSIRECORD *uirow;
4694 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4696 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4697 &hkey, FALSE);
4698 if (r == ERROR_SUCCESS)
4700 RegDeleteValueW(hkey, feature->Feature);
4701 RegCloseKey(hkey);
4704 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4705 &hkey, FALSE);
4706 if (r == ERROR_SUCCESS)
4708 RegDeleteValueW(hkey, feature->Feature);
4709 RegCloseKey(hkey);
4712 uirow = MSI_CreateRecord( 1 );
4713 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4714 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4715 msiobj_release( &uirow->hdr );
4717 return ERROR_SUCCESS;
4720 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4722 MSIFEATURE *feature;
4724 if (!msi_check_unpublish(package))
4725 return ERROR_SUCCESS;
4727 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4729 msi_unpublish_feature(package, feature);
4732 return ERROR_SUCCESS;
4735 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4737 SYSTEMTIME systime;
4738 DWORD size, langid;
4739 WCHAR date[9], *val, *buffer;
4740 const WCHAR *prop, *key;
4742 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4743 static const WCHAR modpath_fmt[] =
4744 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4745 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4746 static const WCHAR szModifyPath[] =
4747 {'M','o','d','i','f','y','P','a','t','h',0};
4748 static const WCHAR szUninstallString[] =
4749 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4750 static const WCHAR szEstimatedSize[] =
4751 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4752 static const WCHAR szDisplayVersion[] =
4753 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4754 static const WCHAR szInstallSource[] =
4755 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4756 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4757 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4758 static const WCHAR szAuthorizedCDFPrefix[] =
4759 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4760 static const WCHAR szARPCONTACT[] =
4761 {'A','R','P','C','O','N','T','A','C','T',0};
4762 static const WCHAR szContact[] =
4763 {'C','o','n','t','a','c','t',0};
4764 static const WCHAR szARPCOMMENTS[] =
4765 {'A','R','P','C','O','M','M','E','N','T','S',0};
4766 static const WCHAR szComments[] =
4767 {'C','o','m','m','e','n','t','s',0};
4768 static const WCHAR szProductName[] =
4769 {'P','r','o','d','u','c','t','N','a','m','e',0};
4770 static const WCHAR szDisplayName[] =
4771 {'D','i','s','p','l','a','y','N','a','m','e',0};
4772 static const WCHAR szARPHELPLINK[] =
4773 {'A','R','P','H','E','L','P','L','I','N','K',0};
4774 static const WCHAR szHelpLink[] =
4775 {'H','e','l','p','L','i','n','k',0};
4776 static const WCHAR szARPHELPTELEPHONE[] =
4777 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4778 static const WCHAR szHelpTelephone[] =
4779 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4780 static const WCHAR szARPINSTALLLOCATION[] =
4781 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4782 static const WCHAR szInstallLocation[] =
4783 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4784 static const WCHAR szManufacturer[] =
4785 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4786 static const WCHAR szPublisher[] =
4787 {'P','u','b','l','i','s','h','e','r',0};
4788 static const WCHAR szARPREADME[] =
4789 {'A','R','P','R','E','A','D','M','E',0};
4790 static const WCHAR szReadme[] =
4791 {'R','e','a','d','M','e',0};
4792 static const WCHAR szARPSIZE[] =
4793 {'A','R','P','S','I','Z','E',0};
4794 static const WCHAR szSize[] =
4795 {'S','i','z','e',0};
4796 static const WCHAR szARPURLINFOABOUT[] =
4797 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4798 static const WCHAR szURLInfoAbout[] =
4799 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4800 static const WCHAR szARPURLUPDATEINFO[] =
4801 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4802 static const WCHAR szURLUpdateInfo[] =
4803 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4804 static const WCHAR szARPSYSTEMCOMPONENT[] =
4805 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4806 static const WCHAR szSystemComponent[] =
4807 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4809 static const WCHAR *propval[] = {
4810 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4811 szARPCONTACT, szContact,
4812 szARPCOMMENTS, szComments,
4813 szProductName, szDisplayName,
4814 szARPHELPLINK, szHelpLink,
4815 szARPHELPTELEPHONE, szHelpTelephone,
4816 szARPINSTALLLOCATION, szInstallLocation,
4817 szSourceDir, szInstallSource,
4818 szManufacturer, szPublisher,
4819 szARPREADME, szReadme,
4820 szARPSIZE, szSize,
4821 szARPURLINFOABOUT, szURLInfoAbout,
4822 szARPURLUPDATEINFO, szURLUpdateInfo,
4823 NULL
4825 const WCHAR **p = propval;
4827 while (*p)
4829 prop = *p++;
4830 key = *p++;
4831 val = msi_dup_property(package->db, prop);
4832 msi_reg_set_val_str(hkey, key, val);
4833 msi_free(val);
4836 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4837 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4839 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4841 size = deformat_string(package, modpath_fmt, &buffer);
4842 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4843 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4844 msi_free(buffer);
4846 /* FIXME: Write real Estimated Size when we have it */
4847 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4849 GetLocalTime(&systime);
4850 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4851 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4853 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4854 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4856 buffer = msi_dup_property(package->db, szProductVersion);
4857 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4858 if (buffer)
4860 DWORD verdword = msi_version_str_to_dword(buffer);
4862 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4863 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4864 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4865 msi_free(buffer);
4868 return ERROR_SUCCESS;
4871 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4873 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4874 MSIRECORD *uirow;
4875 LPWSTR upgrade_code;
4876 HKEY hkey, props, upgrade_key;
4877 UINT rc;
4879 /* FIXME: also need to publish if the product is in advertise mode */
4880 if (!msi_check_publish(package))
4881 return ERROR_SUCCESS;
4883 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4884 if (rc != ERROR_SUCCESS)
4885 return rc;
4887 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4888 NULL, &props, TRUE);
4889 if (rc != ERROR_SUCCESS)
4890 goto done;
4892 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4893 msi_free( package->db->localfile );
4894 package->db->localfile = NULL;
4896 rc = msi_publish_install_properties(package, hkey);
4897 if (rc != ERROR_SUCCESS)
4898 goto done;
4900 rc = msi_publish_install_properties(package, props);
4901 if (rc != ERROR_SUCCESS)
4902 goto done;
4904 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4905 if (upgrade_code)
4907 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4908 if (rc == ERROR_SUCCESS)
4910 squash_guid( package->ProductCode, squashed_pc );
4911 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4912 RegCloseKey( upgrade_key );
4914 msi_free( upgrade_code );
4917 done:
4918 uirow = MSI_CreateRecord( 1 );
4919 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4920 msi_ui_actiondata( package, szRegisterProduct, uirow );
4921 msiobj_release( &uirow->hdr );
4923 RegCloseKey(hkey);
4924 return ERROR_SUCCESS;
4927 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4929 return execute_script(package,INSTALL_SCRIPT);
4932 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4934 WCHAR *upgrade, **features;
4935 BOOL full_uninstall = TRUE;
4936 MSIFEATURE *feature;
4937 MSIPATCHINFO *patch;
4939 static const WCHAR szUpgradeCode[] =
4940 {'U','p','g','r','a','d','e','C','o','d','e',0};
4942 features = msi_split_string(remove, ',');
4943 if (!features)
4945 ERR("REMOVE feature list is empty!\n");
4946 return ERROR_FUNCTION_FAILED;
4949 if (!strcmpW( features[0], szAll ))
4950 full_uninstall = TRUE;
4951 else
4953 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4955 if (feature->Action != INSTALLSTATE_ABSENT)
4956 full_uninstall = FALSE;
4959 msi_free(features);
4961 if (!full_uninstall)
4962 return ERROR_SUCCESS;
4964 MSIREG_DeleteProductKey(package->ProductCode);
4965 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4966 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4968 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4969 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4970 MSIREG_DeleteUserProductKey(package->ProductCode);
4971 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4973 upgrade = msi_dup_property(package->db, szUpgradeCode);
4974 if (upgrade)
4976 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4977 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4978 msi_free(upgrade);
4981 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4983 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4986 return ERROR_SUCCESS;
4989 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4991 UINT rc;
4992 WCHAR *remove;
4994 /* turn off scheduling */
4995 package->script->CurrentlyScripting= FALSE;
4997 /* first do the same as an InstallExecute */
4998 rc = ACTION_InstallExecute(package);
4999 if (rc != ERROR_SUCCESS)
5000 return rc;
5002 /* then handle Commit Actions */
5003 rc = execute_script(package,COMMIT_SCRIPT);
5004 if (rc != ERROR_SUCCESS)
5005 return rc;
5007 remove = msi_dup_property(package->db, szRemove);
5008 if (remove)
5009 rc = msi_unpublish_product(package, remove);
5011 msi_free(remove);
5012 return rc;
5015 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5017 static const WCHAR RunOnce[] = {
5018 'S','o','f','t','w','a','r','e','\\',
5019 'M','i','c','r','o','s','o','f','t','\\',
5020 'W','i','n','d','o','w','s','\\',
5021 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5022 'R','u','n','O','n','c','e',0};
5023 static const WCHAR InstallRunOnce[] = {
5024 'S','o','f','t','w','a','r','e','\\',
5025 'M','i','c','r','o','s','o','f','t','\\',
5026 'W','i','n','d','o','w','s','\\',
5027 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5028 'I','n','s','t','a','l','l','e','r','\\',
5029 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5031 static const WCHAR msiexec_fmt[] = {
5032 '%','s',
5033 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5034 '\"','%','s','\"',0};
5035 static const WCHAR install_fmt[] = {
5036 '/','I',' ','\"','%','s','\"',' ',
5037 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5038 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5039 WCHAR buffer[256], sysdir[MAX_PATH];
5040 HKEY hkey;
5041 WCHAR squished_pc[100];
5043 squash_guid(package->ProductCode,squished_pc);
5045 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5046 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5047 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5048 squished_pc);
5050 msi_reg_set_val_str( hkey, squished_pc, buffer );
5051 RegCloseKey(hkey);
5053 TRACE("Reboot command %s\n",debugstr_w(buffer));
5055 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5056 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5058 msi_reg_set_val_str( hkey, squished_pc, buffer );
5059 RegCloseKey(hkey);
5061 return ERROR_INSTALL_SUSPEND;
5064 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5066 static const WCHAR query[] =
5067 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5068 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5069 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5070 MSIRECORD *rec, *row;
5071 DWORD i, size = 0;
5072 va_list va;
5073 const WCHAR *str;
5074 WCHAR *data;
5076 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5078 rec = MSI_CreateRecord( count + 2 );
5079 str = MSI_RecordGetString( row, 1 );
5080 MSI_RecordSetStringW( rec, 0, str );
5081 msiobj_release( &row->hdr );
5082 MSI_RecordSetInteger( rec, 1, error );
5084 va_start( va, count );
5085 for (i = 0; i < count; i++)
5087 str = va_arg( va, const WCHAR *);
5088 MSI_RecordSetStringW( rec, i + 2, str );
5090 va_end( va );
5092 MSI_FormatRecordW( package, rec, NULL, &size );
5093 size++;
5094 data = msi_alloc( size * sizeof(WCHAR) );
5095 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5096 else data[0] = 0;
5097 msiobj_release( &rec->hdr );
5098 return data;
5101 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5103 DWORD attrib;
5104 UINT rc;
5107 * We are currently doing what should be done here in the top level Install
5108 * however for Administrative and uninstalls this step will be needed
5110 if (!package->PackagePath)
5111 return ERROR_SUCCESS;
5113 msi_set_sourcedir_props(package, TRUE);
5115 attrib = GetFileAttributesW(package->db->path);
5116 if (attrib == INVALID_FILE_ATTRIBUTES)
5118 LPWSTR prompt;
5119 LPWSTR msg;
5120 DWORD size = 0;
5122 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5123 package->Context, MSICODE_PRODUCT,
5124 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5125 if (rc == ERROR_MORE_DATA)
5127 prompt = msi_alloc(size * sizeof(WCHAR));
5128 MsiSourceListGetInfoW(package->ProductCode, NULL,
5129 package->Context, MSICODE_PRODUCT,
5130 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5132 else
5133 prompt = strdupW(package->db->path);
5135 msg = msi_build_error_string(package, 1302, 1, prompt);
5136 while(attrib == INVALID_FILE_ATTRIBUTES)
5138 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5139 if (rc == IDCANCEL)
5141 rc = ERROR_INSTALL_USEREXIT;
5142 break;
5144 attrib = GetFileAttributesW(package->db->path);
5146 msi_free(prompt);
5147 rc = ERROR_SUCCESS;
5149 else
5150 return ERROR_SUCCESS;
5152 return rc;
5155 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5157 HKEY hkey = 0;
5158 LPWSTR buffer, productid = NULL;
5159 UINT i, rc = ERROR_SUCCESS;
5160 MSIRECORD *uirow;
5162 static const WCHAR szPropKeys[][80] =
5164 {'P','r','o','d','u','c','t','I','D',0},
5165 {'U','S','E','R','N','A','M','E',0},
5166 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5167 {0},
5170 static const WCHAR szRegKeys[][80] =
5172 {'P','r','o','d','u','c','t','I','D',0},
5173 {'R','e','g','O','w','n','e','r',0},
5174 {'R','e','g','C','o','m','p','a','n','y',0},
5175 {0},
5178 if (msi_check_unpublish(package))
5180 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5181 goto end;
5184 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5185 if (!productid)
5186 goto end;
5188 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5189 NULL, &hkey, TRUE);
5190 if (rc != ERROR_SUCCESS)
5191 goto end;
5193 for( i = 0; szPropKeys[i][0]; i++ )
5195 buffer = msi_dup_property( package->db, szPropKeys[i] );
5196 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5197 msi_free( buffer );
5200 end:
5201 uirow = MSI_CreateRecord( 1 );
5202 MSI_RecordSetStringW( uirow, 1, productid );
5203 msi_ui_actiondata( package, szRegisterUser, uirow );
5204 msiobj_release( &uirow->hdr );
5206 msi_free(productid);
5207 RegCloseKey(hkey);
5208 return rc;
5212 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5214 UINT rc;
5216 package->script->InWhatSequence |= SEQUENCE_EXEC;
5217 rc = ACTION_ProcessExecSequence(package,FALSE);
5218 return rc;
5221 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5223 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5224 WCHAR productid_85[21], component_85[21], *ret;
5225 GUID clsid;
5226 DWORD sz;
5228 /* > is used if there is a component GUID and < if not. */
5230 productid_85[0] = 0;
5231 component_85[0] = 0;
5232 CLSIDFromString( package->ProductCode, &clsid );
5234 encode_base85_guid( &clsid, productid_85 );
5235 if (component)
5237 CLSIDFromString( component->ComponentId, &clsid );
5238 encode_base85_guid( &clsid, component_85 );
5241 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5242 debugstr_w(component_85));
5244 sz = 20 + strlenW( feature ) + 20 + 3;
5245 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5246 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5247 return ret;
5250 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5252 MSIPACKAGE *package = param;
5253 LPCWSTR compgroupid, component, feature, qualifier, text;
5254 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5255 HKEY hkey = NULL;
5256 UINT rc;
5257 MSICOMPONENT *comp;
5258 MSIFEATURE *feat;
5259 DWORD sz;
5260 MSIRECORD *uirow;
5261 int len;
5263 feature = MSI_RecordGetString(rec, 5);
5264 feat = msi_get_loaded_feature(package, feature);
5265 if (!feat)
5266 return ERROR_SUCCESS;
5268 feat->Action = msi_get_feature_action( package, feat );
5269 if (feat->Action != INSTALLSTATE_LOCAL &&
5270 feat->Action != INSTALLSTATE_SOURCE &&
5271 feat->Action != INSTALLSTATE_ADVERTISED)
5273 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5274 return ERROR_SUCCESS;
5277 component = MSI_RecordGetString(rec, 3);
5278 comp = msi_get_loaded_component(package, component);
5279 if (!comp)
5280 return ERROR_SUCCESS;
5282 compgroupid = MSI_RecordGetString(rec,1);
5283 qualifier = MSI_RecordGetString(rec,2);
5285 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5286 if (rc != ERROR_SUCCESS)
5287 goto end;
5289 advertise = msi_create_component_advertise_string( package, comp, feature );
5290 text = MSI_RecordGetString( rec, 4 );
5291 if (text)
5293 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5294 strcpyW( p, advertise );
5295 strcatW( p, text );
5296 msi_free( advertise );
5297 advertise = p;
5299 existing = msi_reg_get_val_str( hkey, qualifier );
5301 sz = strlenW( advertise ) + 1;
5302 if (existing)
5304 for (p = existing; *p; p += len)
5306 len = strlenW( p ) + 1;
5307 if (strcmpW( advertise, p )) sz += len;
5310 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5312 rc = ERROR_OUTOFMEMORY;
5313 goto end;
5315 q = output;
5316 if (existing)
5318 for (p = existing; *p; p += len)
5320 len = strlenW( p ) + 1;
5321 if (strcmpW( advertise, p ))
5323 memcpy( q, p, len * sizeof(WCHAR) );
5324 q += len;
5328 strcpyW( q, advertise );
5329 q[strlenW( q ) + 1] = 0;
5331 msi_reg_set_val_multi_str( hkey, qualifier, output );
5333 end:
5334 RegCloseKey(hkey);
5335 msi_free( output );
5336 msi_free( advertise );
5337 msi_free( existing );
5339 /* the UI chunk */
5340 uirow = MSI_CreateRecord( 2 );
5341 MSI_RecordSetStringW( uirow, 1, compgroupid );
5342 MSI_RecordSetStringW( uirow, 2, qualifier);
5343 msi_ui_actiondata( package, szPublishComponents, uirow );
5344 msiobj_release( &uirow->hdr );
5345 /* FIXME: call ui_progress? */
5347 return rc;
5351 * At present I am ignorning the advertised components part of this and only
5352 * focusing on the qualified component sets
5354 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5356 UINT rc;
5357 MSIQUERY * view;
5358 static const WCHAR ExecSeqQuery[] =
5359 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5360 '`','P','u','b','l','i','s','h',
5361 'C','o','m','p','o','n','e','n','t','`',0};
5363 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5364 if (rc != ERROR_SUCCESS)
5365 return ERROR_SUCCESS;
5367 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5368 msiobj_release(&view->hdr);
5370 return rc;
5373 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5375 static const WCHAR szInstallerComponents[] = {
5376 'S','o','f','t','w','a','r','e','\\',
5377 'M','i','c','r','o','s','o','f','t','\\',
5378 'I','n','s','t','a','l','l','e','r','\\',
5379 'C','o','m','p','o','n','e','n','t','s','\\',0};
5381 MSIPACKAGE *package = param;
5382 LPCWSTR compgroupid, component, feature, qualifier;
5383 MSICOMPONENT *comp;
5384 MSIFEATURE *feat;
5385 MSIRECORD *uirow;
5386 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5387 LONG res;
5389 feature = MSI_RecordGetString( rec, 5 );
5390 feat = msi_get_loaded_feature( package, feature );
5391 if (!feat)
5392 return ERROR_SUCCESS;
5394 feat->Action = msi_get_feature_action( package, feat );
5395 if (feat->Action != INSTALLSTATE_ABSENT)
5397 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5398 return ERROR_SUCCESS;
5401 component = MSI_RecordGetString( rec, 3 );
5402 comp = msi_get_loaded_component( package, component );
5403 if (!comp)
5404 return ERROR_SUCCESS;
5406 compgroupid = MSI_RecordGetString( rec, 1 );
5407 qualifier = MSI_RecordGetString( rec, 2 );
5409 squash_guid( compgroupid, squashed );
5410 strcpyW( keypath, szInstallerComponents );
5411 strcatW( keypath, squashed );
5413 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5414 if (res != ERROR_SUCCESS)
5416 WARN("Unable to delete component key %d\n", res);
5419 uirow = MSI_CreateRecord( 2 );
5420 MSI_RecordSetStringW( uirow, 1, compgroupid );
5421 MSI_RecordSetStringW( uirow, 2, qualifier );
5422 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5423 msiobj_release( &uirow->hdr );
5425 return ERROR_SUCCESS;
5428 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5430 UINT rc;
5431 MSIQUERY *view;
5432 static const WCHAR query[] =
5433 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5434 '`','P','u','b','l','i','s','h',
5435 'C','o','m','p','o','n','e','n','t','`',0};
5437 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5438 if (rc != ERROR_SUCCESS)
5439 return ERROR_SUCCESS;
5441 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5442 msiobj_release( &view->hdr );
5444 return rc;
5447 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5449 static const WCHAR query[] =
5450 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5451 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5452 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5453 MSIPACKAGE *package = param;
5454 MSICOMPONENT *component;
5455 MSIRECORD *row;
5456 MSIFILE *file;
5457 SC_HANDLE hscm = NULL, service = NULL;
5458 LPCWSTR comp, key;
5459 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5460 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5461 DWORD serv_type, start_type, err_control;
5462 SERVICE_DESCRIPTIONW sd = {NULL};
5464 comp = MSI_RecordGetString( rec, 12 );
5465 component = msi_get_loaded_component( package, comp );
5466 if (!component)
5468 WARN("service component not found\n");
5469 goto done;
5471 component->Action = msi_get_component_action( package, component );
5472 if (component->Action != INSTALLSTATE_LOCAL)
5474 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5475 goto done;
5477 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5478 if (!hscm)
5480 ERR("Failed to open the SC Manager!\n");
5481 goto done;
5484 start_type = MSI_RecordGetInteger(rec, 5);
5485 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5486 goto done;
5488 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5489 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5490 serv_type = MSI_RecordGetInteger(rec, 4);
5491 err_control = MSI_RecordGetInteger(rec, 6);
5492 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5493 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5494 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5495 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5496 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5497 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5499 /* fetch the service path */
5500 row = MSI_QueryGetRecord(package->db, query, comp);
5501 if (!row)
5503 ERR("Query failed\n");
5504 goto done;
5506 key = MSI_RecordGetString(row, 6);
5507 file = msi_get_loaded_file(package, key);
5508 msiobj_release(&row->hdr);
5509 if (!file)
5511 ERR("Failed to load the service file\n");
5512 goto done;
5515 if (!args || !args[0]) image_path = file->TargetPath;
5516 else
5518 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5519 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5520 return ERROR_OUTOFMEMORY;
5522 strcpyW(image_path, file->TargetPath);
5523 strcatW(image_path, szSpace);
5524 strcatW(image_path, args);
5526 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5527 start_type, err_control, image_path, load_order,
5528 NULL, depends, serv_name, pass);
5530 if (!service)
5532 if (GetLastError() != ERROR_SERVICE_EXISTS)
5533 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5535 else if (sd.lpDescription)
5537 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5538 WARN("failed to set service description %u\n", GetLastError());
5541 if (image_path != file->TargetPath) msi_free(image_path);
5542 done:
5543 CloseServiceHandle(service);
5544 CloseServiceHandle(hscm);
5545 msi_free(name);
5546 msi_free(disp);
5547 msi_free(sd.lpDescription);
5548 msi_free(load_order);
5549 msi_free(serv_name);
5550 msi_free(pass);
5551 msi_free(depends);
5552 msi_free(args);
5554 return ERROR_SUCCESS;
5557 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5559 UINT rc;
5560 MSIQUERY * view;
5561 static const WCHAR ExecSeqQuery[] =
5562 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5563 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5565 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5566 if (rc != ERROR_SUCCESS)
5567 return ERROR_SUCCESS;
5569 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5570 msiobj_release(&view->hdr);
5572 return rc;
5575 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5576 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5578 LPCWSTR *vector, *temp_vector;
5579 LPWSTR p, q;
5580 DWORD sep_len;
5582 static const WCHAR separator[] = {'[','~',']',0};
5584 *numargs = 0;
5585 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5587 if (!args)
5588 return NULL;
5590 vector = msi_alloc(sizeof(LPWSTR));
5591 if (!vector)
5592 return NULL;
5594 p = args;
5597 (*numargs)++;
5598 vector[*numargs - 1] = p;
5600 if ((q = strstrW(p, separator)))
5602 *q = '\0';
5604 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5605 if (!temp_vector)
5607 msi_free(vector);
5608 return NULL;
5610 vector = temp_vector;
5612 p = q + sep_len;
5614 } while (q);
5616 return vector;
5619 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5621 MSIPACKAGE *package = param;
5622 MSICOMPONENT *comp;
5623 MSIRECORD *uirow;
5624 SC_HANDLE scm = NULL, service = NULL;
5625 LPCWSTR component, *vector = NULL;
5626 LPWSTR name, args, display_name = NULL;
5627 DWORD event, numargs, len;
5628 UINT r = ERROR_FUNCTION_FAILED;
5630 component = MSI_RecordGetString(rec, 6);
5631 comp = msi_get_loaded_component(package, component);
5632 if (!comp)
5633 return ERROR_SUCCESS;
5635 comp->Action = msi_get_component_action( package, comp );
5636 if (comp->Action != INSTALLSTATE_LOCAL)
5638 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5639 return ERROR_SUCCESS;
5642 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5643 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5644 event = MSI_RecordGetInteger(rec, 3);
5646 if (!(event & msidbServiceControlEventStart))
5648 r = ERROR_SUCCESS;
5649 goto done;
5652 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5653 if (!scm)
5655 ERR("Failed to open the service control manager\n");
5656 goto done;
5659 len = 0;
5660 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5661 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5663 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5664 GetServiceDisplayNameW( scm, name, display_name, &len );
5667 service = OpenServiceW(scm, name, SERVICE_START);
5668 if (!service)
5670 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5671 goto done;
5674 vector = msi_service_args_to_vector(args, &numargs);
5676 if (!StartServiceW(service, numargs, vector) &&
5677 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5679 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5680 goto done;
5683 r = ERROR_SUCCESS;
5685 done:
5686 uirow = MSI_CreateRecord( 2 );
5687 MSI_RecordSetStringW( uirow, 1, display_name );
5688 MSI_RecordSetStringW( uirow, 2, name );
5689 msi_ui_actiondata( package, szStartServices, uirow );
5690 msiobj_release( &uirow->hdr );
5692 CloseServiceHandle(service);
5693 CloseServiceHandle(scm);
5695 msi_free(name);
5696 msi_free(args);
5697 msi_free(vector);
5698 msi_free(display_name);
5699 return r;
5702 static UINT ACTION_StartServices( MSIPACKAGE *package )
5704 UINT rc;
5705 MSIQUERY *view;
5707 static const WCHAR query[] = {
5708 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5709 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5711 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5712 if (rc != ERROR_SUCCESS)
5713 return ERROR_SUCCESS;
5715 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5716 msiobj_release(&view->hdr);
5718 return rc;
5721 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5723 DWORD i, needed, count;
5724 ENUM_SERVICE_STATUSW *dependencies;
5725 SERVICE_STATUS ss;
5726 SC_HANDLE depserv;
5728 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5729 0, &needed, &count))
5730 return TRUE;
5732 if (GetLastError() != ERROR_MORE_DATA)
5733 return FALSE;
5735 dependencies = msi_alloc(needed);
5736 if (!dependencies)
5737 return FALSE;
5739 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5740 needed, &needed, &count))
5741 goto error;
5743 for (i = 0; i < count; i++)
5745 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5746 SERVICE_STOP | SERVICE_QUERY_STATUS);
5747 if (!depserv)
5748 goto error;
5750 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5751 goto error;
5754 return TRUE;
5756 error:
5757 msi_free(dependencies);
5758 return FALSE;
5761 static UINT stop_service( LPCWSTR name )
5763 SC_HANDLE scm = NULL, service = NULL;
5764 SERVICE_STATUS status;
5765 SERVICE_STATUS_PROCESS ssp;
5766 DWORD needed;
5768 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5769 if (!scm)
5771 WARN("Failed to open the SCM: %d\n", GetLastError());
5772 goto done;
5775 service = OpenServiceW(scm, name,
5776 SERVICE_STOP |
5777 SERVICE_QUERY_STATUS |
5778 SERVICE_ENUMERATE_DEPENDENTS);
5779 if (!service)
5781 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5782 goto done;
5785 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5786 sizeof(SERVICE_STATUS_PROCESS), &needed))
5788 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5789 goto done;
5792 if (ssp.dwCurrentState == SERVICE_STOPPED)
5793 goto done;
5795 stop_service_dependents(scm, service);
5797 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5798 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5800 done:
5801 CloseServiceHandle(service);
5802 CloseServiceHandle(scm);
5804 return ERROR_SUCCESS;
5807 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5809 MSIPACKAGE *package = param;
5810 MSICOMPONENT *comp;
5811 MSIRECORD *uirow;
5812 LPCWSTR component;
5813 LPWSTR name = NULL, display_name = NULL;
5814 DWORD event, len;
5815 SC_HANDLE scm;
5817 event = MSI_RecordGetInteger( rec, 3 );
5818 if (!(event & msidbServiceControlEventStop))
5819 return ERROR_SUCCESS;
5821 component = MSI_RecordGetString( rec, 6 );
5822 comp = msi_get_loaded_component( package, component );
5823 if (!comp)
5824 return ERROR_SUCCESS;
5826 comp->Action = msi_get_component_action( package, comp );
5827 if (comp->Action != INSTALLSTATE_ABSENT)
5829 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5830 return ERROR_SUCCESS;
5833 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5834 if (!scm)
5836 ERR("Failed to open the service control manager\n");
5837 goto done;
5840 len = 0;
5841 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5842 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5844 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5845 GetServiceDisplayNameW( scm, name, display_name, &len );
5847 CloseServiceHandle( scm );
5849 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5850 stop_service( name );
5852 done:
5853 uirow = MSI_CreateRecord( 2 );
5854 MSI_RecordSetStringW( uirow, 1, display_name );
5855 MSI_RecordSetStringW( uirow, 2, name );
5856 msi_ui_actiondata( package, szStopServices, uirow );
5857 msiobj_release( &uirow->hdr );
5859 msi_free( name );
5860 msi_free( display_name );
5861 return ERROR_SUCCESS;
5864 static UINT ACTION_StopServices( MSIPACKAGE *package )
5866 UINT rc;
5867 MSIQUERY *view;
5869 static const WCHAR query[] = {
5870 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5871 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5873 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5874 if (rc != ERROR_SUCCESS)
5875 return ERROR_SUCCESS;
5877 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5878 msiobj_release(&view->hdr);
5880 return rc;
5883 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5885 MSIPACKAGE *package = param;
5886 MSICOMPONENT *comp;
5887 MSIRECORD *uirow;
5888 LPCWSTR component;
5889 LPWSTR name = NULL, display_name = NULL;
5890 DWORD event, len;
5891 SC_HANDLE scm = NULL, service = NULL;
5893 event = MSI_RecordGetInteger( rec, 3 );
5894 if (!(event & msidbServiceControlEventDelete))
5895 return ERROR_SUCCESS;
5897 component = MSI_RecordGetString(rec, 6);
5898 comp = msi_get_loaded_component(package, component);
5899 if (!comp)
5900 return ERROR_SUCCESS;
5902 comp->Action = msi_get_component_action( package, comp );
5903 if (comp->Action != INSTALLSTATE_ABSENT)
5905 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5906 return ERROR_SUCCESS;
5909 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5910 stop_service( name );
5912 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5913 if (!scm)
5915 WARN("Failed to open the SCM: %d\n", GetLastError());
5916 goto done;
5919 len = 0;
5920 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5921 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5923 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5924 GetServiceDisplayNameW( scm, name, display_name, &len );
5927 service = OpenServiceW( scm, name, DELETE );
5928 if (!service)
5930 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5931 goto done;
5934 if (!DeleteService( service ))
5935 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5937 done:
5938 uirow = MSI_CreateRecord( 2 );
5939 MSI_RecordSetStringW( uirow, 1, display_name );
5940 MSI_RecordSetStringW( uirow, 2, name );
5941 msi_ui_actiondata( package, szDeleteServices, uirow );
5942 msiobj_release( &uirow->hdr );
5944 CloseServiceHandle( service );
5945 CloseServiceHandle( scm );
5946 msi_free( name );
5947 msi_free( display_name );
5949 return ERROR_SUCCESS;
5952 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5954 UINT rc;
5955 MSIQUERY *view;
5957 static const WCHAR query[] = {
5958 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5959 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5961 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5962 if (rc != ERROR_SUCCESS)
5963 return ERROR_SUCCESS;
5965 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5966 msiobj_release( &view->hdr );
5968 return rc;
5971 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5973 MSIPACKAGE *package = param;
5974 LPWSTR driver, driver_path, ptr;
5975 WCHAR outpath[MAX_PATH];
5976 MSIFILE *driver_file = NULL, *setup_file = NULL;
5977 MSICOMPONENT *comp;
5978 MSIRECORD *uirow;
5979 LPCWSTR desc, file_key, component;
5980 DWORD len, usage;
5981 UINT r = ERROR_SUCCESS;
5983 static const WCHAR driver_fmt[] = {
5984 'D','r','i','v','e','r','=','%','s',0};
5985 static const WCHAR setup_fmt[] = {
5986 'S','e','t','u','p','=','%','s',0};
5987 static const WCHAR usage_fmt[] = {
5988 'F','i','l','e','U','s','a','g','e','=','1',0};
5990 component = MSI_RecordGetString( rec, 2 );
5991 comp = msi_get_loaded_component( package, component );
5992 if (!comp)
5993 return ERROR_SUCCESS;
5995 comp->Action = msi_get_component_action( package, comp );
5996 if (comp->Action != INSTALLSTATE_LOCAL)
5998 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5999 return ERROR_SUCCESS;
6001 desc = MSI_RecordGetString(rec, 3);
6003 file_key = MSI_RecordGetString( rec, 4 );
6004 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6006 file_key = MSI_RecordGetString( rec, 5 );
6007 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6009 if (!driver_file)
6011 ERR("ODBC Driver entry not found!\n");
6012 return ERROR_FUNCTION_FAILED;
6015 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6016 if (setup_file)
6017 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6018 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6020 driver = msi_alloc(len * sizeof(WCHAR));
6021 if (!driver)
6022 return ERROR_OUTOFMEMORY;
6024 ptr = driver;
6025 lstrcpyW(ptr, desc);
6026 ptr += lstrlenW(ptr) + 1;
6028 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6029 ptr += len + 1;
6031 if (setup_file)
6033 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6034 ptr += len + 1;
6037 lstrcpyW(ptr, usage_fmt);
6038 ptr += lstrlenW(ptr) + 1;
6039 *ptr = '\0';
6041 driver_path = strdupW(driver_file->TargetPath);
6042 ptr = strrchrW(driver_path, '\\');
6043 if (ptr) *ptr = '\0';
6045 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6046 NULL, ODBC_INSTALL_COMPLETE, &usage))
6048 ERR("Failed to install SQL driver!\n");
6049 r = ERROR_FUNCTION_FAILED;
6052 uirow = MSI_CreateRecord( 5 );
6053 MSI_RecordSetStringW( uirow, 1, desc );
6054 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6055 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6056 msi_ui_actiondata( package, szInstallODBC, uirow );
6057 msiobj_release( &uirow->hdr );
6059 msi_free(driver);
6060 msi_free(driver_path);
6062 return r;
6065 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6067 MSIPACKAGE *package = param;
6068 LPWSTR translator, translator_path, ptr;
6069 WCHAR outpath[MAX_PATH];
6070 MSIFILE *translator_file = NULL, *setup_file = NULL;
6071 MSICOMPONENT *comp;
6072 MSIRECORD *uirow;
6073 LPCWSTR desc, file_key, component;
6074 DWORD len, usage;
6075 UINT r = ERROR_SUCCESS;
6077 static const WCHAR translator_fmt[] = {
6078 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6079 static const WCHAR setup_fmt[] = {
6080 'S','e','t','u','p','=','%','s',0};
6082 component = MSI_RecordGetString( rec, 2 );
6083 comp = msi_get_loaded_component( package, component );
6084 if (!comp)
6085 return ERROR_SUCCESS;
6087 comp->Action = msi_get_component_action( package, comp );
6088 if (comp->Action != INSTALLSTATE_LOCAL)
6090 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6091 return ERROR_SUCCESS;
6093 desc = MSI_RecordGetString(rec, 3);
6095 file_key = MSI_RecordGetString( rec, 4 );
6096 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6098 file_key = MSI_RecordGetString( rec, 5 );
6099 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6101 if (!translator_file)
6103 ERR("ODBC Translator entry not found!\n");
6104 return ERROR_FUNCTION_FAILED;
6107 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6108 if (setup_file)
6109 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6111 translator = msi_alloc(len * sizeof(WCHAR));
6112 if (!translator)
6113 return ERROR_OUTOFMEMORY;
6115 ptr = translator;
6116 lstrcpyW(ptr, desc);
6117 ptr += lstrlenW(ptr) + 1;
6119 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6120 ptr += len + 1;
6122 if (setup_file)
6124 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6125 ptr += len + 1;
6127 *ptr = '\0';
6129 translator_path = strdupW(translator_file->TargetPath);
6130 ptr = strrchrW(translator_path, '\\');
6131 if (ptr) *ptr = '\0';
6133 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6134 NULL, ODBC_INSTALL_COMPLETE, &usage))
6136 ERR("Failed to install SQL translator!\n");
6137 r = ERROR_FUNCTION_FAILED;
6140 uirow = MSI_CreateRecord( 5 );
6141 MSI_RecordSetStringW( uirow, 1, desc );
6142 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6143 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6144 msi_ui_actiondata( package, szInstallODBC, uirow );
6145 msiobj_release( &uirow->hdr );
6147 msi_free(translator);
6148 msi_free(translator_path);
6150 return r;
6153 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6155 MSIPACKAGE *package = param;
6156 MSICOMPONENT *comp;
6157 LPWSTR attrs;
6158 LPCWSTR desc, driver, component;
6159 WORD request = ODBC_ADD_SYS_DSN;
6160 INT registration;
6161 DWORD len;
6162 UINT r = ERROR_SUCCESS;
6163 MSIRECORD *uirow;
6165 static const WCHAR attrs_fmt[] = {
6166 'D','S','N','=','%','s',0 };
6168 component = MSI_RecordGetString( rec, 2 );
6169 comp = msi_get_loaded_component( package, component );
6170 if (!comp)
6171 return ERROR_SUCCESS;
6173 comp->Action = msi_get_component_action( package, comp );
6174 if (comp->Action != INSTALLSTATE_LOCAL)
6176 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6177 return ERROR_SUCCESS;
6180 desc = MSI_RecordGetString(rec, 3);
6181 driver = MSI_RecordGetString(rec, 4);
6182 registration = MSI_RecordGetInteger(rec, 5);
6184 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6185 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6187 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6188 attrs = msi_alloc(len * sizeof(WCHAR));
6189 if (!attrs)
6190 return ERROR_OUTOFMEMORY;
6192 len = sprintfW(attrs, attrs_fmt, desc);
6193 attrs[len + 1] = 0;
6195 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6197 ERR("Failed to install SQL data source!\n");
6198 r = ERROR_FUNCTION_FAILED;
6201 uirow = MSI_CreateRecord( 5 );
6202 MSI_RecordSetStringW( uirow, 1, desc );
6203 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6204 MSI_RecordSetInteger( uirow, 3, request );
6205 msi_ui_actiondata( package, szInstallODBC, uirow );
6206 msiobj_release( &uirow->hdr );
6208 msi_free(attrs);
6210 return r;
6213 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6215 UINT rc;
6216 MSIQUERY *view;
6218 static const WCHAR driver_query[] = {
6219 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6220 'O','D','B','C','D','r','i','v','e','r',0 };
6222 static const WCHAR translator_query[] = {
6223 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6224 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6226 static const WCHAR source_query[] = {
6227 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6228 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6230 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6231 if (rc != ERROR_SUCCESS)
6232 return ERROR_SUCCESS;
6234 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6235 msiobj_release(&view->hdr);
6237 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6238 if (rc != ERROR_SUCCESS)
6239 return ERROR_SUCCESS;
6241 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6242 msiobj_release(&view->hdr);
6244 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6245 if (rc != ERROR_SUCCESS)
6246 return ERROR_SUCCESS;
6248 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6249 msiobj_release(&view->hdr);
6251 return rc;
6254 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6256 MSIPACKAGE *package = param;
6257 MSICOMPONENT *comp;
6258 MSIRECORD *uirow;
6259 DWORD usage;
6260 LPCWSTR desc, component;
6262 component = MSI_RecordGetString( rec, 2 );
6263 comp = msi_get_loaded_component( package, component );
6264 if (!comp)
6265 return ERROR_SUCCESS;
6267 comp->Action = msi_get_component_action( package, comp );
6268 if (comp->Action != INSTALLSTATE_ABSENT)
6270 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6271 return ERROR_SUCCESS;
6274 desc = MSI_RecordGetString( rec, 3 );
6275 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6277 WARN("Failed to remove ODBC driver\n");
6279 else if (!usage)
6281 FIXME("Usage count reached 0\n");
6284 uirow = MSI_CreateRecord( 2 );
6285 MSI_RecordSetStringW( uirow, 1, desc );
6286 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6287 msi_ui_actiondata( package, szRemoveODBC, uirow );
6288 msiobj_release( &uirow->hdr );
6290 return ERROR_SUCCESS;
6293 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6295 MSIPACKAGE *package = param;
6296 MSICOMPONENT *comp;
6297 MSIRECORD *uirow;
6298 DWORD usage;
6299 LPCWSTR desc, component;
6301 component = MSI_RecordGetString( rec, 2 );
6302 comp = msi_get_loaded_component( package, component );
6303 if (!comp)
6304 return ERROR_SUCCESS;
6306 comp->Action = msi_get_component_action( package, comp );
6307 if (comp->Action != INSTALLSTATE_ABSENT)
6309 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6310 return ERROR_SUCCESS;
6313 desc = MSI_RecordGetString( rec, 3 );
6314 if (!SQLRemoveTranslatorW( desc, &usage ))
6316 WARN("Failed to remove ODBC translator\n");
6318 else if (!usage)
6320 FIXME("Usage count reached 0\n");
6323 uirow = MSI_CreateRecord( 2 );
6324 MSI_RecordSetStringW( uirow, 1, desc );
6325 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6326 msi_ui_actiondata( package, szRemoveODBC, uirow );
6327 msiobj_release( &uirow->hdr );
6329 return ERROR_SUCCESS;
6332 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6334 MSIPACKAGE *package = param;
6335 MSICOMPONENT *comp;
6336 MSIRECORD *uirow;
6337 LPWSTR attrs;
6338 LPCWSTR desc, driver, component;
6339 WORD request = ODBC_REMOVE_SYS_DSN;
6340 INT registration;
6341 DWORD len;
6343 static const WCHAR attrs_fmt[] = {
6344 'D','S','N','=','%','s',0 };
6346 component = MSI_RecordGetString( rec, 2 );
6347 comp = msi_get_loaded_component( package, component );
6348 if (!comp)
6349 return ERROR_SUCCESS;
6351 comp->Action = msi_get_component_action( package, comp );
6352 if (comp->Action != INSTALLSTATE_ABSENT)
6354 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6355 return ERROR_SUCCESS;
6358 desc = MSI_RecordGetString( rec, 3 );
6359 driver = MSI_RecordGetString( rec, 4 );
6360 registration = MSI_RecordGetInteger( rec, 5 );
6362 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6363 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6365 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6366 attrs = msi_alloc( len * sizeof(WCHAR) );
6367 if (!attrs)
6368 return ERROR_OUTOFMEMORY;
6370 FIXME("Use ODBCSourceAttribute table\n");
6372 len = sprintfW( attrs, attrs_fmt, desc );
6373 attrs[len + 1] = 0;
6375 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6377 WARN("Failed to remove ODBC data source\n");
6379 msi_free( attrs );
6381 uirow = MSI_CreateRecord( 3 );
6382 MSI_RecordSetStringW( uirow, 1, desc );
6383 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6384 MSI_RecordSetInteger( uirow, 3, request );
6385 msi_ui_actiondata( package, szRemoveODBC, uirow );
6386 msiobj_release( &uirow->hdr );
6388 return ERROR_SUCCESS;
6391 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6393 UINT rc;
6394 MSIQUERY *view;
6396 static const WCHAR driver_query[] = {
6397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6398 'O','D','B','C','D','r','i','v','e','r',0 };
6400 static const WCHAR translator_query[] = {
6401 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6402 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6404 static const WCHAR source_query[] = {
6405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6406 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6408 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6409 if (rc != ERROR_SUCCESS)
6410 return ERROR_SUCCESS;
6412 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6413 msiobj_release( &view->hdr );
6415 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6416 if (rc != ERROR_SUCCESS)
6417 return ERROR_SUCCESS;
6419 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6420 msiobj_release( &view->hdr );
6422 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6423 if (rc != ERROR_SUCCESS)
6424 return ERROR_SUCCESS;
6426 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6427 msiobj_release( &view->hdr );
6429 return rc;
6432 #define ENV_ACT_SETALWAYS 0x1
6433 #define ENV_ACT_SETABSENT 0x2
6434 #define ENV_ACT_REMOVE 0x4
6435 #define ENV_ACT_REMOVEMATCH 0x8
6437 #define ENV_MOD_MACHINE 0x20000000
6438 #define ENV_MOD_APPEND 0x40000000
6439 #define ENV_MOD_PREFIX 0x80000000
6440 #define ENV_MOD_MASK 0xC0000000
6442 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6444 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6446 LPCWSTR cptr = *name;
6448 static const WCHAR prefix[] = {'[','~',']',0};
6449 static const int prefix_len = 3;
6451 *flags = 0;
6452 while (*cptr)
6454 if (*cptr == '=')
6455 *flags |= ENV_ACT_SETALWAYS;
6456 else if (*cptr == '+')
6457 *flags |= ENV_ACT_SETABSENT;
6458 else if (*cptr == '-')
6459 *flags |= ENV_ACT_REMOVE;
6460 else if (*cptr == '!')
6461 *flags |= ENV_ACT_REMOVEMATCH;
6462 else if (*cptr == '*')
6463 *flags |= ENV_MOD_MACHINE;
6464 else
6465 break;
6467 cptr++;
6468 (*name)++;
6471 if (!*cptr)
6473 ERR("Missing environment variable\n");
6474 return ERROR_FUNCTION_FAILED;
6477 if (*value)
6479 LPCWSTR ptr = *value;
6480 if (!strncmpW(ptr, prefix, prefix_len))
6482 if (ptr[prefix_len] == szSemiColon[0])
6484 *flags |= ENV_MOD_APPEND;
6485 *value += lstrlenW(prefix);
6487 else
6489 *value = NULL;
6492 else if (lstrlenW(*value) >= prefix_len)
6494 ptr += lstrlenW(ptr) - prefix_len;
6495 if (!strcmpW( ptr, prefix ))
6497 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6499 *flags |= ENV_MOD_PREFIX;
6500 /* the "[~]" will be removed by deformat_string */;
6502 else
6504 *value = NULL;
6510 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6511 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6512 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6513 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6515 ERR("Invalid flags: %08x\n", *flags);
6516 return ERROR_FUNCTION_FAILED;
6519 if (!*flags)
6520 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6522 return ERROR_SUCCESS;
6525 static UINT open_env_key( DWORD flags, HKEY *key )
6527 static const WCHAR user_env[] =
6528 {'E','n','v','i','r','o','n','m','e','n','t',0};
6529 static const WCHAR machine_env[] =
6530 {'S','y','s','t','e','m','\\',
6531 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6532 'C','o','n','t','r','o','l','\\',
6533 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6534 'E','n','v','i','r','o','n','m','e','n','t',0};
6535 const WCHAR *env;
6536 HKEY root;
6537 LONG res;
6539 if (flags & ENV_MOD_MACHINE)
6541 env = machine_env;
6542 root = HKEY_LOCAL_MACHINE;
6544 else
6546 env = user_env;
6547 root = HKEY_CURRENT_USER;
6550 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6551 if (res != ERROR_SUCCESS)
6553 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6554 return ERROR_FUNCTION_FAILED;
6557 return ERROR_SUCCESS;
6560 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6562 MSIPACKAGE *package = param;
6563 LPCWSTR name, value, component;
6564 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6565 DWORD flags, type, size;
6566 UINT res;
6567 HKEY env = NULL;
6568 MSICOMPONENT *comp;
6569 MSIRECORD *uirow;
6570 int action = 0;
6572 component = MSI_RecordGetString(rec, 4);
6573 comp = msi_get_loaded_component(package, component);
6574 if (!comp)
6575 return ERROR_SUCCESS;
6577 comp->Action = msi_get_component_action( package, comp );
6578 if (comp->Action != INSTALLSTATE_LOCAL)
6580 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6581 return ERROR_SUCCESS;
6583 name = MSI_RecordGetString(rec, 2);
6584 value = MSI_RecordGetString(rec, 3);
6586 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6588 res = env_parse_flags(&name, &value, &flags);
6589 if (res != ERROR_SUCCESS || !value)
6590 goto done;
6592 if (value && !deformat_string(package, value, &deformatted))
6594 res = ERROR_OUTOFMEMORY;
6595 goto done;
6598 value = deformatted;
6600 res = open_env_key( flags, &env );
6601 if (res != ERROR_SUCCESS)
6602 goto done;
6604 if (flags & ENV_MOD_MACHINE)
6605 action |= 0x20000000;
6607 size = 0;
6608 type = REG_SZ;
6609 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6610 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6611 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6612 goto done;
6614 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6616 action = 0x2;
6618 /* Nothing to do. */
6619 if (!value)
6621 res = ERROR_SUCCESS;
6622 goto done;
6625 /* If we are appending but the string was empty, strip ; */
6626 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6628 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6629 newval = strdupW(value);
6630 if (!newval)
6632 res = ERROR_OUTOFMEMORY;
6633 goto done;
6636 else
6638 action = 0x1;
6640 /* Contrary to MSDN, +-variable to [~];path works */
6641 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6643 res = ERROR_SUCCESS;
6644 goto done;
6647 data = msi_alloc(size);
6648 if (!data)
6650 RegCloseKey(env);
6651 return ERROR_OUTOFMEMORY;
6654 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6655 if (res != ERROR_SUCCESS)
6656 goto done;
6658 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6660 action = 0x4;
6661 res = RegDeleteValueW(env, name);
6662 if (res != ERROR_SUCCESS)
6663 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6664 goto done;
6667 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6668 if (flags & ENV_MOD_MASK)
6670 DWORD mod_size;
6671 int multiplier = 0;
6672 if (flags & ENV_MOD_APPEND) multiplier++;
6673 if (flags & ENV_MOD_PREFIX) multiplier++;
6674 mod_size = lstrlenW(value) * multiplier;
6675 size += mod_size * sizeof(WCHAR);
6678 newval = msi_alloc(size);
6679 ptr = newval;
6680 if (!newval)
6682 res = ERROR_OUTOFMEMORY;
6683 goto done;
6686 if (flags & ENV_MOD_PREFIX)
6688 lstrcpyW(newval, value);
6689 ptr = newval + lstrlenW(value);
6690 action |= 0x80000000;
6693 lstrcpyW(ptr, data);
6695 if (flags & ENV_MOD_APPEND)
6697 lstrcatW(newval, value);
6698 action |= 0x40000000;
6701 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6702 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6703 if (res)
6705 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6708 done:
6709 uirow = MSI_CreateRecord( 3 );
6710 MSI_RecordSetStringW( uirow, 1, name );
6711 MSI_RecordSetStringW( uirow, 2, newval );
6712 MSI_RecordSetInteger( uirow, 3, action );
6713 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6714 msiobj_release( &uirow->hdr );
6716 if (env) RegCloseKey(env);
6717 msi_free(deformatted);
6718 msi_free(data);
6719 msi_free(newval);
6720 return res;
6723 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6725 UINT rc;
6726 MSIQUERY * view;
6727 static const WCHAR ExecSeqQuery[] =
6728 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6729 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6730 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6731 if (rc != ERROR_SUCCESS)
6732 return ERROR_SUCCESS;
6734 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6735 msiobj_release(&view->hdr);
6737 return rc;
6740 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6742 MSIPACKAGE *package = param;
6743 LPCWSTR name, value, component;
6744 LPWSTR deformatted = NULL;
6745 DWORD flags;
6746 HKEY env;
6747 MSICOMPONENT *comp;
6748 MSIRECORD *uirow;
6749 int action = 0;
6750 LONG res;
6751 UINT r;
6753 component = MSI_RecordGetString( rec, 4 );
6754 comp = msi_get_loaded_component( package, component );
6755 if (!comp)
6756 return ERROR_SUCCESS;
6758 comp->Action = msi_get_component_action( package, comp );
6759 if (comp->Action != INSTALLSTATE_ABSENT)
6761 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6762 return ERROR_SUCCESS;
6764 name = MSI_RecordGetString( rec, 2 );
6765 value = MSI_RecordGetString( rec, 3 );
6767 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6769 r = env_parse_flags( &name, &value, &flags );
6770 if (r != ERROR_SUCCESS)
6771 return r;
6773 if (!(flags & ENV_ACT_REMOVE))
6775 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6776 return ERROR_SUCCESS;
6779 if (value && !deformat_string( package, value, &deformatted ))
6780 return ERROR_OUTOFMEMORY;
6782 value = deformatted;
6784 r = open_env_key( flags, &env );
6785 if (r != ERROR_SUCCESS)
6787 r = ERROR_SUCCESS;
6788 goto done;
6791 if (flags & ENV_MOD_MACHINE)
6792 action |= 0x20000000;
6794 TRACE("Removing %s\n", debugstr_w(name));
6796 res = RegDeleteValueW( env, name );
6797 if (res != ERROR_SUCCESS)
6799 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6800 r = ERROR_SUCCESS;
6803 done:
6804 uirow = MSI_CreateRecord( 3 );
6805 MSI_RecordSetStringW( uirow, 1, name );
6806 MSI_RecordSetStringW( uirow, 2, value );
6807 MSI_RecordSetInteger( uirow, 3, action );
6808 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6809 msiobj_release( &uirow->hdr );
6811 if (env) RegCloseKey( env );
6812 msi_free( deformatted );
6813 return r;
6816 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6818 UINT rc;
6819 MSIQUERY *view;
6820 static const WCHAR query[] =
6821 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6822 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6824 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6825 if (rc != ERROR_SUCCESS)
6826 return ERROR_SUCCESS;
6828 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6829 msiobj_release( &view->hdr );
6831 return rc;
6834 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6836 LPWSTR key, template, id;
6837 UINT r = ERROR_SUCCESS;
6839 id = msi_dup_property( package->db, szProductID );
6840 if (id)
6842 msi_free( id );
6843 return ERROR_SUCCESS;
6845 template = msi_dup_property( package->db, szPIDTemplate );
6846 key = msi_dup_property( package->db, szPIDKEY );
6848 if (key && template)
6850 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6851 r = msi_set_property( package->db, szProductID, key );
6853 msi_free( template );
6854 msi_free( key );
6855 return r;
6858 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6860 TRACE("\n");
6861 package->need_reboot = 1;
6862 return ERROR_SUCCESS;
6865 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6867 static const WCHAR szAvailableFreeReg[] =
6868 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6869 MSIRECORD *uirow;
6870 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6872 TRACE("%p %d kilobytes\n", package, space);
6874 uirow = MSI_CreateRecord( 1 );
6875 MSI_RecordSetInteger( uirow, 1, space );
6876 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6877 msiobj_release( &uirow->hdr );
6879 return ERROR_SUCCESS;
6882 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6884 TRACE("%p\n", package);
6886 msi_set_property( package->db, szRollbackDisabled, szOne );
6887 return ERROR_SUCCESS;
6890 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6892 FIXME("%p\n", package);
6893 return ERROR_SUCCESS;
6896 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6898 UINT r, count;
6899 MSIQUERY *view;
6901 static const WCHAR driver_query[] = {
6902 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6903 'O','D','B','C','D','r','i','v','e','r',0 };
6905 static const WCHAR translator_query[] = {
6906 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6907 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6909 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6910 if (r == ERROR_SUCCESS)
6912 count = 0;
6913 r = MSI_IterateRecords( view, &count, NULL, package );
6914 msiobj_release( &view->hdr );
6915 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6918 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6919 if (r == ERROR_SUCCESS)
6921 count = 0;
6922 r = MSI_IterateRecords( view, &count, NULL, package );
6923 msiobj_release( &view->hdr );
6924 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6927 return ERROR_SUCCESS;
6930 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6932 MSIPACKAGE *package = param;
6933 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6934 WCHAR *value;
6936 if ((value = msi_dup_property( package->db, property )))
6938 FIXME("remove %s\n", debugstr_w(value));
6939 msi_free( value );
6941 return ERROR_SUCCESS;
6944 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6946 UINT r;
6947 MSIQUERY *view;
6949 static const WCHAR query[] =
6950 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
6951 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
6953 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6954 if (r == ERROR_SUCCESS)
6956 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6957 msiobj_release( &view->hdr );
6959 return ERROR_SUCCESS;
6962 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6964 MSIPACKAGE *package = param;
6965 int attributes = MSI_RecordGetInteger( rec, 5 );
6967 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6969 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6970 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6971 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6972 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6973 HKEY hkey;
6974 UINT r;
6976 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6978 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6979 if (r != ERROR_SUCCESS)
6980 return ERROR_SUCCESS;
6982 else
6984 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6985 if (r != ERROR_SUCCESS)
6986 return ERROR_SUCCESS;
6988 RegCloseKey( hkey );
6990 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
6991 debugstr_w(upgrade_code), debugstr_w(version_min),
6992 debugstr_w(version_max), debugstr_w(language));
6994 return ERROR_SUCCESS;
6997 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6999 UINT r;
7000 MSIQUERY *view;
7001 static const WCHAR query[] =
7002 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7004 if (msi_get_property_int( package->db, szInstalled, 0 ))
7006 TRACE("product is installed, skipping action\n");
7007 return ERROR_SUCCESS;
7009 if (msi_get_property_int( package->db, szPreselected, 0 ))
7011 TRACE("Preselected property is set, not migrating feature states\n");
7012 return ERROR_SUCCESS;
7015 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7016 if (r == ERROR_SUCCESS)
7018 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7019 msiobj_release( &view->hdr );
7021 return ERROR_SUCCESS;
7024 static void bind_image( const char *filename, const char *path )
7026 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7028 WARN("failed to bind image %u\n", GetLastError());
7032 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7034 UINT i;
7035 MSIFILE *file;
7036 MSIPACKAGE *package = param;
7037 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7038 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7039 char *filenameA, *pathA;
7040 WCHAR *pathW, **path_list;
7042 if (!(file = msi_get_loaded_file( package, key )))
7044 WARN("file %s not found\n", debugstr_w(key));
7045 return ERROR_SUCCESS;
7047 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7048 path_list = msi_split_string( paths, ';' );
7049 if (!path_list) bind_image( filenameA, NULL );
7050 else
7052 for (i = 0; path_list[i] && path_list[i][0]; i++)
7054 deformat_string( package, path_list[i], &pathW );
7055 if ((pathA = strdupWtoA( pathW )))
7057 bind_image( filenameA, pathA );
7058 msi_free( pathA );
7060 msi_free( pathW );
7063 msi_free( path_list );
7064 msi_free( filenameA );
7065 return ERROR_SUCCESS;
7068 static UINT ACTION_BindImage( MSIPACKAGE *package )
7070 UINT r;
7071 MSIQUERY *view;
7072 static const WCHAR query[] =
7073 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','B','i','n','d','I','m','a','g','e',0};
7075 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7076 if (r == ERROR_SUCCESS)
7078 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7079 msiobj_release( &view->hdr );
7081 return ERROR_SUCCESS;
7084 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7085 LPCSTR action, LPCWSTR table )
7087 static const WCHAR query[] = {
7088 'S','E','L','E','C','T',' ','*',' ',
7089 'F','R','O','M',' ','`','%','s','`',0 };
7090 MSIQUERY *view = NULL;
7091 DWORD count = 0;
7092 UINT r;
7094 r = MSI_OpenQuery( package->db, &view, query, table );
7095 if (r == ERROR_SUCCESS)
7097 r = MSI_IterateRecords(view, &count, NULL, package);
7098 msiobj_release(&view->hdr);
7101 if (count)
7102 FIXME("%s -> %u ignored %s table values\n",
7103 action, count, debugstr_w(table));
7105 return ERROR_SUCCESS;
7108 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7110 static const WCHAR table[] = {
7111 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7112 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7115 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7117 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7118 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7121 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7123 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7124 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7127 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7129 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7130 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7133 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7135 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7136 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7139 static const struct
7141 const WCHAR *action;
7142 UINT (*handler)(MSIPACKAGE *);
7143 const WCHAR *action_rollback;
7145 StandardActions[] =
7147 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7148 { szAppSearch, ACTION_AppSearch, NULL },
7149 { szBindImage, ACTION_BindImage, NULL },
7150 { szCCPSearch, ACTION_CCPSearch, NULL },
7151 { szCostFinalize, ACTION_CostFinalize, NULL },
7152 { szCostInitialize, ACTION_CostInitialize, NULL },
7153 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7154 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7155 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7156 { szDisableRollback, ACTION_DisableRollback, NULL },
7157 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7158 { szExecuteAction, ACTION_ExecuteAction, NULL },
7159 { szFileCost, ACTION_FileCost, NULL },
7160 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7161 { szForceReboot, ACTION_ForceReboot, NULL },
7162 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7163 { szInstallExecute, ACTION_InstallExecute, NULL },
7164 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7165 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7166 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7167 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7168 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7169 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7170 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7171 { szInstallValidate, ACTION_InstallValidate, NULL },
7172 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7173 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7174 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7175 { szMoveFiles, ACTION_MoveFiles, NULL },
7176 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7177 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7178 { szPatchFiles, ACTION_PatchFiles, NULL },
7179 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7180 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7181 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7182 { szPublishProduct, ACTION_PublishProduct, NULL },
7183 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7184 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7185 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7186 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7187 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7188 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7189 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7190 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7191 { szRegisterUser, ACTION_RegisterUser, NULL },
7192 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7193 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7194 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7195 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7196 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7197 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7198 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7199 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7200 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7201 { szResolveSource, ACTION_ResolveSource, NULL },
7202 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7203 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7204 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7205 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfUnregModules },
7206 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7207 { szStartServices, ACTION_StartServices, szStopServices },
7208 { szStopServices, ACTION_StopServices, szStartServices },
7209 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7210 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7211 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7212 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7213 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7214 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7215 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7216 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7217 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7218 { szValidateProductID, ACTION_ValidateProductID, NULL },
7219 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7220 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7221 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7222 { NULL, NULL, NULL }
7225 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7227 BOOL ret = FALSE;
7228 UINT i;
7230 i = 0;
7231 while (StandardActions[i].action != NULL)
7233 if (!strcmpW( StandardActions[i].action, action ))
7235 ui_actionstart( package, action );
7236 if (StandardActions[i].handler)
7238 ui_actioninfo( package, action, TRUE, 0 );
7239 *rc = StandardActions[i].handler( package );
7240 ui_actioninfo( package, action, FALSE, *rc );
7242 if (StandardActions[i].action_rollback && !package->need_rollback)
7244 TRACE("scheduling rollback action\n");
7245 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7248 else
7250 FIXME("unhandled standard action %s\n", debugstr_w(action));
7251 *rc = ERROR_SUCCESS;
7253 ret = TRUE;
7254 break;
7256 i++;
7258 return ret;
7261 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7263 UINT rc = ERROR_SUCCESS;
7264 BOOL handled;
7266 TRACE("Performing action (%s)\n", debugstr_w(action));
7268 handled = ACTION_HandleStandardAction(package, action, &rc);
7270 if (!handled)
7271 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7273 if (!handled)
7275 WARN("unhandled msi action %s\n", debugstr_w(action));
7276 rc = ERROR_FUNCTION_NOT_CALLED;
7279 return rc;
7282 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7284 UINT rc = ERROR_SUCCESS;
7285 BOOL handled = FALSE;
7287 TRACE("Performing action (%s)\n", debugstr_w(action));
7289 handled = ACTION_HandleStandardAction(package, action, &rc);
7291 if (!handled)
7292 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7294 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7295 handled = TRUE;
7297 if (!handled)
7299 WARN("unhandled msi action %s\n", debugstr_w(action));
7300 rc = ERROR_FUNCTION_NOT_CALLED;
7303 return rc;
7306 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7308 UINT rc = ERROR_SUCCESS;
7309 MSIRECORD *row;
7311 static const WCHAR ExecSeqQuery[] =
7312 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7313 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7314 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7315 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7316 static const WCHAR UISeqQuery[] =
7317 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7318 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7319 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7320 ' ', '=',' ','%','i',0};
7322 if (needs_ui_sequence(package))
7323 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7324 else
7325 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7327 if (row)
7329 LPCWSTR action, cond;
7331 TRACE("Running the actions\n");
7333 /* check conditions */
7334 cond = MSI_RecordGetString(row, 2);
7336 /* this is a hack to skip errors in the condition code */
7337 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7339 msiobj_release(&row->hdr);
7340 return ERROR_SUCCESS;
7343 action = MSI_RecordGetString(row, 1);
7344 if (!action)
7346 ERR("failed to fetch action\n");
7347 msiobj_release(&row->hdr);
7348 return ERROR_FUNCTION_FAILED;
7351 if (needs_ui_sequence(package))
7352 rc = ACTION_PerformUIAction(package, action, -1);
7353 else
7354 rc = ACTION_PerformAction(package, action, -1);
7356 msiobj_release(&row->hdr);
7359 return rc;
7362 /****************************************************
7363 * TOP level entry points
7364 *****************************************************/
7366 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7367 LPCWSTR szCommandLine )
7369 UINT rc;
7370 BOOL ui_exists;
7371 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7372 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7373 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7375 msi_set_property( package->db, szAction, szInstall );
7377 package->script->InWhatSequence = SEQUENCE_INSTALL;
7379 if (szPackagePath)
7381 LPWSTR p, dir;
7382 LPCWSTR file;
7384 dir = strdupW(szPackagePath);
7385 p = strrchrW(dir, '\\');
7386 if (p)
7388 *(++p) = 0;
7389 file = szPackagePath + (p - dir);
7391 else
7393 msi_free(dir);
7394 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7395 GetCurrentDirectoryW(MAX_PATH, dir);
7396 lstrcatW(dir, szBackSlash);
7397 file = szPackagePath;
7400 msi_free( package->PackagePath );
7401 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7402 if (!package->PackagePath)
7404 msi_free(dir);
7405 return ERROR_OUTOFMEMORY;
7408 lstrcpyW(package->PackagePath, dir);
7409 lstrcatW(package->PackagePath, file);
7410 msi_free(dir);
7412 msi_set_sourcedir_props(package, FALSE);
7415 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7416 if (rc != ERROR_SUCCESS)
7417 return rc;
7419 msi_apply_transforms( package );
7420 msi_apply_patches( package );
7422 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7424 TRACE("setting reinstall property\n");
7425 msi_set_property( package->db, szReinstall, szAll );
7428 /* properties may have been added by a transform */
7429 msi_clone_properties( package );
7431 msi_parse_command_line( package, szCommandLine, FALSE );
7432 msi_adjust_privilege_properties( package );
7433 msi_set_context( package );
7435 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7437 TRACE("disabling rollback\n");
7438 msi_set_property( package->db, szRollbackDisabled, szOne );
7441 if (needs_ui_sequence( package))
7443 package->script->InWhatSequence |= SEQUENCE_UI;
7444 rc = ACTION_ProcessUISequence(package);
7445 ui_exists = ui_sequence_exists(package);
7446 if (rc == ERROR_SUCCESS || !ui_exists)
7448 package->script->InWhatSequence |= SEQUENCE_EXEC;
7449 rc = ACTION_ProcessExecSequence(package, ui_exists);
7452 else
7453 rc = ACTION_ProcessExecSequence(package, FALSE);
7455 package->script->CurrentlyScripting = FALSE;
7457 /* process the ending type action */
7458 if (rc == ERROR_SUCCESS)
7459 ACTION_PerformActionSequence(package, -1);
7460 else if (rc == ERROR_INSTALL_USEREXIT)
7461 ACTION_PerformActionSequence(package, -2);
7462 else if (rc == ERROR_INSTALL_SUSPEND)
7463 ACTION_PerformActionSequence(package, -4);
7464 else /* failed */
7466 ACTION_PerformActionSequence(package, -3);
7467 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7469 package->need_rollback = TRUE;
7473 /* finish up running custom actions */
7474 ACTION_FinishCustomActions(package);
7476 if (package->need_rollback)
7478 WARN("installation failed, running rollback script\n");
7479 execute_script( package, ROLLBACK_SCRIPT );
7482 if (rc == ERROR_SUCCESS && package->need_reboot)
7483 return ERROR_SUCCESS_REBOOT_REQUIRED;
7485 return rc;