msi: Fix the service actions to revert components to the installed state during rollback.
[wine/multimedia.git] / dlls / msi / action.c
blob9a55afcce695a83b4d86e1cef5c9eb2cddf64510
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 if (!count) goto done;
226 in_quotes = 1;
227 ignore = 1;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes) count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 if (!count) in_quotes = 0;
238 else in_quotes = 1;
239 len++;
240 break;
242 break;
244 case state_token:
245 switch (*p)
247 case '"':
248 state = state_quote;
249 if (in_quotes) count--;
250 else count++;
251 break;
252 case ' ':
253 state = state_whitespace;
254 if (!count) goto done;
255 in_quotes = 1;
256 len++;
257 break;
258 default:
259 if (!count) in_quotes = 0;
260 else in_quotes = 1;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes) count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = 1;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (!count) in_quotes = 0;
282 else in_quotes = 1;
283 len++;
284 break;
286 break;
288 default: break;
290 if (!ignore) *out++ = *p;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 MSIQUERY *view;
410 UINT rc;
412 static const WCHAR ExecSeqQuery [] =
413 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
414 '`','I','n','s','t','a','l','l',
415 'U','I','S','e','q','u','e','n','c','e','`',
416 ' ','W','H','E','R','E',' ',
417 '`','S','e','q','u','e','n','c','e','`',' ',
418 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
419 '`','S','e','q','u','e','n','c','e','`',0};
421 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
422 if (rc == ERROR_SUCCESS)
424 msiobj_release(&view->hdr);
425 return TRUE;
428 return FALSE;
431 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
433 LPWSTR source, check;
435 if (msi_get_property_int( package->db, szInstalled, 0 ))
437 HKEY hkey;
439 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
440 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
441 RegCloseKey( hkey );
443 else
445 LPWSTR p, db;
446 DWORD len;
448 db = msi_dup_property( package->db, szOriginalDatabase );
449 if (!db)
450 return ERROR_OUTOFMEMORY;
452 p = strrchrW( db, '\\' );
453 if (!p)
455 p = strrchrW( db, '/' );
456 if (!p)
458 msi_free(db);
459 return ERROR_SUCCESS;
463 len = p - db + 2;
464 source = msi_alloc( len * sizeof(WCHAR) );
465 lstrcpynW( source, db, len );
466 msi_free( db );
469 check = msi_dup_property( package->db, szSourceDir );
470 if (!check || replace)
472 UINT r = msi_set_property( package->db, szSourceDir, source );
473 if (r == ERROR_SUCCESS)
474 msi_reset_folders( package, TRUE );
476 msi_free( check );
478 check = msi_dup_property( package->db, szSOURCEDIR );
479 if (!check || replace)
480 msi_set_property( package->db, szSOURCEDIR, source );
482 msi_free( check );
483 msi_free( source );
485 return ERROR_SUCCESS;
488 static BOOL needs_ui_sequence(MSIPACKAGE *package)
490 INT level = msi_get_property_int(package->db, szUILevel, 0);
491 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
494 UINT msi_set_context(MSIPACKAGE *package)
496 int num;
498 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
500 num = msi_get_property_int(package->db, szAllUsers, 0);
501 if (num == 1 || num == 2)
502 package->Context = MSIINSTALLCONTEXT_MACHINE;
504 return ERROR_SUCCESS;
507 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
509 UINT rc;
510 LPCWSTR cond, action;
511 MSIPACKAGE *package = param;
513 action = MSI_RecordGetString(row,1);
514 if (!action)
516 ERR("Error is retrieving action name\n");
517 return ERROR_FUNCTION_FAILED;
520 /* check conditions */
521 cond = MSI_RecordGetString(row,2);
523 /* this is a hack to skip errors in the condition code */
524 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
526 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
527 return ERROR_SUCCESS;
530 if (needs_ui_sequence(package))
531 rc = ACTION_PerformUIAction(package, action, -1);
532 else
533 rc = ACTION_PerformAction(package, action, -1);
535 msi_dialog_check_messages( NULL );
537 if (package->CurrentInstallState != ERROR_SUCCESS)
538 rc = package->CurrentInstallState;
540 if (rc == ERROR_FUNCTION_NOT_CALLED)
541 rc = ERROR_SUCCESS;
543 if (rc != ERROR_SUCCESS)
544 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
546 return rc;
549 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
551 MSIQUERY * view;
552 UINT r;
553 static const WCHAR query[] =
554 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
555 '`','%','s','`',
556 ' ','W','H','E','R','E',' ',
557 '`','S','e','q','u','e','n','c','e','`',' ',
558 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
559 '`','S','e','q','u','e','n','c','e','`',0};
561 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
563 r = MSI_OpenQuery( package->db, &view, query, szTable );
564 if (r == ERROR_SUCCESS)
566 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
567 msiobj_release(&view->hdr);
570 return r;
573 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
575 MSIQUERY * view;
576 UINT rc;
577 static const WCHAR ExecSeqQuery[] =
578 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
579 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
580 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
581 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
582 'O','R','D','E','R',' ', 'B','Y',' ',
583 '`','S','e','q','u','e','n','c','e','`',0 };
584 static const WCHAR IVQuery[] =
585 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
586 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
587 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
588 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
589 ' ','\'', 'I','n','s','t','a','l','l',
590 'V','a','l','i','d','a','t','e','\'', 0};
591 INT seq = 0;
593 if (package->script->ExecuteSequenceRun)
595 TRACE("Execute Sequence already Run\n");
596 return ERROR_SUCCESS;
599 package->script->ExecuteSequenceRun = TRUE;
601 /* get the sequence number */
602 if (UIran)
604 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
605 if( !row )
606 return ERROR_FUNCTION_FAILED;
607 seq = MSI_RecordGetInteger(row,1);
608 msiobj_release(&row->hdr);
611 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
612 if (rc == ERROR_SUCCESS)
614 TRACE("Running the actions\n");
616 msi_set_property(package->db, szSourceDir, NULL);
618 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
619 msiobj_release(&view->hdr);
622 return rc;
625 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
627 MSIQUERY * view;
628 UINT rc;
629 static const WCHAR ExecSeqQuery [] =
630 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
631 '`','I','n','s','t','a','l','l',
632 'U','I','S','e','q','u','e','n','c','e','`',
633 ' ','W','H','E','R','E',' ',
634 '`','S','e','q','u','e','n','c','e','`',' ',
635 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
636 '`','S','e','q','u','e','n','c','e','`',0};
638 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
639 if (rc == ERROR_SUCCESS)
641 TRACE("Running the actions\n");
643 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
644 msiobj_release(&view->hdr);
647 return rc;
650 /********************************************************
651 * ACTION helper functions and functions that perform the actions
652 *******************************************************/
653 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
654 UINT* rc, UINT script, BOOL force )
656 BOOL ret=FALSE;
657 UINT arc;
659 arc = ACTION_CustomAction(package, action, script, force);
661 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
663 *rc = arc;
664 ret = TRUE;
666 return ret;
669 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
671 MSICOMPONENT *comp;
673 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
675 if (!strcmpW( Component, comp->Component )) return comp;
677 return NULL;
680 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
682 MSIFEATURE *feature;
684 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
686 if (!strcmpW( Feature, feature->Feature )) return feature;
688 return NULL;
691 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
693 MSIFILE *file;
695 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
697 if (!strcmpW( key, file->File )) return file;
699 return NULL;
702 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
704 MSIFILEPATCH *patch;
706 /* FIXME: There might be more than one patch */
707 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
709 if (!strcmpW( key, patch->File->File )) return patch;
711 return NULL;
714 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
716 MSIFOLDER *folder;
718 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
720 if (!strcmpW( dir, folder->Directory )) return folder;
722 return NULL;
726 * Recursively create all directories in the path.
727 * shamelessly stolen from setupapi/queue.c
729 BOOL msi_create_full_path( const WCHAR *path )
731 BOOL ret = TRUE;
732 WCHAR *new_path;
733 int len;
735 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
736 strcpyW( new_path, path );
738 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
739 new_path[len - 1] = 0;
741 while (!CreateDirectoryW( new_path, NULL ))
743 WCHAR *slash;
744 DWORD last_error = GetLastError();
745 if (last_error == ERROR_ALREADY_EXISTS) break;
746 if (last_error != ERROR_PATH_NOT_FOUND)
748 ret = FALSE;
749 break;
751 if (!(slash = strrchrW( new_path, '\\' )))
753 ret = FALSE;
754 break;
756 len = slash - new_path;
757 new_path[len] = 0;
758 if (!msi_create_full_path( new_path ))
760 ret = FALSE;
761 break;
763 new_path[len] = '\\';
765 msi_free( new_path );
766 return ret;
769 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
771 MSIRECORD *row;
773 row = MSI_CreateRecord( 4 );
774 MSI_RecordSetInteger( row, 1, a );
775 MSI_RecordSetInteger( row, 2, b );
776 MSI_RecordSetInteger( row, 3, c );
777 MSI_RecordSetInteger( row, 4, d );
778 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
779 msiobj_release( &row->hdr );
781 msi_dialog_check_messages( NULL );
784 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
786 static const WCHAR query[] =
787 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
788 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
789 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
790 WCHAR message[1024];
791 MSIRECORD *row = 0;
792 DWORD size;
794 if (!package->LastAction || strcmpW( package->LastAction, action ))
796 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
798 if (MSI_RecordIsNull( row, 3 ))
800 msiobj_release( &row->hdr );
801 return;
803 /* update the cached action format */
804 msi_free( package->ActionFormat );
805 package->ActionFormat = msi_dup_record_field( row, 3 );
806 msi_free( package->LastAction );
807 package->LastAction = strdupW( action );
808 msiobj_release( &row->hdr );
810 size = 1024;
811 MSI_RecordSetStringW( record, 0, package->ActionFormat );
812 MSI_FormatRecordW( package, record, message, &size );
813 row = MSI_CreateRecord( 1 );
814 MSI_RecordSetStringW( row, 1, message );
815 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
816 msiobj_release( &row->hdr );
819 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
821 if (!comp->Enabled)
823 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
824 return INSTALLSTATE_UNKNOWN;
826 if (package->need_rollback) return comp->Installed;
827 return comp->ActionRequest;
830 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
832 if (package->need_rollback) return feature->Installed;
833 return feature->ActionRequest;
836 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
838 MSIPACKAGE *package = param;
839 LPCWSTR dir, component, full_path;
840 MSIRECORD *uirow;
841 MSIFOLDER *folder;
842 MSICOMPONENT *comp;
844 component = MSI_RecordGetString(row, 2);
845 if (!component)
846 return ERROR_SUCCESS;
848 comp = msi_get_loaded_component(package, component);
849 if (!comp)
850 return ERROR_SUCCESS;
852 comp->Action = msi_get_component_action( package, comp );
853 if (comp->Action != INSTALLSTATE_LOCAL)
855 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
856 return ERROR_SUCCESS;
859 dir = MSI_RecordGetString(row,1);
860 if (!dir)
862 ERR("Unable to get folder id\n");
863 return ERROR_SUCCESS;
866 uirow = MSI_CreateRecord(1);
867 MSI_RecordSetStringW(uirow, 1, dir);
868 msi_ui_actiondata(package, szCreateFolders, uirow);
869 msiobj_release(&uirow->hdr);
871 full_path = msi_get_target_folder( package, dir );
872 if (!full_path)
874 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
875 return ERROR_SUCCESS;
877 TRACE("folder is %s\n", debugstr_w(full_path));
879 folder = msi_get_loaded_folder( package, dir );
880 if (folder->State == 0) msi_create_full_path( full_path );
881 folder->State = 3;
882 return ERROR_SUCCESS;
885 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
887 static const WCHAR query[] =
888 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
889 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
890 UINT rc;
891 MSIQUERY *view;
893 /* create all the empty folders specified in the CreateFolder table */
894 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
895 if (rc != ERROR_SUCCESS)
896 return ERROR_SUCCESS;
898 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
899 msiobj_release(&view->hdr);
901 return rc;
904 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
906 MSIPACKAGE *package = param;
907 LPCWSTR dir, component, full_path;
908 MSIRECORD *uirow;
909 MSIFOLDER *folder;
910 MSICOMPONENT *comp;
912 component = MSI_RecordGetString(row, 2);
913 if (!component)
914 return ERROR_SUCCESS;
916 comp = msi_get_loaded_component(package, component);
917 if (!comp)
918 return ERROR_SUCCESS;
920 comp->Action = msi_get_component_action( package, comp );
921 if (comp->Action != INSTALLSTATE_ABSENT)
923 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
924 return ERROR_SUCCESS;
927 dir = MSI_RecordGetString( row, 1 );
928 if (!dir)
930 ERR("Unable to get folder id\n");
931 return ERROR_SUCCESS;
934 full_path = msi_get_target_folder( package, dir );
935 if (!full_path)
937 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
938 return ERROR_SUCCESS;
940 TRACE("folder is %s\n", debugstr_w(full_path));
942 uirow = MSI_CreateRecord( 1 );
943 MSI_RecordSetStringW( uirow, 1, dir );
944 msi_ui_actiondata( package, szRemoveFolders, uirow );
945 msiobj_release( &uirow->hdr );
947 RemoveDirectoryW( full_path );
948 folder = msi_get_loaded_folder( package, dir );
949 folder->State = 0;
950 return ERROR_SUCCESS;
953 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
955 static const WCHAR query[] =
956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
957 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
959 MSIQUERY *view;
960 UINT rc;
962 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
963 if (rc != ERROR_SUCCESS)
964 return ERROR_SUCCESS;
966 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
967 msiobj_release( &view->hdr );
969 return rc;
972 static UINT load_component( MSIRECORD *row, LPVOID param )
974 MSIPACKAGE *package = param;
975 MSICOMPONENT *comp;
977 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
978 if (!comp)
979 return ERROR_FUNCTION_FAILED;
981 list_add_tail( &package->components, &comp->entry );
983 /* fill in the data */
984 comp->Component = msi_dup_record_field( row, 1 );
986 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
988 comp->ComponentId = msi_dup_record_field( row, 2 );
989 comp->Directory = msi_dup_record_field( row, 3 );
990 comp->Attributes = MSI_RecordGetInteger(row,4);
991 comp->Condition = msi_dup_record_field( row, 5 );
992 comp->KeyPath = msi_dup_record_field( row, 6 );
994 comp->Installed = INSTALLSTATE_UNKNOWN;
995 comp->Action = INSTALLSTATE_UNKNOWN;
996 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
998 comp->assembly = msi_load_assembly( package, comp );
999 return ERROR_SUCCESS;
1002 static UINT load_all_components( MSIPACKAGE *package )
1004 static const WCHAR query[] = {
1005 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1006 '`','C','o','m','p','o','n','e','n','t','`',0 };
1007 MSIQUERY *view;
1008 UINT r;
1010 if (!list_empty(&package->components))
1011 return ERROR_SUCCESS;
1013 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1014 if (r != ERROR_SUCCESS)
1015 return r;
1017 if (!msi_init_assembly_caches( package ))
1019 ERR("can't initialize assembly caches\n");
1020 msiobj_release( &view->hdr );
1021 return ERROR_FUNCTION_FAILED;
1024 r = MSI_IterateRecords(view, NULL, load_component, package);
1025 msiobj_release(&view->hdr);
1027 msi_destroy_assembly_caches( package );
1028 return r;
1031 typedef struct {
1032 MSIPACKAGE *package;
1033 MSIFEATURE *feature;
1034 } _ilfs;
1036 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1038 ComponentList *cl;
1040 cl = msi_alloc( sizeof (*cl) );
1041 if ( !cl )
1042 return ERROR_NOT_ENOUGH_MEMORY;
1043 cl->component = comp;
1044 list_add_tail( &feature->Components, &cl->entry );
1046 return ERROR_SUCCESS;
1049 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1051 FeatureList *fl;
1053 fl = msi_alloc( sizeof(*fl) );
1054 if ( !fl )
1055 return ERROR_NOT_ENOUGH_MEMORY;
1056 fl->feature = child;
1057 list_add_tail( &parent->Children, &fl->entry );
1059 return ERROR_SUCCESS;
1062 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1064 _ilfs* ilfs = param;
1065 LPCWSTR component;
1066 MSICOMPONENT *comp;
1068 component = MSI_RecordGetString(row,1);
1070 /* check to see if the component is already loaded */
1071 comp = msi_get_loaded_component( ilfs->package, component );
1072 if (!comp)
1074 ERR("unknown component %s\n", debugstr_w(component));
1075 return ERROR_FUNCTION_FAILED;
1078 add_feature_component( ilfs->feature, comp );
1079 comp->Enabled = TRUE;
1081 return ERROR_SUCCESS;
1084 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1086 MSIFEATURE *feature;
1088 if ( !name )
1089 return NULL;
1091 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1093 if ( !strcmpW( feature->Feature, name ) )
1094 return feature;
1097 return NULL;
1100 static UINT load_feature(MSIRECORD * row, LPVOID param)
1102 MSIPACKAGE* package = param;
1103 MSIFEATURE* feature;
1104 static const WCHAR Query1[] =
1105 {'S','E','L','E','C','T',' ',
1106 '`','C','o','m','p','o','n','e','n','t','_','`',
1107 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1108 'C','o','m','p','o','n','e','n','t','s','`',' ',
1109 'W','H','E','R','E',' ',
1110 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1111 MSIQUERY * view;
1112 UINT rc;
1113 _ilfs ilfs;
1115 /* fill in the data */
1117 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1118 if (!feature)
1119 return ERROR_NOT_ENOUGH_MEMORY;
1121 list_init( &feature->Children );
1122 list_init( &feature->Components );
1124 feature->Feature = msi_dup_record_field( row, 1 );
1126 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1128 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1129 feature->Title = msi_dup_record_field( row, 3 );
1130 feature->Description = msi_dup_record_field( row, 4 );
1132 if (!MSI_RecordIsNull(row,5))
1133 feature->Display = MSI_RecordGetInteger(row,5);
1135 feature->Level= MSI_RecordGetInteger(row,6);
1136 feature->Directory = msi_dup_record_field( row, 7 );
1137 feature->Attributes = MSI_RecordGetInteger(row,8);
1139 feature->Installed = INSTALLSTATE_UNKNOWN;
1140 feature->Action = INSTALLSTATE_UNKNOWN;
1141 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1143 list_add_tail( &package->features, &feature->entry );
1145 /* load feature components */
1147 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1148 if (rc != ERROR_SUCCESS)
1149 return ERROR_SUCCESS;
1151 ilfs.package = package;
1152 ilfs.feature = feature;
1154 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1155 msiobj_release(&view->hdr);
1157 return ERROR_SUCCESS;
1160 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1162 MSIPACKAGE* package = param;
1163 MSIFEATURE *parent, *child;
1165 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1166 if (!child)
1167 return ERROR_FUNCTION_FAILED;
1169 if (!child->Feature_Parent)
1170 return ERROR_SUCCESS;
1172 parent = find_feature_by_name( package, child->Feature_Parent );
1173 if (!parent)
1174 return ERROR_FUNCTION_FAILED;
1176 add_feature_child( parent, child );
1177 return ERROR_SUCCESS;
1180 static UINT load_all_features( MSIPACKAGE *package )
1182 static const WCHAR query[] = {
1183 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1184 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1185 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1186 MSIQUERY *view;
1187 UINT r;
1189 if (!list_empty(&package->features))
1190 return ERROR_SUCCESS;
1192 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1193 if (r != ERROR_SUCCESS)
1194 return r;
1196 r = MSI_IterateRecords( view, NULL, load_feature, package );
1197 if (r != ERROR_SUCCESS)
1198 return r;
1200 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1201 msiobj_release( &view->hdr );
1203 return r;
1206 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1208 if (!p)
1209 return p;
1210 p = strchrW(p, ch);
1211 if (!p)
1212 return p;
1213 *p = 0;
1214 return p+1;
1217 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1219 static const WCHAR query[] = {
1220 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1221 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1222 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1223 MSIQUERY *view = NULL;
1224 MSIRECORD *row = NULL;
1225 UINT r;
1227 TRACE("%s\n", debugstr_w(file->File));
1229 r = MSI_OpenQuery(package->db, &view, query, file->File);
1230 if (r != ERROR_SUCCESS)
1231 goto done;
1233 r = MSI_ViewExecute(view, NULL);
1234 if (r != ERROR_SUCCESS)
1235 goto done;
1237 r = MSI_ViewFetch(view, &row);
1238 if (r != ERROR_SUCCESS)
1239 goto done;
1241 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1242 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1243 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1244 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1245 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1247 done:
1248 if (view) msiobj_release(&view->hdr);
1249 if (row) msiobj_release(&row->hdr);
1250 return r;
1253 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1255 MSIRECORD *row;
1256 static const WCHAR query[] = {
1257 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1258 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1259 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1261 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1262 if (!row)
1264 WARN("query failed\n");
1265 return ERROR_FUNCTION_FAILED;
1268 file->disk_id = MSI_RecordGetInteger( row, 1 );
1269 msiobj_release( &row->hdr );
1270 return ERROR_SUCCESS;
1273 static UINT load_file(MSIRECORD *row, LPVOID param)
1275 MSIPACKAGE* package = param;
1276 LPCWSTR component;
1277 MSIFILE *file;
1279 /* fill in the data */
1281 file = msi_alloc_zero( sizeof (MSIFILE) );
1282 if (!file)
1283 return ERROR_NOT_ENOUGH_MEMORY;
1285 file->File = msi_dup_record_field( row, 1 );
1287 component = MSI_RecordGetString( row, 2 );
1288 file->Component = msi_get_loaded_component( package, component );
1290 if (!file->Component)
1292 WARN("Component not found: %s\n", debugstr_w(component));
1293 msi_free(file->File);
1294 msi_free(file);
1295 return ERROR_SUCCESS;
1298 file->FileName = msi_dup_record_field( row, 3 );
1299 msi_reduce_to_long_filename( file->FileName );
1301 file->ShortName = msi_dup_record_field( row, 3 );
1302 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1304 file->FileSize = MSI_RecordGetInteger( row, 4 );
1305 file->Version = msi_dup_record_field( row, 5 );
1306 file->Language = msi_dup_record_field( row, 6 );
1307 file->Attributes = MSI_RecordGetInteger( row, 7 );
1308 file->Sequence = MSI_RecordGetInteger( row, 8 );
1310 file->state = msifs_invalid;
1312 /* if the compressed bits are not set in the file attributes,
1313 * then read the information from the package word count property
1315 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1317 file->IsCompressed = FALSE;
1319 else if (file->Attributes &
1320 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1322 file->IsCompressed = TRUE;
1324 else if (file->Attributes & msidbFileAttributesNoncompressed)
1326 file->IsCompressed = FALSE;
1328 else
1330 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1333 load_file_hash(package, file);
1334 load_file_disk_id(package, file);
1336 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1338 list_add_tail( &package->files, &file->entry );
1340 return ERROR_SUCCESS;
1343 static UINT load_all_files(MSIPACKAGE *package)
1345 MSIQUERY * view;
1346 UINT rc;
1347 static const WCHAR Query[] =
1348 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1349 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1350 '`','S','e','q','u','e','n','c','e','`', 0};
1352 if (!list_empty(&package->files))
1353 return ERROR_SUCCESS;
1355 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1356 if (rc != ERROR_SUCCESS)
1357 return ERROR_SUCCESS;
1359 rc = MSI_IterateRecords(view, NULL, load_file, package);
1360 msiobj_release(&view->hdr);
1362 return ERROR_SUCCESS;
1365 static UINT load_media( MSIRECORD *row, LPVOID param )
1367 MSIPACKAGE *package = param;
1368 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1369 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1371 /* FIXME: load external cabinets and directory sources too */
1372 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1373 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1374 return ERROR_SUCCESS;
1377 static UINT load_all_media( MSIPACKAGE *package )
1379 static const WCHAR query[] =
1380 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
1381 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
1382 MSIQUERY *view;
1383 UINT r;
1385 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1386 if (r != ERROR_SUCCESS) return ERROR_SUCCESS;
1388 MSI_IterateRecords( view, NULL, load_media, package );
1389 msiobj_release( &view->hdr );
1390 return ERROR_SUCCESS;
1393 static UINT load_patch(MSIRECORD *row, LPVOID param)
1395 MSIPACKAGE *package = param;
1396 MSIFILEPATCH *patch;
1397 LPWSTR file_key;
1399 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1400 if (!patch)
1401 return ERROR_NOT_ENOUGH_MEMORY;
1403 file_key = msi_dup_record_field( row, 1 );
1404 patch->File = msi_get_loaded_file( package, file_key );
1405 msi_free(file_key);
1407 if( !patch->File )
1409 ERR("Failed to find target for patch in File table\n");
1410 msi_free(patch);
1411 return ERROR_FUNCTION_FAILED;
1414 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1416 /* FIXME: The database should be properly transformed */
1417 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1419 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1420 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1421 patch->IsApplied = FALSE;
1423 /* FIXME:
1424 * Header field - for patch validation.
1425 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1428 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1430 list_add_tail( &package->filepatches, &patch->entry );
1432 return ERROR_SUCCESS;
1435 static UINT load_all_patches(MSIPACKAGE *package)
1437 MSIQUERY *view;
1438 UINT rc;
1439 static const WCHAR Query[] =
1440 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1441 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1442 '`','S','e','q','u','e','n','c','e','`',0};
1444 if (!list_empty(&package->filepatches))
1445 return ERROR_SUCCESS;
1447 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1448 if (rc != ERROR_SUCCESS)
1449 return ERROR_SUCCESS;
1451 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1452 msiobj_release(&view->hdr);
1454 return ERROR_SUCCESS;
1457 static UINT load_folder( MSIRECORD *row, LPVOID param )
1459 MSIPACKAGE *package = param;
1460 static WCHAR szEmpty[] = { 0 };
1461 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1462 MSIFOLDER *folder;
1464 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1465 list_init( &folder->children );
1466 folder->Directory = msi_dup_record_field( row, 1 );
1467 folder->Parent = msi_dup_record_field( row, 2 );
1468 p = msi_dup_record_field(row, 3);
1470 TRACE("%s\n", debugstr_w(folder->Directory));
1472 /* split src and target dir */
1473 tgt_short = p;
1474 src_short = folder_split_path( p, ':' );
1476 /* split the long and short paths */
1477 tgt_long = folder_split_path( tgt_short, '|' );
1478 src_long = folder_split_path( src_short, '|' );
1480 /* check for no-op dirs */
1481 if (tgt_short && !strcmpW( szDot, tgt_short ))
1482 tgt_short = szEmpty;
1483 if (src_short && !strcmpW( szDot, src_short ))
1484 src_short = szEmpty;
1486 if (!tgt_long)
1487 tgt_long = tgt_short;
1489 if (!src_short) {
1490 src_short = tgt_short;
1491 src_long = tgt_long;
1494 if (!src_long)
1495 src_long = src_short;
1497 /* FIXME: use the target short path too */
1498 folder->TargetDefault = strdupW(tgt_long);
1499 folder->SourceShortPath = strdupW(src_short);
1500 folder->SourceLongPath = strdupW(src_long);
1501 msi_free(p);
1503 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1504 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1505 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1507 list_add_tail( &package->folders, &folder->entry );
1508 return ERROR_SUCCESS;
1511 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1513 FolderList *fl;
1515 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1516 fl->folder = child;
1517 list_add_tail( &parent->children, &fl->entry );
1518 return ERROR_SUCCESS;
1521 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1523 MSIPACKAGE *package = param;
1524 MSIFOLDER *parent, *child;
1526 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1527 return ERROR_FUNCTION_FAILED;
1529 if (!child->Parent) return ERROR_SUCCESS;
1531 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1532 return ERROR_FUNCTION_FAILED;
1534 return add_folder_child( parent, child );
1537 static UINT load_all_folders( MSIPACKAGE *package )
1539 static const WCHAR query[] = {
1540 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1541 '`','D','i','r','e','c','t','o','r','y','`',0 };
1542 MSIQUERY *view;
1543 UINT r;
1545 if (!list_empty(&package->folders))
1546 return ERROR_SUCCESS;
1548 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1549 if (r != ERROR_SUCCESS)
1550 return r;
1552 r = MSI_IterateRecords( view, NULL, load_folder, package );
1553 if (r != ERROR_SUCCESS)
1555 msiobj_release( &view->hdr );
1556 return r;
1558 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1559 msiobj_release( &view->hdr );
1560 return r;
1563 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1565 msi_set_property( package->db, szCostingComplete, szZero );
1566 msi_set_property( package->db, szRootDrive, szCRoot );
1568 load_all_folders( package );
1569 load_all_components( package );
1570 load_all_features( package );
1571 load_all_files( package );
1572 load_all_patches( package );
1573 load_all_media( package );
1575 return ERROR_SUCCESS;
1578 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1580 const WCHAR *action = package->script->Actions[script][index];
1581 ui_actionstart( package, action );
1582 TRACE("executing %s\n", debugstr_w(action));
1583 return ACTION_PerformAction( package, action, script );
1586 static UINT execute_script( MSIPACKAGE *package, UINT script )
1588 UINT i, rc = ERROR_SUCCESS;
1590 TRACE("executing script %u\n", script);
1592 if (!package->script)
1594 ERR("no script!\n");
1595 return ERROR_FUNCTION_FAILED;
1597 if (script == ROLLBACK_SCRIPT)
1599 for (i = package->script->ActionCount[script]; i > 0; i--)
1601 rc = execute_script_action( package, script, i - 1 );
1602 if (rc != ERROR_SUCCESS) break;
1605 else
1607 for (i = 0; i < package->script->ActionCount[script]; i++)
1609 rc = execute_script_action( package, script, i );
1610 if (rc != ERROR_SUCCESS) break;
1613 msi_free_action_script(package, script);
1614 return rc;
1617 static UINT ACTION_FileCost(MSIPACKAGE *package)
1619 return ERROR_SUCCESS;
1622 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1624 MSICOMPONENT *comp;
1625 UINT r;
1627 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1629 if (!comp->ComponentId) continue;
1631 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1632 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1633 &comp->Installed );
1634 if (r != ERROR_SUCCESS)
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1637 &comp->Installed );
1638 if (r != ERROR_SUCCESS)
1639 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1640 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1641 &comp->Installed );
1642 if (r != ERROR_SUCCESS)
1643 comp->Installed = INSTALLSTATE_ABSENT;
1647 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1649 MSIFEATURE *feature;
1651 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1653 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1655 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1656 feature->Installed = INSTALLSTATE_ABSENT;
1657 else
1658 feature->Installed = state;
1662 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1664 return (feature->Level > 0 && feature->Level <= level);
1667 static BOOL process_state_property(MSIPACKAGE* package, int level,
1668 LPCWSTR property, INSTALLSTATE state)
1670 LPWSTR override;
1671 MSIFEATURE *feature;
1673 override = msi_dup_property( package->db, property );
1674 if (!override)
1675 return FALSE;
1677 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1679 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1680 continue;
1682 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1684 if (!strcmpiW( override, szAll ))
1686 if (feature->Installed != state)
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1692 else
1694 LPWSTR ptr = override;
1695 LPWSTR ptr2 = strchrW(override,',');
1697 while (ptr)
1699 int len = ptr2 - ptr;
1701 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1702 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1704 if (feature->Installed != state)
1706 feature->Action = state;
1707 feature->ActionRequest = state;
1709 break;
1711 if (ptr2)
1713 ptr=ptr2+1;
1714 ptr2 = strchrW(ptr,',');
1716 else
1717 break;
1721 msi_free(override);
1722 return TRUE;
1725 static BOOL process_overrides( MSIPACKAGE *package, int level )
1727 static const WCHAR szAddLocal[] =
1728 {'A','D','D','L','O','C','A','L',0};
1729 static const WCHAR szAddSource[] =
1730 {'A','D','D','S','O','U','R','C','E',0};
1731 static const WCHAR szAdvertise[] =
1732 {'A','D','V','E','R','T','I','S','E',0};
1733 BOOL ret = FALSE;
1735 /* all these activation/deactivation things happen in order and things
1736 * later on the list override things earlier on the list.
1738 * 0 INSTALLLEVEL processing
1739 * 1 ADDLOCAL
1740 * 2 REMOVE
1741 * 3 ADDSOURCE
1742 * 4 ADDDEFAULT
1743 * 5 REINSTALL
1744 * 6 ADVERTISE
1745 * 7 COMPADDLOCAL
1746 * 8 COMPADDSOURCE
1747 * 9 FILEADDLOCAL
1748 * 10 FILEADDSOURCE
1749 * 11 FILEADDDEFAULT
1751 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1752 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1753 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1754 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1755 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1757 if (ret)
1758 msi_set_property( package->db, szPreselected, szOne );
1760 return ret;
1763 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1765 int level;
1766 MSICOMPONENT* component;
1767 MSIFEATURE *feature;
1769 TRACE("Checking Install Level\n");
1771 level = msi_get_property_int(package->db, szInstallLevel, 1);
1773 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1775 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1777 if (!is_feature_selected( feature, level )) continue;
1779 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1781 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1783 feature->Action = INSTALLSTATE_SOURCE;
1784 feature->ActionRequest = INSTALLSTATE_SOURCE;
1786 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1788 feature->Action = INSTALLSTATE_ADVERTISED;
1789 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1791 else
1793 feature->Action = INSTALLSTATE_LOCAL;
1794 feature->ActionRequest = INSTALLSTATE_LOCAL;
1798 /* disable child features of unselected parent or follow parent */
1799 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1801 FeatureList *fl;
1803 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1805 if (!is_feature_selected( feature, level ))
1807 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1808 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1810 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1812 fl->feature->Action = feature->Action;
1813 fl->feature->ActionRequest = feature->ActionRequest;
1818 else /* preselected */
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 if (!is_feature_selected( feature, level )) continue;
1824 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1826 if (feature->Installed == INSTALLSTATE_ABSENT)
1828 feature->Action = INSTALLSTATE_UNKNOWN;
1829 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1831 else
1833 feature->Action = feature->Installed;
1834 feature->ActionRequest = feature->Installed;
1838 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1840 FeatureList *fl;
1842 if (!is_feature_selected( feature, level )) continue;
1844 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1846 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1848 fl->feature->Action = feature->Action;
1849 fl->feature->ActionRequest = feature->ActionRequest;
1855 /* now we want to set component state based based on feature state */
1856 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1858 ComponentList *cl;
1860 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1861 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1862 feature->ActionRequest, feature->Action);
1864 if (!is_feature_selected( feature, level )) continue;
1866 /* features with components that have compressed files are made local */
1867 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1869 if (cl->component->ForceLocalState &&
1870 feature->ActionRequest == INSTALLSTATE_SOURCE)
1872 feature->Action = INSTALLSTATE_LOCAL;
1873 feature->ActionRequest = INSTALLSTATE_LOCAL;
1874 break;
1878 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1880 component = cl->component;
1882 switch (feature->ActionRequest)
1884 case INSTALLSTATE_ABSENT:
1885 component->anyAbsent = 1;
1886 break;
1887 case INSTALLSTATE_ADVERTISED:
1888 component->hasAdvertiseFeature = 1;
1889 break;
1890 case INSTALLSTATE_SOURCE:
1891 component->hasSourceFeature = 1;
1892 break;
1893 case INSTALLSTATE_LOCAL:
1894 component->hasLocalFeature = 1;
1895 break;
1896 case INSTALLSTATE_DEFAULT:
1897 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1898 component->hasAdvertiseFeature = 1;
1899 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1900 component->hasSourceFeature = 1;
1901 else
1902 component->hasLocalFeature = 1;
1903 break;
1904 default:
1905 break;
1910 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1912 /* check if it's local or source */
1913 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1914 (component->hasLocalFeature || component->hasSourceFeature))
1916 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1917 !component->ForceLocalState)
1919 component->Action = INSTALLSTATE_SOURCE;
1920 component->ActionRequest = INSTALLSTATE_SOURCE;
1922 else
1924 component->Action = INSTALLSTATE_LOCAL;
1925 component->ActionRequest = INSTALLSTATE_LOCAL;
1927 continue;
1930 /* if any feature is local, the component must be local too */
1931 if (component->hasLocalFeature)
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1935 continue;
1937 if (component->hasSourceFeature)
1939 component->Action = INSTALLSTATE_SOURCE;
1940 component->ActionRequest = INSTALLSTATE_SOURCE;
1941 continue;
1943 if (component->hasAdvertiseFeature)
1945 component->Action = INSTALLSTATE_ADVERTISED;
1946 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1947 continue;
1949 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1950 if (component->anyAbsent &&
1951 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1976 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1979 return ERROR_SUCCESS;
1982 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1984 MSIPACKAGE *package = param;
1985 LPCWSTR name;
1986 MSIFEATURE *feature;
1988 name = MSI_RecordGetString( row, 1 );
1990 feature = msi_get_loaded_feature( package, name );
1991 if (!feature)
1992 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1993 else
1995 LPCWSTR Condition;
1996 Condition = MSI_RecordGetString(row,3);
1998 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2000 int level = MSI_RecordGetInteger(row,2);
2001 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2002 feature->Level = level;
2005 return ERROR_SUCCESS;
2008 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2010 static const WCHAR name[] = {'\\',0};
2011 VS_FIXEDFILEINFO *ptr, *ret;
2012 LPVOID version;
2013 DWORD versize, handle;
2014 UINT sz;
2016 versize = GetFileVersionInfoSizeW( filename, &handle );
2017 if (!versize)
2018 return NULL;
2020 version = msi_alloc( versize );
2021 if (!version)
2022 return NULL;
2024 GetFileVersionInfoW( filename, 0, versize, version );
2026 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2028 msi_free( version );
2029 return NULL;
2032 ret = msi_alloc( sz );
2033 memcpy( ret, ptr, sz );
2035 msi_free( version );
2036 return ret;
2039 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2041 DWORD ms, ls;
2043 msi_parse_version_string( version, &ms, &ls );
2045 if (fi->dwFileVersionMS > ms) return 1;
2046 else if (fi->dwFileVersionMS < ms) return -1;
2047 else if (fi->dwFileVersionLS > ls) return 1;
2048 else if (fi->dwFileVersionLS < ls) return -1;
2049 return 0;
2052 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2054 DWORD ms1, ms2;
2056 msi_parse_version_string( ver1, &ms1, NULL );
2057 msi_parse_version_string( ver2, &ms2, NULL );
2059 if (ms1 > ms2) return 1;
2060 else if (ms1 < ms2) return -1;
2061 return 0;
2064 DWORD msi_get_disk_file_size( LPCWSTR filename )
2066 HANDLE file;
2067 DWORD size;
2069 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2070 if (file == INVALID_HANDLE_VALUE)
2071 return INVALID_FILE_SIZE;
2073 size = GetFileSize( file, NULL );
2074 TRACE("size is %u\n", size);
2075 CloseHandle( file );
2076 return size;
2079 BOOL msi_file_hash_matches( MSIFILE *file )
2081 UINT r;
2082 MSIFILEHASHINFO hash;
2084 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2085 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2086 if (r != ERROR_SUCCESS)
2087 return FALSE;
2089 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2092 static WCHAR *get_temp_dir( void )
2094 static UINT id;
2095 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2097 GetTempPathW( MAX_PATH, tmp );
2098 for (;;)
2100 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2101 if (CreateDirectoryW( dir, NULL )) break;
2103 return strdupW( dir );
2107 * msi_build_directory_name()
2109 * This function is to save messing round with directory names
2110 * It handles adding backslashes between path segments,
2111 * and can add \ at the end of the directory name if told to.
2113 * It takes a variable number of arguments.
2114 * It always allocates a new string for the result, so make sure
2115 * to free the return value when finished with it.
2117 * The first arg is the number of path segments that follow.
2118 * The arguments following count are a list of path segments.
2119 * A path segment may be NULL.
2121 * Path segments will be added with a \ separating them.
2122 * A \ will not be added after the last segment, however if the
2123 * last segment is NULL, then the last character will be a \
2125 WCHAR *msi_build_directory_name( DWORD count, ... )
2127 DWORD sz = 1, i;
2128 WCHAR *dir;
2129 va_list va;
2131 va_start( va, count );
2132 for (i = 0; i < count; i++)
2134 const WCHAR *str = va_arg( va, const WCHAR * );
2135 if (str) sz += strlenW( str ) + 1;
2137 va_end( va );
2139 dir = msi_alloc( sz * sizeof(WCHAR) );
2140 dir[0] = 0;
2142 va_start( va, count );
2143 for (i = 0; i < count; i++)
2145 const WCHAR *str = va_arg( va, const WCHAR * );
2146 if (!str) continue;
2147 strcatW( dir, str );
2148 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2150 va_end( va );
2151 return dir;
2154 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2156 MSIASSEMBLY *assembly = file->Component->assembly;
2158 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2160 msi_free( file->TargetPath );
2161 if (assembly && !assembly->application)
2163 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2164 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2165 msi_track_tempfile( package, file->TargetPath );
2167 else
2169 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2170 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2173 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2176 static UINT calculate_file_cost( MSIPACKAGE *package )
2178 VS_FIXEDFILEINFO *file_version;
2179 WCHAR *font_version;
2180 MSIFILE *file;
2182 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2184 MSICOMPONENT *comp = file->Component;
2185 DWORD file_size;
2187 if (!comp->Enabled) continue;
2189 if (file->IsCompressed)
2190 comp->ForceLocalState = TRUE;
2192 set_target_path( package, file );
2194 if ((comp->assembly && !comp->assembly->installed) ||
2195 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2197 comp->Cost += file->FileSize;
2198 continue;
2200 file_size = msi_get_disk_file_size( file->TargetPath );
2202 if (file->Version)
2204 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2206 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2208 comp->Cost += file->FileSize - file_size;
2210 msi_free( file_version );
2211 continue;
2213 else if ((font_version = font_version_from_file( file->TargetPath )))
2215 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2217 comp->Cost += file->FileSize - file_size;
2219 msi_free( font_version );
2220 continue;
2223 if (file_size != file->FileSize)
2225 comp->Cost += file->FileSize - file_size;
2228 return ERROR_SUCCESS;
2231 void msi_clean_path( WCHAR *p )
2233 WCHAR *q = p;
2234 int n, len = 0;
2236 while (1)
2238 /* copy until the end of the string or a space */
2239 while (*p != ' ' && (*q = *p))
2241 p++, len++;
2242 /* reduce many backslashes to one */
2243 if (*p != '\\' || *q != '\\')
2244 q++;
2247 /* quit at the end of the string */
2248 if (!*p)
2249 break;
2251 /* count the number of spaces */
2252 n = 0;
2253 while (p[n] == ' ')
2254 n++;
2256 /* if it's leading or trailing space, skip it */
2257 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2258 p += n;
2259 else /* copy n spaces */
2260 while (n && (*q++ = *p++)) n--;
2264 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2266 int len;
2267 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2269 if (!target_dir) return NULL;
2271 len = strlenW( target_dir );
2272 if (target_dir[len - 1] == '\\') return target_dir;
2273 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2275 strcpyW( path, target_dir );
2276 path[len] = '\\';
2277 path[len + 1] = 0;
2279 msi_free( target_dir );
2280 return path;
2283 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2285 FolderList *fl;
2286 MSIFOLDER *folder, *parent, *child;
2287 WCHAR *path;
2289 TRACE("resolving %s\n", debugstr_w(name));
2291 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2293 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2295 if (!load_prop || !(path = get_target_dir_property( package->db )))
2297 path = msi_dup_property( package->db, szRootDrive );
2300 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2302 parent = msi_get_loaded_folder( package, folder->Parent );
2303 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2305 msi_clean_path( path );
2306 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2308 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2309 msi_free( path );
2310 return;
2312 msi_set_property( package->db, folder->Directory, path );
2313 msi_free( folder->ResolvedTarget );
2314 folder->ResolvedTarget = path;
2316 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2318 child = fl->folder;
2319 msi_resolve_target_folder( package, child->Directory, load_prop );
2321 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2324 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2326 static const WCHAR condition_query[] =
2327 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
2328 static const WCHAR szOutOfDiskSpace[] =
2329 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2330 MSICOMPONENT *comp;
2331 UINT rc = ERROR_SUCCESS;
2332 MSIQUERY * view;
2333 LPWSTR level;
2335 TRACE("Building directory properties\n");
2336 msi_resolve_target_folder( package, szTargetDir, TRUE );
2338 TRACE("Evaluating component conditions\n");
2339 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2341 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2343 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2344 comp->Enabled = FALSE;
2346 else
2347 comp->Enabled = TRUE;
2350 /* read components states from the registry */
2351 ACTION_GetComponentInstallStates(package);
2352 ACTION_GetFeatureInstallStates(package);
2354 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2356 TRACE("Evaluating feature conditions\n");
2358 rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
2359 if (rc == ERROR_SUCCESS)
2361 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2362 msiobj_release( &view->hdr );
2366 TRACE("Calculating file cost\n");
2367 calculate_file_cost( package );
2369 msi_set_property( package->db, szCostingComplete, szOne );
2370 /* set default run level if not set */
2371 level = msi_dup_property( package->db, szInstallLevel );
2372 if (!level)
2373 msi_set_property( package->db, szInstallLevel, szOne );
2374 msi_free(level);
2376 /* FIXME: check volume disk space */
2377 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2379 return MSI_SetFeatureStates(package);
2382 /* OK this value is "interpreted" and then formatted based on the
2383 first few characters */
2384 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2385 DWORD *size)
2387 LPSTR data = NULL;
2389 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2391 if (value[1]=='x')
2393 LPWSTR ptr;
2394 CHAR byte[5];
2395 LPWSTR deformated = NULL;
2396 int count;
2398 deformat_string(package, &value[2], &deformated);
2400 /* binary value type */
2401 ptr = deformated;
2402 *type = REG_BINARY;
2403 if (strlenW(ptr)%2)
2404 *size = (strlenW(ptr)/2)+1;
2405 else
2406 *size = strlenW(ptr)/2;
2408 data = msi_alloc(*size);
2410 byte[0] = '0';
2411 byte[1] = 'x';
2412 byte[4] = 0;
2413 count = 0;
2414 /* if uneven pad with a zero in front */
2415 if (strlenW(ptr)%2)
2417 byte[2]= '0';
2418 byte[3]= *ptr;
2419 ptr++;
2420 data[count] = (BYTE)strtol(byte,NULL,0);
2421 count ++;
2422 TRACE("Uneven byte count\n");
2424 while (*ptr)
2426 byte[2]= *ptr;
2427 ptr++;
2428 byte[3]= *ptr;
2429 ptr++;
2430 data[count] = (BYTE)strtol(byte,NULL,0);
2431 count ++;
2433 msi_free(deformated);
2435 TRACE("Data %i bytes(%i)\n",*size,count);
2437 else
2439 LPWSTR deformated;
2440 LPWSTR p;
2441 DWORD d = 0;
2442 deformat_string(package, &value[1], &deformated);
2444 *type=REG_DWORD;
2445 *size = sizeof(DWORD);
2446 data = msi_alloc(*size);
2447 p = deformated;
2448 if (*p == '-')
2449 p++;
2450 while (*p)
2452 if ( (*p < '0') || (*p > '9') )
2453 break;
2454 d *= 10;
2455 d += (*p - '0');
2456 p++;
2458 if (deformated[0] == '-')
2459 d = -d;
2460 *(LPDWORD)data = d;
2461 TRACE("DWORD %i\n",*(LPDWORD)data);
2463 msi_free(deformated);
2466 else
2468 static const WCHAR szMulti[] = {'[','~',']',0};
2469 LPCWSTR ptr;
2470 *type=REG_SZ;
2472 if (value[0]=='#')
2474 if (value[1]=='%')
2476 ptr = &value[2];
2477 *type=REG_EXPAND_SZ;
2479 else
2480 ptr = &value[1];
2482 else
2483 ptr=value;
2485 if (strstrW(value, szMulti))
2486 *type = REG_MULTI_SZ;
2488 /* remove initial delimiter */
2489 if (!strncmpW(value, szMulti, 3))
2490 ptr = value + 3;
2492 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2494 /* add double NULL terminator */
2495 if (*type == REG_MULTI_SZ)
2497 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2498 data = msi_realloc_zero(data, *size);
2501 return data;
2504 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2506 const WCHAR *ret;
2508 switch (root)
2510 case -1:
2511 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2513 *root_key = HKEY_LOCAL_MACHINE;
2514 ret = szHLM;
2516 else
2518 *root_key = HKEY_CURRENT_USER;
2519 ret = szHCU;
2521 break;
2522 case 0:
2523 *root_key = HKEY_CLASSES_ROOT;
2524 ret = szHCR;
2525 break;
2526 case 1:
2527 *root_key = HKEY_CURRENT_USER;
2528 ret = szHCU;
2529 break;
2530 case 2:
2531 *root_key = HKEY_LOCAL_MACHINE;
2532 ret = szHLM;
2533 break;
2534 case 3:
2535 *root_key = HKEY_USERS;
2536 ret = szHU;
2537 break;
2538 default:
2539 ERR("Unknown root %i\n", root);
2540 return NULL;
2543 return ret;
2546 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2548 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2549 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2551 if (is_64bit && package->platform == PLATFORM_INTEL &&
2552 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2554 UINT size;
2555 WCHAR *path_32node;
2557 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2558 if (!(path_32node = msi_alloc( size ))) return NULL;
2560 memcpy( path_32node, path, len * sizeof(WCHAR) );
2561 strcpyW( path_32node + len, szWow6432Node );
2562 strcatW( path_32node, szBackSlash );
2563 strcatW( path_32node, path + len );
2564 return path_32node;
2567 return strdupW( path );
2570 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2572 MSIPACKAGE *package = param;
2573 LPSTR value_data = NULL;
2574 HKEY root_key, hkey;
2575 DWORD type,size;
2576 LPWSTR deformated, uikey, keypath;
2577 LPCWSTR szRoot, component, name, key, value;
2578 MSICOMPONENT *comp;
2579 MSIRECORD * uirow;
2580 INT root;
2581 BOOL check_first = FALSE;
2582 UINT rc;
2584 msi_ui_progress( package, 2, 0, 0, 0 );
2586 component = MSI_RecordGetString(row, 6);
2587 comp = msi_get_loaded_component(package,component);
2588 if (!comp)
2589 return ERROR_SUCCESS;
2591 comp->Action = msi_get_component_action( package, comp );
2592 if (comp->Action != INSTALLSTATE_LOCAL)
2594 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2595 return ERROR_SUCCESS;
2598 name = MSI_RecordGetString(row, 4);
2599 if( MSI_RecordIsNull(row,5) && name )
2601 /* null values can have special meanings */
2602 if (name[0]=='-' && name[1] == 0)
2603 return ERROR_SUCCESS;
2604 else if ((name[0]=='+' && name[1] == 0) ||
2605 (name[0] == '*' && name[1] == 0))
2606 name = NULL;
2607 check_first = TRUE;
2610 root = MSI_RecordGetInteger(row,2);
2611 key = MSI_RecordGetString(row, 3);
2613 szRoot = get_root_key( package, root, &root_key );
2614 if (!szRoot)
2615 return ERROR_SUCCESS;
2617 deformat_string(package, key , &deformated);
2618 size = strlenW(deformated) + strlenW(szRoot) + 1;
2619 uikey = msi_alloc(size*sizeof(WCHAR));
2620 strcpyW(uikey,szRoot);
2621 strcatW(uikey,deformated);
2623 keypath = get_keypath( package, root_key, deformated );
2624 msi_free( deformated );
2625 if (RegCreateKeyW( root_key, keypath, &hkey ))
2627 ERR("Could not create key %s\n", debugstr_w(keypath));
2628 msi_free(uikey);
2629 msi_free(keypath);
2630 return ERROR_SUCCESS;
2633 value = MSI_RecordGetString(row,5);
2634 if (value)
2635 value_data = parse_value(package, value, &type, &size);
2636 else
2638 value_data = (LPSTR)strdupW(szEmpty);
2639 size = sizeof(szEmpty);
2640 type = REG_SZ;
2643 deformat_string(package, name, &deformated);
2645 if (!check_first)
2647 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2648 debugstr_w(uikey));
2649 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2651 else
2653 DWORD sz = 0;
2654 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2655 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2657 TRACE("value %s of %s checked already exists\n",
2658 debugstr_w(deformated), debugstr_w(uikey));
2660 else
2662 TRACE("Checked and setting value %s of %s\n",
2663 debugstr_w(deformated), debugstr_w(uikey));
2664 if (deformated || size)
2665 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2668 RegCloseKey(hkey);
2670 uirow = MSI_CreateRecord(3);
2671 MSI_RecordSetStringW(uirow,2,deformated);
2672 MSI_RecordSetStringW(uirow,1,uikey);
2673 if (type == REG_SZ || type == REG_EXPAND_SZ)
2674 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2675 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2676 msiobj_release( &uirow->hdr );
2678 msi_free(value_data);
2679 msi_free(deformated);
2680 msi_free(uikey);
2681 msi_free(keypath);
2683 return ERROR_SUCCESS;
2686 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2688 UINT rc;
2689 MSIQUERY * view;
2690 static const WCHAR ExecSeqQuery[] =
2691 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2692 '`','R','e','g','i','s','t','r','y','`',0 };
2694 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2695 if (rc != ERROR_SUCCESS)
2696 return ERROR_SUCCESS;
2698 /* increment progress bar each time action data is sent */
2699 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2701 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2702 msiobj_release(&view->hdr);
2703 return rc;
2706 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2708 LONG res;
2709 HKEY hkey;
2710 DWORD num_subkeys, num_values;
2712 if (delete_key)
2714 if ((res = RegDeleteTreeW( hkey_root, key )))
2716 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2718 return;
2721 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2723 if ((res = RegDeleteValueW( hkey, value )))
2725 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2727 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2728 NULL, NULL, NULL, NULL );
2729 RegCloseKey( hkey );
2730 if (!res && !num_subkeys && !num_values)
2732 TRACE("Removing empty key %s\n", debugstr_w(key));
2733 RegDeleteKeyW( hkey_root, key );
2735 return;
2737 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2741 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2743 MSIPACKAGE *package = param;
2744 LPCWSTR component, name, key_str, root_key_str;
2745 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2746 MSICOMPONENT *comp;
2747 MSIRECORD *uirow;
2748 BOOL delete_key = FALSE;
2749 HKEY hkey_root;
2750 UINT size;
2751 INT root;
2753 msi_ui_progress( package, 2, 0, 0, 0 );
2755 component = MSI_RecordGetString( row, 6 );
2756 comp = msi_get_loaded_component( package, component );
2757 if (!comp)
2758 return ERROR_SUCCESS;
2760 comp->Action = msi_get_component_action( package, comp );
2761 if (comp->Action != INSTALLSTATE_ABSENT)
2763 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2764 return ERROR_SUCCESS;
2767 name = MSI_RecordGetString( row, 4 );
2768 if (MSI_RecordIsNull( row, 5 ) && name )
2770 if (name[0] == '+' && !name[1])
2771 return ERROR_SUCCESS;
2772 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2774 delete_key = TRUE;
2775 name = NULL;
2779 root = MSI_RecordGetInteger( row, 2 );
2780 key_str = MSI_RecordGetString( row, 3 );
2782 root_key_str = get_root_key( package, root, &hkey_root );
2783 if (!root_key_str)
2784 return ERROR_SUCCESS;
2786 deformat_string( package, key_str, &deformated_key );
2787 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2788 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2789 strcpyW( ui_key_str, root_key_str );
2790 strcatW( ui_key_str, deformated_key );
2792 deformat_string( package, name, &deformated_name );
2794 keypath = get_keypath( package, hkey_root, deformated_key );
2795 msi_free( deformated_key );
2796 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2797 msi_free( keypath );
2799 uirow = MSI_CreateRecord( 2 );
2800 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2801 MSI_RecordSetStringW( uirow, 2, deformated_name );
2803 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2804 msiobj_release( &uirow->hdr );
2806 msi_free( ui_key_str );
2807 msi_free( deformated_name );
2808 return ERROR_SUCCESS;
2811 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2813 MSIPACKAGE *package = param;
2814 LPCWSTR component, name, key_str, root_key_str;
2815 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2816 MSICOMPONENT *comp;
2817 MSIRECORD *uirow;
2818 BOOL delete_key = FALSE;
2819 HKEY hkey_root;
2820 UINT size;
2821 INT root;
2823 msi_ui_progress( package, 2, 0, 0, 0 );
2825 component = MSI_RecordGetString( row, 5 );
2826 comp = msi_get_loaded_component( package, component );
2827 if (!comp)
2828 return ERROR_SUCCESS;
2830 comp->Action = msi_get_component_action( package, comp );
2831 if (comp->Action != INSTALLSTATE_LOCAL)
2833 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2834 return ERROR_SUCCESS;
2837 if ((name = MSI_RecordGetString( row, 4 )))
2839 if (name[0] == '-' && !name[1])
2841 delete_key = TRUE;
2842 name = NULL;
2846 root = MSI_RecordGetInteger( row, 2 );
2847 key_str = MSI_RecordGetString( row, 3 );
2849 root_key_str = get_root_key( package, root, &hkey_root );
2850 if (!root_key_str)
2851 return ERROR_SUCCESS;
2853 deformat_string( package, key_str, &deformated_key );
2854 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2855 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2856 strcpyW( ui_key_str, root_key_str );
2857 strcatW( ui_key_str, deformated_key );
2859 deformat_string( package, name, &deformated_name );
2861 keypath = get_keypath( package, hkey_root, deformated_key );
2862 msi_free( deformated_key );
2863 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2864 msi_free( keypath );
2866 uirow = MSI_CreateRecord( 2 );
2867 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2868 MSI_RecordSetStringW( uirow, 2, deformated_name );
2870 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2871 msiobj_release( &uirow->hdr );
2873 msi_free( ui_key_str );
2874 msi_free( deformated_name );
2875 return ERROR_SUCCESS;
2878 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2880 UINT rc;
2881 MSIQUERY *view;
2882 static const WCHAR registry_query[] =
2883 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2884 '`','R','e','g','i','s','t','r','y','`',0 };
2885 static const WCHAR remove_registry_query[] =
2886 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2887 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2889 /* increment progress bar each time action data is sent */
2890 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2892 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2893 if (rc == ERROR_SUCCESS)
2895 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2896 msiobj_release( &view->hdr );
2897 if (rc != ERROR_SUCCESS)
2898 return rc;
2901 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2902 if (rc == ERROR_SUCCESS)
2904 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2905 msiobj_release( &view->hdr );
2906 if (rc != ERROR_SUCCESS)
2907 return rc;
2910 return ERROR_SUCCESS;
2913 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2915 package->script->CurrentlyScripting = TRUE;
2917 return ERROR_SUCCESS;
2921 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2923 MSICOMPONENT *comp;
2924 DWORD progress = 0;
2925 DWORD total = 0;
2926 static const WCHAR q1[]=
2927 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2928 '`','R','e','g','i','s','t','r','y','`',0};
2929 UINT rc;
2930 MSIQUERY * view;
2931 MSIFEATURE *feature;
2932 MSIFILE *file;
2934 TRACE("InstallValidate\n");
2936 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2937 if (rc == ERROR_SUCCESS)
2939 MSI_IterateRecords( view, &progress, NULL, package );
2940 msiobj_release( &view->hdr );
2941 total += progress * REG_PROGRESS_VALUE;
2944 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2945 total += COMPONENT_PROGRESS_VALUE;
2947 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2948 total += file->FileSize;
2950 msi_ui_progress( package, 0, total, 0, 0 );
2952 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2954 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2955 debugstr_w(feature->Feature), feature->Installed,
2956 feature->ActionRequest, feature->Action);
2959 return ERROR_SUCCESS;
2962 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2964 MSIPACKAGE* package = param;
2965 LPCWSTR cond = NULL;
2966 LPCWSTR message = NULL;
2967 UINT r;
2969 static const WCHAR title[]=
2970 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2972 cond = MSI_RecordGetString(row,1);
2974 r = MSI_EvaluateConditionW(package,cond);
2975 if (r == MSICONDITION_FALSE)
2977 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2979 LPWSTR deformated;
2980 message = MSI_RecordGetString(row,2);
2981 deformat_string(package,message,&deformated);
2982 MessageBoxW(NULL,deformated,title,MB_OK);
2983 msi_free(deformated);
2986 return ERROR_INSTALL_FAILURE;
2989 return ERROR_SUCCESS;
2992 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2994 UINT rc;
2995 MSIQUERY * view = NULL;
2996 static const WCHAR ExecSeqQuery[] =
2997 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2998 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3000 TRACE("Checking launch conditions\n");
3002 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3003 if (rc != ERROR_SUCCESS)
3004 return ERROR_SUCCESS;
3006 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3007 msiobj_release(&view->hdr);
3009 return rc;
3012 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3015 if (!cmp->KeyPath)
3016 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3018 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3020 MSIRECORD * row = 0;
3021 UINT root,len;
3022 LPWSTR deformated,buffer,deformated_name;
3023 LPCWSTR key,name;
3024 static const WCHAR ExecSeqQuery[] =
3025 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3026 '`','R','e','g','i','s','t','r','y','`',' ',
3027 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3028 ' ','=',' ' ,'\'','%','s','\'',0 };
3029 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3030 static const WCHAR fmt2[]=
3031 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3033 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3034 if (!row)
3035 return NULL;
3037 root = MSI_RecordGetInteger(row,2);
3038 key = MSI_RecordGetString(row, 3);
3039 name = MSI_RecordGetString(row, 4);
3040 deformat_string(package, key , &deformated);
3041 deformat_string(package, name, &deformated_name);
3043 len = strlenW(deformated) + 6;
3044 if (deformated_name)
3045 len+=strlenW(deformated_name);
3047 buffer = msi_alloc( len *sizeof(WCHAR));
3049 if (deformated_name)
3050 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3051 else
3052 sprintfW(buffer,fmt,root,deformated);
3054 msi_free(deformated);
3055 msi_free(deformated_name);
3056 msiobj_release(&row->hdr);
3058 return buffer;
3060 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3062 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3063 return NULL;
3065 else
3067 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3069 if (file)
3070 return strdupW( file->TargetPath );
3072 return NULL;
3075 static HKEY openSharedDLLsKey(void)
3077 HKEY hkey=0;
3078 static const WCHAR path[] =
3079 {'S','o','f','t','w','a','r','e','\\',
3080 'M','i','c','r','o','s','o','f','t','\\',
3081 'W','i','n','d','o','w','s','\\',
3082 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3083 'S','h','a','r','e','d','D','L','L','s',0};
3085 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3086 return hkey;
3089 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3091 HKEY hkey;
3092 DWORD count=0;
3093 DWORD type;
3094 DWORD sz = sizeof(count);
3095 DWORD rc;
3097 hkey = openSharedDLLsKey();
3098 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3099 if (rc != ERROR_SUCCESS)
3100 count = 0;
3101 RegCloseKey(hkey);
3102 return count;
3105 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3107 HKEY hkey;
3109 hkey = openSharedDLLsKey();
3110 if (count > 0)
3111 msi_reg_set_val_dword( hkey, path, count );
3112 else
3113 RegDeleteValueW(hkey,path);
3114 RegCloseKey(hkey);
3115 return count;
3118 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3120 MSIFEATURE *feature;
3121 INT count = 0;
3122 BOOL write = FALSE;
3124 /* only refcount DLLs */
3125 if (comp->KeyPath == NULL ||
3126 comp->assembly ||
3127 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3128 comp->Attributes & msidbComponentAttributesODBCDataSource)
3129 write = FALSE;
3130 else
3132 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3133 write = (count > 0);
3135 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3136 write = TRUE;
3139 /* increment counts */
3140 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3142 ComponentList *cl;
3144 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3145 continue;
3147 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3149 if ( cl->component == comp )
3150 count++;
3154 /* decrement counts */
3155 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3157 ComponentList *cl;
3159 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3160 continue;
3162 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3164 if ( cl->component == comp )
3165 count--;
3169 /* ref count all the files in the component */
3170 if (write)
3172 MSIFILE *file;
3174 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3176 if (file->Component == comp)
3177 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3181 /* add a count for permanent */
3182 if (comp->Attributes & msidbComponentAttributesPermanent)
3183 count ++;
3185 comp->RefCount = count;
3187 if (write)
3188 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3191 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3193 WCHAR squished_pc[GUID_SIZE];
3194 WCHAR squished_cc[GUID_SIZE];
3195 UINT rc;
3196 MSICOMPONENT *comp;
3197 HKEY hkey;
3199 TRACE("\n");
3201 squash_guid(package->ProductCode,squished_pc);
3202 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3204 msi_set_sourcedir_props(package, FALSE);
3206 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3208 MSIRECORD * uirow;
3210 msi_ui_progress( package, 2, 0, 0, 0 );
3211 if (!comp->ComponentId)
3212 continue;
3214 squash_guid(comp->ComponentId,squished_cc);
3216 msi_free(comp->FullKeypath);
3217 if (comp->assembly)
3219 const WCHAR prefixW[] = {'<','\\',0};
3220 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3222 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3223 if (comp->FullKeypath)
3225 strcpyW( comp->FullKeypath, prefixW );
3226 strcatW( comp->FullKeypath, comp->assembly->display_name );
3229 else comp->FullKeypath = resolve_keypath( package, comp );
3231 ACTION_RefCountComponent( package, comp );
3233 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3234 debugstr_w(comp->Component),
3235 debugstr_w(squished_cc),
3236 debugstr_w(comp->FullKeypath),
3237 comp->RefCount,
3238 comp->ActionRequest);
3240 comp->Action = msi_get_component_action( package, comp );
3241 if (comp->Action == INSTALLSTATE_LOCAL ||
3242 comp->Action == INSTALLSTATE_SOURCE)
3244 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3245 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3246 else
3247 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3249 if (rc != ERROR_SUCCESS)
3250 continue;
3252 if (comp->Attributes & msidbComponentAttributesPermanent)
3254 static const WCHAR szPermKey[] =
3255 { '0','0','0','0','0','0','0','0','0','0','0','0',
3256 '0','0','0','0','0','0','0','0','0','0','0','0',
3257 '0','0','0','0','0','0','0','0',0 };
3259 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3262 if (comp->Action == INSTALLSTATE_LOCAL)
3263 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3264 else
3266 MSIFILE *file;
3267 MSIRECORD *row;
3268 LPWSTR ptr, ptr2;
3269 WCHAR source[MAX_PATH];
3270 WCHAR base[MAX_PATH];
3271 LPWSTR sourcepath;
3273 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3274 static const WCHAR query[] = {
3275 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3276 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3277 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3278 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3279 '`','D','i','s','k','I','d','`',0};
3281 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3282 continue;
3284 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3285 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3286 ptr2 = strrchrW(source, '\\') + 1;
3287 msiobj_release(&row->hdr);
3289 lstrcpyW(base, package->PackagePath);
3290 ptr = strrchrW(base, '\\');
3291 *(ptr + 1) = '\0';
3293 sourcepath = msi_resolve_file_source(package, file);
3294 ptr = sourcepath + lstrlenW(base);
3295 lstrcpyW(ptr2, ptr);
3296 msi_free(sourcepath);
3298 msi_reg_set_val_str(hkey, squished_pc, source);
3300 RegCloseKey(hkey);
3302 else if (comp->Action == INSTALLSTATE_ABSENT)
3304 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3305 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3306 else
3307 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3310 /* UI stuff */
3311 uirow = MSI_CreateRecord(3);
3312 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3313 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3314 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3315 msi_ui_actiondata( package, szProcessComponents, uirow );
3316 msiobj_release( &uirow->hdr );
3318 return ERROR_SUCCESS;
3321 typedef struct {
3322 CLSID clsid;
3323 LPWSTR source;
3325 LPWSTR path;
3326 ITypeLib *ptLib;
3327 } typelib_struct;
3329 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3330 LPWSTR lpszName, LONG_PTR lParam)
3332 TLIBATTR *attr;
3333 typelib_struct *tl_struct = (typelib_struct*) lParam;
3334 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3335 int sz;
3336 HRESULT res;
3338 if (!IS_INTRESOURCE(lpszName))
3340 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3341 return TRUE;
3344 sz = strlenW(tl_struct->source)+4;
3345 sz *= sizeof(WCHAR);
3347 if ((INT_PTR)lpszName == 1)
3348 tl_struct->path = strdupW(tl_struct->source);
3349 else
3351 tl_struct->path = msi_alloc(sz);
3352 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3355 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3356 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3357 if (FAILED(res))
3359 msi_free(tl_struct->path);
3360 tl_struct->path = NULL;
3362 return TRUE;
3365 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3366 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3368 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3369 return FALSE;
3372 msi_free(tl_struct->path);
3373 tl_struct->path = NULL;
3375 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3376 ITypeLib_Release(tl_struct->ptLib);
3378 return TRUE;
3381 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3383 MSIPACKAGE* package = param;
3384 LPCWSTR component;
3385 MSICOMPONENT *comp;
3386 MSIFILE *file;
3387 typelib_struct tl_struct;
3388 ITypeLib *tlib;
3389 HMODULE module;
3390 HRESULT hr;
3392 component = MSI_RecordGetString(row,3);
3393 comp = msi_get_loaded_component(package,component);
3394 if (!comp)
3395 return ERROR_SUCCESS;
3397 if (!comp->Enabled)
3399 TRACE("component is disabled\n");
3400 return ERROR_SUCCESS;
3403 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3405 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3406 comp->Action = comp->Installed;
3407 return ERROR_SUCCESS;
3409 comp->Action = INSTALLSTATE_LOCAL;
3411 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3413 TRACE("component has no key path\n");
3414 return ERROR_SUCCESS;
3416 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3418 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3419 if (module)
3421 LPCWSTR guid;
3422 guid = MSI_RecordGetString(row,1);
3423 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3424 tl_struct.source = strdupW( file->TargetPath );
3425 tl_struct.path = NULL;
3427 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3428 (LONG_PTR)&tl_struct);
3430 if (tl_struct.path)
3432 LPCWSTR helpid, help_path = NULL;
3433 HRESULT res;
3435 helpid = MSI_RecordGetString(row,6);
3437 if (helpid) help_path = msi_get_target_folder( package, helpid );
3438 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3440 if (FAILED(res))
3441 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3442 else
3443 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3445 ITypeLib_Release(tl_struct.ptLib);
3446 msi_free(tl_struct.path);
3448 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3450 FreeLibrary(module);
3451 msi_free(tl_struct.source);
3453 else
3455 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3456 if (FAILED(hr))
3458 ERR("Failed to load type library: %08x\n", hr);
3459 return ERROR_INSTALL_FAILURE;
3462 ITypeLib_Release(tlib);
3465 return ERROR_SUCCESS;
3468 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3471 * OK this is a bit confusing.. I am given a _Component key and I believe
3472 * that the file that is being registered as a type library is the "key file
3473 * of that component" which I interpret to mean "The file in the KeyPath of
3474 * that component".
3476 UINT rc;
3477 MSIQUERY * view;
3478 static const WCHAR Query[] =
3479 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3480 '`','T','y','p','e','L','i','b','`',0};
3482 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3483 if (rc != ERROR_SUCCESS)
3484 return ERROR_SUCCESS;
3486 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3487 msiobj_release(&view->hdr);
3488 return rc;
3491 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3493 MSIPACKAGE *package = param;
3494 LPCWSTR component, guid;
3495 MSICOMPONENT *comp;
3496 GUID libid;
3497 UINT version;
3498 LCID language;
3499 SYSKIND syskind;
3500 HRESULT hr;
3502 component = MSI_RecordGetString( row, 3 );
3503 comp = msi_get_loaded_component( package, component );
3504 if (!comp)
3505 return ERROR_SUCCESS;
3507 if (!comp->Enabled)
3509 TRACE("component is disabled\n");
3510 return ERROR_SUCCESS;
3513 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3515 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3516 comp->Action = comp->Installed;
3517 return ERROR_SUCCESS;
3519 comp->Action = INSTALLSTATE_ABSENT;
3521 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3523 guid = MSI_RecordGetString( row, 1 );
3524 CLSIDFromString( (LPCWSTR)guid, &libid );
3525 version = MSI_RecordGetInteger( row, 4 );
3526 language = MSI_RecordGetInteger( row, 2 );
3528 #ifdef _WIN64
3529 syskind = SYS_WIN64;
3530 #else
3531 syskind = SYS_WIN32;
3532 #endif
3534 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3535 if (FAILED(hr))
3537 WARN("Failed to unregister typelib: %08x\n", hr);
3540 return ERROR_SUCCESS;
3543 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3545 UINT rc;
3546 MSIQUERY *view;
3547 static const WCHAR query[] =
3548 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3549 '`','T','y','p','e','L','i','b','`',0};
3551 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3552 if (rc != ERROR_SUCCESS)
3553 return ERROR_SUCCESS;
3555 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3556 msiobj_release( &view->hdr );
3557 return rc;
3560 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3562 static const WCHAR szlnk[] = {'.','l','n','k',0};
3563 LPCWSTR directory, extension, link_folder;
3564 LPWSTR link_file, filename;
3566 directory = MSI_RecordGetString( row, 2 );
3567 link_folder = msi_get_target_folder( package, directory );
3569 /* may be needed because of a bug somewhere else */
3570 msi_create_full_path( link_folder );
3572 filename = msi_dup_record_field( row, 3 );
3573 msi_reduce_to_long_filename( filename );
3575 extension = strchrW( filename, '.' );
3576 if (!extension || strcmpiW( extension, szlnk ))
3578 int len = strlenW( filename );
3579 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3580 memcpy( filename + len, szlnk, sizeof(szlnk) );
3582 link_file = msi_build_directory_name( 2, link_folder, filename );
3583 msi_free( filename );
3585 return link_file;
3588 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3590 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3591 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3592 WCHAR *folder, *dest, *path;
3594 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3595 folder = msi_dup_property( package->db, szWindowsFolder );
3596 else
3598 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3599 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3600 msi_free( appdata );
3602 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3603 msi_create_full_path( dest );
3604 path = msi_build_directory_name( 2, dest, icon_name );
3605 msi_free( folder );
3606 msi_free( dest );
3607 return path;
3610 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3612 MSIPACKAGE *package = param;
3613 LPWSTR link_file, deformated, path;
3614 LPCWSTR component, target;
3615 MSICOMPONENT *comp;
3616 IShellLinkW *sl = NULL;
3617 IPersistFile *pf = NULL;
3618 HRESULT res;
3620 component = MSI_RecordGetString(row, 4);
3621 comp = msi_get_loaded_component(package, component);
3622 if (!comp)
3623 return ERROR_SUCCESS;
3625 if (!comp->Enabled)
3627 TRACE("component is disabled\n");
3628 return ERROR_SUCCESS;
3631 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3633 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3634 comp->Action = comp->Installed;
3635 return ERROR_SUCCESS;
3637 comp->Action = INSTALLSTATE_LOCAL;
3639 msi_ui_actiondata( package, szCreateShortcuts, row );
3641 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3642 &IID_IShellLinkW, (LPVOID *) &sl );
3644 if (FAILED( res ))
3646 ERR("CLSID_ShellLink not available\n");
3647 goto err;
3650 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3651 if (FAILED( res ))
3653 ERR("QueryInterface(IID_IPersistFile) failed\n");
3654 goto err;
3657 target = MSI_RecordGetString(row, 5);
3658 if (strchrW(target, '['))
3660 deformat_string(package, target, &deformated);
3661 IShellLinkW_SetPath(sl,deformated);
3662 msi_free(deformated);
3664 else
3666 FIXME("poorly handled shortcut format, advertised shortcut\n");
3667 IShellLinkW_SetPath(sl,comp->FullKeypath);
3670 if (!MSI_RecordIsNull(row,6))
3672 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3673 deformat_string(package, arguments, &deformated);
3674 IShellLinkW_SetArguments(sl,deformated);
3675 msi_free(deformated);
3678 if (!MSI_RecordIsNull(row,7))
3680 LPCWSTR description = MSI_RecordGetString(row, 7);
3681 IShellLinkW_SetDescription(sl, description);
3684 if (!MSI_RecordIsNull(row,8))
3685 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3687 if (!MSI_RecordIsNull(row,9))
3689 INT index;
3690 LPCWSTR icon = MSI_RecordGetString(row, 9);
3692 path = msi_build_icon_path(package, icon);
3693 index = MSI_RecordGetInteger(row,10);
3695 /* no value means 0 */
3696 if (index == MSI_NULL_INTEGER)
3697 index = 0;
3699 IShellLinkW_SetIconLocation(sl, path, index);
3700 msi_free(path);
3703 if (!MSI_RecordIsNull(row,11))
3704 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3706 if (!MSI_RecordIsNull(row,12))
3708 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3709 full_path = msi_get_target_folder( package, wkdir );
3710 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3712 link_file = get_link_file(package, row);
3714 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3715 IPersistFile_Save(pf, link_file, FALSE);
3716 msi_free(link_file);
3718 err:
3719 if (pf)
3720 IPersistFile_Release( pf );
3721 if (sl)
3722 IShellLinkW_Release( sl );
3724 return ERROR_SUCCESS;
3727 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3729 UINT rc;
3730 HRESULT res;
3731 MSIQUERY * view;
3732 static const WCHAR Query[] =
3733 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3734 '`','S','h','o','r','t','c','u','t','`',0};
3736 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3737 if (rc != ERROR_SUCCESS)
3738 return ERROR_SUCCESS;
3740 res = CoInitialize( NULL );
3742 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3743 msiobj_release(&view->hdr);
3745 if (SUCCEEDED(res))
3746 CoUninitialize();
3748 return rc;
3751 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3753 MSIPACKAGE *package = param;
3754 LPWSTR link_file;
3755 LPCWSTR component;
3756 MSICOMPONENT *comp;
3758 component = MSI_RecordGetString( row, 4 );
3759 comp = msi_get_loaded_component( package, component );
3760 if (!comp)
3761 return ERROR_SUCCESS;
3763 if (!comp->Enabled)
3765 TRACE("component is disabled\n");
3766 return ERROR_SUCCESS;
3769 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3771 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3772 comp->Action = comp->Installed;
3773 return ERROR_SUCCESS;
3775 comp->Action = INSTALLSTATE_ABSENT;
3777 msi_ui_actiondata( package, szRemoveShortcuts, row );
3779 link_file = get_link_file( package, row );
3781 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3782 if (!DeleteFileW( link_file ))
3784 WARN("Failed to remove shortcut file %u\n", GetLastError());
3786 msi_free( link_file );
3788 return ERROR_SUCCESS;
3791 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3793 UINT rc;
3794 MSIQUERY *view;
3795 static const WCHAR query[] =
3796 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3797 '`','S','h','o','r','t','c','u','t','`',0};
3799 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3800 if (rc != ERROR_SUCCESS)
3801 return ERROR_SUCCESS;
3803 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3804 msiobj_release( &view->hdr );
3806 return rc;
3809 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3811 MSIPACKAGE* package = param;
3812 HANDLE the_file;
3813 LPWSTR FilePath;
3814 LPCWSTR FileName;
3815 CHAR buffer[1024];
3816 DWORD sz;
3817 UINT rc;
3819 FileName = MSI_RecordGetString(row,1);
3820 if (!FileName)
3822 ERR("Unable to get FileName\n");
3823 return ERROR_SUCCESS;
3826 FilePath = msi_build_icon_path(package, FileName);
3828 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3830 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3831 FILE_ATTRIBUTE_NORMAL, NULL);
3833 if (the_file == INVALID_HANDLE_VALUE)
3835 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3836 msi_free(FilePath);
3837 return ERROR_SUCCESS;
3842 DWORD write;
3843 sz = 1024;
3844 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3845 if (rc != ERROR_SUCCESS)
3847 ERR("Failed to get stream\n");
3848 CloseHandle(the_file);
3849 DeleteFileW(FilePath);
3850 break;
3852 WriteFile(the_file,buffer,sz,&write,NULL);
3853 } while (sz == 1024);
3855 msi_free(FilePath);
3856 CloseHandle(the_file);
3858 return ERROR_SUCCESS;
3861 static UINT msi_publish_icons(MSIPACKAGE *package)
3863 UINT r;
3864 MSIQUERY *view;
3866 static const WCHAR query[]= {
3867 'S','E','L','E','C','T',' ','*',' ',
3868 'F','R','O','M',' ','`','I','c','o','n','`',0};
3870 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3871 if (r == ERROR_SUCCESS)
3873 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3874 msiobj_release(&view->hdr);
3877 return ERROR_SUCCESS;
3880 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3882 UINT r;
3883 HKEY source;
3884 LPWSTR buffer;
3885 MSIMEDIADISK *disk;
3886 MSISOURCELISTINFO *info;
3888 r = RegCreateKeyW(hkey, szSourceList, &source);
3889 if (r != ERROR_SUCCESS)
3890 return r;
3892 RegCloseKey(source);
3894 buffer = strrchrW(package->PackagePath, '\\') + 1;
3895 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3896 package->Context, MSICODE_PRODUCT,
3897 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3898 if (r != ERROR_SUCCESS)
3899 return r;
3901 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3902 package->Context, MSICODE_PRODUCT,
3903 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3904 if (r != ERROR_SUCCESS)
3905 return r;
3907 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3908 package->Context, MSICODE_PRODUCT,
3909 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3910 if (r != ERROR_SUCCESS)
3911 return r;
3913 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3915 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3916 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3917 info->options, info->value);
3918 else
3919 MsiSourceListSetInfoW(package->ProductCode, NULL,
3920 info->context, info->options,
3921 info->property, info->value);
3924 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3926 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3927 disk->context, disk->options,
3928 disk->disk_id, disk->volume_label, disk->disk_prompt);
3931 return ERROR_SUCCESS;
3934 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3936 MSIHANDLE hdb, suminfo;
3937 WCHAR guids[MAX_PATH];
3938 WCHAR packcode[SQUISH_GUID_SIZE];
3939 LPWSTR buffer;
3940 LPWSTR ptr;
3941 DWORD langid;
3942 DWORD size;
3943 UINT r;
3945 static const WCHAR szARPProductIcon[] =
3946 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3947 static const WCHAR szAssignment[] =
3948 {'A','s','s','i','g','n','m','e','n','t',0};
3949 static const WCHAR szAdvertiseFlags[] =
3950 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3951 static const WCHAR szClients[] =
3952 {'C','l','i','e','n','t','s',0};
3953 static const WCHAR szColon[] = {':',0};
3955 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3956 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3957 msi_free(buffer);
3959 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3960 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3962 /* FIXME */
3963 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3965 buffer = msi_dup_property(package->db, szARPProductIcon);
3966 if (buffer)
3968 LPWSTR path = msi_build_icon_path(package, buffer);
3969 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3970 msi_free(path);
3971 msi_free(buffer);
3974 buffer = msi_dup_property(package->db, szProductVersion);
3975 if (buffer)
3977 DWORD verdword = msi_version_str_to_dword(buffer);
3978 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3979 msi_free(buffer);
3982 msi_reg_set_val_dword(hkey, szAssignment, 0);
3983 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3984 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3985 msi_reg_set_val_str(hkey, szClients, szColon);
3987 hdb = alloc_msihandle(&package->db->hdr);
3988 if (!hdb)
3989 return ERROR_NOT_ENOUGH_MEMORY;
3991 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3992 MsiCloseHandle(hdb);
3993 if (r != ERROR_SUCCESS)
3994 goto done;
3996 size = MAX_PATH;
3997 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3998 NULL, guids, &size);
3999 if (r != ERROR_SUCCESS)
4000 goto done;
4002 ptr = strchrW(guids, ';');
4003 if (ptr) *ptr = 0;
4004 squash_guid(guids, packcode);
4005 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4007 done:
4008 MsiCloseHandle(suminfo);
4009 return ERROR_SUCCESS;
4012 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4014 UINT r;
4015 HKEY hkey;
4016 LPWSTR upgrade;
4017 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4019 upgrade = msi_dup_property(package->db, szUpgradeCode);
4020 if (!upgrade)
4021 return ERROR_SUCCESS;
4023 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4024 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4025 else
4026 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4028 if (r != ERROR_SUCCESS)
4030 WARN("failed to open upgrade code key\n");
4031 msi_free(upgrade);
4032 return ERROR_SUCCESS;
4034 squash_guid(package->ProductCode, squashed_pc);
4035 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4036 RegCloseKey(hkey);
4037 msi_free(upgrade);
4038 return ERROR_SUCCESS;
4041 static BOOL msi_check_publish(MSIPACKAGE *package)
4043 MSIFEATURE *feature;
4045 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4047 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4048 return TRUE;
4051 return FALSE;
4054 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4056 MSIFEATURE *feature;
4058 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4060 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4061 return FALSE;
4064 return TRUE;
4067 static UINT msi_publish_patches( MSIPACKAGE *package )
4069 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4070 WCHAR patch_squashed[GUID_SIZE];
4071 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4072 LONG res;
4073 MSIPATCHINFO *patch;
4074 UINT r;
4075 WCHAR *p, *all_patches = NULL;
4076 DWORD len = 0;
4078 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4079 if (r != ERROR_SUCCESS)
4080 return ERROR_FUNCTION_FAILED;
4082 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4083 if (res != ERROR_SUCCESS)
4085 r = ERROR_FUNCTION_FAILED;
4086 goto done;
4089 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4090 if (r != ERROR_SUCCESS)
4091 goto done;
4093 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4095 squash_guid( patch->patchcode, patch_squashed );
4096 len += strlenW( patch_squashed ) + 1;
4099 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4100 if (!all_patches)
4101 goto done;
4103 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4105 HKEY patch_key;
4107 squash_guid( patch->patchcode, p );
4108 p += strlenW( p ) + 1;
4110 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4111 (const BYTE *)patch->transforms,
4112 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4113 if (res != ERROR_SUCCESS)
4114 goto done;
4116 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4117 if (r != ERROR_SUCCESS)
4118 goto done;
4120 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4121 (const BYTE *)patch->localfile,
4122 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4123 RegCloseKey( patch_key );
4124 if (res != ERROR_SUCCESS)
4125 goto done;
4127 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4128 if (res != ERROR_SUCCESS)
4129 goto done;
4131 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4132 RegCloseKey( patch_key );
4133 if (res != ERROR_SUCCESS)
4134 goto done;
4137 all_patches[len] = 0;
4138 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4139 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4140 if (res != ERROR_SUCCESS)
4141 goto done;
4143 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4144 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4145 if (res != ERROR_SUCCESS)
4146 r = ERROR_FUNCTION_FAILED;
4148 done:
4149 RegCloseKey( product_patches_key );
4150 RegCloseKey( patches_key );
4151 RegCloseKey( product_key );
4152 msi_free( all_patches );
4153 return r;
4156 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4158 UINT rc;
4159 HKEY hukey = NULL, hudkey = NULL;
4160 MSIRECORD *uirow;
4162 if (!list_empty(&package->patches))
4164 rc = msi_publish_patches(package);
4165 if (rc != ERROR_SUCCESS)
4166 goto end;
4169 /* FIXME: also need to publish if the product is in advertise mode */
4170 if (!msi_check_publish(package))
4171 return ERROR_SUCCESS;
4173 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4174 &hukey, TRUE);
4175 if (rc != ERROR_SUCCESS)
4176 goto end;
4178 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4179 NULL, &hudkey, TRUE);
4180 if (rc != ERROR_SUCCESS)
4181 goto end;
4183 rc = msi_publish_upgrade_code(package);
4184 if (rc != ERROR_SUCCESS)
4185 goto end;
4187 rc = msi_publish_product_properties(package, hukey);
4188 if (rc != ERROR_SUCCESS)
4189 goto end;
4191 rc = msi_publish_sourcelist(package, hukey);
4192 if (rc != ERROR_SUCCESS)
4193 goto end;
4195 rc = msi_publish_icons(package);
4197 end:
4198 uirow = MSI_CreateRecord( 1 );
4199 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4200 msi_ui_actiondata( package, szPublishProduct, uirow );
4201 msiobj_release( &uirow->hdr );
4203 RegCloseKey(hukey);
4204 RegCloseKey(hudkey);
4206 return rc;
4209 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4211 WCHAR *filename, *ptr, *folder, *ret;
4212 const WCHAR *dirprop;
4214 filename = msi_dup_record_field( row, 2 );
4215 if (filename && (ptr = strchrW( filename, '|' )))
4216 ptr++;
4217 else
4218 ptr = filename;
4220 dirprop = MSI_RecordGetString( row, 3 );
4221 if (dirprop)
4223 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4224 if (!folder) folder = msi_dup_property( package->db, dirprop );
4226 else
4227 folder = msi_dup_property( package->db, szWindowsFolder );
4229 if (!folder)
4231 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4232 msi_free( filename );
4233 return NULL;
4236 ret = msi_build_directory_name( 2, folder, ptr );
4238 msi_free( filename );
4239 msi_free( folder );
4240 return ret;
4243 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4245 MSIPACKAGE *package = param;
4246 LPCWSTR component, section, key, value, identifier;
4247 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4248 MSIRECORD * uirow;
4249 INT action;
4250 MSICOMPONENT *comp;
4252 component = MSI_RecordGetString(row, 8);
4253 comp = msi_get_loaded_component(package,component);
4254 if (!comp)
4255 return ERROR_SUCCESS;
4257 if (!comp->Enabled)
4259 TRACE("component is disabled\n");
4260 return ERROR_SUCCESS;
4263 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4265 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4266 comp->Action = comp->Installed;
4267 return ERROR_SUCCESS;
4269 comp->Action = INSTALLSTATE_LOCAL;
4271 identifier = MSI_RecordGetString(row,1);
4272 section = MSI_RecordGetString(row,4);
4273 key = MSI_RecordGetString(row,5);
4274 value = MSI_RecordGetString(row,6);
4275 action = MSI_RecordGetInteger(row,7);
4277 deformat_string(package,section,&deformated_section);
4278 deformat_string(package,key,&deformated_key);
4279 deformat_string(package,value,&deformated_value);
4281 fullname = get_ini_file_name(package, row);
4283 if (action == 0)
4285 TRACE("Adding value %s to section %s in %s\n",
4286 debugstr_w(deformated_key), debugstr_w(deformated_section),
4287 debugstr_w(fullname));
4288 WritePrivateProfileStringW(deformated_section, deformated_key,
4289 deformated_value, fullname);
4291 else if (action == 1)
4293 WCHAR returned[10];
4294 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4295 returned, 10, fullname);
4296 if (returned[0] == 0)
4298 TRACE("Adding value %s to section %s in %s\n",
4299 debugstr_w(deformated_key), debugstr_w(deformated_section),
4300 debugstr_w(fullname));
4302 WritePrivateProfileStringW(deformated_section, deformated_key,
4303 deformated_value, fullname);
4306 else if (action == 3)
4307 FIXME("Append to existing section not yet implemented\n");
4309 uirow = MSI_CreateRecord(4);
4310 MSI_RecordSetStringW(uirow,1,identifier);
4311 MSI_RecordSetStringW(uirow,2,deformated_section);
4312 MSI_RecordSetStringW(uirow,3,deformated_key);
4313 MSI_RecordSetStringW(uirow,4,deformated_value);
4314 msi_ui_actiondata( package, szWriteIniValues, uirow );
4315 msiobj_release( &uirow->hdr );
4317 msi_free(fullname);
4318 msi_free(deformated_key);
4319 msi_free(deformated_value);
4320 msi_free(deformated_section);
4321 return ERROR_SUCCESS;
4324 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4326 UINT rc;
4327 MSIQUERY * view;
4328 static const WCHAR ExecSeqQuery[] =
4329 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4330 '`','I','n','i','F','i','l','e','`',0};
4332 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4333 if (rc != ERROR_SUCCESS)
4335 TRACE("no IniFile table\n");
4336 return ERROR_SUCCESS;
4339 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4340 msiobj_release(&view->hdr);
4341 return rc;
4344 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4346 MSIPACKAGE *package = param;
4347 LPCWSTR component, section, key, value, identifier;
4348 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4349 MSICOMPONENT *comp;
4350 MSIRECORD *uirow;
4351 INT action;
4353 component = MSI_RecordGetString( row, 8 );
4354 comp = msi_get_loaded_component( package, component );
4355 if (!comp)
4356 return ERROR_SUCCESS;
4358 if (!comp->Enabled)
4360 TRACE("component is disabled\n");
4361 return ERROR_SUCCESS;
4364 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4366 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4367 comp->Action = comp->Installed;
4368 return ERROR_SUCCESS;
4370 comp->Action = INSTALLSTATE_ABSENT;
4372 identifier = MSI_RecordGetString( row, 1 );
4373 section = MSI_RecordGetString( row, 4 );
4374 key = MSI_RecordGetString( row, 5 );
4375 value = MSI_RecordGetString( row, 6 );
4376 action = MSI_RecordGetInteger( row, 7 );
4378 deformat_string( package, section, &deformated_section );
4379 deformat_string( package, key, &deformated_key );
4380 deformat_string( package, value, &deformated_value );
4382 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4384 filename = get_ini_file_name( package, row );
4386 TRACE("Removing key %s from section %s in %s\n",
4387 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4389 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4391 WARN("Unable to remove key %u\n", GetLastError());
4393 msi_free( filename );
4395 else
4396 FIXME("Unsupported action %d\n", action);
4399 uirow = MSI_CreateRecord( 4 );
4400 MSI_RecordSetStringW( uirow, 1, identifier );
4401 MSI_RecordSetStringW( uirow, 2, deformated_section );
4402 MSI_RecordSetStringW( uirow, 3, deformated_key );
4403 MSI_RecordSetStringW( uirow, 4, deformated_value );
4404 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4405 msiobj_release( &uirow->hdr );
4407 msi_free( deformated_key );
4408 msi_free( deformated_value );
4409 msi_free( deformated_section );
4410 return ERROR_SUCCESS;
4413 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4415 MSIPACKAGE *package = param;
4416 LPCWSTR component, section, key, value, identifier;
4417 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4418 MSICOMPONENT *comp;
4419 MSIRECORD *uirow;
4420 INT action;
4422 component = MSI_RecordGetString( row, 8 );
4423 comp = msi_get_loaded_component( package, component );
4424 if (!comp)
4425 return ERROR_SUCCESS;
4427 if (!comp->Enabled)
4429 TRACE("component is disabled\n");
4430 return ERROR_SUCCESS;
4433 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4435 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4436 comp->Action = comp->Installed;
4437 return ERROR_SUCCESS;
4439 comp->Action = INSTALLSTATE_LOCAL;
4441 identifier = MSI_RecordGetString( row, 1 );
4442 section = MSI_RecordGetString( row, 4 );
4443 key = MSI_RecordGetString( row, 5 );
4444 value = MSI_RecordGetString( row, 6 );
4445 action = MSI_RecordGetInteger( row, 7 );
4447 deformat_string( package, section, &deformated_section );
4448 deformat_string( package, key, &deformated_key );
4449 deformat_string( package, value, &deformated_value );
4451 if (action == msidbIniFileActionRemoveLine)
4453 filename = get_ini_file_name( package, row );
4455 TRACE("Removing key %s from section %s in %s\n",
4456 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4458 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4460 WARN("Unable to remove key %u\n", GetLastError());
4462 msi_free( filename );
4464 else
4465 FIXME("Unsupported action %d\n", action);
4467 uirow = MSI_CreateRecord( 4 );
4468 MSI_RecordSetStringW( uirow, 1, identifier );
4469 MSI_RecordSetStringW( uirow, 2, deformated_section );
4470 MSI_RecordSetStringW( uirow, 3, deformated_key );
4471 MSI_RecordSetStringW( uirow, 4, deformated_value );
4472 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4473 msiobj_release( &uirow->hdr );
4475 msi_free( deformated_key );
4476 msi_free( deformated_value );
4477 msi_free( deformated_section );
4478 return ERROR_SUCCESS;
4481 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4483 UINT rc;
4484 MSIQUERY *view;
4485 static const WCHAR query[] =
4486 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4487 '`','I','n','i','F','i','l','e','`',0};
4488 static const WCHAR remove_query[] =
4489 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4490 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4492 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4493 if (rc == ERROR_SUCCESS)
4495 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4496 msiobj_release( &view->hdr );
4497 if (rc != ERROR_SUCCESS)
4498 return rc;
4501 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4502 if (rc == ERROR_SUCCESS)
4504 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4505 msiobj_release( &view->hdr );
4506 if (rc != ERROR_SUCCESS)
4507 return rc;
4510 return ERROR_SUCCESS;
4513 static void register_dll( const WCHAR *dll, BOOL unregister )
4515 HMODULE hmod;
4517 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4518 if (hmod)
4520 HRESULT (WINAPI *func_ptr)( void );
4521 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4523 func_ptr = (void *)GetProcAddress( hmod, func );
4524 if (func_ptr)
4526 HRESULT hr = func_ptr();
4527 if (FAILED( hr ))
4528 WARN("failed to register dll 0x%08x\n", hr);
4530 else
4531 WARN("entry point %s not found\n", func);
4532 FreeLibrary( hmod );
4533 return;
4535 WARN("failed to load library %u\n", GetLastError());
4538 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4540 MSIPACKAGE *package = param;
4541 LPCWSTR filename;
4542 MSIFILE *file;
4543 MSIRECORD *uirow;
4545 filename = MSI_RecordGetString(row,1);
4546 file = msi_get_loaded_file( package, filename );
4548 if (!file)
4550 ERR("Unable to find file id %s\n",debugstr_w(filename));
4551 return ERROR_SUCCESS;
4554 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4556 register_dll( file->TargetPath, FALSE );
4558 uirow = MSI_CreateRecord( 2 );
4559 MSI_RecordSetStringW( uirow, 1, filename );
4560 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4561 msi_ui_actiondata( package, szSelfRegModules, uirow );
4562 msiobj_release( &uirow->hdr );
4564 return ERROR_SUCCESS;
4567 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4569 UINT rc;
4570 MSIQUERY * view;
4571 static const WCHAR ExecSeqQuery[] =
4572 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4573 '`','S','e','l','f','R','e','g','`',0};
4575 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4576 if (rc != ERROR_SUCCESS)
4578 TRACE("no SelfReg table\n");
4579 return ERROR_SUCCESS;
4582 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4583 msiobj_release(&view->hdr);
4585 return ERROR_SUCCESS;
4588 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4590 MSIPACKAGE *package = param;
4591 LPCWSTR filename;
4592 MSIFILE *file;
4593 MSIRECORD *uirow;
4595 filename = MSI_RecordGetString( row, 1 );
4596 file = msi_get_loaded_file( package, filename );
4598 if (!file)
4600 ERR("Unable to find file id %s\n", debugstr_w(filename));
4601 return ERROR_SUCCESS;
4604 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4606 register_dll( file->TargetPath, TRUE );
4608 uirow = MSI_CreateRecord( 2 );
4609 MSI_RecordSetStringW( uirow, 1, filename );
4610 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4611 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4612 msiobj_release( &uirow->hdr );
4614 return ERROR_SUCCESS;
4617 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4619 UINT rc;
4620 MSIQUERY *view;
4621 static const WCHAR query[] =
4622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4623 '`','S','e','l','f','R','e','g','`',0};
4625 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4626 if (rc != ERROR_SUCCESS)
4628 TRACE("no SelfReg table\n");
4629 return ERROR_SUCCESS;
4632 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4633 msiobj_release( &view->hdr );
4635 return ERROR_SUCCESS;
4638 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4640 MSIFEATURE *feature;
4641 UINT rc;
4642 HKEY hkey = NULL, userdata = NULL;
4644 if (!msi_check_publish(package))
4645 return ERROR_SUCCESS;
4647 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4648 &hkey, TRUE);
4649 if (rc != ERROR_SUCCESS)
4650 goto end;
4652 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4653 &userdata, TRUE);
4654 if (rc != ERROR_SUCCESS)
4655 goto end;
4657 /* here the guids are base 85 encoded */
4658 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4660 ComponentList *cl;
4661 LPWSTR data = NULL;
4662 GUID clsid;
4663 INT size;
4664 BOOL absent = FALSE;
4665 MSIRECORD *uirow;
4667 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4668 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4669 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4671 size = 1;
4672 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4674 size += 21;
4676 if (feature->Feature_Parent)
4677 size += strlenW( feature->Feature_Parent )+2;
4679 data = msi_alloc(size * sizeof(WCHAR));
4681 data[0] = 0;
4682 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4684 MSICOMPONENT* component = cl->component;
4685 WCHAR buf[21];
4687 buf[0] = 0;
4688 if (component->ComponentId)
4690 TRACE("From %s\n",debugstr_w(component->ComponentId));
4691 CLSIDFromString(component->ComponentId, &clsid);
4692 encode_base85_guid(&clsid,buf);
4693 TRACE("to %s\n",debugstr_w(buf));
4694 strcatW(data,buf);
4698 if (feature->Feature_Parent)
4700 static const WCHAR sep[] = {'\2',0};
4701 strcatW(data,sep);
4702 strcatW(data,feature->Feature_Parent);
4705 msi_reg_set_val_str( userdata, feature->Feature, data );
4706 msi_free(data);
4708 size = 0;
4709 if (feature->Feature_Parent)
4710 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4711 if (!absent)
4713 size += sizeof(WCHAR);
4714 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4715 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4717 else
4719 size += 2*sizeof(WCHAR);
4720 data = msi_alloc(size);
4721 data[0] = 0x6;
4722 data[1] = 0;
4723 if (feature->Feature_Parent)
4724 strcpyW( &data[1], feature->Feature_Parent );
4725 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4726 (LPBYTE)data,size);
4727 msi_free(data);
4730 /* the UI chunk */
4731 uirow = MSI_CreateRecord( 1 );
4732 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4733 msi_ui_actiondata( package, szPublishFeatures, uirow );
4734 msiobj_release( &uirow->hdr );
4735 /* FIXME: call msi_ui_progress? */
4738 end:
4739 RegCloseKey(hkey);
4740 RegCloseKey(userdata);
4741 return rc;
4744 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4746 UINT r;
4747 HKEY hkey;
4748 MSIRECORD *uirow;
4750 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4752 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4753 &hkey, FALSE);
4754 if (r == ERROR_SUCCESS)
4756 RegDeleteValueW(hkey, feature->Feature);
4757 RegCloseKey(hkey);
4760 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4761 &hkey, FALSE);
4762 if (r == ERROR_SUCCESS)
4764 RegDeleteValueW(hkey, feature->Feature);
4765 RegCloseKey(hkey);
4768 uirow = MSI_CreateRecord( 1 );
4769 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4770 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4771 msiobj_release( &uirow->hdr );
4773 return ERROR_SUCCESS;
4776 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4778 MSIFEATURE *feature;
4780 if (!msi_check_unpublish(package))
4781 return ERROR_SUCCESS;
4783 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4785 msi_unpublish_feature(package, feature);
4788 return ERROR_SUCCESS;
4791 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4793 SYSTEMTIME systime;
4794 DWORD size, langid;
4795 WCHAR date[9], *val, *buffer;
4796 const WCHAR *prop, *key;
4798 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4799 static const WCHAR modpath_fmt[] =
4800 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4801 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4802 static const WCHAR szModifyPath[] =
4803 {'M','o','d','i','f','y','P','a','t','h',0};
4804 static const WCHAR szUninstallString[] =
4805 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4806 static const WCHAR szEstimatedSize[] =
4807 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4808 static const WCHAR szDisplayVersion[] =
4809 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4810 static const WCHAR szInstallSource[] =
4811 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4812 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4813 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4814 static const WCHAR szAuthorizedCDFPrefix[] =
4815 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4816 static const WCHAR szARPCONTACT[] =
4817 {'A','R','P','C','O','N','T','A','C','T',0};
4818 static const WCHAR szContact[] =
4819 {'C','o','n','t','a','c','t',0};
4820 static const WCHAR szARPCOMMENTS[] =
4821 {'A','R','P','C','O','M','M','E','N','T','S',0};
4822 static const WCHAR szComments[] =
4823 {'C','o','m','m','e','n','t','s',0};
4824 static const WCHAR szProductName[] =
4825 {'P','r','o','d','u','c','t','N','a','m','e',0};
4826 static const WCHAR szDisplayName[] =
4827 {'D','i','s','p','l','a','y','N','a','m','e',0};
4828 static const WCHAR szARPHELPLINK[] =
4829 {'A','R','P','H','E','L','P','L','I','N','K',0};
4830 static const WCHAR szHelpLink[] =
4831 {'H','e','l','p','L','i','n','k',0};
4832 static const WCHAR szARPHELPTELEPHONE[] =
4833 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4834 static const WCHAR szHelpTelephone[] =
4835 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4836 static const WCHAR szARPINSTALLLOCATION[] =
4837 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4838 static const WCHAR szInstallLocation[] =
4839 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4840 static const WCHAR szManufacturer[] =
4841 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4842 static const WCHAR szPublisher[] =
4843 {'P','u','b','l','i','s','h','e','r',0};
4844 static const WCHAR szARPREADME[] =
4845 {'A','R','P','R','E','A','D','M','E',0};
4846 static const WCHAR szReadme[] =
4847 {'R','e','a','d','M','e',0};
4848 static const WCHAR szARPSIZE[] =
4849 {'A','R','P','S','I','Z','E',0};
4850 static const WCHAR szSize[] =
4851 {'S','i','z','e',0};
4852 static const WCHAR szARPURLINFOABOUT[] =
4853 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4854 static const WCHAR szURLInfoAbout[] =
4855 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4856 static const WCHAR szARPURLUPDATEINFO[] =
4857 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4858 static const WCHAR szURLUpdateInfo[] =
4859 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4861 static const WCHAR *propval[] = {
4862 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4863 szARPCONTACT, szContact,
4864 szARPCOMMENTS, szComments,
4865 szProductName, szDisplayName,
4866 szARPHELPLINK, szHelpLink,
4867 szARPHELPTELEPHONE, szHelpTelephone,
4868 szARPINSTALLLOCATION, szInstallLocation,
4869 szSourceDir, szInstallSource,
4870 szManufacturer, szPublisher,
4871 szARPREADME, szReadme,
4872 szARPSIZE, szSize,
4873 szARPURLINFOABOUT, szURLInfoAbout,
4874 szARPURLUPDATEINFO, szURLUpdateInfo,
4875 NULL
4877 const WCHAR **p = propval;
4879 while (*p)
4881 prop = *p++;
4882 key = *p++;
4883 val = msi_dup_property(package->db, prop);
4884 msi_reg_set_val_str(hkey, key, val);
4885 msi_free(val);
4888 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4890 size = deformat_string(package, modpath_fmt, &buffer);
4891 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4892 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4893 msi_free(buffer);
4895 /* FIXME: Write real Estimated Size when we have it */
4896 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4898 GetLocalTime(&systime);
4899 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4900 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4902 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4903 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4905 buffer = msi_dup_property(package->db, szProductVersion);
4906 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4907 if (buffer)
4909 DWORD verdword = msi_version_str_to_dword(buffer);
4911 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4912 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4913 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4914 msi_free(buffer);
4917 return ERROR_SUCCESS;
4920 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4922 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4923 MSIRECORD *uirow;
4924 LPWSTR upgrade_code;
4925 HKEY hkey, props, upgrade_key;
4926 UINT rc;
4928 /* FIXME: also need to publish if the product is in advertise mode */
4929 if (!msi_check_publish(package))
4930 return ERROR_SUCCESS;
4932 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4933 if (rc != ERROR_SUCCESS)
4934 return rc;
4936 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4937 NULL, &props, TRUE);
4938 if (rc != ERROR_SUCCESS)
4939 goto done;
4941 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4942 msi_free( package->db->localfile );
4943 package->db->localfile = NULL;
4945 rc = msi_publish_install_properties(package, hkey);
4946 if (rc != ERROR_SUCCESS)
4947 goto done;
4949 rc = msi_publish_install_properties(package, props);
4950 if (rc != ERROR_SUCCESS)
4951 goto done;
4953 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4954 if (upgrade_code)
4956 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4957 if (rc == ERROR_SUCCESS)
4959 squash_guid( package->ProductCode, squashed_pc );
4960 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4961 RegCloseKey( upgrade_key );
4963 msi_free( upgrade_code );
4966 done:
4967 uirow = MSI_CreateRecord( 1 );
4968 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4969 msi_ui_actiondata( package, szRegisterProduct, uirow );
4970 msiobj_release( &uirow->hdr );
4972 RegCloseKey(hkey);
4973 return ERROR_SUCCESS;
4976 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4978 return execute_script(package,INSTALL_SCRIPT);
4981 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4983 WCHAR *upgrade, **features;
4984 BOOL full_uninstall = TRUE;
4985 MSIFEATURE *feature;
4986 MSIPATCHINFO *patch;
4988 static const WCHAR szUpgradeCode[] =
4989 {'U','p','g','r','a','d','e','C','o','d','e',0};
4991 features = msi_split_string(remove, ',');
4992 if (!features)
4994 ERR("REMOVE feature list is empty!\n");
4995 return ERROR_FUNCTION_FAILED;
4998 if (!strcmpW( features[0], szAll ))
4999 full_uninstall = TRUE;
5000 else
5002 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5004 if (feature->Action != INSTALLSTATE_ABSENT)
5005 full_uninstall = FALSE;
5008 msi_free(features);
5010 if (!full_uninstall)
5011 return ERROR_SUCCESS;
5013 MSIREG_DeleteProductKey(package->ProductCode);
5014 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5015 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5017 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5018 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5019 MSIREG_DeleteUserProductKey(package->ProductCode);
5020 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5022 upgrade = msi_dup_property(package->db, szUpgradeCode);
5023 if (upgrade)
5025 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5026 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5027 msi_free(upgrade);
5030 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5032 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5035 return ERROR_SUCCESS;
5038 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5040 UINT rc;
5041 WCHAR *remove;
5043 /* turn off scheduling */
5044 package->script->CurrentlyScripting= FALSE;
5046 /* first do the same as an InstallExecute */
5047 rc = ACTION_InstallExecute(package);
5048 if (rc != ERROR_SUCCESS)
5049 return rc;
5051 /* then handle Commit Actions */
5052 rc = execute_script(package,COMMIT_SCRIPT);
5053 if (rc != ERROR_SUCCESS)
5054 return rc;
5056 remove = msi_dup_property(package->db, szRemove);
5057 if (remove)
5058 rc = msi_unpublish_product(package, remove);
5060 msi_free(remove);
5061 return rc;
5064 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5066 static const WCHAR RunOnce[] = {
5067 'S','o','f','t','w','a','r','e','\\',
5068 'M','i','c','r','o','s','o','f','t','\\',
5069 'W','i','n','d','o','w','s','\\',
5070 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5071 'R','u','n','O','n','c','e',0};
5072 static const WCHAR InstallRunOnce[] = {
5073 'S','o','f','t','w','a','r','e','\\',
5074 'M','i','c','r','o','s','o','f','t','\\',
5075 'W','i','n','d','o','w','s','\\',
5076 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5077 'I','n','s','t','a','l','l','e','r','\\',
5078 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5080 static const WCHAR msiexec_fmt[] = {
5081 '%','s',
5082 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5083 '\"','%','s','\"',0};
5084 static const WCHAR install_fmt[] = {
5085 '/','I',' ','\"','%','s','\"',' ',
5086 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5087 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5088 WCHAR buffer[256], sysdir[MAX_PATH];
5089 HKEY hkey;
5090 WCHAR squished_pc[100];
5092 squash_guid(package->ProductCode,squished_pc);
5094 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5095 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5096 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5097 squished_pc);
5099 msi_reg_set_val_str( hkey, squished_pc, buffer );
5100 RegCloseKey(hkey);
5102 TRACE("Reboot command %s\n",debugstr_w(buffer));
5104 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5105 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5107 msi_reg_set_val_str( hkey, squished_pc, buffer );
5108 RegCloseKey(hkey);
5110 return ERROR_INSTALL_SUSPEND;
5113 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5115 static const WCHAR query[] =
5116 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5117 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5118 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5119 MSIRECORD *rec, *row;
5120 DWORD i, size = 0;
5121 va_list va;
5122 const WCHAR *str;
5123 WCHAR *data;
5125 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5127 rec = MSI_CreateRecord( count + 2 );
5128 str = MSI_RecordGetString( row, 1 );
5129 MSI_RecordSetStringW( rec, 0, str );
5130 msiobj_release( &row->hdr );
5131 MSI_RecordSetInteger( rec, 1, error );
5133 va_start( va, count );
5134 for (i = 0; i < count; i++)
5136 str = va_arg( va, const WCHAR *);
5137 MSI_RecordSetStringW( rec, i + 2, str );
5139 va_end( va );
5141 MSI_FormatRecordW( package, rec, NULL, &size );
5142 size++;
5143 data = msi_alloc( size * sizeof(WCHAR) );
5144 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5145 else data[0] = 0;
5146 msiobj_release( &rec->hdr );
5147 return data;
5150 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5152 DWORD attrib;
5153 UINT rc;
5156 * We are currently doing what should be done here in the top level Install
5157 * however for Administrative and uninstalls this step will be needed
5159 if (!package->PackagePath)
5160 return ERROR_SUCCESS;
5162 msi_set_sourcedir_props(package, TRUE);
5164 attrib = GetFileAttributesW(package->db->path);
5165 if (attrib == INVALID_FILE_ATTRIBUTES)
5167 LPWSTR prompt;
5168 LPWSTR msg;
5169 DWORD size = 0;
5171 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5172 package->Context, MSICODE_PRODUCT,
5173 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5174 if (rc == ERROR_MORE_DATA)
5176 prompt = msi_alloc(size * sizeof(WCHAR));
5177 MsiSourceListGetInfoW(package->ProductCode, NULL,
5178 package->Context, MSICODE_PRODUCT,
5179 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5181 else
5182 prompt = strdupW(package->db->path);
5184 msg = msi_build_error_string(package, 1302, 1, prompt);
5185 while(attrib == INVALID_FILE_ATTRIBUTES)
5187 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5188 if (rc == IDCANCEL)
5190 rc = ERROR_INSTALL_USEREXIT;
5191 break;
5193 attrib = GetFileAttributesW(package->db->path);
5195 msi_free(prompt);
5196 rc = ERROR_SUCCESS;
5198 else
5199 return ERROR_SUCCESS;
5201 return rc;
5204 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5206 HKEY hkey = 0;
5207 LPWSTR buffer, productid = NULL;
5208 UINT i, rc = ERROR_SUCCESS;
5209 MSIRECORD *uirow;
5211 static const WCHAR szPropKeys[][80] =
5213 {'P','r','o','d','u','c','t','I','D',0},
5214 {'U','S','E','R','N','A','M','E',0},
5215 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5216 {0},
5219 static const WCHAR szRegKeys[][80] =
5221 {'P','r','o','d','u','c','t','I','D',0},
5222 {'R','e','g','O','w','n','e','r',0},
5223 {'R','e','g','C','o','m','p','a','n','y',0},
5224 {0},
5227 if (msi_check_unpublish(package))
5229 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5230 goto end;
5233 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5234 if (!productid)
5235 goto end;
5237 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5238 NULL, &hkey, TRUE);
5239 if (rc != ERROR_SUCCESS)
5240 goto end;
5242 for( i = 0; szPropKeys[i][0]; i++ )
5244 buffer = msi_dup_property( package->db, szPropKeys[i] );
5245 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5246 msi_free( buffer );
5249 end:
5250 uirow = MSI_CreateRecord( 1 );
5251 MSI_RecordSetStringW( uirow, 1, productid );
5252 msi_ui_actiondata( package, szRegisterUser, uirow );
5253 msiobj_release( &uirow->hdr );
5255 msi_free(productid);
5256 RegCloseKey(hkey);
5257 return rc;
5261 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5263 UINT rc;
5265 package->script->InWhatSequence |= SEQUENCE_EXEC;
5266 rc = ACTION_ProcessExecSequence(package,FALSE);
5267 return rc;
5270 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5272 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5273 WCHAR productid_85[21], component_85[21], *ret;
5274 GUID clsid;
5275 DWORD sz;
5277 /* > is used if there is a component GUID and < if not. */
5279 productid_85[0] = 0;
5280 component_85[0] = 0;
5281 CLSIDFromString( package->ProductCode, &clsid );
5283 encode_base85_guid( &clsid, productid_85 );
5284 if (component)
5286 CLSIDFromString( component->ComponentId, &clsid );
5287 encode_base85_guid( &clsid, component_85 );
5290 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5291 debugstr_w(component_85));
5293 sz = 20 + strlenW( feature ) + 20 + 3;
5294 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5295 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5296 return ret;
5299 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5301 MSIPACKAGE *package = param;
5302 LPCWSTR compgroupid, component, feature, qualifier, text;
5303 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5304 HKEY hkey = NULL;
5305 UINT rc;
5306 MSICOMPONENT *comp;
5307 MSIFEATURE *feat;
5308 DWORD sz;
5309 MSIRECORD *uirow;
5310 int len;
5312 feature = MSI_RecordGetString(rec, 5);
5313 feat = msi_get_loaded_feature(package, feature);
5314 if (!feat)
5315 return ERROR_SUCCESS;
5317 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5318 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5319 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5321 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5322 feat->Action = feat->Installed;
5323 return ERROR_SUCCESS;
5326 component = MSI_RecordGetString(rec, 3);
5327 comp = msi_get_loaded_component(package, component);
5328 if (!comp)
5329 return ERROR_SUCCESS;
5331 compgroupid = MSI_RecordGetString(rec,1);
5332 qualifier = MSI_RecordGetString(rec,2);
5334 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5335 if (rc != ERROR_SUCCESS)
5336 goto end;
5338 advertise = msi_create_component_advertise_string( package, comp, feature );
5339 text = MSI_RecordGetString( rec, 4 );
5340 if (text)
5342 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5343 strcpyW( p, advertise );
5344 strcatW( p, text );
5345 msi_free( advertise );
5346 advertise = p;
5348 existing = msi_reg_get_val_str( hkey, qualifier );
5350 sz = strlenW( advertise ) + 1;
5351 if (existing)
5353 for (p = existing; *p; p += len)
5355 len = strlenW( p ) + 1;
5356 if (strcmpW( advertise, p )) sz += len;
5359 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5361 rc = ERROR_OUTOFMEMORY;
5362 goto end;
5364 q = output;
5365 if (existing)
5367 for (p = existing; *p; p += len)
5369 len = strlenW( p ) + 1;
5370 if (strcmpW( advertise, p ))
5372 memcpy( q, p, len * sizeof(WCHAR) );
5373 q += len;
5377 strcpyW( q, advertise );
5378 q[strlenW( q ) + 1] = 0;
5380 msi_reg_set_val_multi_str( hkey, qualifier, output );
5382 end:
5383 RegCloseKey(hkey);
5384 msi_free( output );
5385 msi_free( advertise );
5386 msi_free( existing );
5388 /* the UI chunk */
5389 uirow = MSI_CreateRecord( 2 );
5390 MSI_RecordSetStringW( uirow, 1, compgroupid );
5391 MSI_RecordSetStringW( uirow, 2, qualifier);
5392 msi_ui_actiondata( package, szPublishComponents, uirow );
5393 msiobj_release( &uirow->hdr );
5394 /* FIXME: call ui_progress? */
5396 return rc;
5400 * At present I am ignorning the advertised components part of this and only
5401 * focusing on the qualified component sets
5403 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5405 UINT rc;
5406 MSIQUERY * view;
5407 static const WCHAR ExecSeqQuery[] =
5408 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5409 '`','P','u','b','l','i','s','h',
5410 'C','o','m','p','o','n','e','n','t','`',0};
5412 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5413 if (rc != ERROR_SUCCESS)
5414 return ERROR_SUCCESS;
5416 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5417 msiobj_release(&view->hdr);
5419 return rc;
5422 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5424 static const WCHAR szInstallerComponents[] = {
5425 'S','o','f','t','w','a','r','e','\\',
5426 'M','i','c','r','o','s','o','f','t','\\',
5427 'I','n','s','t','a','l','l','e','r','\\',
5428 'C','o','m','p','o','n','e','n','t','s','\\',0};
5430 MSIPACKAGE *package = param;
5431 LPCWSTR compgroupid, component, feature, qualifier;
5432 MSICOMPONENT *comp;
5433 MSIFEATURE *feat;
5434 MSIRECORD *uirow;
5435 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5436 LONG res;
5438 feature = MSI_RecordGetString( rec, 5 );
5439 feat = msi_get_loaded_feature( package, feature );
5440 if (!feat)
5441 return ERROR_SUCCESS;
5443 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5445 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5446 feat->Action = feat->Installed;
5447 return ERROR_SUCCESS;
5450 component = MSI_RecordGetString( rec, 3 );
5451 comp = msi_get_loaded_component( package, component );
5452 if (!comp)
5453 return ERROR_SUCCESS;
5455 compgroupid = MSI_RecordGetString( rec, 1 );
5456 qualifier = MSI_RecordGetString( rec, 2 );
5458 squash_guid( compgroupid, squashed );
5459 strcpyW( keypath, szInstallerComponents );
5460 strcatW( keypath, squashed );
5462 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5463 if (res != ERROR_SUCCESS)
5465 WARN("Unable to delete component key %d\n", res);
5468 uirow = MSI_CreateRecord( 2 );
5469 MSI_RecordSetStringW( uirow, 1, compgroupid );
5470 MSI_RecordSetStringW( uirow, 2, qualifier );
5471 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5472 msiobj_release( &uirow->hdr );
5474 return ERROR_SUCCESS;
5477 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5479 UINT rc;
5480 MSIQUERY *view;
5481 static const WCHAR query[] =
5482 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5483 '`','P','u','b','l','i','s','h',
5484 'C','o','m','p','o','n','e','n','t','`',0};
5486 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5487 if (rc != ERROR_SUCCESS)
5488 return ERROR_SUCCESS;
5490 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5491 msiobj_release( &view->hdr );
5493 return rc;
5496 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5498 static const WCHAR query[] =
5499 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5500 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5501 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5502 MSIPACKAGE *package = param;
5503 MSICOMPONENT *component;
5504 MSIRECORD *row;
5505 MSIFILE *file;
5506 SC_HANDLE hscm = NULL, service = NULL;
5507 LPCWSTR comp, key;
5508 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5509 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5510 DWORD serv_type, start_type, err_control;
5511 SERVICE_DESCRIPTIONW sd = {NULL};
5513 comp = MSI_RecordGetString( rec, 12 );
5514 component = msi_get_loaded_component( package, comp );
5515 if (!component)
5517 WARN("service component not found\n");
5518 goto done;
5520 component->Action = msi_get_component_action( package, component );
5521 if (component->Action != INSTALLSTATE_LOCAL)
5523 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5524 goto done;
5526 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5527 if (!hscm)
5529 ERR("Failed to open the SC Manager!\n");
5530 goto done;
5533 start_type = MSI_RecordGetInteger(rec, 5);
5534 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5535 goto done;
5537 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5538 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5539 serv_type = MSI_RecordGetInteger(rec, 4);
5540 err_control = MSI_RecordGetInteger(rec, 6);
5541 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5542 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5543 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5544 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5545 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5546 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5548 /* fetch the service path */
5549 row = MSI_QueryGetRecord(package->db, query, comp);
5550 if (!row)
5552 ERR("Query failed\n");
5553 goto done;
5555 key = MSI_RecordGetString(row, 6);
5556 file = msi_get_loaded_file(package, key);
5557 msiobj_release(&row->hdr);
5558 if (!file)
5560 ERR("Failed to load the service file\n");
5561 goto done;
5564 if (!args || !args[0]) image_path = file->TargetPath;
5565 else
5567 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5568 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5569 return ERROR_OUTOFMEMORY;
5571 strcpyW(image_path, file->TargetPath);
5572 strcatW(image_path, szSpace);
5573 strcatW(image_path, args);
5575 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5576 start_type, err_control, image_path, load_order,
5577 NULL, depends, serv_name, pass);
5579 if (!service)
5581 if (GetLastError() != ERROR_SERVICE_EXISTS)
5582 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5584 else if (sd.lpDescription)
5586 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5587 WARN("failed to set service description %u\n", GetLastError());
5590 if (image_path != file->TargetPath) msi_free(image_path);
5591 done:
5592 CloseServiceHandle(service);
5593 CloseServiceHandle(hscm);
5594 msi_free(name);
5595 msi_free(disp);
5596 msi_free(sd.lpDescription);
5597 msi_free(load_order);
5598 msi_free(serv_name);
5599 msi_free(pass);
5600 msi_free(depends);
5601 msi_free(args);
5603 return ERROR_SUCCESS;
5606 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5608 UINT rc;
5609 MSIQUERY * view;
5610 static const WCHAR ExecSeqQuery[] =
5611 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5612 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5614 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5615 if (rc != ERROR_SUCCESS)
5616 return ERROR_SUCCESS;
5618 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5619 msiobj_release(&view->hdr);
5621 return rc;
5624 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5625 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5627 LPCWSTR *vector, *temp_vector;
5628 LPWSTR p, q;
5629 DWORD sep_len;
5631 static const WCHAR separator[] = {'[','~',']',0};
5633 *numargs = 0;
5634 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5636 if (!args)
5637 return NULL;
5639 vector = msi_alloc(sizeof(LPWSTR));
5640 if (!vector)
5641 return NULL;
5643 p = args;
5646 (*numargs)++;
5647 vector[*numargs - 1] = p;
5649 if ((q = strstrW(p, separator)))
5651 *q = '\0';
5653 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5654 if (!temp_vector)
5656 msi_free(vector);
5657 return NULL;
5659 vector = temp_vector;
5661 p = q + sep_len;
5663 } while (q);
5665 return vector;
5668 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5670 MSIPACKAGE *package = param;
5671 MSICOMPONENT *comp;
5672 MSIRECORD *uirow;
5673 SC_HANDLE scm = NULL, service = NULL;
5674 LPCWSTR component, *vector = NULL;
5675 LPWSTR name, args, display_name = NULL;
5676 DWORD event, numargs, len;
5677 UINT r = ERROR_FUNCTION_FAILED;
5679 component = MSI_RecordGetString(rec, 6);
5680 comp = msi_get_loaded_component(package, component);
5681 if (!comp)
5682 return ERROR_SUCCESS;
5684 comp->Action = msi_get_component_action( package, comp );
5685 if (comp->Action != INSTALLSTATE_LOCAL)
5687 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5688 return ERROR_SUCCESS;
5691 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5692 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5693 event = MSI_RecordGetInteger(rec, 3);
5695 if (!(event & msidbServiceControlEventStart))
5697 r = ERROR_SUCCESS;
5698 goto done;
5701 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5702 if (!scm)
5704 ERR("Failed to open the service control manager\n");
5705 goto done;
5708 len = 0;
5709 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5710 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5712 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5713 GetServiceDisplayNameW( scm, name, display_name, &len );
5716 service = OpenServiceW(scm, name, SERVICE_START);
5717 if (!service)
5719 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5720 goto done;
5723 vector = msi_service_args_to_vector(args, &numargs);
5725 if (!StartServiceW(service, numargs, vector) &&
5726 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5728 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5729 goto done;
5732 r = ERROR_SUCCESS;
5734 done:
5735 uirow = MSI_CreateRecord( 2 );
5736 MSI_RecordSetStringW( uirow, 1, display_name );
5737 MSI_RecordSetStringW( uirow, 2, name );
5738 msi_ui_actiondata( package, szStartServices, uirow );
5739 msiobj_release( &uirow->hdr );
5741 CloseServiceHandle(service);
5742 CloseServiceHandle(scm);
5744 msi_free(name);
5745 msi_free(args);
5746 msi_free(vector);
5747 msi_free(display_name);
5748 return r;
5751 static UINT ACTION_StartServices( MSIPACKAGE *package )
5753 UINT rc;
5754 MSIQUERY *view;
5756 static const WCHAR query[] = {
5757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5758 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5760 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5761 if (rc != ERROR_SUCCESS)
5762 return ERROR_SUCCESS;
5764 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5765 msiobj_release(&view->hdr);
5767 return rc;
5770 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5772 DWORD i, needed, count;
5773 ENUM_SERVICE_STATUSW *dependencies;
5774 SERVICE_STATUS ss;
5775 SC_HANDLE depserv;
5777 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5778 0, &needed, &count))
5779 return TRUE;
5781 if (GetLastError() != ERROR_MORE_DATA)
5782 return FALSE;
5784 dependencies = msi_alloc(needed);
5785 if (!dependencies)
5786 return FALSE;
5788 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5789 needed, &needed, &count))
5790 goto error;
5792 for (i = 0; i < count; i++)
5794 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5795 SERVICE_STOP | SERVICE_QUERY_STATUS);
5796 if (!depserv)
5797 goto error;
5799 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5800 goto error;
5803 return TRUE;
5805 error:
5806 msi_free(dependencies);
5807 return FALSE;
5810 static UINT stop_service( LPCWSTR name )
5812 SC_HANDLE scm = NULL, service = NULL;
5813 SERVICE_STATUS status;
5814 SERVICE_STATUS_PROCESS ssp;
5815 DWORD needed;
5817 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5818 if (!scm)
5820 WARN("Failed to open the SCM: %d\n", GetLastError());
5821 goto done;
5824 service = OpenServiceW(scm, name,
5825 SERVICE_STOP |
5826 SERVICE_QUERY_STATUS |
5827 SERVICE_ENUMERATE_DEPENDENTS);
5828 if (!service)
5830 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5831 goto done;
5834 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5835 sizeof(SERVICE_STATUS_PROCESS), &needed))
5837 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5838 goto done;
5841 if (ssp.dwCurrentState == SERVICE_STOPPED)
5842 goto done;
5844 stop_service_dependents(scm, service);
5846 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5847 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5849 done:
5850 CloseServiceHandle(service);
5851 CloseServiceHandle(scm);
5853 return ERROR_SUCCESS;
5856 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5858 MSIPACKAGE *package = param;
5859 MSICOMPONENT *comp;
5860 MSIRECORD *uirow;
5861 LPCWSTR component;
5862 LPWSTR name = NULL, display_name = NULL;
5863 DWORD event, len;
5864 SC_HANDLE scm;
5866 event = MSI_RecordGetInteger( rec, 3 );
5867 if (!(event & msidbServiceControlEventStop))
5868 return ERROR_SUCCESS;
5870 component = MSI_RecordGetString( rec, 6 );
5871 comp = msi_get_loaded_component( package, component );
5872 if (!comp)
5873 return ERROR_SUCCESS;
5875 comp->Action = msi_get_component_action( package, comp );
5876 if (comp->Action != INSTALLSTATE_ABSENT)
5878 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5879 return ERROR_SUCCESS;
5882 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5883 if (!scm)
5885 ERR("Failed to open the service control manager\n");
5886 goto done;
5889 len = 0;
5890 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5891 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5893 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5894 GetServiceDisplayNameW( scm, name, display_name, &len );
5896 CloseServiceHandle( scm );
5898 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5899 stop_service( name );
5901 done:
5902 uirow = MSI_CreateRecord( 2 );
5903 MSI_RecordSetStringW( uirow, 1, display_name );
5904 MSI_RecordSetStringW( uirow, 2, name );
5905 msi_ui_actiondata( package, szStopServices, uirow );
5906 msiobj_release( &uirow->hdr );
5908 msi_free( name );
5909 msi_free( display_name );
5910 return ERROR_SUCCESS;
5913 static UINT ACTION_StopServices( MSIPACKAGE *package )
5915 UINT rc;
5916 MSIQUERY *view;
5918 static const WCHAR query[] = {
5919 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5920 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5922 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5923 if (rc != ERROR_SUCCESS)
5924 return ERROR_SUCCESS;
5926 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5927 msiobj_release(&view->hdr);
5929 return rc;
5932 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5934 MSIPACKAGE *package = param;
5935 MSICOMPONENT *comp;
5936 MSIRECORD *uirow;
5937 LPCWSTR component;
5938 LPWSTR name = NULL, display_name = NULL;
5939 DWORD event, len;
5940 SC_HANDLE scm = NULL, service = NULL;
5942 event = MSI_RecordGetInteger( rec, 3 );
5943 if (!(event & msidbServiceControlEventDelete))
5944 return ERROR_SUCCESS;
5946 component = MSI_RecordGetString(rec, 6);
5947 comp = msi_get_loaded_component(package, component);
5948 if (!comp)
5949 return ERROR_SUCCESS;
5951 comp->Action = msi_get_component_action( package, comp );
5952 if (comp->Action != INSTALLSTATE_ABSENT)
5954 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5955 return ERROR_SUCCESS;
5958 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5959 stop_service( name );
5961 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5962 if (!scm)
5964 WARN("Failed to open the SCM: %d\n", GetLastError());
5965 goto done;
5968 len = 0;
5969 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5970 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5972 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5973 GetServiceDisplayNameW( scm, name, display_name, &len );
5976 service = OpenServiceW( scm, name, DELETE );
5977 if (!service)
5979 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5980 goto done;
5983 if (!DeleteService( service ))
5984 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5986 done:
5987 uirow = MSI_CreateRecord( 2 );
5988 MSI_RecordSetStringW( uirow, 1, display_name );
5989 MSI_RecordSetStringW( uirow, 2, name );
5990 msi_ui_actiondata( package, szDeleteServices, uirow );
5991 msiobj_release( &uirow->hdr );
5993 CloseServiceHandle( service );
5994 CloseServiceHandle( scm );
5995 msi_free( name );
5996 msi_free( display_name );
5998 return ERROR_SUCCESS;
6001 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6003 UINT rc;
6004 MSIQUERY *view;
6006 static const WCHAR query[] = {
6007 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6008 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6010 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6011 if (rc != ERROR_SUCCESS)
6012 return ERROR_SUCCESS;
6014 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6015 msiobj_release( &view->hdr );
6017 return rc;
6020 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6022 MSIPACKAGE *package = param;
6023 LPWSTR driver, driver_path, ptr;
6024 WCHAR outpath[MAX_PATH];
6025 MSIFILE *driver_file = NULL, *setup_file = NULL;
6026 MSICOMPONENT *comp;
6027 MSIRECORD *uirow;
6028 LPCWSTR desc, file_key, component;
6029 DWORD len, usage;
6030 UINT r = ERROR_SUCCESS;
6032 static const WCHAR driver_fmt[] = {
6033 'D','r','i','v','e','r','=','%','s',0};
6034 static const WCHAR setup_fmt[] = {
6035 'S','e','t','u','p','=','%','s',0};
6036 static const WCHAR usage_fmt[] = {
6037 'F','i','l','e','U','s','a','g','e','=','1',0};
6039 component = MSI_RecordGetString( rec, 2 );
6040 comp = msi_get_loaded_component( package, component );
6041 if (!comp)
6042 return ERROR_SUCCESS;
6044 if (!comp->Enabled)
6046 TRACE("component is disabled\n");
6047 return ERROR_SUCCESS;
6050 desc = MSI_RecordGetString(rec, 3);
6052 file_key = MSI_RecordGetString( rec, 4 );
6053 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6055 file_key = MSI_RecordGetString( rec, 5 );
6056 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6058 if (!driver_file)
6060 ERR("ODBC Driver entry not found!\n");
6061 return ERROR_FUNCTION_FAILED;
6064 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6065 if (setup_file)
6066 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6067 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6069 driver = msi_alloc(len * sizeof(WCHAR));
6070 if (!driver)
6071 return ERROR_OUTOFMEMORY;
6073 ptr = driver;
6074 lstrcpyW(ptr, desc);
6075 ptr += lstrlenW(ptr) + 1;
6077 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6078 ptr += len + 1;
6080 if (setup_file)
6082 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6083 ptr += len + 1;
6086 lstrcpyW(ptr, usage_fmt);
6087 ptr += lstrlenW(ptr) + 1;
6088 *ptr = '\0';
6090 driver_path = strdupW(driver_file->TargetPath);
6091 ptr = strrchrW(driver_path, '\\');
6092 if (ptr) *ptr = '\0';
6094 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6095 NULL, ODBC_INSTALL_COMPLETE, &usage))
6097 ERR("Failed to install SQL driver!\n");
6098 r = ERROR_FUNCTION_FAILED;
6101 uirow = MSI_CreateRecord( 5 );
6102 MSI_RecordSetStringW( uirow, 1, desc );
6103 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6104 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6105 msi_ui_actiondata( package, szInstallODBC, uirow );
6106 msiobj_release( &uirow->hdr );
6108 msi_free(driver);
6109 msi_free(driver_path);
6111 return r;
6114 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6116 MSIPACKAGE *package = param;
6117 LPWSTR translator, translator_path, ptr;
6118 WCHAR outpath[MAX_PATH];
6119 MSIFILE *translator_file = NULL, *setup_file = NULL;
6120 MSICOMPONENT *comp;
6121 MSIRECORD *uirow;
6122 LPCWSTR desc, file_key, component;
6123 DWORD len, usage;
6124 UINT r = ERROR_SUCCESS;
6126 static const WCHAR translator_fmt[] = {
6127 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6128 static const WCHAR setup_fmt[] = {
6129 'S','e','t','u','p','=','%','s',0};
6131 component = MSI_RecordGetString( rec, 2 );
6132 comp = msi_get_loaded_component( package, component );
6133 if (!comp)
6134 return ERROR_SUCCESS;
6136 if (!comp->Enabled)
6138 TRACE("component is disabled\n");
6139 return ERROR_SUCCESS;
6142 desc = MSI_RecordGetString(rec, 3);
6144 file_key = MSI_RecordGetString( rec, 4 );
6145 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6147 file_key = MSI_RecordGetString( rec, 5 );
6148 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6150 if (!translator_file)
6152 ERR("ODBC Translator entry not found!\n");
6153 return ERROR_FUNCTION_FAILED;
6156 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6157 if (setup_file)
6158 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6160 translator = msi_alloc(len * sizeof(WCHAR));
6161 if (!translator)
6162 return ERROR_OUTOFMEMORY;
6164 ptr = translator;
6165 lstrcpyW(ptr, desc);
6166 ptr += lstrlenW(ptr) + 1;
6168 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6169 ptr += len + 1;
6171 if (setup_file)
6173 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6174 ptr += len + 1;
6176 *ptr = '\0';
6178 translator_path = strdupW(translator_file->TargetPath);
6179 ptr = strrchrW(translator_path, '\\');
6180 if (ptr) *ptr = '\0';
6182 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6183 NULL, ODBC_INSTALL_COMPLETE, &usage))
6185 ERR("Failed to install SQL translator!\n");
6186 r = ERROR_FUNCTION_FAILED;
6189 uirow = MSI_CreateRecord( 5 );
6190 MSI_RecordSetStringW( uirow, 1, desc );
6191 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6192 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6193 msi_ui_actiondata( package, szInstallODBC, uirow );
6194 msiobj_release( &uirow->hdr );
6196 msi_free(translator);
6197 msi_free(translator_path);
6199 return r;
6202 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6204 MSIPACKAGE *package = param;
6205 MSICOMPONENT *comp;
6206 LPWSTR attrs;
6207 LPCWSTR desc, driver, component;
6208 WORD request = ODBC_ADD_SYS_DSN;
6209 INT registration;
6210 DWORD len;
6211 UINT r = ERROR_SUCCESS;
6212 MSIRECORD *uirow;
6214 static const WCHAR attrs_fmt[] = {
6215 'D','S','N','=','%','s',0 };
6217 component = MSI_RecordGetString( rec, 2 );
6218 comp = msi_get_loaded_component( package, component );
6219 if (!comp)
6220 return ERROR_SUCCESS;
6222 if (!comp->Enabled)
6224 TRACE("component is disabled\n");
6225 return ERROR_SUCCESS;
6228 desc = MSI_RecordGetString(rec, 3);
6229 driver = MSI_RecordGetString(rec, 4);
6230 registration = MSI_RecordGetInteger(rec, 5);
6232 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6233 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6235 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6236 attrs = msi_alloc(len * sizeof(WCHAR));
6237 if (!attrs)
6238 return ERROR_OUTOFMEMORY;
6240 len = sprintfW(attrs, attrs_fmt, desc);
6241 attrs[len + 1] = 0;
6243 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6245 ERR("Failed to install SQL data source!\n");
6246 r = ERROR_FUNCTION_FAILED;
6249 uirow = MSI_CreateRecord( 5 );
6250 MSI_RecordSetStringW( uirow, 1, desc );
6251 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6252 MSI_RecordSetInteger( uirow, 3, request );
6253 msi_ui_actiondata( package, szInstallODBC, uirow );
6254 msiobj_release( &uirow->hdr );
6256 msi_free(attrs);
6258 return r;
6261 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6263 UINT rc;
6264 MSIQUERY *view;
6266 static const WCHAR driver_query[] = {
6267 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6268 'O','D','B','C','D','r','i','v','e','r',0 };
6270 static const WCHAR translator_query[] = {
6271 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6272 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6274 static const WCHAR source_query[] = {
6275 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6276 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6278 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6279 if (rc != ERROR_SUCCESS)
6280 return ERROR_SUCCESS;
6282 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6283 msiobj_release(&view->hdr);
6285 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6286 if (rc != ERROR_SUCCESS)
6287 return ERROR_SUCCESS;
6289 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6290 msiobj_release(&view->hdr);
6292 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6293 if (rc != ERROR_SUCCESS)
6294 return ERROR_SUCCESS;
6296 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6297 msiobj_release(&view->hdr);
6299 return rc;
6302 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6304 MSIPACKAGE *package = param;
6305 MSICOMPONENT *comp;
6306 MSIRECORD *uirow;
6307 DWORD usage;
6308 LPCWSTR desc, component;
6310 component = MSI_RecordGetString( rec, 2 );
6311 comp = msi_get_loaded_component( package, component );
6312 if (!comp)
6313 return ERROR_SUCCESS;
6315 if (!comp->Enabled)
6317 TRACE("component is disabled\n");
6318 return ERROR_SUCCESS;
6321 desc = MSI_RecordGetString( rec, 3 );
6322 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6324 WARN("Failed to remove ODBC driver\n");
6326 else if (!usage)
6328 FIXME("Usage count reached 0\n");
6331 uirow = MSI_CreateRecord( 2 );
6332 MSI_RecordSetStringW( uirow, 1, desc );
6333 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6334 msi_ui_actiondata( package, szRemoveODBC, uirow );
6335 msiobj_release( &uirow->hdr );
6337 return ERROR_SUCCESS;
6340 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6342 MSIPACKAGE *package = param;
6343 MSICOMPONENT *comp;
6344 MSIRECORD *uirow;
6345 DWORD usage;
6346 LPCWSTR desc, component;
6348 component = MSI_RecordGetString( rec, 2 );
6349 comp = msi_get_loaded_component( package, component );
6350 if (!comp)
6351 return ERROR_SUCCESS;
6353 if (!comp->Enabled)
6355 TRACE("component is disabled\n");
6356 return ERROR_SUCCESS;
6359 desc = MSI_RecordGetString( rec, 3 );
6360 if (!SQLRemoveTranslatorW( desc, &usage ))
6362 WARN("Failed to remove ODBC translator\n");
6364 else if (!usage)
6366 FIXME("Usage count reached 0\n");
6369 uirow = MSI_CreateRecord( 2 );
6370 MSI_RecordSetStringW( uirow, 1, desc );
6371 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6372 msi_ui_actiondata( package, szRemoveODBC, uirow );
6373 msiobj_release( &uirow->hdr );
6375 return ERROR_SUCCESS;
6378 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6380 MSIPACKAGE *package = param;
6381 MSICOMPONENT *comp;
6382 MSIRECORD *uirow;
6383 LPWSTR attrs;
6384 LPCWSTR desc, driver, component;
6385 WORD request = ODBC_REMOVE_SYS_DSN;
6386 INT registration;
6387 DWORD len;
6389 static const WCHAR attrs_fmt[] = {
6390 'D','S','N','=','%','s',0 };
6392 component = MSI_RecordGetString( rec, 2 );
6393 comp = msi_get_loaded_component( package, component );
6394 if (!comp)
6395 return ERROR_SUCCESS;
6397 if (!comp->Enabled)
6399 TRACE("component is disabled\n");
6400 return ERROR_SUCCESS;
6403 desc = MSI_RecordGetString( rec, 3 );
6404 driver = MSI_RecordGetString( rec, 4 );
6405 registration = MSI_RecordGetInteger( rec, 5 );
6407 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6408 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6410 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6411 attrs = msi_alloc( len * sizeof(WCHAR) );
6412 if (!attrs)
6413 return ERROR_OUTOFMEMORY;
6415 FIXME("Use ODBCSourceAttribute table\n");
6417 len = sprintfW( attrs, attrs_fmt, desc );
6418 attrs[len + 1] = 0;
6420 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6422 WARN("Failed to remove ODBC data source\n");
6424 msi_free( attrs );
6426 uirow = MSI_CreateRecord( 3 );
6427 MSI_RecordSetStringW( uirow, 1, desc );
6428 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6429 MSI_RecordSetInteger( uirow, 3, request );
6430 msi_ui_actiondata( package, szRemoveODBC, uirow );
6431 msiobj_release( &uirow->hdr );
6433 return ERROR_SUCCESS;
6436 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6438 UINT rc;
6439 MSIQUERY *view;
6441 static const WCHAR driver_query[] = {
6442 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6443 'O','D','B','C','D','r','i','v','e','r',0 };
6445 static const WCHAR translator_query[] = {
6446 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6449 static const WCHAR source_query[] = {
6450 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6451 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6453 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6454 if (rc != ERROR_SUCCESS)
6455 return ERROR_SUCCESS;
6457 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6458 msiobj_release( &view->hdr );
6460 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6461 if (rc != ERROR_SUCCESS)
6462 return ERROR_SUCCESS;
6464 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6465 msiobj_release( &view->hdr );
6467 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6468 if (rc != ERROR_SUCCESS)
6469 return ERROR_SUCCESS;
6471 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6472 msiobj_release( &view->hdr );
6474 return rc;
6477 #define ENV_ACT_SETALWAYS 0x1
6478 #define ENV_ACT_SETABSENT 0x2
6479 #define ENV_ACT_REMOVE 0x4
6480 #define ENV_ACT_REMOVEMATCH 0x8
6482 #define ENV_MOD_MACHINE 0x20000000
6483 #define ENV_MOD_APPEND 0x40000000
6484 #define ENV_MOD_PREFIX 0x80000000
6485 #define ENV_MOD_MASK 0xC0000000
6487 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6489 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6491 LPCWSTR cptr = *name;
6493 static const WCHAR prefix[] = {'[','~',']',0};
6494 static const int prefix_len = 3;
6496 *flags = 0;
6497 while (*cptr)
6499 if (*cptr == '=')
6500 *flags |= ENV_ACT_SETALWAYS;
6501 else if (*cptr == '+')
6502 *flags |= ENV_ACT_SETABSENT;
6503 else if (*cptr == '-')
6504 *flags |= ENV_ACT_REMOVE;
6505 else if (*cptr == '!')
6506 *flags |= ENV_ACT_REMOVEMATCH;
6507 else if (*cptr == '*')
6508 *flags |= ENV_MOD_MACHINE;
6509 else
6510 break;
6512 cptr++;
6513 (*name)++;
6516 if (!*cptr)
6518 ERR("Missing environment variable\n");
6519 return ERROR_FUNCTION_FAILED;
6522 if (*value)
6524 LPCWSTR ptr = *value;
6525 if (!strncmpW(ptr, prefix, prefix_len))
6527 if (ptr[prefix_len] == szSemiColon[0])
6529 *flags |= ENV_MOD_APPEND;
6530 *value += lstrlenW(prefix);
6532 else
6534 *value = NULL;
6537 else if (lstrlenW(*value) >= prefix_len)
6539 ptr += lstrlenW(ptr) - prefix_len;
6540 if (!strcmpW( ptr, prefix ))
6542 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6544 *flags |= ENV_MOD_PREFIX;
6545 /* the "[~]" will be removed by deformat_string */;
6547 else
6549 *value = NULL;
6555 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6556 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6557 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6558 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6560 ERR("Invalid flags: %08x\n", *flags);
6561 return ERROR_FUNCTION_FAILED;
6564 if (!*flags)
6565 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6567 return ERROR_SUCCESS;
6570 static UINT open_env_key( DWORD flags, HKEY *key )
6572 static const WCHAR user_env[] =
6573 {'E','n','v','i','r','o','n','m','e','n','t',0};
6574 static const WCHAR machine_env[] =
6575 {'S','y','s','t','e','m','\\',
6576 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6577 'C','o','n','t','r','o','l','\\',
6578 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6579 'E','n','v','i','r','o','n','m','e','n','t',0};
6580 const WCHAR *env;
6581 HKEY root;
6582 LONG res;
6584 if (flags & ENV_MOD_MACHINE)
6586 env = machine_env;
6587 root = HKEY_LOCAL_MACHINE;
6589 else
6591 env = user_env;
6592 root = HKEY_CURRENT_USER;
6595 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6596 if (res != ERROR_SUCCESS)
6598 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6599 return ERROR_FUNCTION_FAILED;
6602 return ERROR_SUCCESS;
6605 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6607 MSIPACKAGE *package = param;
6608 LPCWSTR name, value, component;
6609 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6610 DWORD flags, type, size;
6611 UINT res;
6612 HKEY env = NULL;
6613 MSICOMPONENT *comp;
6614 MSIRECORD *uirow;
6615 int action = 0;
6617 component = MSI_RecordGetString(rec, 4);
6618 comp = msi_get_loaded_component(package, component);
6619 if (!comp)
6620 return ERROR_SUCCESS;
6622 if (!comp->Enabled)
6624 TRACE("component is disabled\n");
6625 return ERROR_SUCCESS;
6628 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6630 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6631 comp->Action = comp->Installed;
6632 return ERROR_SUCCESS;
6634 comp->Action = INSTALLSTATE_LOCAL;
6636 name = MSI_RecordGetString(rec, 2);
6637 value = MSI_RecordGetString(rec, 3);
6639 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6641 res = env_parse_flags(&name, &value, &flags);
6642 if (res != ERROR_SUCCESS || !value)
6643 goto done;
6645 if (value && !deformat_string(package, value, &deformatted))
6647 res = ERROR_OUTOFMEMORY;
6648 goto done;
6651 value = deformatted;
6653 res = open_env_key( flags, &env );
6654 if (res != ERROR_SUCCESS)
6655 goto done;
6657 if (flags & ENV_MOD_MACHINE)
6658 action |= 0x20000000;
6660 size = 0;
6661 type = REG_SZ;
6662 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6663 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6664 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6665 goto done;
6667 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6669 action = 0x2;
6671 /* Nothing to do. */
6672 if (!value)
6674 res = ERROR_SUCCESS;
6675 goto done;
6678 /* If we are appending but the string was empty, strip ; */
6679 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6681 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6682 newval = strdupW(value);
6683 if (!newval)
6685 res = ERROR_OUTOFMEMORY;
6686 goto done;
6689 else
6691 action = 0x1;
6693 /* Contrary to MSDN, +-variable to [~];path works */
6694 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6696 res = ERROR_SUCCESS;
6697 goto done;
6700 data = msi_alloc(size);
6701 if (!data)
6703 RegCloseKey(env);
6704 return ERROR_OUTOFMEMORY;
6707 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6708 if (res != ERROR_SUCCESS)
6709 goto done;
6711 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6713 action = 0x4;
6714 res = RegDeleteValueW(env, name);
6715 if (res != ERROR_SUCCESS)
6716 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6717 goto done;
6720 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6721 if (flags & ENV_MOD_MASK)
6723 DWORD mod_size;
6724 int multiplier = 0;
6725 if (flags & ENV_MOD_APPEND) multiplier++;
6726 if (flags & ENV_MOD_PREFIX) multiplier++;
6727 mod_size = lstrlenW(value) * multiplier;
6728 size += mod_size * sizeof(WCHAR);
6731 newval = msi_alloc(size);
6732 ptr = newval;
6733 if (!newval)
6735 res = ERROR_OUTOFMEMORY;
6736 goto done;
6739 if (flags & ENV_MOD_PREFIX)
6741 lstrcpyW(newval, value);
6742 ptr = newval + lstrlenW(value);
6743 action |= 0x80000000;
6746 lstrcpyW(ptr, data);
6748 if (flags & ENV_MOD_APPEND)
6750 lstrcatW(newval, value);
6751 action |= 0x40000000;
6754 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6755 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6756 if (res)
6758 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6761 done:
6762 uirow = MSI_CreateRecord( 3 );
6763 MSI_RecordSetStringW( uirow, 1, name );
6764 MSI_RecordSetStringW( uirow, 2, newval );
6765 MSI_RecordSetInteger( uirow, 3, action );
6766 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6767 msiobj_release( &uirow->hdr );
6769 if (env) RegCloseKey(env);
6770 msi_free(deformatted);
6771 msi_free(data);
6772 msi_free(newval);
6773 return res;
6776 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6778 UINT rc;
6779 MSIQUERY * view;
6780 static const WCHAR ExecSeqQuery[] =
6781 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6782 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6783 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6784 if (rc != ERROR_SUCCESS)
6785 return ERROR_SUCCESS;
6787 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6788 msiobj_release(&view->hdr);
6790 return rc;
6793 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6795 MSIPACKAGE *package = param;
6796 LPCWSTR name, value, component;
6797 LPWSTR deformatted = NULL;
6798 DWORD flags;
6799 HKEY env;
6800 MSICOMPONENT *comp;
6801 MSIRECORD *uirow;
6802 int action = 0;
6803 LONG res;
6804 UINT r;
6806 component = MSI_RecordGetString( rec, 4 );
6807 comp = msi_get_loaded_component( package, component );
6808 if (!comp)
6809 return ERROR_SUCCESS;
6811 if (!comp->Enabled)
6813 TRACE("component is disabled\n");
6814 return ERROR_SUCCESS;
6817 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6819 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6820 comp->Action = comp->Installed;
6821 return ERROR_SUCCESS;
6823 comp->Action = INSTALLSTATE_ABSENT;
6825 name = MSI_RecordGetString( rec, 2 );
6826 value = MSI_RecordGetString( rec, 3 );
6828 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6830 r = env_parse_flags( &name, &value, &flags );
6831 if (r != ERROR_SUCCESS)
6832 return r;
6834 if (!(flags & ENV_ACT_REMOVE))
6836 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6837 return ERROR_SUCCESS;
6840 if (value && !deformat_string( package, value, &deformatted ))
6841 return ERROR_OUTOFMEMORY;
6843 value = deformatted;
6845 r = open_env_key( flags, &env );
6846 if (r != ERROR_SUCCESS)
6848 r = ERROR_SUCCESS;
6849 goto done;
6852 if (flags & ENV_MOD_MACHINE)
6853 action |= 0x20000000;
6855 TRACE("Removing %s\n", debugstr_w(name));
6857 res = RegDeleteValueW( env, name );
6858 if (res != ERROR_SUCCESS)
6860 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6861 r = ERROR_SUCCESS;
6864 done:
6865 uirow = MSI_CreateRecord( 3 );
6866 MSI_RecordSetStringW( uirow, 1, name );
6867 MSI_RecordSetStringW( uirow, 2, value );
6868 MSI_RecordSetInteger( uirow, 3, action );
6869 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6870 msiobj_release( &uirow->hdr );
6872 if (env) RegCloseKey( env );
6873 msi_free( deformatted );
6874 return r;
6877 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6879 UINT rc;
6880 MSIQUERY *view;
6881 static const WCHAR query[] =
6882 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6883 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6885 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6886 if (rc != ERROR_SUCCESS)
6887 return ERROR_SUCCESS;
6889 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6890 msiobj_release( &view->hdr );
6892 return rc;
6895 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6897 LPWSTR key, template, id;
6898 UINT r = ERROR_SUCCESS;
6900 id = msi_dup_property( package->db, szProductID );
6901 if (id)
6903 msi_free( id );
6904 return ERROR_SUCCESS;
6906 template = msi_dup_property( package->db, szPIDTemplate );
6907 key = msi_dup_property( package->db, szPIDKEY );
6909 if (key && template)
6911 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6912 r = msi_set_property( package->db, szProductID, key );
6914 msi_free( template );
6915 msi_free( key );
6916 return r;
6919 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6921 TRACE("\n");
6922 package->need_reboot = 1;
6923 return ERROR_SUCCESS;
6926 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6928 static const WCHAR szAvailableFreeReg[] =
6929 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6930 MSIRECORD *uirow;
6931 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6933 TRACE("%p %d kilobytes\n", package, space);
6935 uirow = MSI_CreateRecord( 1 );
6936 MSI_RecordSetInteger( uirow, 1, space );
6937 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6938 msiobj_release( &uirow->hdr );
6940 return ERROR_SUCCESS;
6943 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6945 TRACE("%p\n", package);
6947 msi_set_property( package->db, szRollbackDisabled, szOne );
6948 return ERROR_SUCCESS;
6951 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6953 FIXME("%p\n", package);
6954 return ERROR_SUCCESS;
6957 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6959 UINT r, count;
6960 MSIQUERY *view;
6962 static const WCHAR driver_query[] = {
6963 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6964 'O','D','B','C','D','r','i','v','e','r',0 };
6966 static const WCHAR translator_query[] = {
6967 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6968 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6970 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6971 if (r == ERROR_SUCCESS)
6973 count = 0;
6974 r = MSI_IterateRecords( view, &count, NULL, package );
6975 msiobj_release( &view->hdr );
6976 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6979 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6980 if (r == ERROR_SUCCESS)
6982 count = 0;
6983 r = MSI_IterateRecords( view, &count, NULL, package );
6984 msiobj_release( &view->hdr );
6985 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6988 return ERROR_SUCCESS;
6991 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6993 MSIPACKAGE *package = param;
6994 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6995 WCHAR *value;
6997 if ((value = msi_dup_property( package->db, property )))
6999 FIXME("remove %s\n", debugstr_w(value));
7000 msi_free( value );
7002 return ERROR_SUCCESS;
7005 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7007 UINT r;
7008 MSIQUERY *view;
7010 static const WCHAR query[] =
7011 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7012 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7014 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7015 if (r == ERROR_SUCCESS)
7017 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7018 msiobj_release( &view->hdr );
7020 return ERROR_SUCCESS;
7023 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7025 MSIPACKAGE *package = param;
7026 int attributes = MSI_RecordGetInteger( rec, 5 );
7028 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7030 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7031 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7032 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7033 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7034 HKEY hkey;
7035 UINT r;
7037 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7039 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7040 if (r != ERROR_SUCCESS)
7041 return ERROR_SUCCESS;
7043 else
7045 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7046 if (r != ERROR_SUCCESS)
7047 return ERROR_SUCCESS;
7049 RegCloseKey( hkey );
7051 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7052 debugstr_w(upgrade_code), debugstr_w(version_min),
7053 debugstr_w(version_max), debugstr_w(language));
7055 return ERROR_SUCCESS;
7058 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7060 UINT r;
7061 MSIQUERY *view;
7062 static const WCHAR query[] =
7063 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7065 if (msi_get_property_int( package->db, szInstalled, 0 ))
7067 TRACE("product is installed, skipping action\n");
7068 return ERROR_SUCCESS;
7070 if (msi_get_property_int( package->db, szPreselected, 0 ))
7072 TRACE("Preselected property is set, not migrating feature states\n");
7073 return ERROR_SUCCESS;
7076 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7077 if (r == ERROR_SUCCESS)
7079 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7080 msiobj_release( &view->hdr );
7082 return ERROR_SUCCESS;
7085 static void bind_image( const char *filename, const char *path )
7087 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7089 WARN("failed to bind image %u\n", GetLastError());
7093 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7095 UINT i;
7096 MSIFILE *file;
7097 MSIPACKAGE *package = param;
7098 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7099 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7100 char *filenameA, *pathA;
7101 WCHAR *pathW, **path_list;
7103 if (!(file = msi_get_loaded_file( package, key )))
7105 WARN("file %s not found\n", debugstr_w(key));
7106 return ERROR_SUCCESS;
7108 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7109 path_list = msi_split_string( paths, ';' );
7110 if (!path_list) bind_image( filenameA, NULL );
7111 else
7113 for (i = 0; path_list[i] && path_list[i][0]; i++)
7115 deformat_string( package, path_list[i], &pathW );
7116 if ((pathA = strdupWtoA( pathW )))
7118 bind_image( filenameA, pathA );
7119 msi_free( pathA );
7121 msi_free( pathW );
7124 msi_free( path_list );
7125 msi_free( filenameA );
7126 return ERROR_SUCCESS;
7129 static UINT ACTION_BindImage( MSIPACKAGE *package )
7131 UINT r;
7132 MSIQUERY *view;
7133 static const WCHAR query[] =
7134 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','B','i','n','d','I','m','a','g','e',0};
7136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7137 if (r == ERROR_SUCCESS)
7139 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7140 msiobj_release( &view->hdr );
7142 return ERROR_SUCCESS;
7145 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7146 LPCSTR action, LPCWSTR table )
7148 static const WCHAR query[] = {
7149 'S','E','L','E','C','T',' ','*',' ',
7150 'F','R','O','M',' ','`','%','s','`',0 };
7151 MSIQUERY *view = NULL;
7152 DWORD count = 0;
7153 UINT r;
7155 r = MSI_OpenQuery( package->db, &view, query, table );
7156 if (r == ERROR_SUCCESS)
7158 r = MSI_IterateRecords(view, &count, NULL, package);
7159 msiobj_release(&view->hdr);
7162 if (count)
7163 FIXME("%s -> %u ignored %s table values\n",
7164 action, count, debugstr_w(table));
7166 return ERROR_SUCCESS;
7169 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7171 static const WCHAR table[] = {
7172 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7173 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7176 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7178 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7179 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7182 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7184 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7185 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7188 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7190 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7191 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7194 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7196 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7197 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7200 static const struct
7202 const WCHAR *action;
7203 UINT (*handler)(MSIPACKAGE *);
7204 const WCHAR *action_rollback;
7206 StandardActions[] =
7208 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7209 { szAppSearch, ACTION_AppSearch, NULL },
7210 { szBindImage, ACTION_BindImage, NULL },
7211 { szCCPSearch, ACTION_CCPSearch, NULL },
7212 { szCostFinalize, ACTION_CostFinalize, NULL },
7213 { szCostInitialize, ACTION_CostInitialize, NULL },
7214 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7215 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7216 { szDeleteServices, ACTION_DeleteServices, NULL },
7217 { szDisableRollback, ACTION_DisableRollback, NULL },
7218 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7219 { szExecuteAction, ACTION_ExecuteAction, NULL },
7220 { szFileCost, ACTION_FileCost, NULL },
7221 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7222 { szForceReboot, ACTION_ForceReboot, NULL },
7223 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7224 { szInstallExecute, ACTION_InstallExecute, NULL },
7225 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7226 { szInstallFiles, ACTION_InstallFiles, NULL },
7227 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7228 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7229 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7230 { szInstallValidate, ACTION_InstallValidate, NULL },
7231 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7232 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7233 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7234 { szMoveFiles, ACTION_MoveFiles, NULL },
7235 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7236 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, NULL },
7237 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7238 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7239 { szPatchFiles, ACTION_PatchFiles, NULL },
7240 { szProcessComponents, ACTION_ProcessComponents, NULL },
7241 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7242 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7243 { szPublishProduct, ACTION_PublishProduct, NULL },
7244 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7245 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7246 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7247 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7248 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7249 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7250 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7251 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7252 { szRegisterUser, ACTION_RegisterUser, NULL },
7253 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, NULL },
7254 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, NULL },
7255 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7256 { szRemoveFiles, ACTION_RemoveFiles, NULL },
7257 { szRemoveFolders, ACTION_RemoveFolders, NULL },
7258 { szRemoveIniValues, ACTION_RemoveIniValues, NULL },
7259 { szRemoveODBC, ACTION_RemoveODBC, NULL },
7260 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, NULL },
7261 { szRemoveShortcuts, ACTION_RemoveShortcuts, NULL },
7262 { szResolveSource, ACTION_ResolveSource, NULL },
7263 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7264 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7265 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7266 { szSelfUnregModules, ACTION_SelfUnregModules, NULL },
7267 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7268 { szStartServices, ACTION_StartServices, szStopServices },
7269 { szStopServices, ACTION_StopServices, NULL },
7270 { szUnpublishComponents, ACTION_UnpublishComponents, NULL },
7271 { szUnpublishFeatures, ACTION_UnpublishFeatures, NULL },
7272 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, NULL },
7273 { szUnregisterComPlus, ACTION_UnregisterComPlus, NULL },
7274 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, NULL },
7275 { szUnregisterFonts, ACTION_UnregisterFonts, NULL },
7276 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, NULL },
7277 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, NULL },
7278 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, NULL },
7279 { szValidateProductID, ACTION_ValidateProductID, NULL },
7280 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7281 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7282 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7283 { NULL, NULL, NULL }
7286 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7288 BOOL ret = FALSE;
7289 UINT i;
7291 i = 0;
7292 while (StandardActions[i].action != NULL)
7294 if (!strcmpW( StandardActions[i].action, action ))
7296 ui_actionstart( package, action );
7297 if (StandardActions[i].handler)
7299 ui_actioninfo( package, action, TRUE, 0 );
7300 *rc = StandardActions[i].handler( package );
7301 ui_actioninfo( package, action, FALSE, *rc );
7303 if (StandardActions[i].action_rollback &&
7304 !msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7306 TRACE("scheduling rollback action\n");
7307 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7310 else
7312 FIXME("unhandled standard action %s\n", debugstr_w(action));
7313 *rc = ERROR_SUCCESS;
7315 ret = TRUE;
7316 break;
7318 i++;
7320 return ret;
7323 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7325 UINT rc = ERROR_SUCCESS;
7326 BOOL handled;
7328 TRACE("Performing action (%s)\n", debugstr_w(action));
7330 handled = ACTION_HandleStandardAction(package, action, &rc);
7332 if (!handled)
7333 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7335 if (!handled)
7337 WARN("unhandled msi action %s\n", debugstr_w(action));
7338 rc = ERROR_FUNCTION_NOT_CALLED;
7341 return rc;
7344 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7346 UINT rc = ERROR_SUCCESS;
7347 BOOL handled = FALSE;
7349 TRACE("Performing action (%s)\n", debugstr_w(action));
7351 handled = ACTION_HandleStandardAction(package, action, &rc);
7353 if (!handled)
7354 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7356 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7357 handled = TRUE;
7359 if (!handled)
7361 WARN("unhandled msi action %s\n", debugstr_w(action));
7362 rc = ERROR_FUNCTION_NOT_CALLED;
7365 return rc;
7368 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7370 UINT rc = ERROR_SUCCESS;
7371 MSIRECORD *row;
7373 static const WCHAR ExecSeqQuery[] =
7374 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7375 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7376 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7377 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7378 static const WCHAR UISeqQuery[] =
7379 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7380 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7381 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7382 ' ', '=',' ','%','i',0};
7384 if (needs_ui_sequence(package))
7385 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7386 else
7387 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7389 if (row)
7391 LPCWSTR action, cond;
7393 TRACE("Running the actions\n");
7395 /* check conditions */
7396 cond = MSI_RecordGetString(row, 2);
7398 /* this is a hack to skip errors in the condition code */
7399 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7401 msiobj_release(&row->hdr);
7402 return ERROR_SUCCESS;
7405 action = MSI_RecordGetString(row, 1);
7406 if (!action)
7408 ERR("failed to fetch action\n");
7409 msiobj_release(&row->hdr);
7410 return ERROR_FUNCTION_FAILED;
7413 if (needs_ui_sequence(package))
7414 rc = ACTION_PerformUIAction(package, action, -1);
7415 else
7416 rc = ACTION_PerformAction(package, action, -1);
7418 msiobj_release(&row->hdr);
7421 return rc;
7424 /****************************************************
7425 * TOP level entry points
7426 *****************************************************/
7428 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7429 LPCWSTR szCommandLine )
7431 UINT rc;
7432 BOOL ui_exists;
7433 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7434 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7435 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7437 msi_set_property( package->db, szAction, szInstall );
7439 package->script->InWhatSequence = SEQUENCE_INSTALL;
7441 if (szPackagePath)
7443 LPWSTR p, dir;
7444 LPCWSTR file;
7446 dir = strdupW(szPackagePath);
7447 p = strrchrW(dir, '\\');
7448 if (p)
7450 *(++p) = 0;
7451 file = szPackagePath + (p - dir);
7453 else
7455 msi_free(dir);
7456 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7457 GetCurrentDirectoryW(MAX_PATH, dir);
7458 lstrcatW(dir, szBackSlash);
7459 file = szPackagePath;
7462 msi_free( package->PackagePath );
7463 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7464 if (!package->PackagePath)
7466 msi_free(dir);
7467 return ERROR_OUTOFMEMORY;
7470 lstrcpyW(package->PackagePath, dir);
7471 lstrcatW(package->PackagePath, file);
7472 msi_free(dir);
7474 msi_set_sourcedir_props(package, FALSE);
7477 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7478 if (rc != ERROR_SUCCESS)
7479 return rc;
7481 msi_apply_transforms( package );
7482 msi_apply_patches( package );
7484 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7486 TRACE("setting reinstall property\n");
7487 msi_set_property( package->db, szReinstall, szAll );
7490 /* properties may have been added by a transform */
7491 msi_clone_properties( package );
7493 msi_parse_command_line( package, szCommandLine, FALSE );
7494 msi_adjust_privilege_properties( package );
7495 msi_set_context( package );
7497 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7499 TRACE("disabling rollback\n");
7500 msi_set_property( package->db, szRollbackDisabled, szOne );
7503 if (needs_ui_sequence( package))
7505 package->script->InWhatSequence |= SEQUENCE_UI;
7506 rc = ACTION_ProcessUISequence(package);
7507 ui_exists = ui_sequence_exists(package);
7508 if (rc == ERROR_SUCCESS || !ui_exists)
7510 package->script->InWhatSequence |= SEQUENCE_EXEC;
7511 rc = ACTION_ProcessExecSequence(package, ui_exists);
7514 else
7515 rc = ACTION_ProcessExecSequence(package, FALSE);
7517 package->script->CurrentlyScripting = FALSE;
7519 /* process the ending type action */
7520 if (rc == ERROR_SUCCESS)
7521 ACTION_PerformActionSequence(package, -1);
7522 else if (rc == ERROR_INSTALL_USEREXIT)
7523 ACTION_PerformActionSequence(package, -2);
7524 else if (rc == ERROR_INSTALL_SUSPEND)
7525 ACTION_PerformActionSequence(package, -4);
7526 else /* failed */
7528 ACTION_PerformActionSequence(package, -3);
7529 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7531 package->need_rollback = TRUE;
7535 /* finish up running custom actions */
7536 ACTION_FinishCustomActions(package);
7538 if (package->need_rollback)
7540 WARN("installation failed, running rollback script\n");
7541 msi_set_property( package->db, szRollbackDisabled, NULL );
7542 execute_script( package, ROLLBACK_SCRIPT );
7545 if (rc == ERROR_SUCCESS && package->need_reboot)
7546 return ERROR_SUCCESS_REBOOT_REQUIRED;
7548 return rc;