msi: Add an offset to sequence numbers belonging to files added by a patch.
[wine.git] / dlls / msi / action.c
blob145a1a6725d805dd0b1881c57a7ae30af296e65e
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 "wine/unicode.h"
40 #include "winver.h"
42 #define REG_PROGRESS_VALUE 13200
43 #define COMPONENT_PROGRESS_VALUE 24000
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 * consts and values used
50 static const WCHAR c_colon[] = {'C',':','\\',0};
52 static const WCHAR szCreateFolders[] =
53 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
54 static const WCHAR szCostFinalize[] =
55 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
56 static const WCHAR szWriteRegistryValues[] =
57 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
58 static const WCHAR szCostInitialize[] =
59 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szFileCost[] =
61 {'F','i','l','e','C','o','s','t',0};
62 static const WCHAR szInstallInitialize[] =
63 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
64 static const WCHAR szInstallValidate[] =
65 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
66 static const WCHAR szLaunchConditions[] =
67 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
68 static const WCHAR szProcessComponents[] =
69 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
70 static const WCHAR szRegisterTypeLibraries[] =
71 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
72 static const WCHAR szCreateShortcuts[] =
73 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
74 static const WCHAR szPublishProduct[] =
75 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
76 static const WCHAR szWriteIniValues[] =
77 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
78 static const WCHAR szSelfRegModules[] =
79 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
80 static const WCHAR szPublishFeatures[] =
81 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
82 static const WCHAR szRegisterProduct[] =
83 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
84 static const WCHAR szInstallExecute[] =
85 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
86 static const WCHAR szInstallExecuteAgain[] =
87 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
88 static const WCHAR szInstallFinalize[] =
89 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
90 static const WCHAR szForceReboot[] =
91 {'F','o','r','c','e','R','e','b','o','o','t',0};
92 static const WCHAR szResolveSource[] =
93 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
94 static const WCHAR szAllocateRegistrySpace[] =
95 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
96 static const WCHAR szBindImage[] =
97 {'B','i','n','d','I','m','a','g','e',0};
98 static const WCHAR szDeleteServices[] =
99 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
100 static const WCHAR szDisableRollback[] =
101 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
102 static const WCHAR szExecuteAction[] =
103 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
104 static const WCHAR szInstallAdminPackage[] =
105 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
106 static const WCHAR szInstallSFPCatalogFile[] =
107 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
108 static const WCHAR szIsolateComponents[] =
109 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
110 static const WCHAR szMigrateFeatureStates[] =
111 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
112 static const WCHAR szMsiUnpublishAssemblies[] =
113 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
114 static const WCHAR szInstallODBC[] =
115 {'I','n','s','t','a','l','l','O','D','B','C',0};
116 static const WCHAR szInstallServices[] =
117 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
118 static const WCHAR szPatchFiles[] =
119 {'P','a','t','c','h','F','i','l','e','s',0};
120 static const WCHAR szPublishComponents[] =
121 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
122 static const WCHAR szRegisterComPlus[] =
123 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
124 static const WCHAR szRegisterUser[] =
125 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
126 static const WCHAR szRemoveEnvironmentStrings[] =
127 {'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};
128 static const WCHAR szRemoveExistingProducts[] =
129 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
130 static const WCHAR szRemoveFolders[] =
131 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
132 static const WCHAR szRemoveIniValues[] =
133 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
134 static const WCHAR szRemoveODBC[] =
135 {'R','e','m','o','v','e','O','D','B','C',0};
136 static const WCHAR szRemoveRegistryValues[] =
137 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
138 static const WCHAR szRemoveShortcuts[] =
139 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
140 static const WCHAR szRMCCPSearch[] =
141 {'R','M','C','C','P','S','e','a','r','c','h',0};
142 static const WCHAR szScheduleReboot[] =
143 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
144 static const WCHAR szSelfUnregModules[] =
145 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
146 static const WCHAR szSetODBCFolders[] =
147 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
148 static const WCHAR szStartServices[] =
149 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
150 static const WCHAR szStopServices[] =
151 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
152 static const WCHAR szUnpublishComponents[] =
153 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
154 static const WCHAR szUnpublishFeatures[] =
155 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
156 static const WCHAR szUnregisterComPlus[] =
157 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
158 static const WCHAR szUnregisterTypeLibraries[] =
159 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
160 static const WCHAR szValidateProductID[] =
161 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
162 static const WCHAR szWriteEnvironmentStrings[] =
163 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
165 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
167 static const WCHAR Query_t[] =
168 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
169 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
170 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
171 ' ','\'','%','s','\'',0};
172 MSIRECORD * row;
174 row = MSI_QueryGetRecord( package->db, Query_t, action );
175 if (!row)
176 return;
177 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
178 msiobj_release(&row->hdr);
181 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
182 UINT rc)
184 MSIRECORD * row;
185 static const WCHAR template_s[]=
186 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
187 '%','s', '.',0};
188 static const WCHAR template_e[]=
189 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
190 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
191 '%','i','.',0};
192 static const WCHAR format[] =
193 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
194 WCHAR message[1024];
195 WCHAR timet[0x100];
197 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
198 if (start)
199 sprintfW(message,template_s,timet,action);
200 else
201 sprintfW(message,template_e,timet,action,rc);
203 row = MSI_CreateRecord(1);
204 MSI_RecordSetStringW(row,1,message);
206 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
207 msiobj_release(&row->hdr);
210 enum parse_state
212 state_whitespace,
213 state_token,
214 state_quote
217 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
219 enum parse_state state = state_quote;
220 const WCHAR *p;
221 WCHAR *out = value;
222 int ignore, in_quotes = 0, count = 0, len = 0;
224 for (p = str; *p; p++)
226 ignore = 0;
227 switch (state)
229 case state_whitespace:
230 switch (*p)
232 case ' ':
233 if (!count) goto done;
234 in_quotes = 1;
235 ignore = 1;
236 break;
237 case '"':
238 state = state_quote;
239 if (in_quotes) count--;
240 else count++;
241 break;
242 default:
243 state = state_token;
244 if (!count) in_quotes = 0;
245 else in_quotes = 1;
246 len++;
247 break;
249 break;
251 case state_token:
252 switch (*p)
254 case '"':
255 state = state_quote;
256 if (in_quotes) count--;
257 else count++;
258 break;
259 case ' ':
260 state = state_whitespace;
261 if (!count) goto done;
262 in_quotes = 1;
263 break;
264 default:
265 if (!count) in_quotes = 0;
266 else in_quotes = 1;
267 len++;
268 break;
270 break;
272 case state_quote:
273 switch (*p)
275 case '"':
276 if (in_quotes) count--;
277 else count++;
278 break;
279 case ' ':
280 state = state_whitespace;
281 if (!count || !len) goto done;
282 in_quotes = 1;
283 break;
284 default:
285 state = state_token;
286 if (!count) in_quotes = 0;
287 else in_quotes = 1;
288 len++;
289 break;
291 break;
293 default: break;
295 if (!ignore) *out++ = *p;
298 done:
299 if (!len) *value = 0;
300 else *out = 0;
302 *quotes = count;
303 return p - str;
306 static void remove_quotes( WCHAR *str )
308 WCHAR *p = str;
309 int len = strlenW( str );
311 while ((p = strchrW( p, '"' )))
313 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
314 p++;
318 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
319 BOOL preserve_case )
321 LPCWSTR ptr, ptr2;
322 int quotes;
323 DWORD len;
324 WCHAR *prop, *val;
325 UINT r;
327 if (!szCommandLine)
328 return ERROR_SUCCESS;
330 ptr = szCommandLine;
331 while (*ptr)
333 while (*ptr == ' ') ptr++;
334 if (!*ptr) break;
336 ptr2 = strchrW( ptr, '=' );
337 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
339 len = ptr2 - ptr;
340 if (!len) return ERROR_INVALID_COMMAND_LINE;
342 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
343 memcpy( prop, ptr, len * sizeof(WCHAR) );
344 prop[len] = 0;
345 if (!preserve_case) struprW( prop );
347 ptr2++;
348 while (*ptr2 == ' ') ptr2++;
350 quotes = 0;
351 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
352 len = parse_prop( ptr2, val, &quotes );
353 if (quotes % 2)
355 WARN("unbalanced quotes\n");
356 msi_free( val );
357 msi_free( prop );
358 return ERROR_INVALID_COMMAND_LINE;
360 remove_quotes( val );
361 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
363 r = msi_set_property( package->db, prop, val );
364 if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
365 msi_reset_folders( package, TRUE );
367 msi_free( val );
368 msi_free( prop );
370 ptr = ptr2 + len;
373 return ERROR_SUCCESS;
376 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
378 LPCWSTR pc;
379 LPWSTR p, *ret = NULL;
380 UINT count = 0;
382 if (!str)
383 return ret;
385 /* count the number of substrings */
386 for ( pc = str, count = 0; pc; count++ )
388 pc = strchrW( pc, sep );
389 if (pc)
390 pc++;
393 /* allocate space for an array of substring pointers and the substrings */
394 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
395 (lstrlenW(str)+1) * sizeof(WCHAR) );
396 if (!ret)
397 return ret;
399 /* copy the string and set the pointers */
400 p = (LPWSTR) &ret[count+1];
401 lstrcpyW( p, str );
402 for( count = 0; (ret[count] = p); count++ )
404 p = strchrW( p, sep );
405 if (p)
406 *p++ = 0;
409 return ret;
412 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
414 static const WCHAR szSystemLanguageID[] =
415 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
417 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
418 UINT ret = ERROR_FUNCTION_FAILED;
420 prod_code = msi_dup_property( package->db, szProductCode );
421 patch_product = msi_get_suminfo_product( patch );
423 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
425 if ( strstrW( patch_product, prod_code ) )
427 MSISUMMARYINFO *si;
428 const WCHAR *p;
430 si = MSI_GetSummaryInformationW( patch, 0 );
431 if (!si)
433 ERR("no summary information!\n");
434 goto end;
437 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
438 if (!template)
440 ERR("no template property!\n");
441 msiobj_release( &si->hdr );
442 goto end;
445 if (!template[0])
447 ret = ERROR_SUCCESS;
448 msiobj_release( &si->hdr );
449 goto end;
452 langid = msi_dup_property( package->db, szSystemLanguageID );
453 if (!langid)
455 msiobj_release( &si->hdr );
456 goto end;
459 p = strchrW( template, ';' );
460 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
462 TRACE("applicable transform\n");
463 ret = ERROR_SUCCESS;
466 /* FIXME: check platform */
468 msiobj_release( &si->hdr );
471 end:
472 msi_free( patch_product );
473 msi_free( prod_code );
474 msi_free( template );
475 msi_free( langid );
477 return ret;
480 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
481 MSIDATABASE *patch_db, LPCWSTR name )
483 UINT ret = ERROR_FUNCTION_FAILED;
484 IStorage *stg = NULL;
485 HRESULT r;
487 TRACE("%p %s\n", package, debugstr_w(name) );
489 if (*name++ != ':')
491 ERR("expected a colon in %s\n", debugstr_w(name));
492 return ERROR_FUNCTION_FAILED;
495 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
496 if (SUCCEEDED(r))
498 ret = msi_check_transform_applicable( package, stg );
499 if (ret == ERROR_SUCCESS)
500 msi_table_apply_transform( package->db, stg );
501 else
502 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
503 IStorage_Release( stg );
505 else
506 ERR("failed to open substorage %s\n", debugstr_w(name));
508 return ERROR_SUCCESS;
511 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
513 LPWSTR guid_list, *guids, product_code;
514 UINT i, ret = ERROR_FUNCTION_FAILED;
516 product_code = msi_dup_property( package->db, szProductCode );
517 if (!product_code)
519 /* FIXME: the property ProductCode should be written into the DB somewhere */
520 ERR("no product code to check\n");
521 return ERROR_SUCCESS;
524 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
525 guids = msi_split_string( guid_list, ';' );
526 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
528 if (!strcmpW( guids[i], product_code ))
529 ret = ERROR_SUCCESS;
531 msi_free( guids );
532 msi_free( guid_list );
533 msi_free( product_code );
535 return ret;
538 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
540 MSIQUERY *view;
541 MSIRECORD *rec = NULL;
542 LPWSTR patch;
543 LPCWSTR prop;
544 UINT r;
546 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
547 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
548 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
549 '`','S','o','u','r','c','e','`',' ','I','S',' ',
550 'N','O','T',' ','N','U','L','L',0};
552 r = MSI_DatabaseOpenViewW(package->db, query, &view);
553 if (r != ERROR_SUCCESS)
554 return r;
556 r = MSI_ViewExecute(view, 0);
557 if (r != ERROR_SUCCESS)
558 goto done;
560 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
562 prop = MSI_RecordGetString(rec, 1);
563 patch = msi_dup_property(package->db, szPatch);
564 msi_set_property(package->db, prop, patch);
565 msi_free(patch);
568 done:
569 if (rec) msiobj_release(&rec->hdr);
570 msiobj_release(&view->hdr);
572 return r;
575 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
577 MSIPATCHINFO *pi;
578 UINT r = ERROR_SUCCESS;
579 WCHAR *p;
581 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
582 if (!pi)
583 return ERROR_OUTOFMEMORY;
585 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
586 if (!pi->patchcode)
588 msi_free( pi );
589 return ERROR_OUTOFMEMORY;
592 p = pi->patchcode;
593 if (*p != '{')
595 msi_free( pi->patchcode );
596 msi_free( pi );
597 return ERROR_PATCH_PACKAGE_INVALID;
600 p = strchrW( p + 1, '}' );
601 if (!p)
603 msi_free( pi->patchcode );
604 msi_free( pi );
605 return ERROR_PATCH_PACKAGE_INVALID;
608 if (p[1])
610 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
611 p[1] = 0;
614 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
616 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
617 if (!pi->transforms)
619 msi_free( pi->patchcode );
620 msi_free( pi );
621 return ERROR_OUTOFMEMORY;
624 *patch = pi;
625 return r;
628 struct msi_patch_offset
630 struct list entry;
631 LPWSTR Name;
632 UINT Sequence;
635 struct msi_patch_offset_list
637 struct list files;
638 UINT count, min, max;
639 UINT offset_to_apply;
642 static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
644 struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
645 list_init( &pos->files );
646 pos->count = pos->max = 0;
647 pos->min = 999999;
649 return pos;
652 static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
654 struct msi_patch_offset *po, *po2;
656 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
658 msi_free( po->Name );
659 msi_free( po );
662 msi_free( pos );
665 static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
667 MSIQUERY *view;
668 MSIRECORD *rec;
669 UINT r;
670 static const WCHAR query_patch[] = {
671 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
672 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
673 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
675 r = MSI_DatabaseOpenViewW( db, query_patch, &view );
676 if (r != ERROR_SUCCESS)
677 return;
679 rec = MSI_CreateRecord( 1 );
680 MSI_RecordSetInteger(rec, 1, last_sequence);
682 r = MSI_ViewExecute( view, rec );
683 msiobj_release( &rec->hdr );
684 if (r != ERROR_SUCCESS)
685 return;
687 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
689 UINT sequence = MSI_RecordGetInteger( rec, 2 );
691 /* FIXME:
692 * We only use the max/min sequence numbers for now.
695 pos->min = min(pos->min, sequence);
696 pos->max = max(pos->max, sequence);
697 pos->count++;
699 msiobj_release( &rec->hdr );
702 msiobj_release( &view->hdr );
705 static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
707 MSIQUERY *view;
708 MSIRECORD *rec;
709 UINT r;
710 static const WCHAR query_files[] = {
711 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
712 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
713 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
715 r = MSI_DatabaseOpenViewW( db, query_files, &view );
716 if (r != ERROR_SUCCESS)
717 return;
719 rec = MSI_CreateRecord( 1 );
720 MSI_RecordSetInteger(rec, 1, last_sequence);
722 r = MSI_ViewExecute( view, rec );
723 msiobj_release( &rec->hdr );
724 if (r != ERROR_SUCCESS)
725 return;
727 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
729 UINT attributes = MSI_RecordGetInteger( rec, 7 );
730 if (attributes & msidbFileAttributesPatchAdded)
732 struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
734 po->Name = msi_dup_record_field( rec, 1 );
735 po->Sequence = MSI_RecordGetInteger( rec, 8 );
737 pos->min = min(pos->min, po->Sequence);
738 pos->max = max(pos->max, po->Sequence);
740 list_add_tail( &pos->files, &po->entry );
741 pos->count++;
743 msiobj_release( &rec->hdr );
746 msiobj_release( &view->hdr );
749 static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
751 static const WCHAR query_files[] =
752 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
753 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
754 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
755 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
756 struct msi_patch_offset *po;
757 MSIQUERY *view;
758 MSIRECORD *rec;
759 UINT r;
761 r = MSI_DatabaseOpenViewW( db, query_files, &view );
762 if (r != ERROR_SUCCESS)
763 return ERROR_SUCCESS;
765 rec = MSI_CreateRecord( 2 );
766 MSI_RecordSetInteger( rec, 1, pos->min );
767 MSI_RecordSetInteger( rec, 2, pos->max );
769 r = MSI_ViewExecute( view, rec );
770 msiobj_release( &rec->hdr );
771 if (r != ERROR_SUCCESS)
772 goto done;
774 LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
776 UINT r_fetch;
777 while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
779 LPCWSTR file = MSI_RecordGetString( rec, 1 );
780 UINT seq;
782 if (!strcmpiW(file, po->Name))
784 /* Update record */
785 seq = MSI_RecordGetInteger( rec, 8 );
786 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
787 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
788 if (r != ERROR_SUCCESS)
789 ERR("Failed to update offset for file %s.\n", debugstr_w(file));
791 msiobj_release( &rec->hdr );
792 break;
795 msiobj_release( &rec->hdr );
798 if (r_fetch != ERROR_SUCCESS)
799 break;
802 done:
803 msiobj_release( &view->hdr );
805 return ERROR_SUCCESS;
808 static UINT msi_set_patch_offsets(MSIDATABASE *db)
810 static const WCHAR query_media[] = {
811 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','M','e','d','i','a',' ',
812 'W','H','E','R','E',' ','S','o','u','r','c','e',' ','I','S',' ','N','O','T',' ','N','U','L','L',
813 ' ','A','N','D',' ','C','a','b','i','n','e','t',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
814 'O','R','D','E','R',' ','B','Y',' ','D','i','s','k','I','d',0};
815 MSIQUERY *view = NULL;
816 MSIRECORD *rec = NULL;
817 UINT r;
819 r = MSI_DatabaseOpenViewW( db, query_media, &view );
820 if (r != ERROR_SUCCESS)
821 return r;
823 r = MSI_ViewExecute( view, 0 );
824 if (r != ERROR_SUCCESS)
825 goto done;
827 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
829 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
830 struct msi_patch_offset_list *pos;
832 /* FIXME: Set/Check Source field instead? */
833 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
835 msiobj_release( &rec->hdr );
836 continue;
839 pos = msi_patch_offset_list_create();
841 msi_patch_offset_get_files( db, last_sequence, pos );
842 msi_patch_offset_get_patches( db, last_sequence, pos );
844 if (pos->count)
846 UINT offset = db->media_transform_offset - pos->min;
847 last_sequence = offset + pos->max;
849 /* FIXME:
850 * This is for the patch table, which is not yet properly transformed.
852 last_sequence += pos->min;
854 pos->offset_to_apply = offset;
855 msi_patch_offset_modify_db( db, pos );
857 MSI_RecordSetInteger( rec, 2, last_sequence );
858 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
859 if (r != ERROR_SUCCESS)
860 ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
862 db->media_transform_offset = last_sequence + 1;
865 msi_patch_offset_list_free( pos );
866 msiobj_release( &rec->hdr );
869 done:
870 msiobj_release( &view->hdr );
872 return r;
875 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
877 UINT i, r = ERROR_SUCCESS;
878 WCHAR **substorage;
880 /* apply substorage transforms */
881 substorage = msi_split_string( patch->transforms, ';' );
882 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
884 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
885 if (r == ERROR_SUCCESS)
886 msi_set_patch_offsets( package->db );
889 msi_free( substorage );
890 if (r != ERROR_SUCCESS)
891 return r;
893 msi_set_media_source_prop( package );
896 * There might be a CAB file in the patch package,
897 * so append it to the list of storages to search for streams.
899 append_storage_to_db( package->db, patch_db->storage );
901 patch->state = MSIPATCHSTATE_APPLIED;
902 list_add_tail( &package->patches, &patch->entry );
903 return ERROR_SUCCESS;
906 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
908 static const WCHAR dotmsp[] = {'.','m','s','p',0};
909 MSIDATABASE *patch_db = NULL;
910 WCHAR localfile[MAX_PATH];
911 MSISUMMARYINFO *si;
912 MSIPATCHINFO *patch = NULL;
913 UINT r = ERROR_SUCCESS;
915 TRACE("%p %s\n", package, debugstr_w( file ) );
917 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
918 if ( r != ERROR_SUCCESS )
920 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
921 return r;
924 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
925 if (!si)
927 msiobj_release( &patch_db->hdr );
928 return ERROR_FUNCTION_FAILED;
931 r = msi_check_patch_applicable( package, si );
932 if (r != ERROR_SUCCESS)
934 TRACE("patch not applicable\n");
935 r = ERROR_SUCCESS;
936 goto done;
939 r = msi_parse_patch_summary( si, &patch );
940 if ( r != ERROR_SUCCESS )
941 goto done;
943 r = msi_get_local_package_name( localfile, dotmsp );
944 if ( r != ERROR_SUCCESS )
945 goto done;
947 TRACE("copying to local package %s\n", debugstr_w(localfile));
949 if (!CopyFileW( file, localfile, FALSE ))
951 ERR("Unable to copy package (%s -> %s) (error %u)\n",
952 debugstr_w(file), debugstr_w(localfile), GetLastError());
953 r = GetLastError();
954 goto done;
956 patch->localfile = strdupW( localfile );
958 r = msi_apply_patch_db( package, patch_db, patch );
959 if ( r != ERROR_SUCCESS )
960 WARN("patch failed to apply %u\n", r);
962 done:
963 msiobj_release( &si->hdr );
964 msiobj_release( &patch_db->hdr );
965 if (patch && r != ERROR_SUCCESS)
967 if (patch->localfile)
968 DeleteFileW( patch->localfile );
970 msi_free( patch->patchcode );
971 msi_free( patch->transforms );
972 msi_free( patch->localfile );
973 msi_free( patch );
975 return r;
978 /* get the PATCH property, and apply all the patches it specifies */
979 static UINT msi_apply_patches( MSIPACKAGE *package )
981 LPWSTR patch_list, *patches;
982 UINT i, r = ERROR_SUCCESS;
984 patch_list = msi_dup_property( package->db, szPatch );
986 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
988 patches = msi_split_string( patch_list, ';' );
989 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
990 r = msi_apply_patch_package( package, patches[i] );
992 msi_free( patches );
993 msi_free( patch_list );
995 return r;
998 static UINT msi_apply_transforms( MSIPACKAGE *package )
1000 static const WCHAR szTransforms[] = {
1001 'T','R','A','N','S','F','O','R','M','S',0 };
1002 LPWSTR xform_list, *xforms;
1003 UINT i, r = ERROR_SUCCESS;
1005 xform_list = msi_dup_property( package->db, szTransforms );
1006 xforms = msi_split_string( xform_list, ';' );
1008 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
1010 if (xforms[i][0] == ':')
1011 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
1012 else
1014 WCHAR *transform;
1016 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1017 else
1019 WCHAR *p = strrchrW( package->PackagePath, '\\' );
1020 DWORD len = p - package->PackagePath + 1;
1022 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1024 msi_free( xforms );
1025 msi_free( xform_list );
1026 return ERROR_OUTOFMEMORY;
1028 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1029 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1031 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1032 if (transform != xforms[i]) msi_free( transform );
1036 msi_free( xforms );
1037 msi_free( xform_list );
1039 return r;
1042 static BOOL ui_sequence_exists( MSIPACKAGE *package )
1044 MSIQUERY *view;
1045 UINT rc;
1047 static const WCHAR ExecSeqQuery [] =
1048 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1049 '`','I','n','s','t','a','l','l',
1050 'U','I','S','e','q','u','e','n','c','e','`',
1051 ' ','W','H','E','R','E',' ',
1052 '`','S','e','q','u','e','n','c','e','`',' ',
1053 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1054 '`','S','e','q','u','e','n','c','e','`',0};
1056 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1057 if (rc == ERROR_SUCCESS)
1059 msiobj_release(&view->hdr);
1060 return TRUE;
1063 return FALSE;
1066 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
1068 LPWSTR source, check;
1070 if (msi_get_property_int( package->db, szInstalled, 0 ))
1072 HKEY hkey;
1074 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
1075 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
1076 RegCloseKey( hkey );
1078 else
1080 LPWSTR p, db;
1081 DWORD len;
1083 db = msi_dup_property( package->db, szOriginalDatabase );
1084 if (!db)
1085 return ERROR_OUTOFMEMORY;
1087 p = strrchrW( db, '\\' );
1088 if (!p)
1090 p = strrchrW( db, '/' );
1091 if (!p)
1093 msi_free(db);
1094 return ERROR_SUCCESS;
1098 len = p - db + 2;
1099 source = msi_alloc( len * sizeof(WCHAR) );
1100 lstrcpynW( source, db, len );
1101 msi_free( db );
1104 check = msi_dup_property( package->db, cszSourceDir );
1105 if (!check || replace)
1107 UINT r = msi_set_property( package->db, cszSourceDir, source );
1108 if (r == ERROR_SUCCESS)
1109 msi_reset_folders( package, TRUE );
1111 msi_free( check );
1113 check = msi_dup_property( package->db, cszSOURCEDIR );
1114 if (!check || replace)
1115 msi_set_property( package->db, cszSOURCEDIR, source );
1117 msi_free( check );
1118 msi_free( source );
1120 return ERROR_SUCCESS;
1123 static BOOL needs_ui_sequence(MSIPACKAGE *package)
1125 INT level = msi_get_property_int(package->db, szUILevel, 0);
1126 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
1129 UINT msi_set_context(MSIPACKAGE *package)
1131 int num;
1133 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
1135 num = msi_get_property_int(package->db, szAllUsers, 0);
1136 if (num == 1 || num == 2)
1137 package->Context = MSIINSTALLCONTEXT_MACHINE;
1139 return ERROR_SUCCESS;
1142 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
1144 UINT rc;
1145 LPCWSTR cond, action;
1146 MSIPACKAGE *package = param;
1148 action = MSI_RecordGetString(row,1);
1149 if (!action)
1151 ERR("Error is retrieving action name\n");
1152 return ERROR_FUNCTION_FAILED;
1155 /* check conditions */
1156 cond = MSI_RecordGetString(row,2);
1158 /* this is a hack to skip errors in the condition code */
1159 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
1161 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
1162 return ERROR_SUCCESS;
1165 if (needs_ui_sequence(package))
1166 rc = ACTION_PerformUIAction(package, action, -1);
1167 else
1168 rc = ACTION_PerformAction(package, action, -1);
1170 msi_dialog_check_messages( NULL );
1172 if (package->CurrentInstallState != ERROR_SUCCESS)
1173 rc = package->CurrentInstallState;
1175 if (rc == ERROR_FUNCTION_NOT_CALLED)
1176 rc = ERROR_SUCCESS;
1178 if (rc != ERROR_SUCCESS)
1179 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
1181 return rc;
1184 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
1186 MSIQUERY * view;
1187 UINT r;
1188 static const WCHAR query[] =
1189 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1190 '`','%','s','`',
1191 ' ','W','H','E','R','E',' ',
1192 '`','S','e','q','u','e','n','c','e','`',' ',
1193 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1194 '`','S','e','q','u','e','n','c','e','`',0};
1196 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1198 r = MSI_OpenQuery( package->db, &view, query, szTable );
1199 if (r == ERROR_SUCCESS)
1201 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
1202 msiobj_release(&view->hdr);
1205 return r;
1208 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1210 MSIQUERY * view;
1211 UINT rc;
1212 static const WCHAR ExecSeqQuery[] =
1213 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1214 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1215 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1216 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1217 'O','R','D','E','R',' ', 'B','Y',' ',
1218 '`','S','e','q','u','e','n','c','e','`',0 };
1219 static const WCHAR IVQuery[] =
1220 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1221 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1222 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1223 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1224 ' ','\'', 'I','n','s','t','a','l','l',
1225 'V','a','l','i','d','a','t','e','\'', 0};
1226 INT seq = 0;
1228 if (package->script->ExecuteSequenceRun)
1230 TRACE("Execute Sequence already Run\n");
1231 return ERROR_SUCCESS;
1234 package->script->ExecuteSequenceRun = TRUE;
1236 /* get the sequence number */
1237 if (UIran)
1239 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
1240 if( !row )
1241 return ERROR_FUNCTION_FAILED;
1242 seq = MSI_RecordGetInteger(row,1);
1243 msiobj_release(&row->hdr);
1246 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1247 if (rc == ERROR_SUCCESS)
1249 TRACE("Running the actions\n");
1251 msi_set_property(package->db, cszSourceDir, NULL);
1253 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1254 msiobj_release(&view->hdr);
1257 return rc;
1260 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1262 MSIQUERY * view;
1263 UINT rc;
1264 static const WCHAR ExecSeqQuery [] =
1265 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1266 '`','I','n','s','t','a','l','l',
1267 'U','I','S','e','q','u','e','n','c','e','`',
1268 ' ','W','H','E','R','E',' ',
1269 '`','S','e','q','u','e','n','c','e','`',' ',
1270 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1271 '`','S','e','q','u','e','n','c','e','`',0};
1273 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1274 if (rc == ERROR_SUCCESS)
1276 TRACE("Running the actions\n");
1278 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1279 msiobj_release(&view->hdr);
1282 return rc;
1285 /********************************************************
1286 * ACTION helper functions and functions that perform the actions
1287 *******************************************************/
1288 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1289 UINT* rc, UINT script, BOOL force )
1291 BOOL ret=FALSE;
1292 UINT arc;
1294 arc = ACTION_CustomAction(package, action, script, force);
1296 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1298 *rc = arc;
1299 ret = TRUE;
1301 return ret;
1305 * Actual Action Handlers
1308 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1310 MSIPACKAGE *package = param;
1311 LPCWSTR dir, component;
1312 LPWSTR full_path;
1313 MSIRECORD *uirow;
1314 MSIFOLDER *folder;
1315 MSICOMPONENT *comp;
1317 component = MSI_RecordGetString(row, 2);
1318 if (!component)
1319 return ERROR_SUCCESS;
1321 comp = get_loaded_component(package, component);
1322 if (!comp)
1323 return ERROR_SUCCESS;
1325 if (!comp->Enabled)
1327 TRACE("component is disabled\n");
1328 return ERROR_SUCCESS;
1331 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1333 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1334 comp->Action = comp->Installed;
1335 return ERROR_SUCCESS;
1337 comp->Action = INSTALLSTATE_LOCAL;
1339 dir = MSI_RecordGetString(row,1);
1340 if (!dir)
1342 ERR("Unable to get folder id\n");
1343 return ERROR_SUCCESS;
1346 uirow = MSI_CreateRecord(1);
1347 MSI_RecordSetStringW(uirow, 1, dir);
1348 ui_actiondata(package, szCreateFolders, uirow);
1349 msiobj_release(&uirow->hdr);
1351 full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1352 if (!full_path)
1354 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1355 return ERROR_SUCCESS;
1358 TRACE("Folder is %s\n",debugstr_w(full_path));
1360 if (folder->State == 0)
1361 create_full_pathW(full_path);
1363 folder->State = 3;
1365 msi_free(full_path);
1366 return ERROR_SUCCESS;
1369 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1371 static const WCHAR query[] =
1372 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1373 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1374 UINT rc;
1375 MSIQUERY *view;
1377 /* create all the empty folders specified in the CreateFolder table */
1378 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1379 if (rc != ERROR_SUCCESS)
1380 return ERROR_SUCCESS;
1382 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1383 msiobj_release(&view->hdr);
1385 return rc;
1388 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1390 MSIPACKAGE *package = param;
1391 LPCWSTR dir, component;
1392 LPWSTR full_path;
1393 MSIRECORD *uirow;
1394 MSIFOLDER *folder;
1395 MSICOMPONENT *comp;
1397 component = MSI_RecordGetString(row, 2);
1398 if (!component)
1399 return ERROR_SUCCESS;
1401 comp = get_loaded_component(package, component);
1402 if (!comp)
1403 return ERROR_SUCCESS;
1405 if (!comp->Enabled)
1407 TRACE("component is disabled\n");
1408 return ERROR_SUCCESS;
1411 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1413 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1414 comp->Action = comp->Installed;
1415 return ERROR_SUCCESS;
1417 comp->Action = INSTALLSTATE_ABSENT;
1419 dir = MSI_RecordGetString( row, 1 );
1420 if (!dir)
1422 ERR("Unable to get folder id\n");
1423 return ERROR_SUCCESS;
1426 full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1427 if (!full_path)
1429 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1430 return ERROR_SUCCESS;
1433 TRACE("folder is %s\n", debugstr_w(full_path));
1435 uirow = MSI_CreateRecord( 1 );
1436 MSI_RecordSetStringW( uirow, 1, dir );
1437 ui_actiondata( package, szRemoveFolders, uirow );
1438 msiobj_release( &uirow->hdr );
1440 RemoveDirectoryW( full_path );
1441 folder->State = 0;
1443 msi_free( full_path );
1444 return ERROR_SUCCESS;
1447 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1449 static const WCHAR query[] =
1450 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1451 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1453 MSIQUERY *view;
1454 UINT rc;
1456 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1457 if (rc != ERROR_SUCCESS)
1458 return ERROR_SUCCESS;
1460 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1461 msiobj_release( &view->hdr );
1463 return rc;
1466 static UINT load_component( MSIRECORD *row, LPVOID param )
1468 MSIPACKAGE *package = param;
1469 MSICOMPONENT *comp;
1471 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1472 if (!comp)
1473 return ERROR_FUNCTION_FAILED;
1475 list_add_tail( &package->components, &comp->entry );
1477 /* fill in the data */
1478 comp->Component = msi_dup_record_field( row, 1 );
1480 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1482 comp->ComponentId = msi_dup_record_field( row, 2 );
1483 comp->Directory = msi_dup_record_field( row, 3 );
1484 comp->Attributes = MSI_RecordGetInteger(row,4);
1485 comp->Condition = msi_dup_record_field( row, 5 );
1486 comp->KeyPath = msi_dup_record_field( row, 6 );
1488 comp->Installed = INSTALLSTATE_UNKNOWN;
1489 comp->Action = INSTALLSTATE_UNKNOWN;
1490 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1492 comp->assembly = load_assembly( package, comp );
1493 return ERROR_SUCCESS;
1496 static UINT load_all_components( MSIPACKAGE *package )
1498 static const WCHAR query[] = {
1499 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1500 '`','C','o','m','p','o','n','e','n','t','`',0 };
1501 MSIQUERY *view;
1502 UINT r;
1504 if (!list_empty(&package->components))
1505 return ERROR_SUCCESS;
1507 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1508 if (r != ERROR_SUCCESS)
1509 return r;
1511 r = MSI_IterateRecords(view, NULL, load_component, package);
1512 msiobj_release(&view->hdr);
1513 return r;
1516 typedef struct {
1517 MSIPACKAGE *package;
1518 MSIFEATURE *feature;
1519 } _ilfs;
1521 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1523 ComponentList *cl;
1525 cl = msi_alloc( sizeof (*cl) );
1526 if ( !cl )
1527 return ERROR_NOT_ENOUGH_MEMORY;
1528 cl->component = comp;
1529 list_add_tail( &feature->Components, &cl->entry );
1531 return ERROR_SUCCESS;
1534 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1536 FeatureList *fl;
1538 fl = msi_alloc( sizeof(*fl) );
1539 if ( !fl )
1540 return ERROR_NOT_ENOUGH_MEMORY;
1541 fl->feature = child;
1542 list_add_tail( &parent->Children, &fl->entry );
1544 return ERROR_SUCCESS;
1547 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1549 _ilfs* ilfs = param;
1550 LPCWSTR component;
1551 MSICOMPONENT *comp;
1553 component = MSI_RecordGetString(row,1);
1555 /* check to see if the component is already loaded */
1556 comp = get_loaded_component( ilfs->package, component );
1557 if (!comp)
1559 ERR("unknown component %s\n", debugstr_w(component));
1560 return ERROR_FUNCTION_FAILED;
1563 add_feature_component( ilfs->feature, comp );
1564 comp->Enabled = TRUE;
1566 return ERROR_SUCCESS;
1569 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1571 MSIFEATURE *feature;
1573 if ( !name )
1574 return NULL;
1576 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1578 if ( !strcmpW( feature->Feature, name ) )
1579 return feature;
1582 return NULL;
1585 static UINT load_feature(MSIRECORD * row, LPVOID param)
1587 MSIPACKAGE* package = param;
1588 MSIFEATURE* feature;
1589 static const WCHAR Query1[] =
1590 {'S','E','L','E','C','T',' ',
1591 '`','C','o','m','p','o','n','e','n','t','_','`',
1592 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1593 'C','o','m','p','o','n','e','n','t','s','`',' ',
1594 'W','H','E','R','E',' ',
1595 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1596 MSIQUERY * view;
1597 UINT rc;
1598 _ilfs ilfs;
1600 /* fill in the data */
1602 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1603 if (!feature)
1604 return ERROR_NOT_ENOUGH_MEMORY;
1606 list_init( &feature->Children );
1607 list_init( &feature->Components );
1609 feature->Feature = msi_dup_record_field( row, 1 );
1611 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1613 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1614 feature->Title = msi_dup_record_field( row, 3 );
1615 feature->Description = msi_dup_record_field( row, 4 );
1617 if (!MSI_RecordIsNull(row,5))
1618 feature->Display = MSI_RecordGetInteger(row,5);
1620 feature->Level= MSI_RecordGetInteger(row,6);
1621 feature->Directory = msi_dup_record_field( row, 7 );
1622 feature->Attributes = MSI_RecordGetInteger(row,8);
1624 feature->Installed = INSTALLSTATE_UNKNOWN;
1625 feature->Action = INSTALLSTATE_UNKNOWN;
1626 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1628 list_add_tail( &package->features, &feature->entry );
1630 /* load feature components */
1632 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1633 if (rc != ERROR_SUCCESS)
1634 return ERROR_SUCCESS;
1636 ilfs.package = package;
1637 ilfs.feature = feature;
1639 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1640 msiobj_release(&view->hdr);
1642 return ERROR_SUCCESS;
1645 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1647 MSIPACKAGE* package = param;
1648 MSIFEATURE *parent, *child;
1650 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1651 if (!child)
1652 return ERROR_FUNCTION_FAILED;
1654 if (!child->Feature_Parent)
1655 return ERROR_SUCCESS;
1657 parent = find_feature_by_name( package, child->Feature_Parent );
1658 if (!parent)
1659 return ERROR_FUNCTION_FAILED;
1661 add_feature_child( parent, child );
1662 return ERROR_SUCCESS;
1665 static UINT load_all_features( MSIPACKAGE *package )
1667 static const WCHAR query[] = {
1668 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1669 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1670 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1671 MSIQUERY *view;
1672 UINT r;
1674 if (!list_empty(&package->features))
1675 return ERROR_SUCCESS;
1677 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1678 if (r != ERROR_SUCCESS)
1679 return r;
1681 r = MSI_IterateRecords( view, NULL, load_feature, package );
1682 if (r != ERROR_SUCCESS)
1683 return r;
1685 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1686 msiobj_release( &view->hdr );
1688 return r;
1691 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1693 if (!p)
1694 return p;
1695 p = strchrW(p, ch);
1696 if (!p)
1697 return p;
1698 *p = 0;
1699 return p+1;
1702 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1704 static const WCHAR query[] = {
1705 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1706 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1707 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1708 MSIQUERY *view = NULL;
1709 MSIRECORD *row = NULL;
1710 UINT r;
1712 TRACE("%s\n", debugstr_w(file->File));
1714 r = MSI_OpenQuery(package->db, &view, query, file->File);
1715 if (r != ERROR_SUCCESS)
1716 goto done;
1718 r = MSI_ViewExecute(view, NULL);
1719 if (r != ERROR_SUCCESS)
1720 goto done;
1722 r = MSI_ViewFetch(view, &row);
1723 if (r != ERROR_SUCCESS)
1724 goto done;
1726 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1727 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1728 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1729 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1730 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1732 done:
1733 if (view) msiobj_release(&view->hdr);
1734 if (row) msiobj_release(&row->hdr);
1735 return r;
1738 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1740 MSIRECORD *row;
1741 static const WCHAR query[] = {
1742 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1743 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1744 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1746 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1747 if (!row)
1749 WARN("query failed\n");
1750 return ERROR_FUNCTION_FAILED;
1753 file->disk_id = MSI_RecordGetInteger( row, 1 );
1754 msiobj_release( &row->hdr );
1755 return ERROR_SUCCESS;
1758 static UINT load_file(MSIRECORD *row, LPVOID param)
1760 MSIPACKAGE* package = param;
1761 LPCWSTR component;
1762 MSIFILE *file;
1764 /* fill in the data */
1766 file = msi_alloc_zero( sizeof (MSIFILE) );
1767 if (!file)
1768 return ERROR_NOT_ENOUGH_MEMORY;
1770 file->File = msi_dup_record_field( row, 1 );
1772 component = MSI_RecordGetString( row, 2 );
1773 file->Component = get_loaded_component( package, component );
1775 if (!file->Component)
1777 WARN("Component not found: %s\n", debugstr_w(component));
1778 msi_free(file->File);
1779 msi_free(file);
1780 return ERROR_SUCCESS;
1783 file->FileName = msi_dup_record_field( row, 3 );
1784 reduce_to_longfilename( file->FileName );
1786 file->ShortName = msi_dup_record_field( row, 3 );
1787 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1789 file->FileSize = MSI_RecordGetInteger( row, 4 );
1790 file->Version = msi_dup_record_field( row, 5 );
1791 file->Language = msi_dup_record_field( row, 6 );
1792 file->Attributes = MSI_RecordGetInteger( row, 7 );
1793 file->Sequence = MSI_RecordGetInteger( row, 8 );
1795 file->state = msifs_invalid;
1797 /* if the compressed bits are not set in the file attributes,
1798 * then read the information from the package word count property
1800 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1802 file->IsCompressed = FALSE;
1804 else if (file->Attributes &
1805 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1807 file->IsCompressed = TRUE;
1809 else if (file->Attributes & msidbFileAttributesNoncompressed)
1811 file->IsCompressed = FALSE;
1813 else
1815 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1818 load_file_hash(package, file);
1819 load_file_disk_id(package, file);
1821 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1823 list_add_tail( &package->files, &file->entry );
1825 return ERROR_SUCCESS;
1828 static UINT load_all_files(MSIPACKAGE *package)
1830 MSIQUERY * view;
1831 UINT rc;
1832 static const WCHAR Query[] =
1833 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1834 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1835 '`','S','e','q','u','e','n','c','e','`', 0};
1837 if (!list_empty(&package->files))
1838 return ERROR_SUCCESS;
1840 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1841 if (rc != ERROR_SUCCESS)
1842 return ERROR_SUCCESS;
1844 rc = MSI_IterateRecords(view, NULL, load_file, package);
1845 msiobj_release(&view->hdr);
1847 return ERROR_SUCCESS;
1850 static UINT load_folder( MSIRECORD *row, LPVOID param )
1852 MSIPACKAGE *package = param;
1853 static WCHAR szEmpty[] = { 0 };
1854 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1855 MSIFOLDER *folder;
1857 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1858 if (!folder)
1859 return ERROR_NOT_ENOUGH_MEMORY;
1861 folder->Directory = msi_dup_record_field( row, 1 );
1863 TRACE("%s\n", debugstr_w(folder->Directory));
1865 p = msi_dup_record_field(row, 3);
1867 /* split src and target dir */
1868 tgt_short = p;
1869 src_short = folder_split_path( p, ':' );
1871 /* split the long and short paths */
1872 tgt_long = folder_split_path( tgt_short, '|' );
1873 src_long = folder_split_path( src_short, '|' );
1875 /* check for no-op dirs */
1876 if (tgt_short && !strcmpW( szDot, tgt_short ))
1877 tgt_short = szEmpty;
1878 if (src_short && !strcmpW( szDot, src_short ))
1879 src_short = szEmpty;
1881 if (!tgt_long)
1882 tgt_long = tgt_short;
1884 if (!src_short) {
1885 src_short = tgt_short;
1886 src_long = tgt_long;
1889 if (!src_long)
1890 src_long = src_short;
1892 /* FIXME: use the target short path too */
1893 folder->TargetDefault = strdupW(tgt_long);
1894 folder->SourceShortPath = strdupW(src_short);
1895 folder->SourceLongPath = strdupW(src_long);
1896 msi_free(p);
1898 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1899 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1900 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1902 folder->Parent = msi_dup_record_field( row, 2 );
1904 folder->Property = msi_dup_property( package->db, folder->Directory );
1906 list_add_tail( &package->folders, &folder->entry );
1908 TRACE("returning %p\n", folder);
1910 return ERROR_SUCCESS;
1913 static UINT load_all_folders( MSIPACKAGE *package )
1915 static const WCHAR query[] = {
1916 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1917 '`','D','i','r','e','c','t','o','r','y','`',0 };
1918 MSIQUERY *view;
1919 UINT r;
1921 if (!list_empty(&package->folders))
1922 return ERROR_SUCCESS;
1924 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1925 if (r != ERROR_SUCCESS)
1926 return r;
1928 r = MSI_IterateRecords(view, NULL, load_folder, package);
1929 msiobj_release(&view->hdr);
1930 return r;
1934 * I am not doing any of the costing functionality yet.
1935 * Mostly looking at doing the Component and Feature loading
1937 * The native MSI does A LOT of modification to tables here. Mostly adding
1938 * a lot of temporary columns to the Feature and Component tables.
1940 * note: Native msi also tracks the short filename. But I am only going to
1941 * track the long ones. Also looking at this directory table
1942 * it appears that the directory table does not get the parents
1943 * resolved base on property only based on their entries in the
1944 * directory table.
1946 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1948 static const WCHAR szCosting[] =
1949 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1951 msi_set_property( package->db, szCosting, szZero );
1952 msi_set_property( package->db, cszRootDrive, c_colon );
1954 load_all_folders( package );
1955 load_all_components( package );
1956 load_all_features( package );
1957 load_all_files( package );
1959 return ERROR_SUCCESS;
1962 static UINT execute_script(MSIPACKAGE *package, UINT script )
1964 UINT i;
1965 UINT rc = ERROR_SUCCESS;
1967 TRACE("Executing Script %i\n",script);
1969 if (!package->script)
1971 ERR("no script!\n");
1972 return ERROR_FUNCTION_FAILED;
1975 for (i = 0; i < package->script->ActionCount[script]; i++)
1977 LPWSTR action;
1978 action = package->script->Actions[script][i];
1979 ui_actionstart(package, action);
1980 TRACE("Executing Action (%s)\n",debugstr_w(action));
1981 rc = ACTION_PerformAction(package, action, script);
1982 if (rc != ERROR_SUCCESS)
1983 break;
1985 msi_free_action_script(package, script);
1986 return rc;
1989 static UINT ACTION_FileCost(MSIPACKAGE *package)
1991 return ERROR_SUCCESS;
1994 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1996 MSICOMPONENT *comp;
1997 UINT r;
1999 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
2001 if (!comp->ComponentId) continue;
2003 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2004 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
2005 &comp->Installed );
2006 if (r != ERROR_SUCCESS)
2007 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2008 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
2009 &comp->Installed );
2010 if (r != ERROR_SUCCESS)
2011 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2012 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
2013 &comp->Installed );
2014 if (r != ERROR_SUCCESS)
2015 comp->Installed = INSTALLSTATE_ABSENT;
2019 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
2021 MSIFEATURE *feature;
2023 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2025 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
2027 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
2028 feature->Installed = INSTALLSTATE_ABSENT;
2029 else
2030 feature->Installed = state;
2034 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
2036 return (feature->Level > 0 && feature->Level <= level);
2039 static BOOL process_state_property(MSIPACKAGE* package, int level,
2040 LPCWSTR property, INSTALLSTATE state)
2042 LPWSTR override;
2043 MSIFEATURE *feature;
2045 override = msi_dup_property( package->db, property );
2046 if (!override)
2047 return FALSE;
2049 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2051 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
2052 continue;
2054 if (!strcmpW(property, szReinstall)) state = feature->Installed;
2056 if (!strcmpiW( override, szAll ))
2058 if (feature->Installed != state)
2060 feature->Action = state;
2061 feature->ActionRequest = state;
2064 else
2066 LPWSTR ptr = override;
2067 LPWSTR ptr2 = strchrW(override,',');
2069 while (ptr)
2071 int len = ptr2 - ptr;
2073 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
2074 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
2076 if (feature->Installed != state)
2078 feature->Action = state;
2079 feature->ActionRequest = state;
2081 break;
2083 if (ptr2)
2085 ptr=ptr2+1;
2086 ptr2 = strchrW(ptr,',');
2088 else
2089 break;
2093 msi_free(override);
2094 return TRUE;
2097 static BOOL process_overrides( MSIPACKAGE *package, int level )
2099 static const WCHAR szAddLocal[] =
2100 {'A','D','D','L','O','C','A','L',0};
2101 static const WCHAR szAddSource[] =
2102 {'A','D','D','S','O','U','R','C','E',0};
2103 static const WCHAR szAdvertise[] =
2104 {'A','D','V','E','R','T','I','S','E',0};
2105 BOOL ret = FALSE;
2107 /* all these activation/deactivation things happen in order and things
2108 * later on the list override things earlier on the list.
2110 * 0 INSTALLLEVEL processing
2111 * 1 ADDLOCAL
2112 * 2 REMOVE
2113 * 3 ADDSOURCE
2114 * 4 ADDDEFAULT
2115 * 5 REINSTALL
2116 * 6 ADVERTISE
2117 * 7 COMPADDLOCAL
2118 * 8 COMPADDSOURCE
2119 * 9 FILEADDLOCAL
2120 * 10 FILEADDSOURCE
2121 * 11 FILEADDDEFAULT
2123 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
2124 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
2125 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
2126 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
2127 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
2129 if (ret)
2130 msi_set_property( package->db, szPreselected, szOne );
2132 return ret;
2135 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
2137 int level;
2138 static const WCHAR szlevel[] =
2139 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2140 MSICOMPONENT* component;
2141 MSIFEATURE *feature;
2143 TRACE("Checking Install Level\n");
2145 level = msi_get_property_int(package->db, szlevel, 1);
2147 if (!msi_get_property_int( package->db, szPreselected, 0 ))
2149 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2151 if (!is_feature_selected( feature, level )) continue;
2153 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2155 if (feature->Attributes & msidbFeatureAttributesFavorSource)
2157 feature->Action = INSTALLSTATE_SOURCE;
2158 feature->ActionRequest = INSTALLSTATE_SOURCE;
2160 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2162 feature->Action = INSTALLSTATE_ADVERTISED;
2163 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
2165 else
2167 feature->Action = INSTALLSTATE_LOCAL;
2168 feature->ActionRequest = INSTALLSTATE_LOCAL;
2173 /* disable child features of unselected parent features */
2174 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2176 FeatureList *fl;
2178 if (is_feature_selected( feature, level )) continue;
2180 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2182 fl->feature->Action = INSTALLSTATE_UNKNOWN;
2183 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2187 else /* preselected */
2189 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2191 if (!is_feature_selected( feature, level )) continue;
2193 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2195 if (feature->Installed == INSTALLSTATE_ABSENT)
2197 feature->Action = INSTALLSTATE_UNKNOWN;
2198 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2200 else
2202 feature->Action = feature->Installed;
2203 feature->ActionRequest = feature->Installed;
2209 /* now we want to set component state based based on feature state */
2210 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2212 ComponentList *cl;
2214 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
2215 debugstr_w(feature->Feature), feature->Level, feature->Installed,
2216 feature->ActionRequest, feature->Action);
2218 if (!is_feature_selected( feature, level )) continue;
2220 /* features with components that have compressed files are made local */
2221 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2223 if (cl->component->ForceLocalState &&
2224 feature->ActionRequest == INSTALLSTATE_SOURCE)
2226 feature->Action = INSTALLSTATE_LOCAL;
2227 feature->ActionRequest = INSTALLSTATE_LOCAL;
2228 break;
2232 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2234 component = cl->component;
2236 switch (feature->ActionRequest)
2238 case INSTALLSTATE_ABSENT:
2239 component->anyAbsent = 1;
2240 break;
2241 case INSTALLSTATE_ADVERTISED:
2242 component->hasAdvertiseFeature = 1;
2243 break;
2244 case INSTALLSTATE_SOURCE:
2245 component->hasSourceFeature = 1;
2246 break;
2247 case INSTALLSTATE_LOCAL:
2248 component->hasLocalFeature = 1;
2249 break;
2250 case INSTALLSTATE_DEFAULT:
2251 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2252 component->hasAdvertiseFeature = 1;
2253 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2254 component->hasSourceFeature = 1;
2255 else
2256 component->hasLocalFeature = 1;
2257 break;
2258 default:
2259 break;
2264 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2266 /* check if it's local or source */
2267 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2268 (component->hasLocalFeature || component->hasSourceFeature))
2270 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2271 !component->ForceLocalState)
2273 component->Action = INSTALLSTATE_SOURCE;
2274 component->ActionRequest = INSTALLSTATE_SOURCE;
2276 else
2278 component->Action = INSTALLSTATE_LOCAL;
2279 component->ActionRequest = INSTALLSTATE_LOCAL;
2281 continue;
2284 /* if any feature is local, the component must be local too */
2285 if (component->hasLocalFeature)
2287 component->Action = INSTALLSTATE_LOCAL;
2288 component->ActionRequest = INSTALLSTATE_LOCAL;
2289 continue;
2291 if (component->hasSourceFeature)
2293 component->Action = INSTALLSTATE_SOURCE;
2294 component->ActionRequest = INSTALLSTATE_SOURCE;
2295 continue;
2297 if (component->hasAdvertiseFeature)
2299 component->Action = INSTALLSTATE_ADVERTISED;
2300 component->ActionRequest = INSTALLSTATE_ADVERTISED;
2301 continue;
2303 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2304 if (component->anyAbsent &&
2305 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
2307 component->Action = INSTALLSTATE_ABSENT;
2308 component->ActionRequest = INSTALLSTATE_ABSENT;
2312 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2314 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2316 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2317 component->Action = INSTALLSTATE_LOCAL;
2318 component->ActionRequest = INSTALLSTATE_LOCAL;
2321 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2322 component->Installed == INSTALLSTATE_SOURCE &&
2323 component->hasSourceFeature)
2325 component->Action = INSTALLSTATE_UNKNOWN;
2326 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2329 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2330 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2333 return ERROR_SUCCESS;
2336 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2338 MSIPACKAGE *package = param;
2339 LPCWSTR name;
2340 LPWSTR path;
2341 MSIFOLDER *f;
2343 name = MSI_RecordGetString(row,1);
2345 f = get_loaded_folder(package, name);
2346 if (!f) return ERROR_SUCCESS;
2348 /* reset the ResolvedTarget */
2349 msi_free(f->ResolvedTarget);
2350 f->ResolvedTarget = NULL;
2352 TRACE("directory %s ...\n", debugstr_w(name));
2353 path = resolve_target_folder( package, name, TRUE, TRUE, NULL );
2354 TRACE("resolves to %s\n", debugstr_w(path));
2355 msi_free(path);
2357 return ERROR_SUCCESS;
2360 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2362 MSIPACKAGE *package = param;
2363 LPCWSTR name;
2364 MSIFEATURE *feature;
2366 name = MSI_RecordGetString( row, 1 );
2368 feature = get_loaded_feature( package, name );
2369 if (!feature)
2370 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2371 else
2373 LPCWSTR Condition;
2374 Condition = MSI_RecordGetString(row,3);
2376 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2378 int level = MSI_RecordGetInteger(row,2);
2379 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2380 feature->Level = level;
2383 return ERROR_SUCCESS;
2386 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2388 static const WCHAR name[] = {'\\',0};
2389 VS_FIXEDFILEINFO *ptr, *ret;
2390 LPVOID version;
2391 DWORD versize, handle;
2392 UINT sz;
2394 TRACE("%s\n", debugstr_w(filename));
2396 versize = GetFileVersionInfoSizeW( filename, &handle );
2397 if (!versize)
2398 return NULL;
2400 version = msi_alloc( versize );
2401 if (!version)
2402 return NULL;
2404 GetFileVersionInfoW( filename, 0, versize, version );
2406 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2408 msi_free( version );
2409 return NULL;
2412 ret = msi_alloc( sz );
2413 memcpy( ret, ptr, sz );
2415 msi_free( version );
2416 return ret;
2419 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2421 DWORD ms, ls;
2423 msi_parse_version_string( version, &ms, &ls );
2425 if (fi->dwFileVersionMS > ms) return 1;
2426 else if (fi->dwFileVersionMS < ms) return -1;
2427 else if (fi->dwFileVersionLS > ls) return 1;
2428 else if (fi->dwFileVersionLS < ls) return -1;
2429 return 0;
2432 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2434 DWORD ms1, ms2;
2436 msi_parse_version_string( ver1, &ms1, NULL );
2437 msi_parse_version_string( ver2, &ms2, NULL );
2439 if (ms1 > ms2) return 1;
2440 else if (ms1 < ms2) return -1;
2441 return 0;
2444 DWORD msi_get_disk_file_size( LPCWSTR filename )
2446 HANDLE file;
2447 DWORD size;
2449 TRACE("%s\n", debugstr_w(filename));
2451 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2452 if (file == INVALID_HANDLE_VALUE)
2453 return INVALID_FILE_SIZE;
2455 size = GetFileSize( file, NULL );
2456 CloseHandle( file );
2457 return size;
2460 BOOL msi_file_hash_matches( MSIFILE *file )
2462 UINT r;
2463 MSIFILEHASHINFO hash;
2465 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2466 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2467 if (r != ERROR_SUCCESS)
2468 return FALSE;
2470 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2473 static WCHAR *get_temp_dir( void )
2475 static UINT id;
2476 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2478 GetTempPathW( MAX_PATH, tmp );
2479 for (;;)
2481 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2482 if (CreateDirectoryW( dir, NULL )) break;
2484 return strdupW( dir );
2487 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2489 MSIASSEMBLY *assembly = file->Component->assembly;
2491 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2493 msi_free( file->TargetPath );
2494 if (assembly && !assembly->application)
2496 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2497 file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName );
2498 track_tempfile( package, file->TargetPath );
2500 else
2502 WCHAR *dir = resolve_target_folder( package, file->Component->Directory, FALSE, TRUE, NULL );
2503 file->TargetPath = build_directory_name( 2, dir, file->FileName );
2504 msi_free( dir );
2507 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2510 static UINT calculate_file_cost( MSIPACKAGE *package )
2512 VS_FIXEDFILEINFO *file_version;
2513 WCHAR *font_version;
2514 MSIFILE *file;
2516 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2518 MSICOMPONENT *comp = file->Component;
2519 DWORD file_size;
2521 if (!comp->Enabled) continue;
2523 if (file->IsCompressed)
2524 comp->ForceLocalState = TRUE;
2526 set_target_path( package, file );
2528 if ((comp->assembly && !comp->assembly->installed) ||
2529 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2531 comp->Cost += file->FileSize;
2532 continue;
2534 file_size = msi_get_disk_file_size( file->TargetPath );
2536 if (file->Version)
2538 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2540 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2542 comp->Cost += file->FileSize - file_size;
2544 msi_free( file_version );
2545 continue;
2547 else if ((font_version = font_version_from_file( file->TargetPath )))
2549 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2551 comp->Cost += file->FileSize - file_size;
2553 msi_free( font_version );
2554 continue;
2557 if (file_size != file->FileSize)
2559 comp->Cost += file->FileSize - file_size;
2562 return ERROR_SUCCESS;
2566 * A lot is done in this function aside from just the costing.
2567 * The costing needs to be implemented at some point but for now I am going
2568 * to focus on the directory building
2571 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2573 static const WCHAR ExecSeqQuery[] =
2574 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2575 '`','D','i','r','e','c','t','o','r','y','`',0};
2576 static const WCHAR ConditionQuery[] =
2577 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2578 '`','C','o','n','d','i','t','i','o','n','`',0};
2579 static const WCHAR szCosting[] =
2580 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2581 static const WCHAR szlevel[] =
2582 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2583 static const WCHAR szOutOfDiskSpace[] =
2584 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2585 MSICOMPONENT *comp;
2586 UINT rc = ERROR_SUCCESS;
2587 MSIQUERY * view;
2588 LPWSTR level;
2590 TRACE("Building Directory properties\n");
2592 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2593 if (rc == ERROR_SUCCESS)
2595 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2596 package);
2597 msiobj_release(&view->hdr);
2600 TRACE("Evaluating component conditions\n");
2601 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2603 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2605 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2606 comp->Enabled = FALSE;
2608 else
2609 comp->Enabled = TRUE;
2612 /* read components states from the registry */
2613 ACTION_GetComponentInstallStates(package);
2614 ACTION_GetFeatureInstallStates(package);
2616 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2618 TRACE("Evaluating feature conditions\n");
2620 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2621 if (rc == ERROR_SUCCESS)
2623 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2624 msiobj_release( &view->hdr );
2628 TRACE("Calculating file cost\n");
2629 calculate_file_cost( package );
2631 msi_set_property( package->db, szCosting, szOne );
2632 /* set default run level if not set */
2633 level = msi_dup_property( package->db, szlevel );
2634 if (!level)
2635 msi_set_property( package->db, szlevel, szOne );
2636 msi_free(level);
2638 /* FIXME: check volume disk space */
2639 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2641 return MSI_SetFeatureStates(package);
2644 /* OK this value is "interpreted" and then formatted based on the
2645 first few characters */
2646 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2647 DWORD *size)
2649 LPSTR data = NULL;
2651 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2653 if (value[1]=='x')
2655 LPWSTR ptr;
2656 CHAR byte[5];
2657 LPWSTR deformated = NULL;
2658 int count;
2660 deformat_string(package, &value[2], &deformated);
2662 /* binary value type */
2663 ptr = deformated;
2664 *type = REG_BINARY;
2665 if (strlenW(ptr)%2)
2666 *size = (strlenW(ptr)/2)+1;
2667 else
2668 *size = strlenW(ptr)/2;
2670 data = msi_alloc(*size);
2672 byte[0] = '0';
2673 byte[1] = 'x';
2674 byte[4] = 0;
2675 count = 0;
2676 /* if uneven pad with a zero in front */
2677 if (strlenW(ptr)%2)
2679 byte[2]= '0';
2680 byte[3]= *ptr;
2681 ptr++;
2682 data[count] = (BYTE)strtol(byte,NULL,0);
2683 count ++;
2684 TRACE("Uneven byte count\n");
2686 while (*ptr)
2688 byte[2]= *ptr;
2689 ptr++;
2690 byte[3]= *ptr;
2691 ptr++;
2692 data[count] = (BYTE)strtol(byte,NULL,0);
2693 count ++;
2695 msi_free(deformated);
2697 TRACE("Data %i bytes(%i)\n",*size,count);
2699 else
2701 LPWSTR deformated;
2702 LPWSTR p;
2703 DWORD d = 0;
2704 deformat_string(package, &value[1], &deformated);
2706 *type=REG_DWORD;
2707 *size = sizeof(DWORD);
2708 data = msi_alloc(*size);
2709 p = deformated;
2710 if (*p == '-')
2711 p++;
2712 while (*p)
2714 if ( (*p < '0') || (*p > '9') )
2715 break;
2716 d *= 10;
2717 d += (*p - '0');
2718 p++;
2720 if (deformated[0] == '-')
2721 d = -d;
2722 *(LPDWORD)data = d;
2723 TRACE("DWORD %i\n",*(LPDWORD)data);
2725 msi_free(deformated);
2728 else
2730 static const WCHAR szMulti[] = {'[','~',']',0};
2731 LPCWSTR ptr;
2732 *type=REG_SZ;
2734 if (value[0]=='#')
2736 if (value[1]=='%')
2738 ptr = &value[2];
2739 *type=REG_EXPAND_SZ;
2741 else
2742 ptr = &value[1];
2744 else
2745 ptr=value;
2747 if (strstrW(value, szMulti))
2748 *type = REG_MULTI_SZ;
2750 /* remove initial delimiter */
2751 if (!strncmpW(value, szMulti, 3))
2752 ptr = value + 3;
2754 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2756 /* add double NULL terminator */
2757 if (*type == REG_MULTI_SZ)
2759 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2760 data = msi_realloc_zero(data, *size);
2763 return data;
2766 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2768 const WCHAR *ret;
2770 switch (root)
2772 case -1:
2773 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2775 *root_key = HKEY_LOCAL_MACHINE;
2776 ret = szHLM;
2778 else
2780 *root_key = HKEY_CURRENT_USER;
2781 ret = szHCU;
2783 break;
2784 case 0:
2785 *root_key = HKEY_CLASSES_ROOT;
2786 ret = szHCR;
2787 break;
2788 case 1:
2789 *root_key = HKEY_CURRENT_USER;
2790 ret = szHCU;
2791 break;
2792 case 2:
2793 *root_key = HKEY_LOCAL_MACHINE;
2794 ret = szHLM;
2795 break;
2796 case 3:
2797 *root_key = HKEY_USERS;
2798 ret = szHU;
2799 break;
2800 default:
2801 ERR("Unknown root %i\n", root);
2802 return NULL;
2805 return ret;
2808 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2810 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2811 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2813 if (is_64bit && package->platform == PLATFORM_INTEL &&
2814 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2816 UINT size;
2817 WCHAR *path_32node;
2819 size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR);
2820 path_32node = msi_alloc( size );
2821 if (!path_32node)
2822 return NULL;
2824 memcpy( path_32node, path, len * sizeof(WCHAR) );
2825 path_32node[len] = 0;
2826 strcatW( path_32node, szWow6432Node );
2827 strcatW( path_32node, szBackSlash );
2828 strcatW( path_32node, path + len );
2829 return path_32node;
2832 return strdupW( path );
2835 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2837 MSIPACKAGE *package = param;
2838 LPSTR value_data = NULL;
2839 HKEY root_key, hkey;
2840 DWORD type,size;
2841 LPWSTR deformated, uikey, keypath;
2842 LPCWSTR szRoot, component, name, key, value;
2843 MSICOMPONENT *comp;
2844 MSIRECORD * uirow;
2845 INT root;
2846 BOOL check_first = FALSE;
2847 UINT rc;
2849 ui_progress(package,2,0,0,0);
2851 component = MSI_RecordGetString(row, 6);
2852 comp = get_loaded_component(package,component);
2853 if (!comp)
2854 return ERROR_SUCCESS;
2856 if (!comp->Enabled)
2858 TRACE("component is disabled\n");
2859 return ERROR_SUCCESS;
2862 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2864 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2865 comp->Action = comp->Installed;
2866 return ERROR_SUCCESS;
2868 comp->Action = INSTALLSTATE_LOCAL;
2870 name = MSI_RecordGetString(row, 4);
2871 if( MSI_RecordIsNull(row,5) && name )
2873 /* null values can have special meanings */
2874 if (name[0]=='-' && name[1] == 0)
2875 return ERROR_SUCCESS;
2876 else if ((name[0]=='+' && name[1] == 0) ||
2877 (name[0] == '*' && name[1] == 0))
2878 name = NULL;
2879 check_first = TRUE;
2882 root = MSI_RecordGetInteger(row,2);
2883 key = MSI_RecordGetString(row, 3);
2885 szRoot = get_root_key( package, root, &root_key );
2886 if (!szRoot)
2887 return ERROR_SUCCESS;
2889 deformat_string(package, key , &deformated);
2890 size = strlenW(deformated) + strlenW(szRoot) + 1;
2891 uikey = msi_alloc(size*sizeof(WCHAR));
2892 strcpyW(uikey,szRoot);
2893 strcatW(uikey,deformated);
2895 keypath = get_keypath( package, root_key, deformated );
2896 msi_free( deformated );
2897 if (RegCreateKeyW( root_key, keypath, &hkey ))
2899 ERR("Could not create key %s\n", debugstr_w(keypath));
2900 msi_free(uikey);
2901 msi_free(keypath);
2902 return ERROR_SUCCESS;
2905 value = MSI_RecordGetString(row,5);
2906 if (value)
2907 value_data = parse_value(package, value, &type, &size);
2908 else
2910 value_data = (LPSTR)strdupW(szEmpty);
2911 size = sizeof(szEmpty);
2912 type = REG_SZ;
2915 deformat_string(package, name, &deformated);
2917 if (!check_first)
2919 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2920 debugstr_w(uikey));
2921 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2923 else
2925 DWORD sz = 0;
2926 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2927 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2929 TRACE("value %s of %s checked already exists\n",
2930 debugstr_w(deformated), debugstr_w(uikey));
2932 else
2934 TRACE("Checked and setting value %s of %s\n",
2935 debugstr_w(deformated), debugstr_w(uikey));
2936 if (deformated || size)
2937 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2940 RegCloseKey(hkey);
2942 uirow = MSI_CreateRecord(3);
2943 MSI_RecordSetStringW(uirow,2,deformated);
2944 MSI_RecordSetStringW(uirow,1,uikey);
2945 if (type == REG_SZ || type == REG_EXPAND_SZ)
2946 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2947 ui_actiondata(package,szWriteRegistryValues,uirow);
2948 msiobj_release( &uirow->hdr );
2950 msi_free(value_data);
2951 msi_free(deformated);
2952 msi_free(uikey);
2953 msi_free(keypath);
2955 return ERROR_SUCCESS;
2958 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2960 UINT rc;
2961 MSIQUERY * view;
2962 static const WCHAR ExecSeqQuery[] =
2963 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2964 '`','R','e','g','i','s','t','r','y','`',0 };
2966 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2967 if (rc != ERROR_SUCCESS)
2968 return ERROR_SUCCESS;
2970 /* increment progress bar each time action data is sent */
2971 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2973 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2975 msiobj_release(&view->hdr);
2976 return rc;
2979 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2981 LONG res;
2982 HKEY hkey;
2983 DWORD num_subkeys, num_values;
2985 if (delete_key)
2987 if ((res = RegDeleteTreeW( hkey_root, key )))
2989 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2991 return;
2994 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2996 if ((res = RegDeleteValueW( hkey, value )))
2998 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
3000 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3001 NULL, NULL, NULL, NULL );
3002 RegCloseKey( hkey );
3003 if (!res && !num_subkeys && !num_values)
3005 TRACE("Removing empty key %s\n", debugstr_w(key));
3006 RegDeleteKeyW( hkey_root, key );
3008 return;
3010 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
3014 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3016 MSIPACKAGE *package = param;
3017 LPCWSTR component, name, key_str, root_key_str;
3018 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3019 MSICOMPONENT *comp;
3020 MSIRECORD *uirow;
3021 BOOL delete_key = FALSE;
3022 HKEY hkey_root;
3023 UINT size;
3024 INT root;
3026 ui_progress( package, 2, 0, 0, 0 );
3028 component = MSI_RecordGetString( row, 6 );
3029 comp = get_loaded_component( package, component );
3030 if (!comp)
3031 return ERROR_SUCCESS;
3033 if (!comp->Enabled)
3035 TRACE("component is disabled\n");
3036 return ERROR_SUCCESS;
3039 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3041 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
3042 comp->Action = comp->Installed;
3043 return ERROR_SUCCESS;
3045 comp->Action = INSTALLSTATE_ABSENT;
3047 name = MSI_RecordGetString( row, 4 );
3048 if (MSI_RecordIsNull( row, 5 ) && name )
3050 if (name[0] == '+' && !name[1])
3051 return ERROR_SUCCESS;
3052 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
3054 delete_key = TRUE;
3055 name = NULL;
3059 root = MSI_RecordGetInteger( row, 2 );
3060 key_str = MSI_RecordGetString( row, 3 );
3062 root_key_str = get_root_key( package, root, &hkey_root );
3063 if (!root_key_str)
3064 return ERROR_SUCCESS;
3066 deformat_string( package, key_str, &deformated_key );
3067 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3068 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3069 strcpyW( ui_key_str, root_key_str );
3070 strcatW( ui_key_str, deformated_key );
3072 deformat_string( package, name, &deformated_name );
3074 keypath = get_keypath( package, hkey_root, deformated_key );
3075 msi_free( deformated_key );
3076 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3077 msi_free( keypath );
3079 uirow = MSI_CreateRecord( 2 );
3080 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3081 MSI_RecordSetStringW( uirow, 2, deformated_name );
3083 ui_actiondata( package, szRemoveRegistryValues, uirow );
3084 msiobj_release( &uirow->hdr );
3086 msi_free( ui_key_str );
3087 msi_free( deformated_name );
3088 return ERROR_SUCCESS;
3091 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3093 MSIPACKAGE *package = param;
3094 LPCWSTR component, name, key_str, root_key_str;
3095 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3096 MSICOMPONENT *comp;
3097 MSIRECORD *uirow;
3098 BOOL delete_key = FALSE;
3099 HKEY hkey_root;
3100 UINT size;
3101 INT root;
3103 ui_progress( package, 2, 0, 0, 0 );
3105 component = MSI_RecordGetString( row, 5 );
3106 comp = get_loaded_component( package, component );
3107 if (!comp)
3108 return ERROR_SUCCESS;
3110 if (!comp->Enabled)
3112 TRACE("component is disabled\n");
3113 return ERROR_SUCCESS;
3116 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3118 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3119 comp->Action = comp->Installed;
3120 return ERROR_SUCCESS;
3122 comp->Action = INSTALLSTATE_LOCAL;
3124 if ((name = MSI_RecordGetString( row, 4 )))
3126 if (name[0] == '-' && !name[1])
3128 delete_key = TRUE;
3129 name = NULL;
3133 root = MSI_RecordGetInteger( row, 2 );
3134 key_str = MSI_RecordGetString( row, 3 );
3136 root_key_str = get_root_key( package, root, &hkey_root );
3137 if (!root_key_str)
3138 return ERROR_SUCCESS;
3140 deformat_string( package, key_str, &deformated_key );
3141 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3142 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3143 strcpyW( ui_key_str, root_key_str );
3144 strcatW( ui_key_str, deformated_key );
3146 deformat_string( package, name, &deformated_name );
3148 keypath = get_keypath( package, hkey_root, deformated_key );
3149 msi_free( deformated_key );
3150 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3151 msi_free( keypath );
3153 uirow = MSI_CreateRecord( 2 );
3154 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3155 MSI_RecordSetStringW( uirow, 2, deformated_name );
3157 ui_actiondata( package, szRemoveRegistryValues, uirow );
3158 msiobj_release( &uirow->hdr );
3160 msi_free( ui_key_str );
3161 msi_free( deformated_name );
3162 return ERROR_SUCCESS;
3165 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3167 UINT rc;
3168 MSIQUERY *view;
3169 static const WCHAR registry_query[] =
3170 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3171 '`','R','e','g','i','s','t','r','y','`',0 };
3172 static const WCHAR remove_registry_query[] =
3173 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3174 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
3176 /* increment progress bar each time action data is sent */
3177 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3179 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3180 if (rc == ERROR_SUCCESS)
3182 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3183 msiobj_release( &view->hdr );
3184 if (rc != ERROR_SUCCESS)
3185 return rc;
3188 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3189 if (rc == ERROR_SUCCESS)
3191 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3192 msiobj_release( &view->hdr );
3193 if (rc != ERROR_SUCCESS)
3194 return rc;
3197 return ERROR_SUCCESS;
3200 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3202 package->script->CurrentlyScripting = TRUE;
3204 return ERROR_SUCCESS;
3208 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3210 MSICOMPONENT *comp;
3211 DWORD progress = 0;
3212 DWORD total = 0;
3213 static const WCHAR q1[]=
3214 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3215 '`','R','e','g','i','s','t','r','y','`',0};
3216 UINT rc;
3217 MSIQUERY * view;
3218 MSIFEATURE *feature;
3219 MSIFILE *file;
3221 TRACE("InstallValidate\n");
3223 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
3224 if (rc == ERROR_SUCCESS)
3226 MSI_IterateRecords( view, &progress, NULL, package );
3227 msiobj_release( &view->hdr );
3228 total += progress * REG_PROGRESS_VALUE;
3231 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3232 total += COMPONENT_PROGRESS_VALUE;
3234 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3235 total += file->FileSize;
3237 ui_progress(package,0,total,0,0);
3239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3241 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3242 debugstr_w(feature->Feature), feature->Installed,
3243 feature->ActionRequest, feature->Action);
3246 return ERROR_SUCCESS;
3249 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3251 MSIPACKAGE* package = param;
3252 LPCWSTR cond = NULL;
3253 LPCWSTR message = NULL;
3254 UINT r;
3256 static const WCHAR title[]=
3257 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3259 cond = MSI_RecordGetString(row,1);
3261 r = MSI_EvaluateConditionW(package,cond);
3262 if (r == MSICONDITION_FALSE)
3264 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3266 LPWSTR deformated;
3267 message = MSI_RecordGetString(row,2);
3268 deformat_string(package,message,&deformated);
3269 MessageBoxW(NULL,deformated,title,MB_OK);
3270 msi_free(deformated);
3273 return ERROR_INSTALL_FAILURE;
3276 return ERROR_SUCCESS;
3279 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3281 UINT rc;
3282 MSIQUERY * view = NULL;
3283 static const WCHAR ExecSeqQuery[] =
3284 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3285 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3287 TRACE("Checking launch conditions\n");
3289 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3290 if (rc != ERROR_SUCCESS)
3291 return ERROR_SUCCESS;
3293 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3294 msiobj_release(&view->hdr);
3296 return rc;
3299 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3302 if (!cmp->KeyPath)
3303 return resolve_target_folder( package, cmp->Directory, FALSE, TRUE, NULL );
3305 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3307 MSIRECORD * row = 0;
3308 UINT root,len;
3309 LPWSTR deformated,buffer,deformated_name;
3310 LPCWSTR key,name;
3311 static const WCHAR ExecSeqQuery[] =
3312 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3313 '`','R','e','g','i','s','t','r','y','`',' ',
3314 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3315 ' ','=',' ' ,'\'','%','s','\'',0 };
3316 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3317 static const WCHAR fmt2[]=
3318 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3320 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3321 if (!row)
3322 return NULL;
3324 root = MSI_RecordGetInteger(row,2);
3325 key = MSI_RecordGetString(row, 3);
3326 name = MSI_RecordGetString(row, 4);
3327 deformat_string(package, key , &deformated);
3328 deformat_string(package, name, &deformated_name);
3330 len = strlenW(deformated) + 6;
3331 if (deformated_name)
3332 len+=strlenW(deformated_name);
3334 buffer = msi_alloc( len *sizeof(WCHAR));
3336 if (deformated_name)
3337 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3338 else
3339 sprintfW(buffer,fmt,root,deformated);
3341 msi_free(deformated);
3342 msi_free(deformated_name);
3343 msiobj_release(&row->hdr);
3345 return buffer;
3347 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3349 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3350 return NULL;
3352 else
3354 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
3356 if (file)
3357 return strdupW( file->TargetPath );
3359 return NULL;
3362 static HKEY openSharedDLLsKey(void)
3364 HKEY hkey=0;
3365 static const WCHAR path[] =
3366 {'S','o','f','t','w','a','r','e','\\',
3367 'M','i','c','r','o','s','o','f','t','\\',
3368 'W','i','n','d','o','w','s','\\',
3369 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3370 'S','h','a','r','e','d','D','L','L','s',0};
3372 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3373 return hkey;
3376 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3378 HKEY hkey;
3379 DWORD count=0;
3380 DWORD type;
3381 DWORD sz = sizeof(count);
3382 DWORD rc;
3384 hkey = openSharedDLLsKey();
3385 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3386 if (rc != ERROR_SUCCESS)
3387 count = 0;
3388 RegCloseKey(hkey);
3389 return count;
3392 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3394 HKEY hkey;
3396 hkey = openSharedDLLsKey();
3397 if (count > 0)
3398 msi_reg_set_val_dword( hkey, path, count );
3399 else
3400 RegDeleteValueW(hkey,path);
3401 RegCloseKey(hkey);
3402 return count;
3405 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3407 MSIFEATURE *feature;
3408 INT count = 0;
3409 BOOL write = FALSE;
3411 /* only refcount DLLs */
3412 if (comp->KeyPath == NULL ||
3413 comp->assembly ||
3414 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3415 comp->Attributes & msidbComponentAttributesODBCDataSource)
3416 write = FALSE;
3417 else
3419 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3420 write = (count > 0);
3422 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3423 write = TRUE;
3426 /* increment counts */
3427 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3429 ComponentList *cl;
3431 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3432 continue;
3434 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3436 if ( cl->component == comp )
3437 count++;
3441 /* decrement counts */
3442 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3444 ComponentList *cl;
3446 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3447 continue;
3449 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3451 if ( cl->component == comp )
3452 count--;
3456 /* ref count all the files in the component */
3457 if (write)
3459 MSIFILE *file;
3461 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3463 if (file->Component == comp)
3464 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3468 /* add a count for permanent */
3469 if (comp->Attributes & msidbComponentAttributesPermanent)
3470 count ++;
3472 comp->RefCount = count;
3474 if (write)
3475 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3478 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3480 WCHAR squished_pc[GUID_SIZE];
3481 WCHAR squished_cc[GUID_SIZE];
3482 UINT rc;
3483 MSICOMPONENT *comp;
3484 HKEY hkey;
3486 TRACE("\n");
3488 squash_guid(package->ProductCode,squished_pc);
3489 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3491 msi_set_sourcedir_props(package, FALSE);
3493 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3495 MSIRECORD * uirow;
3497 ui_progress(package,2,0,0,0);
3498 if (!comp->ComponentId)
3499 continue;
3501 squash_guid(comp->ComponentId,squished_cc);
3503 msi_free(comp->FullKeypath);
3504 if (comp->assembly)
3506 const WCHAR prefixW[] = {'<','\\',0};
3507 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3509 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3510 if (comp->FullKeypath)
3512 strcpyW( comp->FullKeypath, prefixW );
3513 strcatW( comp->FullKeypath, comp->assembly->display_name );
3516 else comp->FullKeypath = resolve_keypath( package, comp );
3518 ACTION_RefCountComponent( package, comp );
3520 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3521 debugstr_w(comp->Component),
3522 debugstr_w(squished_cc),
3523 debugstr_w(comp->FullKeypath),
3524 comp->RefCount,
3525 comp->ActionRequest);
3527 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3528 comp->ActionRequest == INSTALLSTATE_SOURCE)
3530 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3531 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3532 else
3533 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3535 if (rc != ERROR_SUCCESS)
3536 continue;
3538 if (comp->Attributes & msidbComponentAttributesPermanent)
3540 static const WCHAR szPermKey[] =
3541 { '0','0','0','0','0','0','0','0','0','0','0','0',
3542 '0','0','0','0','0','0','0','0','0','0','0','0',
3543 '0','0','0','0','0','0','0','0',0 };
3545 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3548 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3549 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3550 else
3552 MSIFILE *file;
3553 MSIRECORD *row;
3554 LPWSTR ptr, ptr2;
3555 WCHAR source[MAX_PATH];
3556 WCHAR base[MAX_PATH];
3557 LPWSTR sourcepath;
3559 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3560 static const WCHAR query[] = {
3561 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3562 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3563 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3564 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3565 '`','D','i','s','k','I','d','`',0};
3567 if (!comp->KeyPath || !(file = get_loaded_file(package, comp->KeyPath)))
3568 continue;
3570 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3571 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3572 ptr2 = strrchrW(source, '\\') + 1;
3573 msiobj_release(&row->hdr);
3575 lstrcpyW(base, package->PackagePath);
3576 ptr = strrchrW(base, '\\');
3577 *(ptr + 1) = '\0';
3579 sourcepath = resolve_file_source(package, file);
3580 ptr = sourcepath + lstrlenW(base);
3581 lstrcpyW(ptr2, ptr);
3582 msi_free(sourcepath);
3584 msi_reg_set_val_str(hkey, squished_pc, source);
3586 RegCloseKey(hkey);
3588 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3590 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3591 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3592 else
3593 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3595 comp->Action = comp->ActionRequest;
3597 /* UI stuff */
3598 uirow = MSI_CreateRecord(3);
3599 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3600 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3601 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3602 ui_actiondata(package,szProcessComponents,uirow);
3603 msiobj_release( &uirow->hdr );
3606 return ERROR_SUCCESS;
3609 typedef struct {
3610 CLSID clsid;
3611 LPWSTR source;
3613 LPWSTR path;
3614 ITypeLib *ptLib;
3615 } typelib_struct;
3617 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3618 LPWSTR lpszName, LONG_PTR lParam)
3620 TLIBATTR *attr;
3621 typelib_struct *tl_struct = (typelib_struct*) lParam;
3622 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3623 int sz;
3624 HRESULT res;
3626 if (!IS_INTRESOURCE(lpszName))
3628 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3629 return TRUE;
3632 sz = strlenW(tl_struct->source)+4;
3633 sz *= sizeof(WCHAR);
3635 if ((INT_PTR)lpszName == 1)
3636 tl_struct->path = strdupW(tl_struct->source);
3637 else
3639 tl_struct->path = msi_alloc(sz);
3640 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3643 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3644 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3645 if (FAILED(res))
3647 msi_free(tl_struct->path);
3648 tl_struct->path = NULL;
3650 return TRUE;
3653 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3654 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3656 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3657 return FALSE;
3660 msi_free(tl_struct->path);
3661 tl_struct->path = NULL;
3663 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3664 ITypeLib_Release(tl_struct->ptLib);
3666 return TRUE;
3669 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3671 MSIPACKAGE* package = param;
3672 LPCWSTR component;
3673 MSICOMPONENT *comp;
3674 MSIFILE *file;
3675 typelib_struct tl_struct;
3676 ITypeLib *tlib;
3677 HMODULE module;
3678 HRESULT hr;
3680 component = MSI_RecordGetString(row,3);
3681 comp = get_loaded_component(package,component);
3682 if (!comp)
3683 return ERROR_SUCCESS;
3685 if (!comp->Enabled)
3687 TRACE("component is disabled\n");
3688 return ERROR_SUCCESS;
3691 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3693 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3694 comp->Action = comp->Installed;
3695 return ERROR_SUCCESS;
3697 comp->Action = INSTALLSTATE_LOCAL;
3699 if (!comp->KeyPath || !(file = get_loaded_file( package, comp->KeyPath )))
3701 TRACE("component has no key path\n");
3702 return ERROR_SUCCESS;
3704 ui_actiondata( package, szRegisterTypeLibraries, row );
3706 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3707 if (module)
3709 LPCWSTR guid;
3710 guid = MSI_RecordGetString(row,1);
3711 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3712 tl_struct.source = strdupW( file->TargetPath );
3713 tl_struct.path = NULL;
3715 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3716 (LONG_PTR)&tl_struct);
3718 if (tl_struct.path)
3720 LPWSTR help = NULL;
3721 LPCWSTR helpid;
3722 HRESULT res;
3724 helpid = MSI_RecordGetString(row,6);
3726 if (helpid) help = resolve_target_folder( package, helpid, FALSE, TRUE, NULL );
3727 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3728 msi_free(help);
3730 if (FAILED(res))
3731 ERR("Failed to register type library %s\n",
3732 debugstr_w(tl_struct.path));
3733 else
3734 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3736 ITypeLib_Release(tl_struct.ptLib);
3737 msi_free(tl_struct.path);
3739 else
3740 ERR("Failed to load type library %s\n",
3741 debugstr_w(tl_struct.source));
3743 FreeLibrary(module);
3744 msi_free(tl_struct.source);
3746 else
3748 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3749 if (FAILED(hr))
3751 ERR("Failed to load type library: %08x\n", hr);
3752 return ERROR_INSTALL_FAILURE;
3755 ITypeLib_Release(tlib);
3758 return ERROR_SUCCESS;
3761 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3764 * OK this is a bit confusing.. I am given a _Component key and I believe
3765 * that the file that is being registered as a type library is the "key file
3766 * of that component" which I interpret to mean "The file in the KeyPath of
3767 * that component".
3769 UINT rc;
3770 MSIQUERY * view;
3771 static const WCHAR Query[] =
3772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3773 '`','T','y','p','e','L','i','b','`',0};
3775 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3776 if (rc != ERROR_SUCCESS)
3777 return ERROR_SUCCESS;
3779 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3780 msiobj_release(&view->hdr);
3781 return rc;
3784 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3786 MSIPACKAGE *package = param;
3787 LPCWSTR component, guid;
3788 MSICOMPONENT *comp;
3789 GUID libid;
3790 UINT version;
3791 LCID language;
3792 SYSKIND syskind;
3793 HRESULT hr;
3795 component = MSI_RecordGetString( row, 3 );
3796 comp = get_loaded_component( package, component );
3797 if (!comp)
3798 return ERROR_SUCCESS;
3800 if (!comp->Enabled)
3802 TRACE("component is disabled\n");
3803 return ERROR_SUCCESS;
3806 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3808 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3809 comp->Action = comp->Installed;
3810 return ERROR_SUCCESS;
3812 comp->Action = INSTALLSTATE_ABSENT;
3814 ui_actiondata( package, szUnregisterTypeLibraries, row );
3816 guid = MSI_RecordGetString( row, 1 );
3817 CLSIDFromString( (LPCWSTR)guid, &libid );
3818 version = MSI_RecordGetInteger( row, 4 );
3819 language = MSI_RecordGetInteger( row, 2 );
3821 #ifdef _WIN64
3822 syskind = SYS_WIN64;
3823 #else
3824 syskind = SYS_WIN32;
3825 #endif
3827 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3828 if (FAILED(hr))
3830 WARN("Failed to unregister typelib: %08x\n", hr);
3833 return ERROR_SUCCESS;
3836 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3838 UINT rc;
3839 MSIQUERY *view;
3840 static const WCHAR query[] =
3841 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3842 '`','T','y','p','e','L','i','b','`',0};
3844 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3845 if (rc != ERROR_SUCCESS)
3846 return ERROR_SUCCESS;
3848 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3849 msiobj_release( &view->hdr );
3850 return rc;
3853 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3855 static const WCHAR szlnk[] = {'.','l','n','k',0};
3856 LPCWSTR directory, extension;
3857 LPWSTR link_folder, link_file, filename;
3859 directory = MSI_RecordGetString( row, 2 );
3860 link_folder = resolve_target_folder( package, directory, FALSE, TRUE, NULL );
3862 /* may be needed because of a bug somewhere else */
3863 create_full_pathW( link_folder );
3865 filename = msi_dup_record_field( row, 3 );
3866 reduce_to_longfilename( filename );
3868 extension = strchrW( filename, '.' );
3869 if (!extension || strcmpiW( extension, szlnk ))
3871 int len = strlenW( filename );
3872 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3873 memcpy( filename + len, szlnk, sizeof(szlnk) );
3875 link_file = build_directory_name( 2, link_folder, filename );
3876 msi_free( link_folder );
3877 msi_free( filename );
3879 return link_file;
3882 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3884 MSIPACKAGE *package = param;
3885 LPWSTR link_file, deformated, path;
3886 LPCWSTR component, target;
3887 MSICOMPONENT *comp;
3888 IShellLinkW *sl = NULL;
3889 IPersistFile *pf = NULL;
3890 HRESULT res;
3892 component = MSI_RecordGetString(row, 4);
3893 comp = get_loaded_component(package, component);
3894 if (!comp)
3895 return ERROR_SUCCESS;
3897 if (!comp->Enabled)
3899 TRACE("component is disabled\n");
3900 return ERROR_SUCCESS;
3903 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3905 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3906 comp->Action = comp->Installed;
3907 return ERROR_SUCCESS;
3909 comp->Action = INSTALLSTATE_LOCAL;
3911 ui_actiondata(package,szCreateShortcuts,row);
3913 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3914 &IID_IShellLinkW, (LPVOID *) &sl );
3916 if (FAILED( res ))
3918 ERR("CLSID_ShellLink not available\n");
3919 goto err;
3922 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3923 if (FAILED( res ))
3925 ERR("QueryInterface(IID_IPersistFile) failed\n");
3926 goto err;
3929 target = MSI_RecordGetString(row, 5);
3930 if (strchrW(target, '['))
3932 deformat_string(package, target, &deformated);
3933 IShellLinkW_SetPath(sl,deformated);
3934 msi_free(deformated);
3936 else
3938 FIXME("poorly handled shortcut format, advertised shortcut\n");
3939 IShellLinkW_SetPath(sl,comp->FullKeypath);
3942 if (!MSI_RecordIsNull(row,6))
3944 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3945 deformat_string(package, arguments, &deformated);
3946 IShellLinkW_SetArguments(sl,deformated);
3947 msi_free(deformated);
3950 if (!MSI_RecordIsNull(row,7))
3952 LPCWSTR description = MSI_RecordGetString(row, 7);
3953 IShellLinkW_SetDescription(sl, description);
3956 if (!MSI_RecordIsNull(row,8))
3957 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3959 if (!MSI_RecordIsNull(row,9))
3961 INT index;
3962 LPCWSTR icon = MSI_RecordGetString(row, 9);
3964 path = build_icon_path(package, icon);
3965 index = MSI_RecordGetInteger(row,10);
3967 /* no value means 0 */
3968 if (index == MSI_NULL_INTEGER)
3969 index = 0;
3971 IShellLinkW_SetIconLocation(sl, path, index);
3972 msi_free(path);
3975 if (!MSI_RecordIsNull(row,11))
3976 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3978 if (!MSI_RecordIsNull(row,12))
3980 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3981 path = resolve_target_folder( package, wkdir, FALSE, TRUE, NULL );
3982 if (path)
3983 IShellLinkW_SetWorkingDirectory(sl, path);
3984 msi_free(path);
3987 link_file = get_link_file(package, row);
3989 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3990 IPersistFile_Save(pf, link_file, FALSE);
3992 msi_free(link_file);
3994 err:
3995 if (pf)
3996 IPersistFile_Release( pf );
3997 if (sl)
3998 IShellLinkW_Release( sl );
4000 return ERROR_SUCCESS;
4003 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4005 UINT rc;
4006 HRESULT res;
4007 MSIQUERY * view;
4008 static const WCHAR Query[] =
4009 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4010 '`','S','h','o','r','t','c','u','t','`',0};
4012 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4013 if (rc != ERROR_SUCCESS)
4014 return ERROR_SUCCESS;
4016 res = CoInitialize( NULL );
4018 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4019 msiobj_release(&view->hdr);
4021 if (SUCCEEDED(res))
4022 CoUninitialize();
4024 return rc;
4027 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4029 MSIPACKAGE *package = param;
4030 LPWSTR link_file;
4031 LPCWSTR component;
4032 MSICOMPONENT *comp;
4034 component = MSI_RecordGetString( row, 4 );
4035 comp = get_loaded_component( package, component );
4036 if (!comp)
4037 return ERROR_SUCCESS;
4039 if (!comp->Enabled)
4041 TRACE("component is disabled\n");
4042 return ERROR_SUCCESS;
4045 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4047 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4048 comp->Action = comp->Installed;
4049 return ERROR_SUCCESS;
4051 comp->Action = INSTALLSTATE_ABSENT;
4053 ui_actiondata( package, szRemoveShortcuts, row );
4055 link_file = get_link_file( package, row );
4057 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4058 if (!DeleteFileW( link_file ))
4060 WARN("Failed to remove shortcut file %u\n", GetLastError());
4062 msi_free( link_file );
4064 return ERROR_SUCCESS;
4067 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4069 UINT rc;
4070 MSIQUERY *view;
4071 static const WCHAR query[] =
4072 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4073 '`','S','h','o','r','t','c','u','t','`',0};
4075 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4076 if (rc != ERROR_SUCCESS)
4077 return ERROR_SUCCESS;
4079 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4080 msiobj_release( &view->hdr );
4082 return rc;
4085 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4087 MSIPACKAGE* package = param;
4088 HANDLE the_file;
4089 LPWSTR FilePath;
4090 LPCWSTR FileName;
4091 CHAR buffer[1024];
4092 DWORD sz;
4093 UINT rc;
4095 FileName = MSI_RecordGetString(row,1);
4096 if (!FileName)
4098 ERR("Unable to get FileName\n");
4099 return ERROR_SUCCESS;
4102 FilePath = build_icon_path(package,FileName);
4104 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4106 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4107 FILE_ATTRIBUTE_NORMAL, NULL);
4109 if (the_file == INVALID_HANDLE_VALUE)
4111 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4112 msi_free(FilePath);
4113 return ERROR_SUCCESS;
4118 DWORD write;
4119 sz = 1024;
4120 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4121 if (rc != ERROR_SUCCESS)
4123 ERR("Failed to get stream\n");
4124 CloseHandle(the_file);
4125 DeleteFileW(FilePath);
4126 break;
4128 WriteFile(the_file,buffer,sz,&write,NULL);
4129 } while (sz == 1024);
4131 msi_free(FilePath);
4132 CloseHandle(the_file);
4134 return ERROR_SUCCESS;
4137 static UINT msi_publish_icons(MSIPACKAGE *package)
4139 UINT r;
4140 MSIQUERY *view;
4142 static const WCHAR query[]= {
4143 'S','E','L','E','C','T',' ','*',' ',
4144 'F','R','O','M',' ','`','I','c','o','n','`',0};
4146 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4147 if (r == ERROR_SUCCESS)
4149 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4150 msiobj_release(&view->hdr);
4153 return ERROR_SUCCESS;
4156 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4158 UINT r;
4159 HKEY source;
4160 LPWSTR buffer;
4161 MSIMEDIADISK *disk;
4162 MSISOURCELISTINFO *info;
4164 r = RegCreateKeyW(hkey, szSourceList, &source);
4165 if (r != ERROR_SUCCESS)
4166 return r;
4168 RegCloseKey(source);
4170 buffer = strrchrW(package->PackagePath, '\\') + 1;
4171 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4172 package->Context, MSICODE_PRODUCT,
4173 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4174 if (r != ERROR_SUCCESS)
4175 return r;
4177 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4178 package->Context, MSICODE_PRODUCT,
4179 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4180 if (r != ERROR_SUCCESS)
4181 return r;
4183 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4184 package->Context, MSICODE_PRODUCT,
4185 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4186 if (r != ERROR_SUCCESS)
4187 return r;
4189 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4191 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4192 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4193 info->options, info->value);
4194 else
4195 MsiSourceListSetInfoW(package->ProductCode, NULL,
4196 info->context, info->options,
4197 info->property, info->value);
4200 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4202 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4203 disk->context, disk->options,
4204 disk->disk_id, disk->volume_label, disk->disk_prompt);
4207 return ERROR_SUCCESS;
4210 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4212 MSIHANDLE hdb, suminfo;
4213 WCHAR guids[MAX_PATH];
4214 WCHAR packcode[SQUISH_GUID_SIZE];
4215 LPWSTR buffer;
4216 LPWSTR ptr;
4217 DWORD langid;
4218 DWORD size;
4219 UINT r;
4221 static const WCHAR szProductLanguage[] =
4222 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4223 static const WCHAR szARPProductIcon[] =
4224 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4225 static const WCHAR szProductVersion[] =
4226 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4227 static const WCHAR szAssignment[] =
4228 {'A','s','s','i','g','n','m','e','n','t',0};
4229 static const WCHAR szAdvertiseFlags[] =
4230 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4231 static const WCHAR szClients[] =
4232 {'C','l','i','e','n','t','s',0};
4233 static const WCHAR szColon[] = {':',0};
4235 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4236 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4237 msi_free(buffer);
4239 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4240 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4242 /* FIXME */
4243 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4245 buffer = msi_dup_property(package->db, szARPProductIcon);
4246 if (buffer)
4248 LPWSTR path = build_icon_path(package,buffer);
4249 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4250 msi_free(path);
4251 msi_free(buffer);
4254 buffer = msi_dup_property(package->db, szProductVersion);
4255 if (buffer)
4257 DWORD verdword = msi_version_str_to_dword(buffer);
4258 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4259 msi_free(buffer);
4262 msi_reg_set_val_dword(hkey, szAssignment, 0);
4263 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4264 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4265 msi_reg_set_val_str(hkey, szClients, szColon);
4267 hdb = alloc_msihandle(&package->db->hdr);
4268 if (!hdb)
4269 return ERROR_NOT_ENOUGH_MEMORY;
4271 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4272 MsiCloseHandle(hdb);
4273 if (r != ERROR_SUCCESS)
4274 goto done;
4276 size = MAX_PATH;
4277 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4278 NULL, guids, &size);
4279 if (r != ERROR_SUCCESS)
4280 goto done;
4282 ptr = strchrW(guids, ';');
4283 if (ptr) *ptr = 0;
4284 squash_guid(guids, packcode);
4285 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4287 done:
4288 MsiCloseHandle(suminfo);
4289 return ERROR_SUCCESS;
4292 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4294 UINT r;
4295 HKEY hkey;
4296 LPWSTR upgrade;
4297 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4299 upgrade = msi_dup_property(package->db, szUpgradeCode);
4300 if (!upgrade)
4301 return ERROR_SUCCESS;
4303 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4305 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4306 if (r != ERROR_SUCCESS)
4307 goto done;
4309 else
4311 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4312 if (r != ERROR_SUCCESS)
4313 goto done;
4316 squash_guid(package->ProductCode, squashed_pc);
4317 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4319 RegCloseKey(hkey);
4321 done:
4322 msi_free(upgrade);
4323 return r;
4326 static BOOL msi_check_publish(MSIPACKAGE *package)
4328 MSIFEATURE *feature;
4330 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4332 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4333 return TRUE;
4336 return FALSE;
4339 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4341 MSIFEATURE *feature;
4343 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4345 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4346 return FALSE;
4349 return TRUE;
4352 static UINT msi_publish_patches( MSIPACKAGE *package )
4354 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4355 WCHAR patch_squashed[GUID_SIZE];
4356 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4357 LONG res;
4358 MSIPATCHINFO *patch;
4359 UINT r;
4360 WCHAR *p, *all_patches = NULL;
4361 DWORD len = 0;
4363 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4364 if (r != ERROR_SUCCESS)
4365 return ERROR_FUNCTION_FAILED;
4367 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4368 if (res != ERROR_SUCCESS)
4370 r = ERROR_FUNCTION_FAILED;
4371 goto done;
4374 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4375 if (r != ERROR_SUCCESS)
4376 goto done;
4378 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4380 squash_guid( patch->patchcode, patch_squashed );
4381 len += strlenW( patch_squashed ) + 1;
4384 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4385 if (!all_patches)
4386 goto done;
4388 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4390 HKEY patch_key;
4392 squash_guid( patch->patchcode, p );
4393 p += strlenW( p ) + 1;
4395 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4396 (const BYTE *)patch->transforms,
4397 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4398 if (res != ERROR_SUCCESS)
4399 goto done;
4401 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4402 if (r != ERROR_SUCCESS)
4403 goto done;
4405 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4406 (const BYTE *)patch->localfile,
4407 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4408 RegCloseKey( patch_key );
4409 if (res != ERROR_SUCCESS)
4410 goto done;
4412 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4413 if (res != ERROR_SUCCESS)
4414 goto done;
4416 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4417 RegCloseKey( patch_key );
4418 if (res != ERROR_SUCCESS)
4419 goto done;
4422 all_patches[len] = 0;
4423 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4424 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4425 if (res != ERROR_SUCCESS)
4426 goto done;
4428 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4429 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4430 if (res != ERROR_SUCCESS)
4431 r = ERROR_FUNCTION_FAILED;
4433 done:
4434 RegCloseKey( product_patches_key );
4435 RegCloseKey( patches_key );
4436 RegCloseKey( product_key );
4437 msi_free( all_patches );
4438 return r;
4442 * 99% of the work done here is only done for
4443 * advertised installs. However this is where the
4444 * Icon table is processed and written out
4445 * so that is what I am going to do here.
4447 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4449 UINT rc;
4450 HKEY hukey = NULL, hudkey = NULL;
4451 MSIRECORD *uirow;
4453 if (!list_empty(&package->patches))
4455 rc = msi_publish_patches(package);
4456 if (rc != ERROR_SUCCESS)
4457 goto end;
4460 /* FIXME: also need to publish if the product is in advertise mode */
4461 if (!msi_check_publish(package))
4462 return ERROR_SUCCESS;
4464 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4465 &hukey, TRUE);
4466 if (rc != ERROR_SUCCESS)
4467 goto end;
4469 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4470 NULL, &hudkey, TRUE);
4471 if (rc != ERROR_SUCCESS)
4472 goto end;
4474 rc = msi_publish_upgrade_code(package);
4475 if (rc != ERROR_SUCCESS)
4476 goto end;
4478 rc = msi_publish_product_properties(package, hukey);
4479 if (rc != ERROR_SUCCESS)
4480 goto end;
4482 rc = msi_publish_sourcelist(package, hukey);
4483 if (rc != ERROR_SUCCESS)
4484 goto end;
4486 rc = msi_publish_icons(package);
4488 end:
4489 uirow = MSI_CreateRecord( 1 );
4490 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4491 ui_actiondata( package, szPublishProduct, uirow );
4492 msiobj_release( &uirow->hdr );
4494 RegCloseKey(hukey);
4495 RegCloseKey(hudkey);
4497 return rc;
4500 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4502 WCHAR *filename, *ptr, *folder, *ret;
4503 const WCHAR *dirprop;
4505 filename = msi_dup_record_field( row, 2 );
4506 if (filename && (ptr = strchrW( filename, '|' )))
4507 ptr++;
4508 else
4509 ptr = filename;
4511 dirprop = MSI_RecordGetString( row, 3 );
4512 if (dirprop)
4514 folder = resolve_target_folder( package, dirprop, FALSE, TRUE, NULL );
4515 if (!folder)
4516 folder = msi_dup_property( package->db, dirprop );
4518 else
4519 folder = msi_dup_property( package->db, szWindowsFolder );
4521 if (!folder)
4523 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4524 msi_free( filename );
4525 return NULL;
4528 ret = build_directory_name( 2, folder, ptr );
4530 msi_free( filename );
4531 msi_free( folder );
4532 return ret;
4535 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4537 MSIPACKAGE *package = param;
4538 LPCWSTR component, section, key, value, identifier;
4539 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4540 MSIRECORD * uirow;
4541 INT action;
4542 MSICOMPONENT *comp;
4544 component = MSI_RecordGetString(row, 8);
4545 comp = get_loaded_component(package,component);
4546 if (!comp)
4547 return ERROR_SUCCESS;
4549 if (!comp->Enabled)
4551 TRACE("component is disabled\n");
4552 return ERROR_SUCCESS;
4555 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4557 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4558 comp->Action = comp->Installed;
4559 return ERROR_SUCCESS;
4561 comp->Action = INSTALLSTATE_LOCAL;
4563 identifier = MSI_RecordGetString(row,1);
4564 section = MSI_RecordGetString(row,4);
4565 key = MSI_RecordGetString(row,5);
4566 value = MSI_RecordGetString(row,6);
4567 action = MSI_RecordGetInteger(row,7);
4569 deformat_string(package,section,&deformated_section);
4570 deformat_string(package,key,&deformated_key);
4571 deformat_string(package,value,&deformated_value);
4573 fullname = get_ini_file_name(package, row);
4575 if (action == 0)
4577 TRACE("Adding value %s to section %s in %s\n",
4578 debugstr_w(deformated_key), debugstr_w(deformated_section),
4579 debugstr_w(fullname));
4580 WritePrivateProfileStringW(deformated_section, deformated_key,
4581 deformated_value, fullname);
4583 else if (action == 1)
4585 WCHAR returned[10];
4586 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4587 returned, 10, fullname);
4588 if (returned[0] == 0)
4590 TRACE("Adding value %s to section %s in %s\n",
4591 debugstr_w(deformated_key), debugstr_w(deformated_section),
4592 debugstr_w(fullname));
4594 WritePrivateProfileStringW(deformated_section, deformated_key,
4595 deformated_value, fullname);
4598 else if (action == 3)
4599 FIXME("Append to existing section not yet implemented\n");
4601 uirow = MSI_CreateRecord(4);
4602 MSI_RecordSetStringW(uirow,1,identifier);
4603 MSI_RecordSetStringW(uirow,2,deformated_section);
4604 MSI_RecordSetStringW(uirow,3,deformated_key);
4605 MSI_RecordSetStringW(uirow,4,deformated_value);
4606 ui_actiondata(package,szWriteIniValues,uirow);
4607 msiobj_release( &uirow->hdr );
4609 msi_free(fullname);
4610 msi_free(deformated_key);
4611 msi_free(deformated_value);
4612 msi_free(deformated_section);
4613 return ERROR_SUCCESS;
4616 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4618 UINT rc;
4619 MSIQUERY * view;
4620 static const WCHAR ExecSeqQuery[] =
4621 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4622 '`','I','n','i','F','i','l','e','`',0};
4624 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4625 if (rc != ERROR_SUCCESS)
4627 TRACE("no IniFile table\n");
4628 return ERROR_SUCCESS;
4631 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4632 msiobj_release(&view->hdr);
4633 return rc;
4636 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4638 MSIPACKAGE *package = param;
4639 LPCWSTR component, section, key, value, identifier;
4640 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4641 MSICOMPONENT *comp;
4642 MSIRECORD *uirow;
4643 INT action;
4645 component = MSI_RecordGetString( row, 8 );
4646 comp = get_loaded_component( package, component );
4647 if (!comp)
4648 return ERROR_SUCCESS;
4650 if (!comp->Enabled)
4652 TRACE("component is disabled\n");
4653 return ERROR_SUCCESS;
4656 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4658 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4659 comp->Action = comp->Installed;
4660 return ERROR_SUCCESS;
4662 comp->Action = INSTALLSTATE_ABSENT;
4664 identifier = MSI_RecordGetString( row, 1 );
4665 section = MSI_RecordGetString( row, 4 );
4666 key = MSI_RecordGetString( row, 5 );
4667 value = MSI_RecordGetString( row, 6 );
4668 action = MSI_RecordGetInteger( row, 7 );
4670 deformat_string( package, section, &deformated_section );
4671 deformat_string( package, key, &deformated_key );
4672 deformat_string( package, value, &deformated_value );
4674 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4676 filename = get_ini_file_name( package, row );
4678 TRACE("Removing key %s from section %s in %s\n",
4679 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4681 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4683 WARN("Unable to remove key %u\n", GetLastError());
4685 msi_free( filename );
4687 else
4688 FIXME("Unsupported action %d\n", action);
4691 uirow = MSI_CreateRecord( 4 );
4692 MSI_RecordSetStringW( uirow, 1, identifier );
4693 MSI_RecordSetStringW( uirow, 2, deformated_section );
4694 MSI_RecordSetStringW( uirow, 3, deformated_key );
4695 MSI_RecordSetStringW( uirow, 4, deformated_value );
4696 ui_actiondata( package, szRemoveIniValues, uirow );
4697 msiobj_release( &uirow->hdr );
4699 msi_free( deformated_key );
4700 msi_free( deformated_value );
4701 msi_free( deformated_section );
4702 return ERROR_SUCCESS;
4705 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4707 MSIPACKAGE *package = param;
4708 LPCWSTR component, section, key, value, identifier;
4709 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4710 MSICOMPONENT *comp;
4711 MSIRECORD *uirow;
4712 INT action;
4714 component = MSI_RecordGetString( row, 8 );
4715 comp = get_loaded_component( package, component );
4716 if (!comp)
4717 return ERROR_SUCCESS;
4719 if (!comp->Enabled)
4721 TRACE("component is disabled\n");
4722 return ERROR_SUCCESS;
4725 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4727 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4728 comp->Action = comp->Installed;
4729 return ERROR_SUCCESS;
4731 comp->Action = INSTALLSTATE_LOCAL;
4733 identifier = MSI_RecordGetString( row, 1 );
4734 section = MSI_RecordGetString( row, 4 );
4735 key = MSI_RecordGetString( row, 5 );
4736 value = MSI_RecordGetString( row, 6 );
4737 action = MSI_RecordGetInteger( row, 7 );
4739 deformat_string( package, section, &deformated_section );
4740 deformat_string( package, key, &deformated_key );
4741 deformat_string( package, value, &deformated_value );
4743 if (action == msidbIniFileActionRemoveLine)
4745 filename = get_ini_file_name( package, row );
4747 TRACE("Removing key %s from section %s in %s\n",
4748 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4750 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4752 WARN("Unable to remove key %u\n", GetLastError());
4754 msi_free( filename );
4756 else
4757 FIXME("Unsupported action %d\n", action);
4759 uirow = MSI_CreateRecord( 4 );
4760 MSI_RecordSetStringW( uirow, 1, identifier );
4761 MSI_RecordSetStringW( uirow, 2, deformated_section );
4762 MSI_RecordSetStringW( uirow, 3, deformated_key );
4763 MSI_RecordSetStringW( uirow, 4, deformated_value );
4764 ui_actiondata( package, szRemoveIniValues, uirow );
4765 msiobj_release( &uirow->hdr );
4767 msi_free( deformated_key );
4768 msi_free( deformated_value );
4769 msi_free( deformated_section );
4770 return ERROR_SUCCESS;
4773 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4775 UINT rc;
4776 MSIQUERY *view;
4777 static const WCHAR query[] =
4778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4779 '`','I','n','i','F','i','l','e','`',0};
4780 static const WCHAR remove_query[] =
4781 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4782 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4784 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4785 if (rc == ERROR_SUCCESS)
4787 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4788 msiobj_release( &view->hdr );
4789 if (rc != ERROR_SUCCESS)
4790 return rc;
4793 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4794 if (rc == ERROR_SUCCESS)
4796 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4797 msiobj_release( &view->hdr );
4798 if (rc != ERROR_SUCCESS)
4799 return rc;
4802 return ERROR_SUCCESS;
4805 static void register_dll( const WCHAR *dll, BOOL unregister )
4807 HMODULE hmod;
4809 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4810 if (hmod)
4812 HRESULT (WINAPI *func_ptr)( void );
4813 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4815 func_ptr = (void *)GetProcAddress( hmod, func );
4816 if (func_ptr)
4818 HRESULT hr = func_ptr();
4819 if (FAILED( hr ))
4820 WARN("failed to register dll 0x%08x\n", hr);
4822 else
4823 WARN("entry point %s not found\n", func);
4824 FreeLibrary( hmod );
4825 return;
4827 WARN("failed to load library %u\n", GetLastError());
4830 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4832 MSIPACKAGE *package = param;
4833 LPCWSTR filename;
4834 MSIFILE *file;
4835 MSIRECORD *uirow;
4837 filename = MSI_RecordGetString(row,1);
4838 file = get_loaded_file( package, filename );
4840 if (!file)
4842 ERR("Unable to find file id %s\n",debugstr_w(filename));
4843 return ERROR_SUCCESS;
4846 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4848 register_dll( file->TargetPath, FALSE );
4850 uirow = MSI_CreateRecord( 2 );
4851 MSI_RecordSetStringW( uirow, 1, filename );
4852 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4853 ui_actiondata( package, szSelfRegModules, uirow );
4854 msiobj_release( &uirow->hdr );
4856 return ERROR_SUCCESS;
4859 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4861 UINT rc;
4862 MSIQUERY * view;
4863 static const WCHAR ExecSeqQuery[] =
4864 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4865 '`','S','e','l','f','R','e','g','`',0};
4867 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4868 if (rc != ERROR_SUCCESS)
4870 TRACE("no SelfReg table\n");
4871 return ERROR_SUCCESS;
4874 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4875 msiobj_release(&view->hdr);
4877 return ERROR_SUCCESS;
4880 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4882 MSIPACKAGE *package = param;
4883 LPCWSTR filename;
4884 MSIFILE *file;
4885 MSIRECORD *uirow;
4887 filename = MSI_RecordGetString( row, 1 );
4888 file = get_loaded_file( package, filename );
4890 if (!file)
4892 ERR("Unable to find file id %s\n", debugstr_w(filename));
4893 return ERROR_SUCCESS;
4896 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4898 register_dll( file->TargetPath, TRUE );
4900 uirow = MSI_CreateRecord( 2 );
4901 MSI_RecordSetStringW( uirow, 1, filename );
4902 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4903 ui_actiondata( package, szSelfUnregModules, uirow );
4904 msiobj_release( &uirow->hdr );
4906 return ERROR_SUCCESS;
4909 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4911 UINT rc;
4912 MSIQUERY *view;
4913 static const WCHAR query[] =
4914 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4915 '`','S','e','l','f','R','e','g','`',0};
4917 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4918 if (rc != ERROR_SUCCESS)
4920 TRACE("no SelfReg table\n");
4921 return ERROR_SUCCESS;
4924 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4925 msiobj_release( &view->hdr );
4927 return ERROR_SUCCESS;
4930 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4932 MSIFEATURE *feature;
4933 UINT rc;
4934 HKEY hkey = NULL, userdata = NULL;
4936 if (!msi_check_publish(package))
4937 return ERROR_SUCCESS;
4939 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4940 &hkey, TRUE);
4941 if (rc != ERROR_SUCCESS)
4942 goto end;
4944 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4945 &userdata, TRUE);
4946 if (rc != ERROR_SUCCESS)
4947 goto end;
4949 /* here the guids are base 85 encoded */
4950 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4952 ComponentList *cl;
4953 LPWSTR data = NULL;
4954 GUID clsid;
4955 INT size;
4956 BOOL absent = FALSE;
4957 MSIRECORD *uirow;
4959 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4960 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4961 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4963 size = 1;
4964 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4966 size += 21;
4968 if (feature->Feature_Parent)
4969 size += strlenW( feature->Feature_Parent )+2;
4971 data = msi_alloc(size * sizeof(WCHAR));
4973 data[0] = 0;
4974 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4976 MSICOMPONENT* component = cl->component;
4977 WCHAR buf[21];
4979 buf[0] = 0;
4980 if (component->ComponentId)
4982 TRACE("From %s\n",debugstr_w(component->ComponentId));
4983 CLSIDFromString(component->ComponentId, &clsid);
4984 encode_base85_guid(&clsid,buf);
4985 TRACE("to %s\n",debugstr_w(buf));
4986 strcatW(data,buf);
4990 if (feature->Feature_Parent)
4992 static const WCHAR sep[] = {'\2',0};
4993 strcatW(data,sep);
4994 strcatW(data,feature->Feature_Parent);
4997 msi_reg_set_val_str( userdata, feature->Feature, data );
4998 msi_free(data);
5000 size = 0;
5001 if (feature->Feature_Parent)
5002 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5003 if (!absent)
5005 size += sizeof(WCHAR);
5006 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5007 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5009 else
5011 size += 2*sizeof(WCHAR);
5012 data = msi_alloc(size);
5013 data[0] = 0x6;
5014 data[1] = 0;
5015 if (feature->Feature_Parent)
5016 strcpyW( &data[1], feature->Feature_Parent );
5017 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5018 (LPBYTE)data,size);
5019 msi_free(data);
5022 /* the UI chunk */
5023 uirow = MSI_CreateRecord( 1 );
5024 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5025 ui_actiondata( package, szPublishFeatures, uirow);
5026 msiobj_release( &uirow->hdr );
5027 /* FIXME: call ui_progress? */
5030 end:
5031 RegCloseKey(hkey);
5032 RegCloseKey(userdata);
5033 return rc;
5036 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5038 UINT r;
5039 HKEY hkey;
5040 MSIRECORD *uirow;
5042 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5044 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5045 &hkey, FALSE);
5046 if (r == ERROR_SUCCESS)
5048 RegDeleteValueW(hkey, feature->Feature);
5049 RegCloseKey(hkey);
5052 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5053 &hkey, FALSE);
5054 if (r == ERROR_SUCCESS)
5056 RegDeleteValueW(hkey, feature->Feature);
5057 RegCloseKey(hkey);
5060 uirow = MSI_CreateRecord( 1 );
5061 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5062 ui_actiondata( package, szUnpublishFeatures, uirow );
5063 msiobj_release( &uirow->hdr );
5065 return ERROR_SUCCESS;
5068 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5070 MSIFEATURE *feature;
5072 if (!msi_check_unpublish(package))
5073 return ERROR_SUCCESS;
5075 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5077 msi_unpublish_feature(package, feature);
5080 return ERROR_SUCCESS;
5083 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5085 SYSTEMTIME systime;
5086 DWORD size, langid;
5087 WCHAR date[9], *val, *buffer;
5088 const WCHAR *prop, *key;
5090 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5091 static const WCHAR szWindowsInstaller[] =
5092 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
5093 static const WCHAR modpath_fmt[] =
5094 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5095 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5096 static const WCHAR szModifyPath[] =
5097 {'M','o','d','i','f','y','P','a','t','h',0};
5098 static const WCHAR szUninstallString[] =
5099 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5100 static const WCHAR szEstimatedSize[] =
5101 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5102 static const WCHAR szProductLanguage[] =
5103 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
5104 static const WCHAR szProductVersion[] =
5105 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
5106 static const WCHAR szDisplayVersion[] =
5107 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5108 static const WCHAR szInstallSource[] =
5109 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5110 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5111 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5112 static const WCHAR szAuthorizedCDFPrefix[] =
5113 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5114 static const WCHAR szARPCONTACT[] =
5115 {'A','R','P','C','O','N','T','A','C','T',0};
5116 static const WCHAR szContact[] =
5117 {'C','o','n','t','a','c','t',0};
5118 static const WCHAR szARPCOMMENTS[] =
5119 {'A','R','P','C','O','M','M','E','N','T','S',0};
5120 static const WCHAR szComments[] =
5121 {'C','o','m','m','e','n','t','s',0};
5122 static const WCHAR szProductName[] =
5123 {'P','r','o','d','u','c','t','N','a','m','e',0};
5124 static const WCHAR szDisplayName[] =
5125 {'D','i','s','p','l','a','y','N','a','m','e',0};
5126 static const WCHAR szARPHELPLINK[] =
5127 {'A','R','P','H','E','L','P','L','I','N','K',0};
5128 static const WCHAR szHelpLink[] =
5129 {'H','e','l','p','L','i','n','k',0};
5130 static const WCHAR szARPHELPTELEPHONE[] =
5131 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5132 static const WCHAR szHelpTelephone[] =
5133 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5134 static const WCHAR szARPINSTALLLOCATION[] =
5135 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5136 static const WCHAR szInstallLocation[] =
5137 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
5138 static const WCHAR szManufacturer[] =
5139 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5140 static const WCHAR szPublisher[] =
5141 {'P','u','b','l','i','s','h','e','r',0};
5142 static const WCHAR szARPREADME[] =
5143 {'A','R','P','R','E','A','D','M','E',0};
5144 static const WCHAR szReadme[] =
5145 {'R','e','a','d','M','e',0};
5146 static const WCHAR szARPSIZE[] =
5147 {'A','R','P','S','I','Z','E',0};
5148 static const WCHAR szSize[] =
5149 {'S','i','z','e',0};
5150 static const WCHAR szARPURLINFOABOUT[] =
5151 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5152 static const WCHAR szURLInfoAbout[] =
5153 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5154 static const WCHAR szARPURLUPDATEINFO[] =
5155 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5156 static const WCHAR szURLUpdateInfo[] =
5157 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5159 static const WCHAR *propval[] = {
5160 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5161 szARPCONTACT, szContact,
5162 szARPCOMMENTS, szComments,
5163 szProductName, szDisplayName,
5164 szARPHELPLINK, szHelpLink,
5165 szARPHELPTELEPHONE, szHelpTelephone,
5166 szARPINSTALLLOCATION, szInstallLocation,
5167 cszSourceDir, szInstallSource,
5168 szManufacturer, szPublisher,
5169 szARPREADME, szReadme,
5170 szARPSIZE, szSize,
5171 szARPURLINFOABOUT, szURLInfoAbout,
5172 szARPURLUPDATEINFO, szURLUpdateInfo,
5173 NULL
5175 const WCHAR **p = propval;
5177 while (*p)
5179 prop = *p++;
5180 key = *p++;
5181 val = msi_dup_property(package->db, prop);
5182 msi_reg_set_val_str(hkey, key, val);
5183 msi_free(val);
5186 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5188 size = deformat_string(package, modpath_fmt, &buffer);
5189 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5190 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5191 msi_free(buffer);
5193 /* FIXME: Write real Estimated Size when we have it */
5194 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5196 GetLocalTime(&systime);
5197 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5198 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5200 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5201 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5203 buffer = msi_dup_property(package->db, szProductVersion);
5204 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5205 if (buffer)
5207 DWORD verdword = msi_version_str_to_dword(buffer);
5209 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5210 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5211 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5212 msi_free(buffer);
5215 return ERROR_SUCCESS;
5218 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5220 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5221 MSIRECORD *uirow;
5222 LPWSTR upgrade_code;
5223 HKEY hkey, props;
5224 HKEY upgrade;
5225 UINT rc;
5227 /* FIXME: also need to publish if the product is in advertise mode */
5228 if (!msi_check_publish(package))
5229 return ERROR_SUCCESS;
5231 rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE);
5232 if (rc != ERROR_SUCCESS)
5233 return rc;
5235 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5236 NULL, &props, TRUE);
5237 if (rc != ERROR_SUCCESS)
5238 goto done;
5240 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
5241 msi_free( package->db->localfile );
5242 package->db->localfile = NULL;
5244 rc = msi_publish_install_properties(package, hkey);
5245 if (rc != ERROR_SUCCESS)
5246 goto done;
5248 rc = msi_publish_install_properties(package, props);
5249 if (rc != ERROR_SUCCESS)
5250 goto done;
5252 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5253 if (upgrade_code)
5255 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
5256 squash_guid(package->ProductCode, squashed_pc);
5257 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
5258 RegCloseKey(upgrade);
5259 msi_free(upgrade_code);
5262 done:
5263 uirow = MSI_CreateRecord( 1 );
5264 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5265 ui_actiondata( package, szRegisterProduct, uirow );
5266 msiobj_release( &uirow->hdr );
5268 RegCloseKey(hkey);
5269 return ERROR_SUCCESS;
5272 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5274 return execute_script(package,INSTALL_SCRIPT);
5277 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
5279 WCHAR *upgrade, **features;
5280 BOOL full_uninstall = TRUE;
5281 MSIFEATURE *feature;
5282 MSIPATCHINFO *patch;
5284 static const WCHAR szUpgradeCode[] =
5285 {'U','p','g','r','a','d','e','C','o','d','e',0};
5287 features = msi_split_string(remove, ',');
5288 if (!features)
5290 ERR("REMOVE feature list is empty!\n");
5291 return ERROR_FUNCTION_FAILED;
5294 if (!strcmpW( features[0], szAll ))
5295 full_uninstall = TRUE;
5296 else
5298 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5300 if (feature->Action != INSTALLSTATE_ABSENT)
5301 full_uninstall = FALSE;
5304 msi_free(features);
5306 if (!full_uninstall)
5307 return ERROR_SUCCESS;
5309 MSIREG_DeleteProductKey(package->ProductCode);
5310 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5311 MSIREG_DeleteUninstallKey(package);
5313 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5314 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5315 MSIREG_DeleteUserProductKey(package->ProductCode);
5316 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5318 upgrade = msi_dup_property(package->db, szUpgradeCode);
5319 if (upgrade)
5321 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5322 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5323 msi_free(upgrade);
5326 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5328 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5331 return ERROR_SUCCESS;
5334 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5336 UINT rc;
5337 WCHAR *remove;
5339 /* turn off scheduling */
5340 package->script->CurrentlyScripting= FALSE;
5342 /* first do the same as an InstallExecute */
5343 rc = ACTION_InstallExecute(package);
5344 if (rc != ERROR_SUCCESS)
5345 return rc;
5347 /* then handle Commit Actions */
5348 rc = execute_script(package,COMMIT_SCRIPT);
5349 if (rc != ERROR_SUCCESS)
5350 return rc;
5352 remove = msi_dup_property(package->db, szRemove);
5353 if (remove)
5354 rc = msi_unpublish_product(package, remove);
5356 msi_free(remove);
5357 return rc;
5360 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5362 static const WCHAR RunOnce[] = {
5363 'S','o','f','t','w','a','r','e','\\',
5364 'M','i','c','r','o','s','o','f','t','\\',
5365 'W','i','n','d','o','w','s','\\',
5366 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5367 'R','u','n','O','n','c','e',0};
5368 static const WCHAR InstallRunOnce[] = {
5369 'S','o','f','t','w','a','r','e','\\',
5370 'M','i','c','r','o','s','o','f','t','\\',
5371 'W','i','n','d','o','w','s','\\',
5372 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5373 'I','n','s','t','a','l','l','e','r','\\',
5374 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5376 static const WCHAR msiexec_fmt[] = {
5377 '%','s',
5378 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5379 '\"','%','s','\"',0};
5380 static const WCHAR install_fmt[] = {
5381 '/','I',' ','\"','%','s','\"',' ',
5382 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5383 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5384 WCHAR buffer[256], sysdir[MAX_PATH];
5385 HKEY hkey;
5386 WCHAR squished_pc[100];
5388 squash_guid(package->ProductCode,squished_pc);
5390 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5391 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5392 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5393 squished_pc);
5395 msi_reg_set_val_str( hkey, squished_pc, buffer );
5396 RegCloseKey(hkey);
5398 TRACE("Reboot command %s\n",debugstr_w(buffer));
5400 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5401 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5403 msi_reg_set_val_str( hkey, squished_pc, buffer );
5404 RegCloseKey(hkey);
5406 return ERROR_INSTALL_SUSPEND;
5409 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5411 DWORD attrib;
5412 UINT rc;
5415 * We are currently doing what should be done here in the top level Install
5416 * however for Administrative and uninstalls this step will be needed
5418 if (!package->PackagePath)
5419 return ERROR_SUCCESS;
5421 msi_set_sourcedir_props(package, TRUE);
5423 attrib = GetFileAttributesW(package->db->path);
5424 if (attrib == INVALID_FILE_ATTRIBUTES)
5426 LPWSTR prompt;
5427 LPWSTR msg;
5428 DWORD size = 0;
5430 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5431 package->Context, MSICODE_PRODUCT,
5432 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5433 if (rc == ERROR_MORE_DATA)
5435 prompt = msi_alloc(size * sizeof(WCHAR));
5436 MsiSourceListGetInfoW(package->ProductCode, NULL,
5437 package->Context, MSICODE_PRODUCT,
5438 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5440 else
5441 prompt = strdupW(package->db->path);
5443 msg = generate_error_string(package,1302,1,prompt);
5444 while(attrib == INVALID_FILE_ATTRIBUTES)
5446 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5447 if (rc == IDCANCEL)
5449 rc = ERROR_INSTALL_USEREXIT;
5450 break;
5452 attrib = GetFileAttributesW(package->db->path);
5454 msi_free(prompt);
5455 rc = ERROR_SUCCESS;
5457 else
5458 return ERROR_SUCCESS;
5460 return rc;
5463 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5465 HKEY hkey = 0;
5466 LPWSTR buffer, productid = NULL;
5467 UINT i, rc = ERROR_SUCCESS;
5468 MSIRECORD *uirow;
5470 static const WCHAR szPropKeys[][80] =
5472 {'P','r','o','d','u','c','t','I','D',0},
5473 {'U','S','E','R','N','A','M','E',0},
5474 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5475 {0},
5478 static const WCHAR szRegKeys[][80] =
5480 {'P','r','o','d','u','c','t','I','D',0},
5481 {'R','e','g','O','w','n','e','r',0},
5482 {'R','e','g','C','o','m','p','a','n','y',0},
5483 {0},
5486 if (msi_check_unpublish(package))
5488 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5489 goto end;
5492 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5493 if (!productid)
5494 goto end;
5496 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5497 NULL, &hkey, TRUE);
5498 if (rc != ERROR_SUCCESS)
5499 goto end;
5501 for( i = 0; szPropKeys[i][0]; i++ )
5503 buffer = msi_dup_property( package->db, szPropKeys[i] );
5504 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5505 msi_free( buffer );
5508 end:
5509 uirow = MSI_CreateRecord( 1 );
5510 MSI_RecordSetStringW( uirow, 1, productid );
5511 ui_actiondata( package, szRegisterUser, uirow );
5512 msiobj_release( &uirow->hdr );
5514 msi_free(productid);
5515 RegCloseKey(hkey);
5516 return rc;
5520 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5522 UINT rc;
5524 package->script->InWhatSequence |= SEQUENCE_EXEC;
5525 rc = ACTION_ProcessExecSequence(package,FALSE);
5526 return rc;
5530 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5532 MSIPACKAGE *package = param;
5533 LPCWSTR compgroupid, component, feature, qualifier, text;
5534 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5535 HKEY hkey = NULL;
5536 UINT rc;
5537 MSICOMPONENT *comp;
5538 MSIFEATURE *feat;
5539 DWORD sz;
5540 MSIRECORD *uirow;
5541 int len;
5543 feature = MSI_RecordGetString(rec, 5);
5544 feat = get_loaded_feature(package, feature);
5545 if (!feat)
5546 return ERROR_SUCCESS;
5548 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5549 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5550 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5552 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5553 feat->Action = feat->Installed;
5554 return ERROR_SUCCESS;
5557 component = MSI_RecordGetString(rec, 3);
5558 comp = get_loaded_component(package, component);
5559 if (!comp)
5560 return ERROR_SUCCESS;
5562 compgroupid = MSI_RecordGetString(rec,1);
5563 qualifier = MSI_RecordGetString(rec,2);
5565 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5566 if (rc != ERROR_SUCCESS)
5567 goto end;
5569 advertise = create_component_advertise_string( package, comp, feature );
5570 text = MSI_RecordGetString( rec, 4 );
5571 if (text)
5573 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5574 strcpyW( p, advertise );
5575 strcatW( p, text );
5576 msi_free( advertise );
5577 advertise = p;
5579 existing = msi_reg_get_val_str( hkey, qualifier );
5581 sz = strlenW( advertise ) + 1;
5582 if (existing)
5584 for (p = existing; *p; p += len)
5586 len = strlenW( p ) + 1;
5587 if (strcmpW( advertise, p )) sz += len;
5590 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5592 rc = ERROR_OUTOFMEMORY;
5593 goto end;
5595 q = output;
5596 if (existing)
5598 for (p = existing; *p; p += len)
5600 len = strlenW( p ) + 1;
5601 if (strcmpW( advertise, p ))
5603 memcpy( q, p, len * sizeof(WCHAR) );
5604 q += len;
5608 strcpyW( q, advertise );
5609 q[strlenW( q ) + 1] = 0;
5611 msi_reg_set_val_multi_str( hkey, qualifier, output );
5613 end:
5614 RegCloseKey(hkey);
5615 msi_free( output );
5616 msi_free( advertise );
5617 msi_free( existing );
5619 /* the UI chunk */
5620 uirow = MSI_CreateRecord( 2 );
5621 MSI_RecordSetStringW( uirow, 1, compgroupid );
5622 MSI_RecordSetStringW( uirow, 2, qualifier);
5623 ui_actiondata( package, szPublishComponents, uirow);
5624 msiobj_release( &uirow->hdr );
5625 /* FIXME: call ui_progress? */
5627 return rc;
5631 * At present I am ignorning the advertised components part of this and only
5632 * focusing on the qualified component sets
5634 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5636 UINT rc;
5637 MSIQUERY * view;
5638 static const WCHAR ExecSeqQuery[] =
5639 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5640 '`','P','u','b','l','i','s','h',
5641 'C','o','m','p','o','n','e','n','t','`',0};
5643 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5644 if (rc != ERROR_SUCCESS)
5645 return ERROR_SUCCESS;
5647 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5648 msiobj_release(&view->hdr);
5650 return rc;
5653 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5655 static const WCHAR szInstallerComponents[] = {
5656 'S','o','f','t','w','a','r','e','\\',
5657 'M','i','c','r','o','s','o','f','t','\\',
5658 'I','n','s','t','a','l','l','e','r','\\',
5659 'C','o','m','p','o','n','e','n','t','s','\\',0};
5661 MSIPACKAGE *package = param;
5662 LPCWSTR compgroupid, component, feature, qualifier;
5663 MSICOMPONENT *comp;
5664 MSIFEATURE *feat;
5665 MSIRECORD *uirow;
5666 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5667 LONG res;
5669 feature = MSI_RecordGetString( rec, 5 );
5670 feat = get_loaded_feature( package, feature );
5671 if (!feat)
5672 return ERROR_SUCCESS;
5674 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5676 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5677 feat->Action = feat->Installed;
5678 return ERROR_SUCCESS;
5681 component = MSI_RecordGetString( rec, 3 );
5682 comp = get_loaded_component( package, component );
5683 if (!comp)
5684 return ERROR_SUCCESS;
5686 compgroupid = MSI_RecordGetString( rec, 1 );
5687 qualifier = MSI_RecordGetString( rec, 2 );
5689 squash_guid( compgroupid, squashed );
5690 strcpyW( keypath, szInstallerComponents );
5691 strcatW( keypath, squashed );
5693 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5694 if (res != ERROR_SUCCESS)
5696 WARN("Unable to delete component key %d\n", res);
5699 uirow = MSI_CreateRecord( 2 );
5700 MSI_RecordSetStringW( uirow, 1, compgroupid );
5701 MSI_RecordSetStringW( uirow, 2, qualifier );
5702 ui_actiondata( package, szUnpublishComponents, uirow );
5703 msiobj_release( &uirow->hdr );
5705 return ERROR_SUCCESS;
5708 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5710 UINT rc;
5711 MSIQUERY *view;
5712 static const WCHAR query[] =
5713 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5714 '`','P','u','b','l','i','s','h',
5715 'C','o','m','p','o','n','e','n','t','`',0};
5717 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5718 if (rc != ERROR_SUCCESS)
5719 return ERROR_SUCCESS;
5721 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5722 msiobj_release( &view->hdr );
5724 return rc;
5727 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5729 MSIPACKAGE *package = param;
5730 MSIRECORD *row;
5731 MSIFILE *file;
5732 SC_HANDLE hscm, service = NULL;
5733 LPCWSTR comp, key;
5734 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5735 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5736 DWORD serv_type, start_type, err_control;
5737 SERVICE_DESCRIPTIONW sd = {NULL};
5739 static const WCHAR query[] =
5740 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5741 '`','C','o','m','p','o','n','e','n','t','`',' ',
5742 'W','H','E','R','E',' ',
5743 '`','C','o','m','p','o','n','e','n','t','`',' ',
5744 '=','\'','%','s','\'',0};
5746 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5747 if (!hscm)
5749 ERR("Failed to open the SC Manager!\n");
5750 goto done;
5753 comp = MSI_RecordGetString( rec, 12 );
5754 if (!get_loaded_component( package, comp ))
5755 goto done;
5757 start_type = MSI_RecordGetInteger(rec, 5);
5758 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5759 goto done;
5761 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5762 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5763 serv_type = MSI_RecordGetInteger(rec, 4);
5764 err_control = MSI_RecordGetInteger(rec, 6);
5765 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5766 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5767 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5768 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5769 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5770 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5772 /* fetch the service path */
5773 row = MSI_QueryGetRecord(package->db, query, comp);
5774 if (!row)
5776 ERR("Control query failed!\n");
5777 goto done;
5779 key = MSI_RecordGetString(row, 6);
5781 file = get_loaded_file(package, key);
5782 msiobj_release(&row->hdr);
5783 if (!file)
5785 ERR("Failed to load the service file\n");
5786 goto done;
5789 if (!args || !args[0]) image_path = file->TargetPath;
5790 else
5792 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5793 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5794 return ERROR_OUTOFMEMORY;
5796 strcpyW(image_path, file->TargetPath);
5797 strcatW(image_path, szSpace);
5798 strcatW(image_path, args);
5800 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5801 start_type, err_control, image_path, load_order,
5802 NULL, depends, serv_name, pass);
5804 if (!service)
5806 if (GetLastError() != ERROR_SERVICE_EXISTS)
5807 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5809 else if (sd.lpDescription)
5811 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5812 WARN("failed to set service description %u\n", GetLastError());
5815 if (image_path != file->TargetPath) msi_free(image_path);
5816 done:
5817 CloseServiceHandle(service);
5818 CloseServiceHandle(hscm);
5819 msi_free(name);
5820 msi_free(disp);
5821 msi_free(sd.lpDescription);
5822 msi_free(load_order);
5823 msi_free(serv_name);
5824 msi_free(pass);
5825 msi_free(depends);
5826 msi_free(args);
5828 return ERROR_SUCCESS;
5831 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5833 UINT rc;
5834 MSIQUERY * view;
5835 static const WCHAR ExecSeqQuery[] =
5836 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5837 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5839 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5840 if (rc != ERROR_SUCCESS)
5841 return ERROR_SUCCESS;
5843 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5844 msiobj_release(&view->hdr);
5846 return rc;
5849 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5850 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5852 LPCWSTR *vector, *temp_vector;
5853 LPWSTR p, q;
5854 DWORD sep_len;
5856 static const WCHAR separator[] = {'[','~',']',0};
5858 *numargs = 0;
5859 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5861 if (!args)
5862 return NULL;
5864 vector = msi_alloc(sizeof(LPWSTR));
5865 if (!vector)
5866 return NULL;
5868 p = args;
5871 (*numargs)++;
5872 vector[*numargs - 1] = p;
5874 if ((q = strstrW(p, separator)))
5876 *q = '\0';
5878 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5879 if (!temp_vector)
5881 msi_free(vector);
5882 return NULL;
5884 vector = temp_vector;
5886 p = q + sep_len;
5888 } while (q);
5890 return vector;
5893 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5895 MSIPACKAGE *package = param;
5896 MSICOMPONENT *comp;
5897 MSIRECORD *uirow;
5898 SC_HANDLE scm = NULL, service = NULL;
5899 LPCWSTR component, *vector = NULL;
5900 LPWSTR name, args, display_name = NULL;
5901 DWORD event, numargs, len;
5902 UINT r = ERROR_FUNCTION_FAILED;
5904 component = MSI_RecordGetString(rec, 6);
5905 comp = get_loaded_component(package, component);
5906 if (!comp)
5907 return ERROR_SUCCESS;
5909 if (!comp->Enabled)
5911 TRACE("component is disabled\n");
5912 return ERROR_SUCCESS;
5915 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5917 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5918 comp->Action = comp->Installed;
5919 return ERROR_SUCCESS;
5921 comp->Action = INSTALLSTATE_LOCAL;
5923 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5924 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5925 event = MSI_RecordGetInteger(rec, 3);
5927 if (!(event & msidbServiceControlEventStart))
5929 r = ERROR_SUCCESS;
5930 goto done;
5933 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5934 if (!scm)
5936 ERR("Failed to open the service control manager\n");
5937 goto done;
5940 len = 0;
5941 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5942 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5944 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5945 GetServiceDisplayNameW( scm, name, display_name, &len );
5948 service = OpenServiceW(scm, name, SERVICE_START);
5949 if (!service)
5951 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5952 goto done;
5955 vector = msi_service_args_to_vector(args, &numargs);
5957 if (!StartServiceW(service, numargs, vector) &&
5958 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5960 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5961 goto done;
5964 r = ERROR_SUCCESS;
5966 done:
5967 uirow = MSI_CreateRecord( 2 );
5968 MSI_RecordSetStringW( uirow, 1, display_name );
5969 MSI_RecordSetStringW( uirow, 2, name );
5970 ui_actiondata( package, szStartServices, uirow );
5971 msiobj_release( &uirow->hdr );
5973 CloseServiceHandle(service);
5974 CloseServiceHandle(scm);
5976 msi_free(name);
5977 msi_free(args);
5978 msi_free(vector);
5979 msi_free(display_name);
5980 return r;
5983 static UINT ACTION_StartServices( MSIPACKAGE *package )
5985 UINT rc;
5986 MSIQUERY *view;
5988 static const WCHAR query[] = {
5989 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5990 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5992 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5993 if (rc != ERROR_SUCCESS)
5994 return ERROR_SUCCESS;
5996 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5997 msiobj_release(&view->hdr);
5999 return rc;
6002 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6004 DWORD i, needed, count;
6005 ENUM_SERVICE_STATUSW *dependencies;
6006 SERVICE_STATUS ss;
6007 SC_HANDLE depserv;
6009 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6010 0, &needed, &count))
6011 return TRUE;
6013 if (GetLastError() != ERROR_MORE_DATA)
6014 return FALSE;
6016 dependencies = msi_alloc(needed);
6017 if (!dependencies)
6018 return FALSE;
6020 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6021 needed, &needed, &count))
6022 goto error;
6024 for (i = 0; i < count; i++)
6026 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6027 SERVICE_STOP | SERVICE_QUERY_STATUS);
6028 if (!depserv)
6029 goto error;
6031 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
6032 goto error;
6035 return TRUE;
6037 error:
6038 msi_free(dependencies);
6039 return FALSE;
6042 static UINT stop_service( LPCWSTR name )
6044 SC_HANDLE scm = NULL, service = NULL;
6045 SERVICE_STATUS status;
6046 SERVICE_STATUS_PROCESS ssp;
6047 DWORD needed;
6049 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6050 if (!scm)
6052 WARN("Failed to open the SCM: %d\n", GetLastError());
6053 goto done;
6056 service = OpenServiceW(scm, name,
6057 SERVICE_STOP |
6058 SERVICE_QUERY_STATUS |
6059 SERVICE_ENUMERATE_DEPENDENTS);
6060 if (!service)
6062 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6063 goto done;
6066 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6067 sizeof(SERVICE_STATUS_PROCESS), &needed))
6069 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6070 goto done;
6073 if (ssp.dwCurrentState == SERVICE_STOPPED)
6074 goto done;
6076 stop_service_dependents(scm, service);
6078 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6079 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6081 done:
6082 CloseServiceHandle(service);
6083 CloseServiceHandle(scm);
6085 return ERROR_SUCCESS;
6088 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6090 MSIPACKAGE *package = param;
6091 MSICOMPONENT *comp;
6092 MSIRECORD *uirow;
6093 LPCWSTR component;
6094 LPWSTR name = NULL, display_name = NULL;
6095 DWORD event, len;
6096 SC_HANDLE scm;
6098 event = MSI_RecordGetInteger( rec, 3 );
6099 if (!(event & msidbServiceControlEventStop))
6100 return ERROR_SUCCESS;
6102 component = MSI_RecordGetString( rec, 6 );
6103 comp = get_loaded_component( package, component );
6104 if (!comp)
6105 return ERROR_SUCCESS;
6107 if (!comp->Enabled)
6109 TRACE("component is disabled\n");
6110 return ERROR_SUCCESS;
6113 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6115 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6116 comp->Action = comp->Installed;
6117 return ERROR_SUCCESS;
6119 comp->Action = INSTALLSTATE_ABSENT;
6121 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6122 if (!scm)
6124 ERR("Failed to open the service control manager\n");
6125 goto done;
6128 len = 0;
6129 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6130 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6132 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6133 GetServiceDisplayNameW( scm, name, display_name, &len );
6135 CloseServiceHandle( scm );
6137 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6138 stop_service( name );
6140 done:
6141 uirow = MSI_CreateRecord( 2 );
6142 MSI_RecordSetStringW( uirow, 1, display_name );
6143 MSI_RecordSetStringW( uirow, 2, name );
6144 ui_actiondata( package, szStopServices, uirow );
6145 msiobj_release( &uirow->hdr );
6147 msi_free( name );
6148 msi_free( display_name );
6149 return ERROR_SUCCESS;
6152 static UINT ACTION_StopServices( MSIPACKAGE *package )
6154 UINT rc;
6155 MSIQUERY *view;
6157 static const WCHAR query[] = {
6158 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6159 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6161 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6162 if (rc != ERROR_SUCCESS)
6163 return ERROR_SUCCESS;
6165 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6166 msiobj_release(&view->hdr);
6168 return rc;
6171 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6173 MSIPACKAGE *package = param;
6174 MSICOMPONENT *comp;
6175 MSIRECORD *uirow;
6176 LPCWSTR component;
6177 LPWSTR name = NULL, display_name = NULL;
6178 DWORD event, len;
6179 SC_HANDLE scm = NULL, service = NULL;
6181 event = MSI_RecordGetInteger( rec, 3 );
6182 if (!(event & msidbServiceControlEventDelete))
6183 return ERROR_SUCCESS;
6185 component = MSI_RecordGetString(rec, 6);
6186 comp = get_loaded_component(package, component);
6187 if (!comp)
6188 return ERROR_SUCCESS;
6190 if (!comp->Enabled)
6192 TRACE("component is disabled\n");
6193 return ERROR_SUCCESS;
6196 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6198 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6199 comp->Action = comp->Installed;
6200 return ERROR_SUCCESS;
6202 comp->Action = INSTALLSTATE_ABSENT;
6204 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6205 stop_service( name );
6207 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6208 if (!scm)
6210 WARN("Failed to open the SCM: %d\n", GetLastError());
6211 goto done;
6214 len = 0;
6215 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6216 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6218 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6219 GetServiceDisplayNameW( scm, name, display_name, &len );
6222 service = OpenServiceW( scm, name, DELETE );
6223 if (!service)
6225 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6226 goto done;
6229 if (!DeleteService( service ))
6230 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6232 done:
6233 uirow = MSI_CreateRecord( 2 );
6234 MSI_RecordSetStringW( uirow, 1, display_name );
6235 MSI_RecordSetStringW( uirow, 2, name );
6236 ui_actiondata( package, szDeleteServices, uirow );
6237 msiobj_release( &uirow->hdr );
6239 CloseServiceHandle( service );
6240 CloseServiceHandle( scm );
6241 msi_free( name );
6242 msi_free( display_name );
6244 return ERROR_SUCCESS;
6247 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6249 UINT rc;
6250 MSIQUERY *view;
6252 static const WCHAR query[] = {
6253 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6254 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6256 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6257 if (rc != ERROR_SUCCESS)
6258 return ERROR_SUCCESS;
6260 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6261 msiobj_release( &view->hdr );
6263 return rc;
6266 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6268 MSIPACKAGE *package = param;
6269 LPWSTR driver, driver_path, ptr;
6270 WCHAR outpath[MAX_PATH];
6271 MSIFILE *driver_file = NULL, *setup_file = NULL;
6272 MSICOMPONENT *comp;
6273 MSIRECORD *uirow;
6274 LPCWSTR desc, file_key, component;
6275 DWORD len, usage;
6276 UINT r = ERROR_SUCCESS;
6278 static const WCHAR driver_fmt[] = {
6279 'D','r','i','v','e','r','=','%','s',0};
6280 static const WCHAR setup_fmt[] = {
6281 'S','e','t','u','p','=','%','s',0};
6282 static const WCHAR usage_fmt[] = {
6283 'F','i','l','e','U','s','a','g','e','=','1',0};
6285 component = MSI_RecordGetString( rec, 2 );
6286 comp = get_loaded_component( package, component );
6287 if (!comp)
6288 return ERROR_SUCCESS;
6290 if (!comp->Enabled)
6292 TRACE("component is disabled\n");
6293 return ERROR_SUCCESS;
6296 desc = MSI_RecordGetString(rec, 3);
6298 file_key = MSI_RecordGetString( rec, 4 );
6299 if (file_key) driver_file = get_loaded_file( package, file_key );
6301 file_key = MSI_RecordGetString( rec, 5 );
6302 if (file_key) setup_file = get_loaded_file( package, file_key );
6304 if (!driver_file)
6306 ERR("ODBC Driver entry not found!\n");
6307 return ERROR_FUNCTION_FAILED;
6310 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6311 if (setup_file)
6312 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6313 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6315 driver = msi_alloc(len * sizeof(WCHAR));
6316 if (!driver)
6317 return ERROR_OUTOFMEMORY;
6319 ptr = driver;
6320 lstrcpyW(ptr, desc);
6321 ptr += lstrlenW(ptr) + 1;
6323 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6324 ptr += len + 1;
6326 if (setup_file)
6328 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6329 ptr += len + 1;
6332 lstrcpyW(ptr, usage_fmt);
6333 ptr += lstrlenW(ptr) + 1;
6334 *ptr = '\0';
6336 driver_path = strdupW(driver_file->TargetPath);
6337 ptr = strrchrW(driver_path, '\\');
6338 if (ptr) *ptr = '\0';
6340 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6341 NULL, ODBC_INSTALL_COMPLETE, &usage))
6343 ERR("Failed to install SQL driver!\n");
6344 r = ERROR_FUNCTION_FAILED;
6347 uirow = MSI_CreateRecord( 5 );
6348 MSI_RecordSetStringW( uirow, 1, desc );
6349 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6350 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6351 ui_actiondata( package, szInstallODBC, uirow );
6352 msiobj_release( &uirow->hdr );
6354 msi_free(driver);
6355 msi_free(driver_path);
6357 return r;
6360 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6362 MSIPACKAGE *package = param;
6363 LPWSTR translator, translator_path, ptr;
6364 WCHAR outpath[MAX_PATH];
6365 MSIFILE *translator_file = NULL, *setup_file = NULL;
6366 MSICOMPONENT *comp;
6367 MSIRECORD *uirow;
6368 LPCWSTR desc, file_key, component;
6369 DWORD len, usage;
6370 UINT r = ERROR_SUCCESS;
6372 static const WCHAR translator_fmt[] = {
6373 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6374 static const WCHAR setup_fmt[] = {
6375 'S','e','t','u','p','=','%','s',0};
6377 component = MSI_RecordGetString( rec, 2 );
6378 comp = get_loaded_component( package, component );
6379 if (!comp)
6380 return ERROR_SUCCESS;
6382 if (!comp->Enabled)
6384 TRACE("component is disabled\n");
6385 return ERROR_SUCCESS;
6388 desc = MSI_RecordGetString(rec, 3);
6390 file_key = MSI_RecordGetString( rec, 4 );
6391 if (file_key) translator_file = get_loaded_file( package, file_key );
6393 file_key = MSI_RecordGetString( rec, 5 );
6394 if (file_key) setup_file = get_loaded_file( package, file_key );
6396 if (!translator_file)
6398 ERR("ODBC Translator entry not found!\n");
6399 return ERROR_FUNCTION_FAILED;
6402 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6403 if (setup_file)
6404 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6406 translator = msi_alloc(len * sizeof(WCHAR));
6407 if (!translator)
6408 return ERROR_OUTOFMEMORY;
6410 ptr = translator;
6411 lstrcpyW(ptr, desc);
6412 ptr += lstrlenW(ptr) + 1;
6414 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6415 ptr += len + 1;
6417 if (setup_file)
6419 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6420 ptr += len + 1;
6422 *ptr = '\0';
6424 translator_path = strdupW(translator_file->TargetPath);
6425 ptr = strrchrW(translator_path, '\\');
6426 if (ptr) *ptr = '\0';
6428 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6429 NULL, ODBC_INSTALL_COMPLETE, &usage))
6431 ERR("Failed to install SQL translator!\n");
6432 r = ERROR_FUNCTION_FAILED;
6435 uirow = MSI_CreateRecord( 5 );
6436 MSI_RecordSetStringW( uirow, 1, desc );
6437 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6438 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6439 ui_actiondata( package, szInstallODBC, uirow );
6440 msiobj_release( &uirow->hdr );
6442 msi_free(translator);
6443 msi_free(translator_path);
6445 return r;
6448 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6450 MSIPACKAGE *package = param;
6451 MSICOMPONENT *comp;
6452 LPWSTR attrs;
6453 LPCWSTR desc, driver, component;
6454 WORD request = ODBC_ADD_SYS_DSN;
6455 INT registration;
6456 DWORD len;
6457 UINT r = ERROR_SUCCESS;
6458 MSIRECORD *uirow;
6460 static const WCHAR attrs_fmt[] = {
6461 'D','S','N','=','%','s',0 };
6463 component = MSI_RecordGetString( rec, 2 );
6464 comp = get_loaded_component( package, component );
6465 if (!comp)
6466 return ERROR_SUCCESS;
6468 if (!comp->Enabled)
6470 TRACE("component is disabled\n");
6471 return ERROR_SUCCESS;
6474 desc = MSI_RecordGetString(rec, 3);
6475 driver = MSI_RecordGetString(rec, 4);
6476 registration = MSI_RecordGetInteger(rec, 5);
6478 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6479 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6481 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6482 attrs = msi_alloc(len * sizeof(WCHAR));
6483 if (!attrs)
6484 return ERROR_OUTOFMEMORY;
6486 len = sprintfW(attrs, attrs_fmt, desc);
6487 attrs[len + 1] = 0;
6489 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6491 ERR("Failed to install SQL data source!\n");
6492 r = ERROR_FUNCTION_FAILED;
6495 uirow = MSI_CreateRecord( 5 );
6496 MSI_RecordSetStringW( uirow, 1, desc );
6497 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6498 MSI_RecordSetInteger( uirow, 3, request );
6499 ui_actiondata( package, szInstallODBC, uirow );
6500 msiobj_release( &uirow->hdr );
6502 msi_free(attrs);
6504 return r;
6507 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6509 UINT rc;
6510 MSIQUERY *view;
6512 static const WCHAR driver_query[] = {
6513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6514 'O','D','B','C','D','r','i','v','e','r',0 };
6516 static const WCHAR translator_query[] = {
6517 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6518 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6520 static const WCHAR source_query[] = {
6521 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6522 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6524 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6525 if (rc != ERROR_SUCCESS)
6526 return ERROR_SUCCESS;
6528 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6529 msiobj_release(&view->hdr);
6531 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6532 if (rc != ERROR_SUCCESS)
6533 return ERROR_SUCCESS;
6535 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6536 msiobj_release(&view->hdr);
6538 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6539 if (rc != ERROR_SUCCESS)
6540 return ERROR_SUCCESS;
6542 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6543 msiobj_release(&view->hdr);
6545 return rc;
6548 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6550 MSIPACKAGE *package = param;
6551 MSICOMPONENT *comp;
6552 MSIRECORD *uirow;
6553 DWORD usage;
6554 LPCWSTR desc, component;
6556 component = MSI_RecordGetString( rec, 2 );
6557 comp = get_loaded_component( package, component );
6558 if (!comp)
6559 return ERROR_SUCCESS;
6561 if (!comp->Enabled)
6563 TRACE("component is disabled\n");
6564 return ERROR_SUCCESS;
6567 desc = MSI_RecordGetString( rec, 3 );
6568 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6570 WARN("Failed to remove ODBC driver\n");
6572 else if (!usage)
6574 FIXME("Usage count reached 0\n");
6577 uirow = MSI_CreateRecord( 2 );
6578 MSI_RecordSetStringW( uirow, 1, desc );
6579 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6580 ui_actiondata( package, szRemoveODBC, uirow );
6581 msiobj_release( &uirow->hdr );
6583 return ERROR_SUCCESS;
6586 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6588 MSIPACKAGE *package = param;
6589 MSICOMPONENT *comp;
6590 MSIRECORD *uirow;
6591 DWORD usage;
6592 LPCWSTR desc, component;
6594 component = MSI_RecordGetString( rec, 2 );
6595 comp = get_loaded_component( package, component );
6596 if (!comp)
6597 return ERROR_SUCCESS;
6599 if (!comp->Enabled)
6601 TRACE("component is disabled\n");
6602 return ERROR_SUCCESS;
6605 desc = MSI_RecordGetString( rec, 3 );
6606 if (!SQLRemoveTranslatorW( desc, &usage ))
6608 WARN("Failed to remove ODBC translator\n");
6610 else if (!usage)
6612 FIXME("Usage count reached 0\n");
6615 uirow = MSI_CreateRecord( 2 );
6616 MSI_RecordSetStringW( uirow, 1, desc );
6617 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6618 ui_actiondata( package, szRemoveODBC, uirow );
6619 msiobj_release( &uirow->hdr );
6621 return ERROR_SUCCESS;
6624 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6626 MSIPACKAGE *package = param;
6627 MSICOMPONENT *comp;
6628 MSIRECORD *uirow;
6629 LPWSTR attrs;
6630 LPCWSTR desc, driver, component;
6631 WORD request = ODBC_REMOVE_SYS_DSN;
6632 INT registration;
6633 DWORD len;
6635 static const WCHAR attrs_fmt[] = {
6636 'D','S','N','=','%','s',0 };
6638 component = MSI_RecordGetString( rec, 2 );
6639 comp = get_loaded_component( package, component );
6640 if (!comp)
6641 return ERROR_SUCCESS;
6643 if (!comp->Enabled)
6645 TRACE("component is disabled\n");
6646 return ERROR_SUCCESS;
6649 desc = MSI_RecordGetString( rec, 3 );
6650 driver = MSI_RecordGetString( rec, 4 );
6651 registration = MSI_RecordGetInteger( rec, 5 );
6653 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6654 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6656 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6657 attrs = msi_alloc( len * sizeof(WCHAR) );
6658 if (!attrs)
6659 return ERROR_OUTOFMEMORY;
6661 FIXME("Use ODBCSourceAttribute table\n");
6663 len = sprintfW( attrs, attrs_fmt, desc );
6664 attrs[len + 1] = 0;
6666 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6668 WARN("Failed to remove ODBC data source\n");
6670 msi_free( attrs );
6672 uirow = MSI_CreateRecord( 3 );
6673 MSI_RecordSetStringW( uirow, 1, desc );
6674 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6675 MSI_RecordSetInteger( uirow, 3, request );
6676 ui_actiondata( package, szRemoveODBC, uirow );
6677 msiobj_release( &uirow->hdr );
6679 return ERROR_SUCCESS;
6682 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6684 UINT rc;
6685 MSIQUERY *view;
6687 static const WCHAR driver_query[] = {
6688 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6689 'O','D','B','C','D','r','i','v','e','r',0 };
6691 static const WCHAR translator_query[] = {
6692 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6693 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6695 static const WCHAR source_query[] = {
6696 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6697 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6699 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6700 if (rc != ERROR_SUCCESS)
6701 return ERROR_SUCCESS;
6703 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6704 msiobj_release( &view->hdr );
6706 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6707 if (rc != ERROR_SUCCESS)
6708 return ERROR_SUCCESS;
6710 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6711 msiobj_release( &view->hdr );
6713 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6714 if (rc != ERROR_SUCCESS)
6715 return ERROR_SUCCESS;
6717 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6718 msiobj_release( &view->hdr );
6720 return rc;
6723 #define ENV_ACT_SETALWAYS 0x1
6724 #define ENV_ACT_SETABSENT 0x2
6725 #define ENV_ACT_REMOVE 0x4
6726 #define ENV_ACT_REMOVEMATCH 0x8
6728 #define ENV_MOD_MACHINE 0x20000000
6729 #define ENV_MOD_APPEND 0x40000000
6730 #define ENV_MOD_PREFIX 0x80000000
6731 #define ENV_MOD_MASK 0xC0000000
6733 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6735 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6737 LPCWSTR cptr = *name;
6739 static const WCHAR prefix[] = {'[','~',']',0};
6740 static const int prefix_len = 3;
6742 *flags = 0;
6743 while (*cptr)
6745 if (*cptr == '=')
6746 *flags |= ENV_ACT_SETALWAYS;
6747 else if (*cptr == '+')
6748 *flags |= ENV_ACT_SETABSENT;
6749 else if (*cptr == '-')
6750 *flags |= ENV_ACT_REMOVE;
6751 else if (*cptr == '!')
6752 *flags |= ENV_ACT_REMOVEMATCH;
6753 else if (*cptr == '*')
6754 *flags |= ENV_MOD_MACHINE;
6755 else
6756 break;
6758 cptr++;
6759 (*name)++;
6762 if (!*cptr)
6764 ERR("Missing environment variable\n");
6765 return ERROR_FUNCTION_FAILED;
6768 if (*value)
6770 LPCWSTR ptr = *value;
6771 if (!strncmpW(ptr, prefix, prefix_len))
6773 if (ptr[prefix_len] == szSemiColon[0])
6775 *flags |= ENV_MOD_APPEND;
6776 *value += lstrlenW(prefix);
6778 else
6780 *value = NULL;
6783 else if (lstrlenW(*value) >= prefix_len)
6785 ptr += lstrlenW(ptr) - prefix_len;
6786 if (!strcmpW( ptr, prefix ))
6788 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6790 *flags |= ENV_MOD_PREFIX;
6791 /* the "[~]" will be removed by deformat_string */;
6793 else
6795 *value = NULL;
6801 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6802 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6803 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6804 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6806 ERR("Invalid flags: %08x\n", *flags);
6807 return ERROR_FUNCTION_FAILED;
6810 if (!*flags)
6811 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6813 return ERROR_SUCCESS;
6816 static UINT open_env_key( DWORD flags, HKEY *key )
6818 static const WCHAR user_env[] =
6819 {'E','n','v','i','r','o','n','m','e','n','t',0};
6820 static const WCHAR machine_env[] =
6821 {'S','y','s','t','e','m','\\',
6822 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6823 'C','o','n','t','r','o','l','\\',
6824 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6825 'E','n','v','i','r','o','n','m','e','n','t',0};
6826 const WCHAR *env;
6827 HKEY root;
6828 LONG res;
6830 if (flags & ENV_MOD_MACHINE)
6832 env = machine_env;
6833 root = HKEY_LOCAL_MACHINE;
6835 else
6837 env = user_env;
6838 root = HKEY_CURRENT_USER;
6841 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6842 if (res != ERROR_SUCCESS)
6844 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6845 return ERROR_FUNCTION_FAILED;
6848 return ERROR_SUCCESS;
6851 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6853 MSIPACKAGE *package = param;
6854 LPCWSTR name, value, component;
6855 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6856 DWORD flags, type, size;
6857 UINT res;
6858 HKEY env = NULL;
6859 MSICOMPONENT *comp;
6860 MSIRECORD *uirow;
6861 int action = 0;
6863 component = MSI_RecordGetString(rec, 4);
6864 comp = get_loaded_component(package, component);
6865 if (!comp)
6866 return ERROR_SUCCESS;
6868 if (!comp->Enabled)
6870 TRACE("component is disabled\n");
6871 return ERROR_SUCCESS;
6874 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6876 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6877 comp->Action = comp->Installed;
6878 return ERROR_SUCCESS;
6880 comp->Action = INSTALLSTATE_LOCAL;
6882 name = MSI_RecordGetString(rec, 2);
6883 value = MSI_RecordGetString(rec, 3);
6885 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6887 res = env_parse_flags(&name, &value, &flags);
6888 if (res != ERROR_SUCCESS || !value)
6889 goto done;
6891 if (value && !deformat_string(package, value, &deformatted))
6893 res = ERROR_OUTOFMEMORY;
6894 goto done;
6897 value = deformatted;
6899 res = open_env_key( flags, &env );
6900 if (res != ERROR_SUCCESS)
6901 goto done;
6903 if (flags & ENV_MOD_MACHINE)
6904 action |= 0x20000000;
6906 size = 0;
6907 type = REG_SZ;
6908 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6909 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6910 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6911 goto done;
6913 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6915 action = 0x2;
6917 /* Nothing to do. */
6918 if (!value)
6920 res = ERROR_SUCCESS;
6921 goto done;
6924 /* If we are appending but the string was empty, strip ; */
6925 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6927 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6928 newval = strdupW(value);
6929 if (!newval)
6931 res = ERROR_OUTOFMEMORY;
6932 goto done;
6935 else
6937 action = 0x1;
6939 /* Contrary to MSDN, +-variable to [~];path works */
6940 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6942 res = ERROR_SUCCESS;
6943 goto done;
6946 data = msi_alloc(size);
6947 if (!data)
6949 RegCloseKey(env);
6950 return ERROR_OUTOFMEMORY;
6953 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6954 if (res != ERROR_SUCCESS)
6955 goto done;
6957 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6959 action = 0x4;
6960 res = RegDeleteValueW(env, name);
6961 if (res != ERROR_SUCCESS)
6962 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6963 goto done;
6966 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6967 if (flags & ENV_MOD_MASK)
6969 DWORD mod_size;
6970 int multiplier = 0;
6971 if (flags & ENV_MOD_APPEND) multiplier++;
6972 if (flags & ENV_MOD_PREFIX) multiplier++;
6973 mod_size = lstrlenW(value) * multiplier;
6974 size += mod_size * sizeof(WCHAR);
6977 newval = msi_alloc(size);
6978 ptr = newval;
6979 if (!newval)
6981 res = ERROR_OUTOFMEMORY;
6982 goto done;
6985 if (flags & ENV_MOD_PREFIX)
6987 lstrcpyW(newval, value);
6988 ptr = newval + lstrlenW(value);
6989 action |= 0x80000000;
6992 lstrcpyW(ptr, data);
6994 if (flags & ENV_MOD_APPEND)
6996 lstrcatW(newval, value);
6997 action |= 0x40000000;
7000 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7001 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7002 if (res)
7004 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7007 done:
7008 uirow = MSI_CreateRecord( 3 );
7009 MSI_RecordSetStringW( uirow, 1, name );
7010 MSI_RecordSetStringW( uirow, 2, newval );
7011 MSI_RecordSetInteger( uirow, 3, action );
7012 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7013 msiobj_release( &uirow->hdr );
7015 if (env) RegCloseKey(env);
7016 msi_free(deformatted);
7017 msi_free(data);
7018 msi_free(newval);
7019 return res;
7022 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7024 UINT rc;
7025 MSIQUERY * view;
7026 static const WCHAR ExecSeqQuery[] =
7027 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7028 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7029 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
7030 if (rc != ERROR_SUCCESS)
7031 return ERROR_SUCCESS;
7033 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7034 msiobj_release(&view->hdr);
7036 return rc;
7039 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7041 MSIPACKAGE *package = param;
7042 LPCWSTR name, value, component;
7043 LPWSTR deformatted = NULL;
7044 DWORD flags;
7045 HKEY env;
7046 MSICOMPONENT *comp;
7047 MSIRECORD *uirow;
7048 int action = 0;
7049 LONG res;
7050 UINT r;
7052 component = MSI_RecordGetString( rec, 4 );
7053 comp = get_loaded_component( package, component );
7054 if (!comp)
7055 return ERROR_SUCCESS;
7057 if (!comp->Enabled)
7059 TRACE("component is disabled\n");
7060 return ERROR_SUCCESS;
7063 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
7065 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
7066 comp->Action = comp->Installed;
7067 return ERROR_SUCCESS;
7069 comp->Action = INSTALLSTATE_ABSENT;
7071 name = MSI_RecordGetString( rec, 2 );
7072 value = MSI_RecordGetString( rec, 3 );
7074 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7076 r = env_parse_flags( &name, &value, &flags );
7077 if (r != ERROR_SUCCESS)
7078 return r;
7080 if (!(flags & ENV_ACT_REMOVE))
7082 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7083 return ERROR_SUCCESS;
7086 if (value && !deformat_string( package, value, &deformatted ))
7087 return ERROR_OUTOFMEMORY;
7089 value = deformatted;
7091 r = open_env_key( flags, &env );
7092 if (r != ERROR_SUCCESS)
7094 r = ERROR_SUCCESS;
7095 goto done;
7098 if (flags & ENV_MOD_MACHINE)
7099 action |= 0x20000000;
7101 TRACE("Removing %s\n", debugstr_w(name));
7103 res = RegDeleteValueW( env, name );
7104 if (res != ERROR_SUCCESS)
7106 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7107 r = ERROR_SUCCESS;
7110 done:
7111 uirow = MSI_CreateRecord( 3 );
7112 MSI_RecordSetStringW( uirow, 1, name );
7113 MSI_RecordSetStringW( uirow, 2, value );
7114 MSI_RecordSetInteger( uirow, 3, action );
7115 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7116 msiobj_release( &uirow->hdr );
7118 if (env) RegCloseKey( env );
7119 msi_free( deformatted );
7120 return r;
7123 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7125 UINT rc;
7126 MSIQUERY *view;
7127 static const WCHAR query[] =
7128 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7129 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7131 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7132 if (rc != ERROR_SUCCESS)
7133 return ERROR_SUCCESS;
7135 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7136 msiobj_release( &view->hdr );
7138 return rc;
7141 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7143 LPWSTR key, template, id;
7144 UINT r = ERROR_SUCCESS;
7146 id = msi_dup_property( package->db, szProductID );
7147 if (id)
7149 msi_free( id );
7150 return ERROR_SUCCESS;
7152 template = msi_dup_property( package->db, szPIDTemplate );
7153 key = msi_dup_property( package->db, szPIDKEY );
7155 if (key && template)
7157 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7158 r = msi_set_property( package->db, szProductID, key );
7160 msi_free( template );
7161 msi_free( key );
7162 return r;
7165 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7167 TRACE("\n");
7168 package->need_reboot = 1;
7169 return ERROR_SUCCESS;
7172 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7174 static const WCHAR szAvailableFreeReg[] =
7175 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7176 MSIRECORD *uirow;
7177 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7179 TRACE("%p %d kilobytes\n", package, space);
7181 uirow = MSI_CreateRecord( 1 );
7182 MSI_RecordSetInteger( uirow, 1, space );
7183 ui_actiondata( package, szAllocateRegistrySpace, uirow );
7184 msiobj_release( &uirow->hdr );
7186 return ERROR_SUCCESS;
7189 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7191 FIXME("%p\n", package);
7192 return ERROR_SUCCESS;
7195 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7197 FIXME("%p\n", package);
7198 return ERROR_SUCCESS;
7201 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7203 UINT r, count;
7204 MSIQUERY *view;
7206 static const WCHAR driver_query[] = {
7207 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7208 'O','D','B','C','D','r','i','v','e','r',0 };
7210 static const WCHAR translator_query[] = {
7211 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7212 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7214 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7215 if (r == ERROR_SUCCESS)
7217 count = 0;
7218 r = MSI_IterateRecords( view, &count, NULL, package );
7219 msiobj_release( &view->hdr );
7220 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7223 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7224 if (r == ERROR_SUCCESS)
7226 count = 0;
7227 r = MSI_IterateRecords( view, &count, NULL, package );
7228 msiobj_release( &view->hdr );
7229 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7232 return ERROR_SUCCESS;
7235 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7237 MSIPACKAGE *package = param;
7238 const WCHAR *property = MSI_RecordGetString( rec, 1 );
7239 WCHAR *value;
7241 if ((value = msi_dup_property( package->db, property )))
7243 FIXME("remove %s\n", debugstr_w(value));
7244 msi_free( value );
7246 return ERROR_SUCCESS;
7249 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7251 UINT r;
7252 MSIQUERY *view;
7254 static const WCHAR query[] =
7255 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7256 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7258 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7259 if (r == ERROR_SUCCESS)
7261 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7262 msiobj_release( &view->hdr );
7264 return ERROR_SUCCESS;
7267 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7269 MSIPACKAGE *package = param;
7270 int attributes = MSI_RecordGetInteger( rec, 5 );
7272 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7274 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7275 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7276 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7277 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7278 HKEY hkey;
7279 UINT r;
7281 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7283 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7284 if (r != ERROR_SUCCESS)
7285 return ERROR_SUCCESS;
7287 else
7289 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7290 if (r != ERROR_SUCCESS)
7291 return ERROR_SUCCESS;
7293 RegCloseKey( hkey );
7295 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7296 debugstr_w(upgrade_code), debugstr_w(version_min),
7297 debugstr_w(version_max), debugstr_w(language));
7299 return ERROR_SUCCESS;
7302 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7304 UINT r;
7305 MSIQUERY *view;
7307 static const WCHAR query[] =
7308 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7310 if (msi_get_property_int( package->db, szInstalled, 0 ))
7312 TRACE("product is installed, skipping action\n");
7313 return ERROR_SUCCESS;
7315 if (msi_get_property_int( package->db, szPreselected, 0 ))
7317 TRACE("Preselected property is set, not migrating feature states\n");
7318 return ERROR_SUCCESS;
7321 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7322 if (r == ERROR_SUCCESS)
7324 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7325 msiobj_release( &view->hdr );
7327 return ERROR_SUCCESS;
7330 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7331 LPCSTR action, LPCWSTR table )
7333 static const WCHAR query[] = {
7334 'S','E','L','E','C','T',' ','*',' ',
7335 'F','R','O','M',' ','`','%','s','`',0 };
7336 MSIQUERY *view = NULL;
7337 DWORD count = 0;
7338 UINT r;
7340 r = MSI_OpenQuery( package->db, &view, query, table );
7341 if (r == ERROR_SUCCESS)
7343 r = MSI_IterateRecords(view, &count, NULL, package);
7344 msiobj_release(&view->hdr);
7347 if (count)
7348 FIXME("%s -> %u ignored %s table values\n",
7349 action, count, debugstr_w(table));
7351 return ERROR_SUCCESS;
7354 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7356 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7357 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7360 static UINT ACTION_BindImage( MSIPACKAGE *package )
7362 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7363 return msi_unimplemented_action_stub( package, "BindImage", table );
7366 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7368 static const WCHAR table[] = {
7369 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7370 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7373 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7375 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7376 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7379 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7381 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7382 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7385 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7387 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7388 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7391 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7393 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7394 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7397 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7399 static const struct
7401 const WCHAR *action;
7402 UINT (*handler)(MSIPACKAGE *);
7404 StandardActions[] =
7406 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7407 { szAppSearch, ACTION_AppSearch },
7408 { szBindImage, ACTION_BindImage },
7409 { szCCPSearch, ACTION_CCPSearch },
7410 { szCostFinalize, ACTION_CostFinalize },
7411 { szCostInitialize, ACTION_CostInitialize },
7412 { szCreateFolders, ACTION_CreateFolders },
7413 { szCreateShortcuts, ACTION_CreateShortcuts },
7414 { szDeleteServices, ACTION_DeleteServices },
7415 { szDisableRollback, ACTION_DisableRollback },
7416 { szDuplicateFiles, ACTION_DuplicateFiles },
7417 { szExecuteAction, ACTION_ExecuteAction },
7418 { szFileCost, ACTION_FileCost },
7419 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7420 { szForceReboot, ACTION_ForceReboot },
7421 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7422 { szInstallExecute, ACTION_InstallExecute },
7423 { szInstallExecuteAgain, ACTION_InstallExecute },
7424 { szInstallFiles, ACTION_InstallFiles},
7425 { szInstallFinalize, ACTION_InstallFinalize },
7426 { szInstallInitialize, ACTION_InstallInitialize },
7427 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7428 { szInstallValidate, ACTION_InstallValidate },
7429 { szIsolateComponents, ACTION_IsolateComponents },
7430 { szLaunchConditions, ACTION_LaunchConditions },
7431 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7432 { szMoveFiles, ACTION_MoveFiles },
7433 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7434 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7435 { szInstallODBC, ACTION_InstallODBC },
7436 { szInstallServices, ACTION_InstallServices },
7437 { szPatchFiles, ACTION_PatchFiles },
7438 { szProcessComponents, ACTION_ProcessComponents },
7439 { szPublishComponents, ACTION_PublishComponents },
7440 { szPublishFeatures, ACTION_PublishFeatures },
7441 { szPublishProduct, ACTION_PublishProduct },
7442 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7443 { szRegisterComPlus, ACTION_RegisterComPlus},
7444 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7445 { szRegisterFonts, ACTION_RegisterFonts },
7446 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7447 { szRegisterProduct, ACTION_RegisterProduct },
7448 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7449 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7450 { szRegisterUser, ACTION_RegisterUser },
7451 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7452 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7453 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7454 { szRemoveFiles, ACTION_RemoveFiles },
7455 { szRemoveFolders, ACTION_RemoveFolders },
7456 { szRemoveIniValues, ACTION_RemoveIniValues },
7457 { szRemoveODBC, ACTION_RemoveODBC },
7458 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7459 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7460 { szResolveSource, ACTION_ResolveSource },
7461 { szRMCCPSearch, ACTION_RMCCPSearch },
7462 { szScheduleReboot, ACTION_ScheduleReboot },
7463 { szSelfRegModules, ACTION_SelfRegModules },
7464 { szSelfUnregModules, ACTION_SelfUnregModules },
7465 { szSetODBCFolders, ACTION_SetODBCFolders },
7466 { szStartServices, ACTION_StartServices },
7467 { szStopServices, ACTION_StopServices },
7468 { szUnpublishComponents, ACTION_UnpublishComponents },
7469 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7470 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7471 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7472 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7473 { szUnregisterFonts, ACTION_UnregisterFonts },
7474 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7475 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7476 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7477 { szValidateProductID, ACTION_ValidateProductID },
7478 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7479 { szWriteIniValues, ACTION_WriteIniValues },
7480 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7481 { NULL, NULL },
7484 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7486 BOOL ret = FALSE;
7487 UINT i;
7489 i = 0;
7490 while (StandardActions[i].action != NULL)
7492 if (!strcmpW( StandardActions[i].action, action ))
7494 ui_actionstart( package, action );
7495 if (StandardActions[i].handler)
7497 ui_actioninfo( package, action, TRUE, 0 );
7498 *rc = StandardActions[i].handler( package );
7499 ui_actioninfo( package, action, FALSE, *rc );
7501 else
7503 FIXME("unhandled standard action %s\n", debugstr_w(action));
7504 *rc = ERROR_SUCCESS;
7506 ret = TRUE;
7507 break;
7509 i++;
7511 return ret;
7514 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7516 UINT rc = ERROR_SUCCESS;
7517 BOOL handled;
7519 TRACE("Performing action (%s)\n", debugstr_w(action));
7521 handled = ACTION_HandleStandardAction(package, action, &rc);
7523 if (!handled)
7524 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7526 if (!handled)
7528 WARN("unhandled msi action %s\n", debugstr_w(action));
7529 rc = ERROR_FUNCTION_NOT_CALLED;
7532 return rc;
7535 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7537 UINT rc = ERROR_SUCCESS;
7538 BOOL handled = FALSE;
7540 TRACE("Performing action (%s)\n", debugstr_w(action));
7542 handled = ACTION_HandleStandardAction(package, action, &rc);
7544 if (!handled)
7545 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7547 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7548 handled = TRUE;
7550 if (!handled)
7552 WARN("unhandled msi action %s\n", debugstr_w(action));
7553 rc = ERROR_FUNCTION_NOT_CALLED;
7556 return rc;
7559 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7561 UINT rc = ERROR_SUCCESS;
7562 MSIRECORD *row;
7564 static const WCHAR ExecSeqQuery[] =
7565 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7566 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7567 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7568 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7569 static const WCHAR UISeqQuery[] =
7570 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7571 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7572 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7573 ' ', '=',' ','%','i',0};
7575 if (needs_ui_sequence(package))
7576 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7577 else
7578 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7580 if (row)
7582 LPCWSTR action, cond;
7584 TRACE("Running the actions\n");
7586 /* check conditions */
7587 cond = MSI_RecordGetString(row, 2);
7589 /* this is a hack to skip errors in the condition code */
7590 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7592 msiobj_release(&row->hdr);
7593 return ERROR_SUCCESS;
7596 action = MSI_RecordGetString(row, 1);
7597 if (!action)
7599 ERR("failed to fetch action\n");
7600 msiobj_release(&row->hdr);
7601 return ERROR_FUNCTION_FAILED;
7604 if (needs_ui_sequence(package))
7605 rc = ACTION_PerformUIAction(package, action, -1);
7606 else
7607 rc = ACTION_PerformAction(package, action, -1);
7609 msiobj_release(&row->hdr);
7612 return rc;
7615 /****************************************************
7616 * TOP level entry points
7617 *****************************************************/
7619 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7620 LPCWSTR szCommandLine )
7622 UINT rc;
7623 BOOL ui_exists;
7625 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7626 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7628 msi_set_property( package->db, szAction, szInstall );
7630 package->script->InWhatSequence = SEQUENCE_INSTALL;
7632 if (szPackagePath)
7634 LPWSTR p, dir;
7635 LPCWSTR file;
7637 dir = strdupW(szPackagePath);
7638 p = strrchrW(dir, '\\');
7639 if (p)
7641 *(++p) = 0;
7642 file = szPackagePath + (p - dir);
7644 else
7646 msi_free(dir);
7647 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7648 GetCurrentDirectoryW(MAX_PATH, dir);
7649 lstrcatW(dir, szBackSlash);
7650 file = szPackagePath;
7653 msi_free( package->PackagePath );
7654 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7655 if (!package->PackagePath)
7657 msi_free(dir);
7658 return ERROR_OUTOFMEMORY;
7661 lstrcpyW(package->PackagePath, dir);
7662 lstrcatW(package->PackagePath, file);
7663 msi_free(dir);
7665 msi_set_sourcedir_props(package, FALSE);
7668 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7669 if (rc != ERROR_SUCCESS)
7670 return rc;
7672 msi_apply_transforms( package );
7673 msi_apply_patches( package );
7675 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7677 TRACE("setting reinstall property\n");
7678 msi_set_property( package->db, szReinstall, szAll );
7681 /* properties may have been added by a transform */
7682 msi_clone_properties( package );
7684 msi_parse_command_line( package, szCommandLine, FALSE );
7685 msi_adjust_privilege_properties( package );
7686 msi_set_context( package );
7688 if (needs_ui_sequence( package))
7690 package->script->InWhatSequence |= SEQUENCE_UI;
7691 rc = ACTION_ProcessUISequence(package);
7692 ui_exists = ui_sequence_exists(package);
7693 if (rc == ERROR_SUCCESS || !ui_exists)
7695 package->script->InWhatSequence |= SEQUENCE_EXEC;
7696 rc = ACTION_ProcessExecSequence(package, ui_exists);
7699 else
7700 rc = ACTION_ProcessExecSequence(package, FALSE);
7702 package->script->CurrentlyScripting = FALSE;
7704 /* process the ending type action */
7705 if (rc == ERROR_SUCCESS)
7706 ACTION_PerformActionSequence(package, -1);
7707 else if (rc == ERROR_INSTALL_USEREXIT)
7708 ACTION_PerformActionSequence(package, -2);
7709 else if (rc == ERROR_INSTALL_SUSPEND)
7710 ACTION_PerformActionSequence(package, -4);
7711 else /* failed */
7712 ACTION_PerformActionSequence(package, -3);
7714 /* finish up running custom actions */
7715 ACTION_FinishCustomActions(package);
7717 if (rc == ERROR_SUCCESS && package->need_reboot)
7718 return ERROR_SUCCESS_REBOOT_REQUIRED;
7720 return rc;