msi: Add support for patching files.
[wine.git] / dlls / msi / action.c
blob2f0b73954892054f3fa5bc4d2b59e357bc28c8ff
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 szPublishComponents[] =
119 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
120 static const WCHAR szRegisterComPlus[] =
121 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
122 static const WCHAR szRegisterUser[] =
123 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
124 static const WCHAR szRemoveEnvironmentStrings[] =
125 {'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};
126 static const WCHAR szRemoveExistingProducts[] =
127 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
128 static const WCHAR szRemoveFolders[] =
129 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
130 static const WCHAR szRemoveIniValues[] =
131 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
132 static const WCHAR szRemoveODBC[] =
133 {'R','e','m','o','v','e','O','D','B','C',0};
134 static const WCHAR szRemoveRegistryValues[] =
135 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
136 static const WCHAR szRemoveShortcuts[] =
137 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
138 static const WCHAR szRMCCPSearch[] =
139 {'R','M','C','C','P','S','e','a','r','c','h',0};
140 static const WCHAR szScheduleReboot[] =
141 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
142 static const WCHAR szSelfUnregModules[] =
143 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
144 static const WCHAR szSetODBCFolders[] =
145 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
146 static const WCHAR szStartServices[] =
147 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
148 static const WCHAR szStopServices[] =
149 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
150 static const WCHAR szUnpublishComponents[] =
151 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
152 static const WCHAR szUnpublishFeatures[] =
153 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
154 static const WCHAR szUnregisterComPlus[] =
155 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
156 static const WCHAR szUnregisterTypeLibraries[] =
157 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
158 static const WCHAR szValidateProductID[] =
159 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
160 static const WCHAR szWriteEnvironmentStrings[] =
161 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
163 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
165 static const WCHAR Query_t[] =
166 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
167 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
168 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
169 ' ','\'','%','s','\'',0};
170 MSIRECORD * row;
172 row = MSI_QueryGetRecord( package->db, Query_t, action );
173 if (!row)
174 return;
175 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
176 msiobj_release(&row->hdr);
179 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
180 UINT rc)
182 MSIRECORD * row;
183 static const WCHAR template_s[]=
184 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
185 '%','s', '.',0};
186 static const WCHAR template_e[]=
187 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
188 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
189 '%','i','.',0};
190 static const WCHAR format[] =
191 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
192 WCHAR message[1024];
193 WCHAR timet[0x100];
195 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
196 if (start)
197 sprintfW(message,template_s,timet,action);
198 else
199 sprintfW(message,template_e,timet,action,rc);
201 row = MSI_CreateRecord(1);
202 MSI_RecordSetStringW(row,1,message);
204 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
205 msiobj_release(&row->hdr);
208 enum parse_state
210 state_whitespace,
211 state_token,
212 state_quote
215 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
217 enum parse_state state = state_quote;
218 const WCHAR *p;
219 WCHAR *out = value;
220 int ignore, in_quotes = 0, count = 0, len = 0;
222 for (p = str; *p; p++)
224 ignore = 0;
225 switch (state)
227 case state_whitespace:
228 switch (*p)
230 case ' ':
231 if (!count) goto done;
232 in_quotes = 1;
233 ignore = 1;
234 break;
235 case '"':
236 state = state_quote;
237 if (in_quotes) count--;
238 else count++;
239 break;
240 default:
241 state = state_token;
242 if (!count) in_quotes = 0;
243 else in_quotes = 1;
244 len++;
245 break;
247 break;
249 case state_token:
250 switch (*p)
252 case '"':
253 state = state_quote;
254 if (in_quotes) count--;
255 else count++;
256 break;
257 case ' ':
258 state = state_whitespace;
259 if (!count) goto done;
260 in_quotes = 1;
261 break;
262 default:
263 if (!count) in_quotes = 0;
264 else in_quotes = 1;
265 len++;
266 break;
268 break;
270 case state_quote:
271 switch (*p)
273 case '"':
274 if (in_quotes) count--;
275 else count++;
276 break;
277 case ' ':
278 state = state_whitespace;
279 if (!count || !len) goto done;
280 in_quotes = 1;
281 break;
282 default:
283 state = state_token;
284 if (!count) in_quotes = 0;
285 else in_quotes = 1;
286 len++;
287 break;
289 break;
291 default: break;
293 if (!ignore) *out++ = *p;
296 done:
297 if (!len) *value = 0;
298 else *out = 0;
300 *quotes = count;
301 return p - str;
304 static void remove_quotes( WCHAR *str )
306 WCHAR *p = str;
307 int len = strlenW( str );
309 while ((p = strchrW( p, '"' )))
311 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
312 p++;
316 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
317 BOOL preserve_case )
319 LPCWSTR ptr, ptr2;
320 int quotes;
321 DWORD len;
322 WCHAR *prop, *val;
323 UINT r;
325 if (!szCommandLine)
326 return ERROR_SUCCESS;
328 ptr = szCommandLine;
329 while (*ptr)
331 while (*ptr == ' ') ptr++;
332 if (!*ptr) break;
334 ptr2 = strchrW( ptr, '=' );
335 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
337 len = ptr2 - ptr;
338 if (!len) return ERROR_INVALID_COMMAND_LINE;
340 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
341 memcpy( prop, ptr, len * sizeof(WCHAR) );
342 prop[len] = 0;
343 if (!preserve_case) struprW( prop );
345 ptr2++;
346 while (*ptr2 == ' ') ptr2++;
348 quotes = 0;
349 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
350 len = parse_prop( ptr2, val, &quotes );
351 if (quotes % 2)
353 WARN("unbalanced quotes\n");
354 msi_free( val );
355 msi_free( prop );
356 return ERROR_INVALID_COMMAND_LINE;
358 remove_quotes( val );
359 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
361 r = msi_set_property( package->db, prop, val );
362 if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
363 msi_reset_folders( package, TRUE );
365 msi_free( val );
366 msi_free( prop );
368 ptr = ptr2 + len;
371 return ERROR_SUCCESS;
374 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
376 LPCWSTR pc;
377 LPWSTR p, *ret = NULL;
378 UINT count = 0;
380 if (!str)
381 return ret;
383 /* count the number of substrings */
384 for ( pc = str, count = 0; pc; count++ )
386 pc = strchrW( pc, sep );
387 if (pc)
388 pc++;
391 /* allocate space for an array of substring pointers and the substrings */
392 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
393 (lstrlenW(str)+1) * sizeof(WCHAR) );
394 if (!ret)
395 return ret;
397 /* copy the string and set the pointers */
398 p = (LPWSTR) &ret[count+1];
399 lstrcpyW( p, str );
400 for( count = 0; (ret[count] = p); count++ )
402 p = strchrW( p, sep );
403 if (p)
404 *p++ = 0;
407 return ret;
410 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
412 static const WCHAR szSystemLanguageID[] =
413 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
415 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
416 UINT ret = ERROR_FUNCTION_FAILED;
418 prod_code = msi_dup_property( package->db, szProductCode );
419 patch_product = msi_get_suminfo_product( patch );
421 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
423 if ( strstrW( patch_product, prod_code ) )
425 MSISUMMARYINFO *si;
426 const WCHAR *p;
428 si = MSI_GetSummaryInformationW( patch, 0 );
429 if (!si)
431 ERR("no summary information!\n");
432 goto end;
435 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
436 if (!template)
438 ERR("no template property!\n");
439 msiobj_release( &si->hdr );
440 goto end;
443 if (!template[0])
445 ret = ERROR_SUCCESS;
446 msiobj_release( &si->hdr );
447 goto end;
450 langid = msi_dup_property( package->db, szSystemLanguageID );
451 if (!langid)
453 msiobj_release( &si->hdr );
454 goto end;
457 p = strchrW( template, ';' );
458 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
460 TRACE("applicable transform\n");
461 ret = ERROR_SUCCESS;
464 /* FIXME: check platform */
466 msiobj_release( &si->hdr );
469 end:
470 msi_free( patch_product );
471 msi_free( prod_code );
472 msi_free( template );
473 msi_free( langid );
475 return ret;
478 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
479 MSIDATABASE *patch_db, LPCWSTR name )
481 UINT ret = ERROR_FUNCTION_FAILED;
482 IStorage *stg = NULL;
483 HRESULT r;
485 TRACE("%p %s\n", package, debugstr_w(name) );
487 if (*name++ != ':')
489 ERR("expected a colon in %s\n", debugstr_w(name));
490 return ERROR_FUNCTION_FAILED;
493 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
494 if (SUCCEEDED(r))
496 ret = msi_check_transform_applicable( package, stg );
497 if (ret == ERROR_SUCCESS)
498 msi_table_apply_transform( package->db, stg );
499 else
500 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
501 IStorage_Release( stg );
503 else
504 ERR("failed to open substorage %s\n", debugstr_w(name));
506 return ERROR_SUCCESS;
509 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
511 LPWSTR guid_list, *guids, product_code;
512 UINT i, ret = ERROR_FUNCTION_FAILED;
514 product_code = msi_dup_property( package->db, szProductCode );
515 if (!product_code)
517 /* FIXME: the property ProductCode should be written into the DB somewhere */
518 ERR("no product code to check\n");
519 return ERROR_SUCCESS;
522 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
523 guids = msi_split_string( guid_list, ';' );
524 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
526 if (!strcmpW( guids[i], product_code ))
527 ret = ERROR_SUCCESS;
529 msi_free( guids );
530 msi_free( guid_list );
531 msi_free( product_code );
533 return ret;
536 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
538 MSIQUERY *view;
539 MSIRECORD *rec = NULL;
540 LPWSTR patch;
541 LPCWSTR prop;
542 UINT r;
544 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
545 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
546 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
547 '`','S','o','u','r','c','e','`',' ','I','S',' ',
548 'N','O','T',' ','N','U','L','L',0};
550 r = MSI_DatabaseOpenViewW(package->db, query, &view);
551 if (r != ERROR_SUCCESS)
552 return r;
554 r = MSI_ViewExecute(view, 0);
555 if (r != ERROR_SUCCESS)
556 goto done;
558 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
560 prop = MSI_RecordGetString(rec, 1);
561 patch = msi_dup_property(package->db, szPatch);
562 msi_set_property(package->db, prop, patch);
563 msi_free(patch);
566 done:
567 if (rec) msiobj_release(&rec->hdr);
568 msiobj_release(&view->hdr);
570 return r;
573 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
575 MSIPATCHINFO *pi;
576 UINT r = ERROR_SUCCESS;
577 WCHAR *p;
579 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
580 if (!pi)
581 return ERROR_OUTOFMEMORY;
583 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
584 if (!pi->patchcode)
586 msi_free( pi );
587 return ERROR_OUTOFMEMORY;
590 p = pi->patchcode;
591 if (*p != '{')
593 msi_free( pi->patchcode );
594 msi_free( pi );
595 return ERROR_PATCH_PACKAGE_INVALID;
598 p = strchrW( p + 1, '}' );
599 if (!p)
601 msi_free( pi->patchcode );
602 msi_free( pi );
603 return ERROR_PATCH_PACKAGE_INVALID;
606 if (p[1])
608 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
609 p[1] = 0;
612 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
614 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
615 if (!pi->transforms)
617 msi_free( pi->patchcode );
618 msi_free( pi );
619 return ERROR_OUTOFMEMORY;
622 *patch = pi;
623 return r;
626 struct msi_patch_offset
628 struct list entry;
629 LPWSTR Name;
630 UINT Sequence;
633 struct msi_patch_offset_list
635 struct list files;
636 UINT count, min, max;
637 UINT offset_to_apply;
640 static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
642 struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
643 list_init( &pos->files );
644 pos->count = pos->max = 0;
645 pos->min = 999999;
647 return pos;
650 static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
652 struct msi_patch_offset *po, *po2;
654 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
656 msi_free( po->Name );
657 msi_free( po );
660 msi_free( pos );
663 static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
665 MSIQUERY *view;
666 MSIRECORD *rec;
667 UINT r;
668 static const WCHAR query_patch[] = {
669 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
670 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
671 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
673 r = MSI_DatabaseOpenViewW( db, query_patch, &view );
674 if (r != ERROR_SUCCESS)
675 return;
677 rec = MSI_CreateRecord( 1 );
678 MSI_RecordSetInteger(rec, 1, last_sequence);
680 r = MSI_ViewExecute( view, rec );
681 msiobj_release( &rec->hdr );
682 if (r != ERROR_SUCCESS)
683 return;
685 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
687 UINT sequence = MSI_RecordGetInteger( rec, 2 );
689 /* FIXME:
690 * We only use the max/min sequence numbers for now.
693 pos->min = min(pos->min, sequence);
694 pos->max = max(pos->max, sequence);
695 pos->count++;
697 msiobj_release( &rec->hdr );
700 msiobj_release( &view->hdr );
703 static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
705 MSIQUERY *view;
706 MSIRECORD *rec;
707 UINT r;
708 static const WCHAR query_files[] = {
709 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
710 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
711 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
713 r = MSI_DatabaseOpenViewW( db, query_files, &view );
714 if (r != ERROR_SUCCESS)
715 return;
717 rec = MSI_CreateRecord( 1 );
718 MSI_RecordSetInteger(rec, 1, last_sequence);
720 r = MSI_ViewExecute( view, rec );
721 msiobj_release( &rec->hdr );
722 if (r != ERROR_SUCCESS)
723 return;
725 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
727 UINT attributes = MSI_RecordGetInteger( rec, 7 );
728 if (attributes & msidbFileAttributesPatchAdded)
730 struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
732 po->Name = msi_dup_record_field( rec, 1 );
733 po->Sequence = MSI_RecordGetInteger( rec, 8 );
735 pos->min = min(pos->min, po->Sequence);
736 pos->max = max(pos->max, po->Sequence);
738 list_add_tail( &pos->files, &po->entry );
739 pos->count++;
741 msiobj_release( &rec->hdr );
744 msiobj_release( &view->hdr );
747 static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
749 static const WCHAR query_files[] =
750 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
751 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
752 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
753 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
754 struct msi_patch_offset *po;
755 MSIQUERY *view;
756 MSIRECORD *rec;
757 UINT r;
759 r = MSI_DatabaseOpenViewW( db, query_files, &view );
760 if (r != ERROR_SUCCESS)
761 return ERROR_SUCCESS;
763 rec = MSI_CreateRecord( 2 );
764 MSI_RecordSetInteger( rec, 1, pos->min );
765 MSI_RecordSetInteger( rec, 2, pos->max );
767 r = MSI_ViewExecute( view, rec );
768 msiobj_release( &rec->hdr );
769 if (r != ERROR_SUCCESS)
770 goto done;
772 LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
774 UINT r_fetch;
775 while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
777 LPCWSTR file = MSI_RecordGetString( rec, 1 );
778 UINT seq;
780 if (!strcmpiW(file, po->Name))
782 /* Update record */
783 seq = MSI_RecordGetInteger( rec, 8 );
784 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
785 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
786 if (r != ERROR_SUCCESS)
787 ERR("Failed to update offset for file %s.\n", debugstr_w(file));
789 msiobj_release( &rec->hdr );
790 break;
793 msiobj_release( &rec->hdr );
796 if (r_fetch != ERROR_SUCCESS)
797 break;
800 done:
801 msiobj_release( &view->hdr );
803 return ERROR_SUCCESS;
806 static UINT msi_set_patch_offsets(MSIDATABASE *db)
808 static const WCHAR query_media[] = {
809 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','M','e','d','i','a',' ',
810 'W','H','E','R','E',' ','S','o','u','r','c','e',' ','I','S',' ','N','O','T',' ','N','U','L','L',
811 ' ','A','N','D',' ','C','a','b','i','n','e','t',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
812 'O','R','D','E','R',' ','B','Y',' ','D','i','s','k','I','d',0};
813 MSIQUERY *view = NULL;
814 MSIRECORD *rec = NULL;
815 UINT r;
817 r = MSI_DatabaseOpenViewW( db, query_media, &view );
818 if (r != ERROR_SUCCESS)
819 return r;
821 r = MSI_ViewExecute( view, 0 );
822 if (r != ERROR_SUCCESS)
823 goto done;
825 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
827 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
828 struct msi_patch_offset_list *pos;
830 /* FIXME: Set/Check Source field instead? */
831 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
833 msiobj_release( &rec->hdr );
834 continue;
837 pos = msi_patch_offset_list_create();
839 msi_patch_offset_get_files( db, last_sequence, pos );
840 msi_patch_offset_get_patches( db, last_sequence, pos );
842 if (pos->count)
844 UINT offset = db->media_transform_offset - pos->min;
845 last_sequence = offset + pos->max;
847 /* FIXME:
848 * This is for the patch table, which is not yet properly transformed.
850 last_sequence += pos->min;
852 pos->offset_to_apply = offset;
853 msi_patch_offset_modify_db( db, pos );
855 MSI_RecordSetInteger( rec, 2, last_sequence );
856 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
857 if (r != ERROR_SUCCESS)
858 ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
860 db->media_transform_offset = last_sequence + 1;
863 msi_patch_offset_list_free( pos );
864 msiobj_release( &rec->hdr );
867 done:
868 msiobj_release( &view->hdr );
870 return r;
873 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
875 UINT i, r = ERROR_SUCCESS;
876 WCHAR **substorage;
878 /* apply substorage transforms */
879 substorage = msi_split_string( patch->transforms, ';' );
880 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
882 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
883 if (r == ERROR_SUCCESS)
884 msi_set_patch_offsets( package->db );
887 msi_free( substorage );
888 if (r != ERROR_SUCCESS)
889 return r;
891 msi_set_media_source_prop( package );
894 * There might be a CAB file in the patch package,
895 * so append it to the list of storages to search for streams.
897 append_storage_to_db( package->db, patch_db->storage );
899 patch->state = MSIPATCHSTATE_APPLIED;
900 list_add_tail( &package->patches, &patch->entry );
901 return ERROR_SUCCESS;
904 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
906 static const WCHAR dotmsp[] = {'.','m','s','p',0};
907 MSIDATABASE *patch_db = NULL;
908 WCHAR localfile[MAX_PATH];
909 MSISUMMARYINFO *si;
910 MSIPATCHINFO *patch = NULL;
911 UINT r = ERROR_SUCCESS;
913 TRACE("%p %s\n", package, debugstr_w( file ) );
915 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
916 if ( r != ERROR_SUCCESS )
918 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
919 return r;
922 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
923 if (!si)
925 msiobj_release( &patch_db->hdr );
926 return ERROR_FUNCTION_FAILED;
929 r = msi_check_patch_applicable( package, si );
930 if (r != ERROR_SUCCESS)
932 TRACE("patch not applicable\n");
933 r = ERROR_SUCCESS;
934 goto done;
937 r = msi_parse_patch_summary( si, &patch );
938 if ( r != ERROR_SUCCESS )
939 goto done;
941 r = msi_get_local_package_name( localfile, dotmsp );
942 if ( r != ERROR_SUCCESS )
943 goto done;
945 TRACE("copying to local package %s\n", debugstr_w(localfile));
947 if (!CopyFileW( file, localfile, FALSE ))
949 ERR("Unable to copy package (%s -> %s) (error %u)\n",
950 debugstr_w(file), debugstr_w(localfile), GetLastError());
951 r = GetLastError();
952 goto done;
954 patch->localfile = strdupW( localfile );
956 r = msi_apply_patch_db( package, patch_db, patch );
957 if ( r != ERROR_SUCCESS )
958 WARN("patch failed to apply %u\n", r);
960 done:
961 msiobj_release( &si->hdr );
962 msiobj_release( &patch_db->hdr );
963 if (patch && r != ERROR_SUCCESS)
965 if (patch->localfile)
966 DeleteFileW( patch->localfile );
968 msi_free( patch->patchcode );
969 msi_free( patch->transforms );
970 msi_free( patch->localfile );
971 msi_free( patch );
973 return r;
976 /* get the PATCH property, and apply all the patches it specifies */
977 static UINT msi_apply_patches( MSIPACKAGE *package )
979 LPWSTR patch_list, *patches;
980 UINT i, r = ERROR_SUCCESS;
982 patch_list = msi_dup_property( package->db, szPatch );
984 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
986 patches = msi_split_string( patch_list, ';' );
987 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
988 r = msi_apply_patch_package( package, patches[i] );
990 msi_free( patches );
991 msi_free( patch_list );
993 return r;
996 static UINT msi_apply_transforms( MSIPACKAGE *package )
998 static const WCHAR szTransforms[] = {
999 'T','R','A','N','S','F','O','R','M','S',0 };
1000 LPWSTR xform_list, *xforms;
1001 UINT i, r = ERROR_SUCCESS;
1003 xform_list = msi_dup_property( package->db, szTransforms );
1004 xforms = msi_split_string( xform_list, ';' );
1006 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
1008 if (xforms[i][0] == ':')
1009 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
1010 else
1012 WCHAR *transform;
1014 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1015 else
1017 WCHAR *p = strrchrW( package->PackagePath, '\\' );
1018 DWORD len = p - package->PackagePath + 1;
1020 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1022 msi_free( xforms );
1023 msi_free( xform_list );
1024 return ERROR_OUTOFMEMORY;
1026 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1027 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1029 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1030 if (transform != xforms[i]) msi_free( transform );
1034 msi_free( xforms );
1035 msi_free( xform_list );
1037 return r;
1040 static BOOL ui_sequence_exists( MSIPACKAGE *package )
1042 MSIQUERY *view;
1043 UINT rc;
1045 static const WCHAR ExecSeqQuery [] =
1046 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1047 '`','I','n','s','t','a','l','l',
1048 'U','I','S','e','q','u','e','n','c','e','`',
1049 ' ','W','H','E','R','E',' ',
1050 '`','S','e','q','u','e','n','c','e','`',' ',
1051 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1052 '`','S','e','q','u','e','n','c','e','`',0};
1054 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1055 if (rc == ERROR_SUCCESS)
1057 msiobj_release(&view->hdr);
1058 return TRUE;
1061 return FALSE;
1064 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
1066 LPWSTR source, check;
1068 if (msi_get_property_int( package->db, szInstalled, 0 ))
1070 HKEY hkey;
1072 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
1073 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
1074 RegCloseKey( hkey );
1076 else
1078 LPWSTR p, db;
1079 DWORD len;
1081 db = msi_dup_property( package->db, szOriginalDatabase );
1082 if (!db)
1083 return ERROR_OUTOFMEMORY;
1085 p = strrchrW( db, '\\' );
1086 if (!p)
1088 p = strrchrW( db, '/' );
1089 if (!p)
1091 msi_free(db);
1092 return ERROR_SUCCESS;
1096 len = p - db + 2;
1097 source = msi_alloc( len * sizeof(WCHAR) );
1098 lstrcpynW( source, db, len );
1099 msi_free( db );
1102 check = msi_dup_property( package->db, cszSourceDir );
1103 if (!check || replace)
1105 UINT r = msi_set_property( package->db, cszSourceDir, source );
1106 if (r == ERROR_SUCCESS)
1107 msi_reset_folders( package, TRUE );
1109 msi_free( check );
1111 check = msi_dup_property( package->db, cszSOURCEDIR );
1112 if (!check || replace)
1113 msi_set_property( package->db, cszSOURCEDIR, source );
1115 msi_free( check );
1116 msi_free( source );
1118 return ERROR_SUCCESS;
1121 static BOOL needs_ui_sequence(MSIPACKAGE *package)
1123 INT level = msi_get_property_int(package->db, szUILevel, 0);
1124 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
1127 UINT msi_set_context(MSIPACKAGE *package)
1129 int num;
1131 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
1133 num = msi_get_property_int(package->db, szAllUsers, 0);
1134 if (num == 1 || num == 2)
1135 package->Context = MSIINSTALLCONTEXT_MACHINE;
1137 return ERROR_SUCCESS;
1140 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
1142 UINT rc;
1143 LPCWSTR cond, action;
1144 MSIPACKAGE *package = param;
1146 action = MSI_RecordGetString(row,1);
1147 if (!action)
1149 ERR("Error is retrieving action name\n");
1150 return ERROR_FUNCTION_FAILED;
1153 /* check conditions */
1154 cond = MSI_RecordGetString(row,2);
1156 /* this is a hack to skip errors in the condition code */
1157 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
1159 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
1160 return ERROR_SUCCESS;
1163 if (needs_ui_sequence(package))
1164 rc = ACTION_PerformUIAction(package, action, -1);
1165 else
1166 rc = ACTION_PerformAction(package, action, -1);
1168 msi_dialog_check_messages( NULL );
1170 if (package->CurrentInstallState != ERROR_SUCCESS)
1171 rc = package->CurrentInstallState;
1173 if (rc == ERROR_FUNCTION_NOT_CALLED)
1174 rc = ERROR_SUCCESS;
1176 if (rc != ERROR_SUCCESS)
1177 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
1179 return rc;
1182 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
1184 MSIQUERY * view;
1185 UINT r;
1186 static const WCHAR query[] =
1187 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1188 '`','%','s','`',
1189 ' ','W','H','E','R','E',' ',
1190 '`','S','e','q','u','e','n','c','e','`',' ',
1191 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1192 '`','S','e','q','u','e','n','c','e','`',0};
1194 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1196 r = MSI_OpenQuery( package->db, &view, query, szTable );
1197 if (r == ERROR_SUCCESS)
1199 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
1200 msiobj_release(&view->hdr);
1203 return r;
1206 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1208 MSIQUERY * view;
1209 UINT rc;
1210 static const WCHAR ExecSeqQuery[] =
1211 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1212 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1213 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1214 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1215 'O','R','D','E','R',' ', 'B','Y',' ',
1216 '`','S','e','q','u','e','n','c','e','`',0 };
1217 static const WCHAR IVQuery[] =
1218 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1219 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1220 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1221 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1222 ' ','\'', 'I','n','s','t','a','l','l',
1223 'V','a','l','i','d','a','t','e','\'', 0};
1224 INT seq = 0;
1226 if (package->script->ExecuteSequenceRun)
1228 TRACE("Execute Sequence already Run\n");
1229 return ERROR_SUCCESS;
1232 package->script->ExecuteSequenceRun = TRUE;
1234 /* get the sequence number */
1235 if (UIran)
1237 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
1238 if( !row )
1239 return ERROR_FUNCTION_FAILED;
1240 seq = MSI_RecordGetInteger(row,1);
1241 msiobj_release(&row->hdr);
1244 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1245 if (rc == ERROR_SUCCESS)
1247 TRACE("Running the actions\n");
1249 msi_set_property(package->db, cszSourceDir, NULL);
1251 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1252 msiobj_release(&view->hdr);
1255 return rc;
1258 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1260 MSIQUERY * view;
1261 UINT rc;
1262 static const WCHAR ExecSeqQuery [] =
1263 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1264 '`','I','n','s','t','a','l','l',
1265 'U','I','S','e','q','u','e','n','c','e','`',
1266 ' ','W','H','E','R','E',' ',
1267 '`','S','e','q','u','e','n','c','e','`',' ',
1268 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1269 '`','S','e','q','u','e','n','c','e','`',0};
1271 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1272 if (rc == ERROR_SUCCESS)
1274 TRACE("Running the actions\n");
1276 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1277 msiobj_release(&view->hdr);
1280 return rc;
1283 /********************************************************
1284 * ACTION helper functions and functions that perform the actions
1285 *******************************************************/
1286 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1287 UINT* rc, UINT script, BOOL force )
1289 BOOL ret=FALSE;
1290 UINT arc;
1292 arc = ACTION_CustomAction(package, action, script, force);
1294 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1296 *rc = arc;
1297 ret = TRUE;
1299 return ret;
1303 * Actual Action Handlers
1306 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1308 MSIPACKAGE *package = param;
1309 LPCWSTR dir, component;
1310 LPWSTR full_path;
1311 MSIRECORD *uirow;
1312 MSIFOLDER *folder;
1313 MSICOMPONENT *comp;
1315 component = MSI_RecordGetString(row, 2);
1316 if (!component)
1317 return ERROR_SUCCESS;
1319 comp = get_loaded_component(package, component);
1320 if (!comp)
1321 return ERROR_SUCCESS;
1323 if (!comp->Enabled)
1325 TRACE("component is disabled\n");
1326 return ERROR_SUCCESS;
1329 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1331 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1332 comp->Action = comp->Installed;
1333 return ERROR_SUCCESS;
1335 comp->Action = INSTALLSTATE_LOCAL;
1337 dir = MSI_RecordGetString(row,1);
1338 if (!dir)
1340 ERR("Unable to get folder id\n");
1341 return ERROR_SUCCESS;
1344 uirow = MSI_CreateRecord(1);
1345 MSI_RecordSetStringW(uirow, 1, dir);
1346 ui_actiondata(package, szCreateFolders, uirow);
1347 msiobj_release(&uirow->hdr);
1349 full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1350 if (!full_path)
1352 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1353 return ERROR_SUCCESS;
1356 TRACE("Folder is %s\n",debugstr_w(full_path));
1358 if (folder->State == 0)
1359 create_full_pathW(full_path);
1361 folder->State = 3;
1363 msi_free(full_path);
1364 return ERROR_SUCCESS;
1367 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1369 static const WCHAR query[] =
1370 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1371 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1372 UINT rc;
1373 MSIQUERY *view;
1375 /* create all the empty folders specified in the CreateFolder table */
1376 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1377 if (rc != ERROR_SUCCESS)
1378 return ERROR_SUCCESS;
1380 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1381 msiobj_release(&view->hdr);
1383 return rc;
1386 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1388 MSIPACKAGE *package = param;
1389 LPCWSTR dir, component;
1390 LPWSTR full_path;
1391 MSIRECORD *uirow;
1392 MSIFOLDER *folder;
1393 MSICOMPONENT *comp;
1395 component = MSI_RecordGetString(row, 2);
1396 if (!component)
1397 return ERROR_SUCCESS;
1399 comp = get_loaded_component(package, component);
1400 if (!comp)
1401 return ERROR_SUCCESS;
1403 if (!comp->Enabled)
1405 TRACE("component is disabled\n");
1406 return ERROR_SUCCESS;
1409 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1411 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1412 comp->Action = comp->Installed;
1413 return ERROR_SUCCESS;
1415 comp->Action = INSTALLSTATE_ABSENT;
1417 dir = MSI_RecordGetString( row, 1 );
1418 if (!dir)
1420 ERR("Unable to get folder id\n");
1421 return ERROR_SUCCESS;
1424 full_path = resolve_target_folder( package, dir, FALSE, TRUE, &folder );
1425 if (!full_path)
1427 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1428 return ERROR_SUCCESS;
1431 TRACE("folder is %s\n", debugstr_w(full_path));
1433 uirow = MSI_CreateRecord( 1 );
1434 MSI_RecordSetStringW( uirow, 1, dir );
1435 ui_actiondata( package, szRemoveFolders, uirow );
1436 msiobj_release( &uirow->hdr );
1438 RemoveDirectoryW( full_path );
1439 folder->State = 0;
1441 msi_free( full_path );
1442 return ERROR_SUCCESS;
1445 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1447 static const WCHAR query[] =
1448 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1449 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1451 MSIQUERY *view;
1452 UINT rc;
1454 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1455 if (rc != ERROR_SUCCESS)
1456 return ERROR_SUCCESS;
1458 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1459 msiobj_release( &view->hdr );
1461 return rc;
1464 static UINT load_component( MSIRECORD *row, LPVOID param )
1466 MSIPACKAGE *package = param;
1467 MSICOMPONENT *comp;
1469 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1470 if (!comp)
1471 return ERROR_FUNCTION_FAILED;
1473 list_add_tail( &package->components, &comp->entry );
1475 /* fill in the data */
1476 comp->Component = msi_dup_record_field( row, 1 );
1478 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1480 comp->ComponentId = msi_dup_record_field( row, 2 );
1481 comp->Directory = msi_dup_record_field( row, 3 );
1482 comp->Attributes = MSI_RecordGetInteger(row,4);
1483 comp->Condition = msi_dup_record_field( row, 5 );
1484 comp->KeyPath = msi_dup_record_field( row, 6 );
1486 comp->Installed = INSTALLSTATE_UNKNOWN;
1487 comp->Action = INSTALLSTATE_UNKNOWN;
1488 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1490 comp->assembly = load_assembly( package, comp );
1491 return ERROR_SUCCESS;
1494 static UINT load_all_components( MSIPACKAGE *package )
1496 static const WCHAR query[] = {
1497 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1498 '`','C','o','m','p','o','n','e','n','t','`',0 };
1499 MSIQUERY *view;
1500 UINT r;
1502 if (!list_empty(&package->components))
1503 return ERROR_SUCCESS;
1505 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1506 if (r != ERROR_SUCCESS)
1507 return r;
1509 r = MSI_IterateRecords(view, NULL, load_component, package);
1510 msiobj_release(&view->hdr);
1511 return r;
1514 typedef struct {
1515 MSIPACKAGE *package;
1516 MSIFEATURE *feature;
1517 } _ilfs;
1519 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1521 ComponentList *cl;
1523 cl = msi_alloc( sizeof (*cl) );
1524 if ( !cl )
1525 return ERROR_NOT_ENOUGH_MEMORY;
1526 cl->component = comp;
1527 list_add_tail( &feature->Components, &cl->entry );
1529 return ERROR_SUCCESS;
1532 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1534 FeatureList *fl;
1536 fl = msi_alloc( sizeof(*fl) );
1537 if ( !fl )
1538 return ERROR_NOT_ENOUGH_MEMORY;
1539 fl->feature = child;
1540 list_add_tail( &parent->Children, &fl->entry );
1542 return ERROR_SUCCESS;
1545 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1547 _ilfs* ilfs = param;
1548 LPCWSTR component;
1549 MSICOMPONENT *comp;
1551 component = MSI_RecordGetString(row,1);
1553 /* check to see if the component is already loaded */
1554 comp = get_loaded_component( ilfs->package, component );
1555 if (!comp)
1557 ERR("unknown component %s\n", debugstr_w(component));
1558 return ERROR_FUNCTION_FAILED;
1561 add_feature_component( ilfs->feature, comp );
1562 comp->Enabled = TRUE;
1564 return ERROR_SUCCESS;
1567 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1569 MSIFEATURE *feature;
1571 if ( !name )
1572 return NULL;
1574 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1576 if ( !strcmpW( feature->Feature, name ) )
1577 return feature;
1580 return NULL;
1583 static UINT load_feature(MSIRECORD * row, LPVOID param)
1585 MSIPACKAGE* package = param;
1586 MSIFEATURE* feature;
1587 static const WCHAR Query1[] =
1588 {'S','E','L','E','C','T',' ',
1589 '`','C','o','m','p','o','n','e','n','t','_','`',
1590 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1591 'C','o','m','p','o','n','e','n','t','s','`',' ',
1592 'W','H','E','R','E',' ',
1593 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1594 MSIQUERY * view;
1595 UINT rc;
1596 _ilfs ilfs;
1598 /* fill in the data */
1600 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1601 if (!feature)
1602 return ERROR_NOT_ENOUGH_MEMORY;
1604 list_init( &feature->Children );
1605 list_init( &feature->Components );
1607 feature->Feature = msi_dup_record_field( row, 1 );
1609 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1611 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1612 feature->Title = msi_dup_record_field( row, 3 );
1613 feature->Description = msi_dup_record_field( row, 4 );
1615 if (!MSI_RecordIsNull(row,5))
1616 feature->Display = MSI_RecordGetInteger(row,5);
1618 feature->Level= MSI_RecordGetInteger(row,6);
1619 feature->Directory = msi_dup_record_field( row, 7 );
1620 feature->Attributes = MSI_RecordGetInteger(row,8);
1622 feature->Installed = INSTALLSTATE_UNKNOWN;
1623 feature->Action = INSTALLSTATE_UNKNOWN;
1624 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1626 list_add_tail( &package->features, &feature->entry );
1628 /* load feature components */
1630 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1631 if (rc != ERROR_SUCCESS)
1632 return ERROR_SUCCESS;
1634 ilfs.package = package;
1635 ilfs.feature = feature;
1637 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1638 msiobj_release(&view->hdr);
1640 return ERROR_SUCCESS;
1643 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1645 MSIPACKAGE* package = param;
1646 MSIFEATURE *parent, *child;
1648 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1649 if (!child)
1650 return ERROR_FUNCTION_FAILED;
1652 if (!child->Feature_Parent)
1653 return ERROR_SUCCESS;
1655 parent = find_feature_by_name( package, child->Feature_Parent );
1656 if (!parent)
1657 return ERROR_FUNCTION_FAILED;
1659 add_feature_child( parent, child );
1660 return ERROR_SUCCESS;
1663 static UINT load_all_features( MSIPACKAGE *package )
1665 static const WCHAR query[] = {
1666 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1667 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1668 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1669 MSIQUERY *view;
1670 UINT r;
1672 if (!list_empty(&package->features))
1673 return ERROR_SUCCESS;
1675 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1676 if (r != ERROR_SUCCESS)
1677 return r;
1679 r = MSI_IterateRecords( view, NULL, load_feature, package );
1680 if (r != ERROR_SUCCESS)
1681 return r;
1683 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1684 msiobj_release( &view->hdr );
1686 return r;
1689 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1691 if (!p)
1692 return p;
1693 p = strchrW(p, ch);
1694 if (!p)
1695 return p;
1696 *p = 0;
1697 return p+1;
1700 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1702 static const WCHAR query[] = {
1703 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1704 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1705 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1706 MSIQUERY *view = NULL;
1707 MSIRECORD *row = NULL;
1708 UINT r;
1710 TRACE("%s\n", debugstr_w(file->File));
1712 r = MSI_OpenQuery(package->db, &view, query, file->File);
1713 if (r != ERROR_SUCCESS)
1714 goto done;
1716 r = MSI_ViewExecute(view, NULL);
1717 if (r != ERROR_SUCCESS)
1718 goto done;
1720 r = MSI_ViewFetch(view, &row);
1721 if (r != ERROR_SUCCESS)
1722 goto done;
1724 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1725 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1726 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1727 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1728 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1730 done:
1731 if (view) msiobj_release(&view->hdr);
1732 if (row) msiobj_release(&row->hdr);
1733 return r;
1736 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1738 MSIRECORD *row;
1739 static const WCHAR query[] = {
1740 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1741 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1742 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1744 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1745 if (!row)
1747 WARN("query failed\n");
1748 return ERROR_FUNCTION_FAILED;
1751 file->disk_id = MSI_RecordGetInteger( row, 1 );
1752 msiobj_release( &row->hdr );
1753 return ERROR_SUCCESS;
1756 static UINT load_file(MSIRECORD *row, LPVOID param)
1758 MSIPACKAGE* package = param;
1759 LPCWSTR component;
1760 MSIFILE *file;
1762 /* fill in the data */
1764 file = msi_alloc_zero( sizeof (MSIFILE) );
1765 if (!file)
1766 return ERROR_NOT_ENOUGH_MEMORY;
1768 file->File = msi_dup_record_field( row, 1 );
1770 component = MSI_RecordGetString( row, 2 );
1771 file->Component = get_loaded_component( package, component );
1773 if (!file->Component)
1775 WARN("Component not found: %s\n", debugstr_w(component));
1776 msi_free(file->File);
1777 msi_free(file);
1778 return ERROR_SUCCESS;
1781 file->FileName = msi_dup_record_field( row, 3 );
1782 reduce_to_longfilename( file->FileName );
1784 file->ShortName = msi_dup_record_field( row, 3 );
1785 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1787 file->FileSize = MSI_RecordGetInteger( row, 4 );
1788 file->Version = msi_dup_record_field( row, 5 );
1789 file->Language = msi_dup_record_field( row, 6 );
1790 file->Attributes = MSI_RecordGetInteger( row, 7 );
1791 file->Sequence = MSI_RecordGetInteger( row, 8 );
1793 file->state = msifs_invalid;
1795 /* if the compressed bits are not set in the file attributes,
1796 * then read the information from the package word count property
1798 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1800 file->IsCompressed = FALSE;
1802 else if (file->Attributes &
1803 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1805 file->IsCompressed = TRUE;
1807 else if (file->Attributes & msidbFileAttributesNoncompressed)
1809 file->IsCompressed = FALSE;
1811 else
1813 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1816 load_file_hash(package, file);
1817 load_file_disk_id(package, file);
1819 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1821 list_add_tail( &package->files, &file->entry );
1823 return ERROR_SUCCESS;
1826 static UINT load_all_files(MSIPACKAGE *package)
1828 MSIQUERY * view;
1829 UINT rc;
1830 static const WCHAR Query[] =
1831 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1832 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1833 '`','S','e','q','u','e','n','c','e','`', 0};
1835 if (!list_empty(&package->files))
1836 return ERROR_SUCCESS;
1838 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1839 if (rc != ERROR_SUCCESS)
1840 return ERROR_SUCCESS;
1842 rc = MSI_IterateRecords(view, NULL, load_file, package);
1843 msiobj_release(&view->hdr);
1845 return ERROR_SUCCESS;
1848 static UINT load_patch(MSIRECORD *row, LPVOID param)
1850 MSIPACKAGE *package = param;
1851 MSIFILEPATCH *patch;
1852 LPWSTR file_key;
1854 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1855 if (!patch)
1856 return ERROR_NOT_ENOUGH_MEMORY;
1858 file_key = msi_dup_record_field( row, 1 );
1859 patch->File = get_loaded_file( package, file_key );
1860 msi_free(file_key);
1862 if( !patch->File )
1864 ERR("Failed to find target for patch in File table\n");
1865 msi_free(patch);
1866 return ERROR_FUNCTION_FAILED;
1869 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1871 /* FIXME: The database should be properly transformed */
1872 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1874 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1875 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1876 patch->IsApplied = FALSE;
1878 /* FIXME:
1879 * Header field - for patch validation.
1880 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1883 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1885 list_add_tail( &package->filepatches, &patch->entry );
1887 return ERROR_SUCCESS;
1890 static UINT load_all_patches(MSIPACKAGE *package)
1892 MSIQUERY *view;
1893 UINT rc;
1894 static const WCHAR Query[] =
1895 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1896 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1897 '`','S','e','q','u','e','n','c','e','`',0};
1899 if (!list_empty(&package->filepatches))
1900 return ERROR_SUCCESS;
1902 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1903 if (rc != ERROR_SUCCESS)
1904 return ERROR_SUCCESS;
1906 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1907 msiobj_release(&view->hdr);
1909 return ERROR_SUCCESS;
1912 static UINT load_folder( MSIRECORD *row, LPVOID param )
1914 MSIPACKAGE *package = param;
1915 static WCHAR szEmpty[] = { 0 };
1916 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1917 MSIFOLDER *folder;
1919 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1920 if (!folder)
1921 return ERROR_NOT_ENOUGH_MEMORY;
1923 folder->Directory = msi_dup_record_field( row, 1 );
1925 TRACE("%s\n", debugstr_w(folder->Directory));
1927 p = msi_dup_record_field(row, 3);
1929 /* split src and target dir */
1930 tgt_short = p;
1931 src_short = folder_split_path( p, ':' );
1933 /* split the long and short paths */
1934 tgt_long = folder_split_path( tgt_short, '|' );
1935 src_long = folder_split_path( src_short, '|' );
1937 /* check for no-op dirs */
1938 if (tgt_short && !strcmpW( szDot, tgt_short ))
1939 tgt_short = szEmpty;
1940 if (src_short && !strcmpW( szDot, src_short ))
1941 src_short = szEmpty;
1943 if (!tgt_long)
1944 tgt_long = tgt_short;
1946 if (!src_short) {
1947 src_short = tgt_short;
1948 src_long = tgt_long;
1951 if (!src_long)
1952 src_long = src_short;
1954 /* FIXME: use the target short path too */
1955 folder->TargetDefault = strdupW(tgt_long);
1956 folder->SourceShortPath = strdupW(src_short);
1957 folder->SourceLongPath = strdupW(src_long);
1958 msi_free(p);
1960 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1961 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1962 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1964 folder->Parent = msi_dup_record_field( row, 2 );
1966 folder->Property = msi_dup_property( package->db, folder->Directory );
1968 list_add_tail( &package->folders, &folder->entry );
1970 TRACE("returning %p\n", folder);
1972 return ERROR_SUCCESS;
1975 static UINT load_all_folders( MSIPACKAGE *package )
1977 static const WCHAR query[] = {
1978 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1979 '`','D','i','r','e','c','t','o','r','y','`',0 };
1980 MSIQUERY *view;
1981 UINT r;
1983 if (!list_empty(&package->folders))
1984 return ERROR_SUCCESS;
1986 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1987 if (r != ERROR_SUCCESS)
1988 return r;
1990 r = MSI_IterateRecords(view, NULL, load_folder, package);
1991 msiobj_release(&view->hdr);
1992 return r;
1996 * I am not doing any of the costing functionality yet.
1997 * Mostly looking at doing the Component and Feature loading
1999 * The native MSI does A LOT of modification to tables here. Mostly adding
2000 * a lot of temporary columns to the Feature and Component tables.
2002 * note: Native msi also tracks the short filename. But I am only going to
2003 * track the long ones. Also looking at this directory table
2004 * it appears that the directory table does not get the parents
2005 * resolved base on property only based on their entries in the
2006 * directory table.
2008 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
2010 static const WCHAR szCosting[] =
2011 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2013 msi_set_property( package->db, szCosting, szZero );
2014 msi_set_property( package->db, cszRootDrive, c_colon );
2016 load_all_folders( package );
2017 load_all_components( package );
2018 load_all_features( package );
2019 load_all_files( package );
2020 load_all_patches( package );
2022 return ERROR_SUCCESS;
2025 static UINT execute_script(MSIPACKAGE *package, UINT script )
2027 UINT i;
2028 UINT rc = ERROR_SUCCESS;
2030 TRACE("Executing Script %i\n",script);
2032 if (!package->script)
2034 ERR("no script!\n");
2035 return ERROR_FUNCTION_FAILED;
2038 for (i = 0; i < package->script->ActionCount[script]; i++)
2040 LPWSTR action;
2041 action = package->script->Actions[script][i];
2042 ui_actionstart(package, action);
2043 TRACE("Executing Action (%s)\n",debugstr_w(action));
2044 rc = ACTION_PerformAction(package, action, script);
2045 if (rc != ERROR_SUCCESS)
2046 break;
2048 msi_free_action_script(package, script);
2049 return rc;
2052 static UINT ACTION_FileCost(MSIPACKAGE *package)
2054 return ERROR_SUCCESS;
2057 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
2059 MSICOMPONENT *comp;
2060 UINT r;
2062 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
2064 if (!comp->ComponentId) continue;
2066 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2067 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
2068 &comp->Installed );
2069 if (r != ERROR_SUCCESS)
2070 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2071 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
2072 &comp->Installed );
2073 if (r != ERROR_SUCCESS)
2074 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2075 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
2076 &comp->Installed );
2077 if (r != ERROR_SUCCESS)
2078 comp->Installed = INSTALLSTATE_ABSENT;
2082 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
2084 MSIFEATURE *feature;
2086 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2088 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
2090 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
2091 feature->Installed = INSTALLSTATE_ABSENT;
2092 else
2093 feature->Installed = state;
2097 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
2099 return (feature->Level > 0 && feature->Level <= level);
2102 static BOOL process_state_property(MSIPACKAGE* package, int level,
2103 LPCWSTR property, INSTALLSTATE state)
2105 LPWSTR override;
2106 MSIFEATURE *feature;
2108 override = msi_dup_property( package->db, property );
2109 if (!override)
2110 return FALSE;
2112 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2114 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
2115 continue;
2117 if (!strcmpW(property, szReinstall)) state = feature->Installed;
2119 if (!strcmpiW( override, szAll ))
2121 if (feature->Installed != state)
2123 feature->Action = state;
2124 feature->ActionRequest = state;
2127 else
2129 LPWSTR ptr = override;
2130 LPWSTR ptr2 = strchrW(override,',');
2132 while (ptr)
2134 int len = ptr2 - ptr;
2136 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
2137 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
2139 if (feature->Installed != state)
2141 feature->Action = state;
2142 feature->ActionRequest = state;
2144 break;
2146 if (ptr2)
2148 ptr=ptr2+1;
2149 ptr2 = strchrW(ptr,',');
2151 else
2152 break;
2156 msi_free(override);
2157 return TRUE;
2160 static BOOL process_overrides( MSIPACKAGE *package, int level )
2162 static const WCHAR szAddLocal[] =
2163 {'A','D','D','L','O','C','A','L',0};
2164 static const WCHAR szAddSource[] =
2165 {'A','D','D','S','O','U','R','C','E',0};
2166 static const WCHAR szAdvertise[] =
2167 {'A','D','V','E','R','T','I','S','E',0};
2168 BOOL ret = FALSE;
2170 /* all these activation/deactivation things happen in order and things
2171 * later on the list override things earlier on the list.
2173 * 0 INSTALLLEVEL processing
2174 * 1 ADDLOCAL
2175 * 2 REMOVE
2176 * 3 ADDSOURCE
2177 * 4 ADDDEFAULT
2178 * 5 REINSTALL
2179 * 6 ADVERTISE
2180 * 7 COMPADDLOCAL
2181 * 8 COMPADDSOURCE
2182 * 9 FILEADDLOCAL
2183 * 10 FILEADDSOURCE
2184 * 11 FILEADDDEFAULT
2186 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
2187 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
2188 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
2189 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
2190 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
2192 if (ret)
2193 msi_set_property( package->db, szPreselected, szOne );
2195 return ret;
2198 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
2200 int level;
2201 static const WCHAR szlevel[] =
2202 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2203 MSICOMPONENT* component;
2204 MSIFEATURE *feature;
2206 TRACE("Checking Install Level\n");
2208 level = msi_get_property_int(package->db, szlevel, 1);
2210 if (!msi_get_property_int( package->db, szPreselected, 0 ))
2212 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2214 if (!is_feature_selected( feature, level )) continue;
2216 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2218 if (feature->Attributes & msidbFeatureAttributesFavorSource)
2220 feature->Action = INSTALLSTATE_SOURCE;
2221 feature->ActionRequest = INSTALLSTATE_SOURCE;
2223 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2225 feature->Action = INSTALLSTATE_ADVERTISED;
2226 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
2228 else
2230 feature->Action = INSTALLSTATE_LOCAL;
2231 feature->ActionRequest = INSTALLSTATE_LOCAL;
2236 /* disable child features of unselected parent features */
2237 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2239 FeatureList *fl;
2241 if (is_feature_selected( feature, level )) continue;
2243 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2245 fl->feature->Action = INSTALLSTATE_UNKNOWN;
2246 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2250 else /* preselected */
2252 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2254 if (!is_feature_selected( feature, level )) continue;
2256 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2258 if (feature->Installed == INSTALLSTATE_ABSENT)
2260 feature->Action = INSTALLSTATE_UNKNOWN;
2261 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2263 else
2265 feature->Action = feature->Installed;
2266 feature->ActionRequest = feature->Installed;
2272 /* now we want to set component state based based on feature state */
2273 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2275 ComponentList *cl;
2277 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
2278 debugstr_w(feature->Feature), feature->Level, feature->Installed,
2279 feature->ActionRequest, feature->Action);
2281 if (!is_feature_selected( feature, level )) continue;
2283 /* features with components that have compressed files are made local */
2284 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2286 if (cl->component->ForceLocalState &&
2287 feature->ActionRequest == INSTALLSTATE_SOURCE)
2289 feature->Action = INSTALLSTATE_LOCAL;
2290 feature->ActionRequest = INSTALLSTATE_LOCAL;
2291 break;
2295 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2297 component = cl->component;
2299 switch (feature->ActionRequest)
2301 case INSTALLSTATE_ABSENT:
2302 component->anyAbsent = 1;
2303 break;
2304 case INSTALLSTATE_ADVERTISED:
2305 component->hasAdvertiseFeature = 1;
2306 break;
2307 case INSTALLSTATE_SOURCE:
2308 component->hasSourceFeature = 1;
2309 break;
2310 case INSTALLSTATE_LOCAL:
2311 component->hasLocalFeature = 1;
2312 break;
2313 case INSTALLSTATE_DEFAULT:
2314 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2315 component->hasAdvertiseFeature = 1;
2316 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2317 component->hasSourceFeature = 1;
2318 else
2319 component->hasLocalFeature = 1;
2320 break;
2321 default:
2322 break;
2327 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2329 /* check if it's local or source */
2330 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2331 (component->hasLocalFeature || component->hasSourceFeature))
2333 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2334 !component->ForceLocalState)
2336 component->Action = INSTALLSTATE_SOURCE;
2337 component->ActionRequest = INSTALLSTATE_SOURCE;
2339 else
2341 component->Action = INSTALLSTATE_LOCAL;
2342 component->ActionRequest = INSTALLSTATE_LOCAL;
2344 continue;
2347 /* if any feature is local, the component must be local too */
2348 if (component->hasLocalFeature)
2350 component->Action = INSTALLSTATE_LOCAL;
2351 component->ActionRequest = INSTALLSTATE_LOCAL;
2352 continue;
2354 if (component->hasSourceFeature)
2356 component->Action = INSTALLSTATE_SOURCE;
2357 component->ActionRequest = INSTALLSTATE_SOURCE;
2358 continue;
2360 if (component->hasAdvertiseFeature)
2362 component->Action = INSTALLSTATE_ADVERTISED;
2363 component->ActionRequest = INSTALLSTATE_ADVERTISED;
2364 continue;
2366 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2367 if (component->anyAbsent &&
2368 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
2370 component->Action = INSTALLSTATE_ABSENT;
2371 component->ActionRequest = INSTALLSTATE_ABSENT;
2375 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2377 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2379 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2380 component->Action = INSTALLSTATE_LOCAL;
2381 component->ActionRequest = INSTALLSTATE_LOCAL;
2384 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2385 component->Installed == INSTALLSTATE_SOURCE &&
2386 component->hasSourceFeature)
2388 component->Action = INSTALLSTATE_UNKNOWN;
2389 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2392 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2393 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2396 return ERROR_SUCCESS;
2399 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2401 MSIPACKAGE *package = param;
2402 LPCWSTR name;
2403 LPWSTR path;
2404 MSIFOLDER *f;
2406 name = MSI_RecordGetString(row,1);
2408 f = get_loaded_folder(package, name);
2409 if (!f) return ERROR_SUCCESS;
2411 /* reset the ResolvedTarget */
2412 msi_free(f->ResolvedTarget);
2413 f->ResolvedTarget = NULL;
2415 TRACE("directory %s ...\n", debugstr_w(name));
2416 path = resolve_target_folder( package, name, TRUE, TRUE, NULL );
2417 TRACE("resolves to %s\n", debugstr_w(path));
2418 msi_free(path);
2420 return ERROR_SUCCESS;
2423 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2425 MSIPACKAGE *package = param;
2426 LPCWSTR name;
2427 MSIFEATURE *feature;
2429 name = MSI_RecordGetString( row, 1 );
2431 feature = get_loaded_feature( package, name );
2432 if (!feature)
2433 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2434 else
2436 LPCWSTR Condition;
2437 Condition = MSI_RecordGetString(row,3);
2439 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2441 int level = MSI_RecordGetInteger(row,2);
2442 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2443 feature->Level = level;
2446 return ERROR_SUCCESS;
2449 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2451 static const WCHAR name[] = {'\\',0};
2452 VS_FIXEDFILEINFO *ptr, *ret;
2453 LPVOID version;
2454 DWORD versize, handle;
2455 UINT sz;
2457 TRACE("%s\n", debugstr_w(filename));
2459 versize = GetFileVersionInfoSizeW( filename, &handle );
2460 if (!versize)
2461 return NULL;
2463 version = msi_alloc( versize );
2464 if (!version)
2465 return NULL;
2467 GetFileVersionInfoW( filename, 0, versize, version );
2469 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2471 msi_free( version );
2472 return NULL;
2475 ret = msi_alloc( sz );
2476 memcpy( ret, ptr, sz );
2478 msi_free( version );
2479 return ret;
2482 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2484 DWORD ms, ls;
2486 msi_parse_version_string( version, &ms, &ls );
2488 if (fi->dwFileVersionMS > ms) return 1;
2489 else if (fi->dwFileVersionMS < ms) return -1;
2490 else if (fi->dwFileVersionLS > ls) return 1;
2491 else if (fi->dwFileVersionLS < ls) return -1;
2492 return 0;
2495 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2497 DWORD ms1, ms2;
2499 msi_parse_version_string( ver1, &ms1, NULL );
2500 msi_parse_version_string( ver2, &ms2, NULL );
2502 if (ms1 > ms2) return 1;
2503 else if (ms1 < ms2) return -1;
2504 return 0;
2507 DWORD msi_get_disk_file_size( LPCWSTR filename )
2509 HANDLE file;
2510 DWORD size;
2512 TRACE("%s\n", debugstr_w(filename));
2514 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2515 if (file == INVALID_HANDLE_VALUE)
2516 return INVALID_FILE_SIZE;
2518 size = GetFileSize( file, NULL );
2519 CloseHandle( file );
2520 return size;
2523 BOOL msi_file_hash_matches( MSIFILE *file )
2525 UINT r;
2526 MSIFILEHASHINFO hash;
2528 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2529 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2530 if (r != ERROR_SUCCESS)
2531 return FALSE;
2533 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2536 static WCHAR *get_temp_dir( void )
2538 static UINT id;
2539 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2541 GetTempPathW( MAX_PATH, tmp );
2542 for (;;)
2544 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2545 if (CreateDirectoryW( dir, NULL )) break;
2547 return strdupW( dir );
2550 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2552 MSIASSEMBLY *assembly = file->Component->assembly;
2554 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2556 msi_free( file->TargetPath );
2557 if (assembly && !assembly->application)
2559 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2560 file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName );
2561 track_tempfile( package, file->TargetPath );
2563 else
2565 WCHAR *dir = resolve_target_folder( package, file->Component->Directory, FALSE, TRUE, NULL );
2566 file->TargetPath = build_directory_name( 2, dir, file->FileName );
2567 msi_free( dir );
2570 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2573 static UINT calculate_file_cost( MSIPACKAGE *package )
2575 VS_FIXEDFILEINFO *file_version;
2576 WCHAR *font_version;
2577 MSIFILE *file;
2579 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2581 MSICOMPONENT *comp = file->Component;
2582 DWORD file_size;
2584 if (!comp->Enabled) continue;
2586 if (file->IsCompressed)
2587 comp->ForceLocalState = TRUE;
2589 set_target_path( package, file );
2591 if ((comp->assembly && !comp->assembly->installed) ||
2592 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2594 comp->Cost += file->FileSize;
2595 continue;
2597 file_size = msi_get_disk_file_size( file->TargetPath );
2599 if (file->Version)
2601 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2603 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2605 comp->Cost += file->FileSize - file_size;
2607 msi_free( file_version );
2608 continue;
2610 else if ((font_version = font_version_from_file( file->TargetPath )))
2612 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2614 comp->Cost += file->FileSize - file_size;
2616 msi_free( font_version );
2617 continue;
2620 if (file_size != file->FileSize)
2622 comp->Cost += file->FileSize - file_size;
2625 return ERROR_SUCCESS;
2629 * A lot is done in this function aside from just the costing.
2630 * The costing needs to be implemented at some point but for now I am going
2631 * to focus on the directory building
2634 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2636 static const WCHAR ExecSeqQuery[] =
2637 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2638 '`','D','i','r','e','c','t','o','r','y','`',0};
2639 static const WCHAR ConditionQuery[] =
2640 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2641 '`','C','o','n','d','i','t','i','o','n','`',0};
2642 static const WCHAR szCosting[] =
2643 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2644 static const WCHAR szlevel[] =
2645 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2646 static const WCHAR szOutOfDiskSpace[] =
2647 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2648 MSICOMPONENT *comp;
2649 UINT rc = ERROR_SUCCESS;
2650 MSIQUERY * view;
2651 LPWSTR level;
2653 TRACE("Building Directory properties\n");
2655 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2656 if (rc == ERROR_SUCCESS)
2658 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2659 package);
2660 msiobj_release(&view->hdr);
2663 TRACE("Evaluating component conditions\n");
2664 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2666 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2668 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2669 comp->Enabled = FALSE;
2671 else
2672 comp->Enabled = TRUE;
2675 /* read components states from the registry */
2676 ACTION_GetComponentInstallStates(package);
2677 ACTION_GetFeatureInstallStates(package);
2679 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2681 TRACE("Evaluating feature conditions\n");
2683 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2684 if (rc == ERROR_SUCCESS)
2686 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2687 msiobj_release( &view->hdr );
2691 TRACE("Calculating file cost\n");
2692 calculate_file_cost( package );
2694 msi_set_property( package->db, szCosting, szOne );
2695 /* set default run level if not set */
2696 level = msi_dup_property( package->db, szlevel );
2697 if (!level)
2698 msi_set_property( package->db, szlevel, szOne );
2699 msi_free(level);
2701 /* FIXME: check volume disk space */
2702 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2704 return MSI_SetFeatureStates(package);
2707 /* OK this value is "interpreted" and then formatted based on the
2708 first few characters */
2709 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2710 DWORD *size)
2712 LPSTR data = NULL;
2714 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2716 if (value[1]=='x')
2718 LPWSTR ptr;
2719 CHAR byte[5];
2720 LPWSTR deformated = NULL;
2721 int count;
2723 deformat_string(package, &value[2], &deformated);
2725 /* binary value type */
2726 ptr = deformated;
2727 *type = REG_BINARY;
2728 if (strlenW(ptr)%2)
2729 *size = (strlenW(ptr)/2)+1;
2730 else
2731 *size = strlenW(ptr)/2;
2733 data = msi_alloc(*size);
2735 byte[0] = '0';
2736 byte[1] = 'x';
2737 byte[4] = 0;
2738 count = 0;
2739 /* if uneven pad with a zero in front */
2740 if (strlenW(ptr)%2)
2742 byte[2]= '0';
2743 byte[3]= *ptr;
2744 ptr++;
2745 data[count] = (BYTE)strtol(byte,NULL,0);
2746 count ++;
2747 TRACE("Uneven byte count\n");
2749 while (*ptr)
2751 byte[2]= *ptr;
2752 ptr++;
2753 byte[3]= *ptr;
2754 ptr++;
2755 data[count] = (BYTE)strtol(byte,NULL,0);
2756 count ++;
2758 msi_free(deformated);
2760 TRACE("Data %i bytes(%i)\n",*size,count);
2762 else
2764 LPWSTR deformated;
2765 LPWSTR p;
2766 DWORD d = 0;
2767 deformat_string(package, &value[1], &deformated);
2769 *type=REG_DWORD;
2770 *size = sizeof(DWORD);
2771 data = msi_alloc(*size);
2772 p = deformated;
2773 if (*p == '-')
2774 p++;
2775 while (*p)
2777 if ( (*p < '0') || (*p > '9') )
2778 break;
2779 d *= 10;
2780 d += (*p - '0');
2781 p++;
2783 if (deformated[0] == '-')
2784 d = -d;
2785 *(LPDWORD)data = d;
2786 TRACE("DWORD %i\n",*(LPDWORD)data);
2788 msi_free(deformated);
2791 else
2793 static const WCHAR szMulti[] = {'[','~',']',0};
2794 LPCWSTR ptr;
2795 *type=REG_SZ;
2797 if (value[0]=='#')
2799 if (value[1]=='%')
2801 ptr = &value[2];
2802 *type=REG_EXPAND_SZ;
2804 else
2805 ptr = &value[1];
2807 else
2808 ptr=value;
2810 if (strstrW(value, szMulti))
2811 *type = REG_MULTI_SZ;
2813 /* remove initial delimiter */
2814 if (!strncmpW(value, szMulti, 3))
2815 ptr = value + 3;
2817 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2819 /* add double NULL terminator */
2820 if (*type == REG_MULTI_SZ)
2822 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2823 data = msi_realloc_zero(data, *size);
2826 return data;
2829 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2831 const WCHAR *ret;
2833 switch (root)
2835 case -1:
2836 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2838 *root_key = HKEY_LOCAL_MACHINE;
2839 ret = szHLM;
2841 else
2843 *root_key = HKEY_CURRENT_USER;
2844 ret = szHCU;
2846 break;
2847 case 0:
2848 *root_key = HKEY_CLASSES_ROOT;
2849 ret = szHCR;
2850 break;
2851 case 1:
2852 *root_key = HKEY_CURRENT_USER;
2853 ret = szHCU;
2854 break;
2855 case 2:
2856 *root_key = HKEY_LOCAL_MACHINE;
2857 ret = szHLM;
2858 break;
2859 case 3:
2860 *root_key = HKEY_USERS;
2861 ret = szHU;
2862 break;
2863 default:
2864 ERR("Unknown root %i\n", root);
2865 return NULL;
2868 return ret;
2871 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2873 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2874 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2876 if (is_64bit && package->platform == PLATFORM_INTEL &&
2877 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2879 UINT size;
2880 WCHAR *path_32node;
2882 size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR);
2883 path_32node = msi_alloc( size );
2884 if (!path_32node)
2885 return NULL;
2887 memcpy( path_32node, path, len * sizeof(WCHAR) );
2888 path_32node[len] = 0;
2889 strcatW( path_32node, szWow6432Node );
2890 strcatW( path_32node, szBackSlash );
2891 strcatW( path_32node, path + len );
2892 return path_32node;
2895 return strdupW( path );
2898 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2900 MSIPACKAGE *package = param;
2901 LPSTR value_data = NULL;
2902 HKEY root_key, hkey;
2903 DWORD type,size;
2904 LPWSTR deformated, uikey, keypath;
2905 LPCWSTR szRoot, component, name, key, value;
2906 MSICOMPONENT *comp;
2907 MSIRECORD * uirow;
2908 INT root;
2909 BOOL check_first = FALSE;
2910 UINT rc;
2912 ui_progress(package,2,0,0,0);
2914 component = MSI_RecordGetString(row, 6);
2915 comp = get_loaded_component(package,component);
2916 if (!comp)
2917 return ERROR_SUCCESS;
2919 if (!comp->Enabled)
2921 TRACE("component is disabled\n");
2922 return ERROR_SUCCESS;
2925 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2927 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2928 comp->Action = comp->Installed;
2929 return ERROR_SUCCESS;
2931 comp->Action = INSTALLSTATE_LOCAL;
2933 name = MSI_RecordGetString(row, 4);
2934 if( MSI_RecordIsNull(row,5) && name )
2936 /* null values can have special meanings */
2937 if (name[0]=='-' && name[1] == 0)
2938 return ERROR_SUCCESS;
2939 else if ((name[0]=='+' && name[1] == 0) ||
2940 (name[0] == '*' && name[1] == 0))
2941 name = NULL;
2942 check_first = TRUE;
2945 root = MSI_RecordGetInteger(row,2);
2946 key = MSI_RecordGetString(row, 3);
2948 szRoot = get_root_key( package, root, &root_key );
2949 if (!szRoot)
2950 return ERROR_SUCCESS;
2952 deformat_string(package, key , &deformated);
2953 size = strlenW(deformated) + strlenW(szRoot) + 1;
2954 uikey = msi_alloc(size*sizeof(WCHAR));
2955 strcpyW(uikey,szRoot);
2956 strcatW(uikey,deformated);
2958 keypath = get_keypath( package, root_key, deformated );
2959 msi_free( deformated );
2960 if (RegCreateKeyW( root_key, keypath, &hkey ))
2962 ERR("Could not create key %s\n", debugstr_w(keypath));
2963 msi_free(uikey);
2964 msi_free(keypath);
2965 return ERROR_SUCCESS;
2968 value = MSI_RecordGetString(row,5);
2969 if (value)
2970 value_data = parse_value(package, value, &type, &size);
2971 else
2973 value_data = (LPSTR)strdupW(szEmpty);
2974 size = sizeof(szEmpty);
2975 type = REG_SZ;
2978 deformat_string(package, name, &deformated);
2980 if (!check_first)
2982 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2983 debugstr_w(uikey));
2984 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2986 else
2988 DWORD sz = 0;
2989 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2990 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2992 TRACE("value %s of %s checked already exists\n",
2993 debugstr_w(deformated), debugstr_w(uikey));
2995 else
2997 TRACE("Checked and setting value %s of %s\n",
2998 debugstr_w(deformated), debugstr_w(uikey));
2999 if (deformated || size)
3000 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
3003 RegCloseKey(hkey);
3005 uirow = MSI_CreateRecord(3);
3006 MSI_RecordSetStringW(uirow,2,deformated);
3007 MSI_RecordSetStringW(uirow,1,uikey);
3008 if (type == REG_SZ || type == REG_EXPAND_SZ)
3009 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
3010 ui_actiondata(package,szWriteRegistryValues,uirow);
3011 msiobj_release( &uirow->hdr );
3013 msi_free(value_data);
3014 msi_free(deformated);
3015 msi_free(uikey);
3016 msi_free(keypath);
3018 return ERROR_SUCCESS;
3021 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
3023 UINT rc;
3024 MSIQUERY * view;
3025 static const WCHAR ExecSeqQuery[] =
3026 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3027 '`','R','e','g','i','s','t','r','y','`',0 };
3029 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3030 if (rc != ERROR_SUCCESS)
3031 return ERROR_SUCCESS;
3033 /* increment progress bar each time action data is sent */
3034 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
3036 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
3038 msiobj_release(&view->hdr);
3039 return rc;
3042 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
3044 LONG res;
3045 HKEY hkey;
3046 DWORD num_subkeys, num_values;
3048 if (delete_key)
3050 if ((res = RegDeleteTreeW( hkey_root, key )))
3052 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
3054 return;
3057 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
3059 if ((res = RegDeleteValueW( hkey, value )))
3061 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
3063 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3064 NULL, NULL, NULL, NULL );
3065 RegCloseKey( hkey );
3066 if (!res && !num_subkeys && !num_values)
3068 TRACE("Removing empty key %s\n", debugstr_w(key));
3069 RegDeleteKeyW( hkey_root, key );
3071 return;
3073 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
3077 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3079 MSIPACKAGE *package = param;
3080 LPCWSTR component, name, key_str, root_key_str;
3081 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3082 MSICOMPONENT *comp;
3083 MSIRECORD *uirow;
3084 BOOL delete_key = FALSE;
3085 HKEY hkey_root;
3086 UINT size;
3087 INT root;
3089 ui_progress( package, 2, 0, 0, 0 );
3091 component = MSI_RecordGetString( row, 6 );
3092 comp = get_loaded_component( package, component );
3093 if (!comp)
3094 return ERROR_SUCCESS;
3096 if (!comp->Enabled)
3098 TRACE("component is disabled\n");
3099 return ERROR_SUCCESS;
3102 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3104 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
3105 comp->Action = comp->Installed;
3106 return ERROR_SUCCESS;
3108 comp->Action = INSTALLSTATE_ABSENT;
3110 name = MSI_RecordGetString( row, 4 );
3111 if (MSI_RecordIsNull( row, 5 ) && name )
3113 if (name[0] == '+' && !name[1])
3114 return ERROR_SUCCESS;
3115 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
3117 delete_key = TRUE;
3118 name = NULL;
3122 root = MSI_RecordGetInteger( row, 2 );
3123 key_str = MSI_RecordGetString( row, 3 );
3125 root_key_str = get_root_key( package, root, &hkey_root );
3126 if (!root_key_str)
3127 return ERROR_SUCCESS;
3129 deformat_string( package, key_str, &deformated_key );
3130 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3131 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3132 strcpyW( ui_key_str, root_key_str );
3133 strcatW( ui_key_str, deformated_key );
3135 deformat_string( package, name, &deformated_name );
3137 keypath = get_keypath( package, hkey_root, deformated_key );
3138 msi_free( deformated_key );
3139 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3140 msi_free( keypath );
3142 uirow = MSI_CreateRecord( 2 );
3143 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3144 MSI_RecordSetStringW( uirow, 2, deformated_name );
3146 ui_actiondata( package, szRemoveRegistryValues, uirow );
3147 msiobj_release( &uirow->hdr );
3149 msi_free( ui_key_str );
3150 msi_free( deformated_name );
3151 return ERROR_SUCCESS;
3154 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3156 MSIPACKAGE *package = param;
3157 LPCWSTR component, name, key_str, root_key_str;
3158 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3159 MSICOMPONENT *comp;
3160 MSIRECORD *uirow;
3161 BOOL delete_key = FALSE;
3162 HKEY hkey_root;
3163 UINT size;
3164 INT root;
3166 ui_progress( package, 2, 0, 0, 0 );
3168 component = MSI_RecordGetString( row, 5 );
3169 comp = get_loaded_component( package, component );
3170 if (!comp)
3171 return ERROR_SUCCESS;
3173 if (!comp->Enabled)
3175 TRACE("component is disabled\n");
3176 return ERROR_SUCCESS;
3179 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3181 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3182 comp->Action = comp->Installed;
3183 return ERROR_SUCCESS;
3185 comp->Action = INSTALLSTATE_LOCAL;
3187 if ((name = MSI_RecordGetString( row, 4 )))
3189 if (name[0] == '-' && !name[1])
3191 delete_key = TRUE;
3192 name = NULL;
3196 root = MSI_RecordGetInteger( row, 2 );
3197 key_str = MSI_RecordGetString( row, 3 );
3199 root_key_str = get_root_key( package, root, &hkey_root );
3200 if (!root_key_str)
3201 return ERROR_SUCCESS;
3203 deformat_string( package, key_str, &deformated_key );
3204 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3205 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3206 strcpyW( ui_key_str, root_key_str );
3207 strcatW( ui_key_str, deformated_key );
3209 deformat_string( package, name, &deformated_name );
3211 keypath = get_keypath( package, hkey_root, deformated_key );
3212 msi_free( deformated_key );
3213 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3214 msi_free( keypath );
3216 uirow = MSI_CreateRecord( 2 );
3217 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3218 MSI_RecordSetStringW( uirow, 2, deformated_name );
3220 ui_actiondata( package, szRemoveRegistryValues, uirow );
3221 msiobj_release( &uirow->hdr );
3223 msi_free( ui_key_str );
3224 msi_free( deformated_name );
3225 return ERROR_SUCCESS;
3228 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3230 UINT rc;
3231 MSIQUERY *view;
3232 static const WCHAR registry_query[] =
3233 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3234 '`','R','e','g','i','s','t','r','y','`',0 };
3235 static const WCHAR remove_registry_query[] =
3236 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3237 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
3239 /* increment progress bar each time action data is sent */
3240 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3242 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3243 if (rc == ERROR_SUCCESS)
3245 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3246 msiobj_release( &view->hdr );
3247 if (rc != ERROR_SUCCESS)
3248 return rc;
3251 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3252 if (rc == ERROR_SUCCESS)
3254 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3255 msiobj_release( &view->hdr );
3256 if (rc != ERROR_SUCCESS)
3257 return rc;
3260 return ERROR_SUCCESS;
3263 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3265 package->script->CurrentlyScripting = TRUE;
3267 return ERROR_SUCCESS;
3271 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3273 MSICOMPONENT *comp;
3274 DWORD progress = 0;
3275 DWORD total = 0;
3276 static const WCHAR q1[]=
3277 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3278 '`','R','e','g','i','s','t','r','y','`',0};
3279 UINT rc;
3280 MSIQUERY * view;
3281 MSIFEATURE *feature;
3282 MSIFILE *file;
3284 TRACE("InstallValidate\n");
3286 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
3287 if (rc == ERROR_SUCCESS)
3289 MSI_IterateRecords( view, &progress, NULL, package );
3290 msiobj_release( &view->hdr );
3291 total += progress * REG_PROGRESS_VALUE;
3294 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3295 total += COMPONENT_PROGRESS_VALUE;
3297 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3298 total += file->FileSize;
3300 ui_progress(package,0,total,0,0);
3302 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3304 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3305 debugstr_w(feature->Feature), feature->Installed,
3306 feature->ActionRequest, feature->Action);
3309 return ERROR_SUCCESS;
3312 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3314 MSIPACKAGE* package = param;
3315 LPCWSTR cond = NULL;
3316 LPCWSTR message = NULL;
3317 UINT r;
3319 static const WCHAR title[]=
3320 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3322 cond = MSI_RecordGetString(row,1);
3324 r = MSI_EvaluateConditionW(package,cond);
3325 if (r == MSICONDITION_FALSE)
3327 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3329 LPWSTR deformated;
3330 message = MSI_RecordGetString(row,2);
3331 deformat_string(package,message,&deformated);
3332 MessageBoxW(NULL,deformated,title,MB_OK);
3333 msi_free(deformated);
3336 return ERROR_INSTALL_FAILURE;
3339 return ERROR_SUCCESS;
3342 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3344 UINT rc;
3345 MSIQUERY * view = NULL;
3346 static const WCHAR ExecSeqQuery[] =
3347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3348 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3350 TRACE("Checking launch conditions\n");
3352 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3353 if (rc != ERROR_SUCCESS)
3354 return ERROR_SUCCESS;
3356 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3357 msiobj_release(&view->hdr);
3359 return rc;
3362 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3365 if (!cmp->KeyPath)
3366 return resolve_target_folder( package, cmp->Directory, FALSE, TRUE, NULL );
3368 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3370 MSIRECORD * row = 0;
3371 UINT root,len;
3372 LPWSTR deformated,buffer,deformated_name;
3373 LPCWSTR key,name;
3374 static const WCHAR ExecSeqQuery[] =
3375 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3376 '`','R','e','g','i','s','t','r','y','`',' ',
3377 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3378 ' ','=',' ' ,'\'','%','s','\'',0 };
3379 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3380 static const WCHAR fmt2[]=
3381 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3383 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3384 if (!row)
3385 return NULL;
3387 root = MSI_RecordGetInteger(row,2);
3388 key = MSI_RecordGetString(row, 3);
3389 name = MSI_RecordGetString(row, 4);
3390 deformat_string(package, key , &deformated);
3391 deformat_string(package, name, &deformated_name);
3393 len = strlenW(deformated) + 6;
3394 if (deformated_name)
3395 len+=strlenW(deformated_name);
3397 buffer = msi_alloc( len *sizeof(WCHAR));
3399 if (deformated_name)
3400 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3401 else
3402 sprintfW(buffer,fmt,root,deformated);
3404 msi_free(deformated);
3405 msi_free(deformated_name);
3406 msiobj_release(&row->hdr);
3408 return buffer;
3410 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3412 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3413 return NULL;
3415 else
3417 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
3419 if (file)
3420 return strdupW( file->TargetPath );
3422 return NULL;
3425 static HKEY openSharedDLLsKey(void)
3427 HKEY hkey=0;
3428 static const WCHAR path[] =
3429 {'S','o','f','t','w','a','r','e','\\',
3430 'M','i','c','r','o','s','o','f','t','\\',
3431 'W','i','n','d','o','w','s','\\',
3432 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3433 'S','h','a','r','e','d','D','L','L','s',0};
3435 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3436 return hkey;
3439 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3441 HKEY hkey;
3442 DWORD count=0;
3443 DWORD type;
3444 DWORD sz = sizeof(count);
3445 DWORD rc;
3447 hkey = openSharedDLLsKey();
3448 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3449 if (rc != ERROR_SUCCESS)
3450 count = 0;
3451 RegCloseKey(hkey);
3452 return count;
3455 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3457 HKEY hkey;
3459 hkey = openSharedDLLsKey();
3460 if (count > 0)
3461 msi_reg_set_val_dword( hkey, path, count );
3462 else
3463 RegDeleteValueW(hkey,path);
3464 RegCloseKey(hkey);
3465 return count;
3468 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3470 MSIFEATURE *feature;
3471 INT count = 0;
3472 BOOL write = FALSE;
3474 /* only refcount DLLs */
3475 if (comp->KeyPath == NULL ||
3476 comp->assembly ||
3477 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3478 comp->Attributes & msidbComponentAttributesODBCDataSource)
3479 write = FALSE;
3480 else
3482 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3483 write = (count > 0);
3485 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3486 write = TRUE;
3489 /* increment counts */
3490 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3492 ComponentList *cl;
3494 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3495 continue;
3497 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3499 if ( cl->component == comp )
3500 count++;
3504 /* decrement counts */
3505 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3507 ComponentList *cl;
3509 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3510 continue;
3512 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3514 if ( cl->component == comp )
3515 count--;
3519 /* ref count all the files in the component */
3520 if (write)
3522 MSIFILE *file;
3524 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3526 if (file->Component == comp)
3527 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3531 /* add a count for permanent */
3532 if (comp->Attributes & msidbComponentAttributesPermanent)
3533 count ++;
3535 comp->RefCount = count;
3537 if (write)
3538 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3541 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3543 WCHAR squished_pc[GUID_SIZE];
3544 WCHAR squished_cc[GUID_SIZE];
3545 UINT rc;
3546 MSICOMPONENT *comp;
3547 HKEY hkey;
3549 TRACE("\n");
3551 squash_guid(package->ProductCode,squished_pc);
3552 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3554 msi_set_sourcedir_props(package, FALSE);
3556 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3558 MSIRECORD * uirow;
3560 ui_progress(package,2,0,0,0);
3561 if (!comp->ComponentId)
3562 continue;
3564 squash_guid(comp->ComponentId,squished_cc);
3566 msi_free(comp->FullKeypath);
3567 if (comp->assembly)
3569 const WCHAR prefixW[] = {'<','\\',0};
3570 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3572 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3573 if (comp->FullKeypath)
3575 strcpyW( comp->FullKeypath, prefixW );
3576 strcatW( comp->FullKeypath, comp->assembly->display_name );
3579 else comp->FullKeypath = resolve_keypath( package, comp );
3581 ACTION_RefCountComponent( package, comp );
3583 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3584 debugstr_w(comp->Component),
3585 debugstr_w(squished_cc),
3586 debugstr_w(comp->FullKeypath),
3587 comp->RefCount,
3588 comp->ActionRequest);
3590 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3591 comp->ActionRequest == INSTALLSTATE_SOURCE)
3593 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3594 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3595 else
3596 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3598 if (rc != ERROR_SUCCESS)
3599 continue;
3601 if (comp->Attributes & msidbComponentAttributesPermanent)
3603 static const WCHAR szPermKey[] =
3604 { '0','0','0','0','0','0','0','0','0','0','0','0',
3605 '0','0','0','0','0','0','0','0','0','0','0','0',
3606 '0','0','0','0','0','0','0','0',0 };
3608 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3611 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3612 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3613 else
3615 MSIFILE *file;
3616 MSIRECORD *row;
3617 LPWSTR ptr, ptr2;
3618 WCHAR source[MAX_PATH];
3619 WCHAR base[MAX_PATH];
3620 LPWSTR sourcepath;
3622 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3623 static const WCHAR query[] = {
3624 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3625 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3626 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3627 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3628 '`','D','i','s','k','I','d','`',0};
3630 if (!comp->KeyPath || !(file = get_loaded_file(package, comp->KeyPath)))
3631 continue;
3633 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3634 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3635 ptr2 = strrchrW(source, '\\') + 1;
3636 msiobj_release(&row->hdr);
3638 lstrcpyW(base, package->PackagePath);
3639 ptr = strrchrW(base, '\\');
3640 *(ptr + 1) = '\0';
3642 sourcepath = resolve_file_source(package, file);
3643 ptr = sourcepath + lstrlenW(base);
3644 lstrcpyW(ptr2, ptr);
3645 msi_free(sourcepath);
3647 msi_reg_set_val_str(hkey, squished_pc, source);
3649 RegCloseKey(hkey);
3651 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3653 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3654 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3655 else
3656 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3658 comp->Action = comp->ActionRequest;
3660 /* UI stuff */
3661 uirow = MSI_CreateRecord(3);
3662 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3663 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3664 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3665 ui_actiondata(package,szProcessComponents,uirow);
3666 msiobj_release( &uirow->hdr );
3669 return ERROR_SUCCESS;
3672 typedef struct {
3673 CLSID clsid;
3674 LPWSTR source;
3676 LPWSTR path;
3677 ITypeLib *ptLib;
3678 } typelib_struct;
3680 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3681 LPWSTR lpszName, LONG_PTR lParam)
3683 TLIBATTR *attr;
3684 typelib_struct *tl_struct = (typelib_struct*) lParam;
3685 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3686 int sz;
3687 HRESULT res;
3689 if (!IS_INTRESOURCE(lpszName))
3691 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3692 return TRUE;
3695 sz = strlenW(tl_struct->source)+4;
3696 sz *= sizeof(WCHAR);
3698 if ((INT_PTR)lpszName == 1)
3699 tl_struct->path = strdupW(tl_struct->source);
3700 else
3702 tl_struct->path = msi_alloc(sz);
3703 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3706 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3707 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3708 if (FAILED(res))
3710 msi_free(tl_struct->path);
3711 tl_struct->path = NULL;
3713 return TRUE;
3716 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3717 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3719 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3720 return FALSE;
3723 msi_free(tl_struct->path);
3724 tl_struct->path = NULL;
3726 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3727 ITypeLib_Release(tl_struct->ptLib);
3729 return TRUE;
3732 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3734 MSIPACKAGE* package = param;
3735 LPCWSTR component;
3736 MSICOMPONENT *comp;
3737 MSIFILE *file;
3738 typelib_struct tl_struct;
3739 ITypeLib *tlib;
3740 HMODULE module;
3741 HRESULT hr;
3743 component = MSI_RecordGetString(row,3);
3744 comp = get_loaded_component(package,component);
3745 if (!comp)
3746 return ERROR_SUCCESS;
3748 if (!comp->Enabled)
3750 TRACE("component is disabled\n");
3751 return ERROR_SUCCESS;
3754 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3756 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3757 comp->Action = comp->Installed;
3758 return ERROR_SUCCESS;
3760 comp->Action = INSTALLSTATE_LOCAL;
3762 if (!comp->KeyPath || !(file = get_loaded_file( package, comp->KeyPath )))
3764 TRACE("component has no key path\n");
3765 return ERROR_SUCCESS;
3767 ui_actiondata( package, szRegisterTypeLibraries, row );
3769 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3770 if (module)
3772 LPCWSTR guid;
3773 guid = MSI_RecordGetString(row,1);
3774 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3775 tl_struct.source = strdupW( file->TargetPath );
3776 tl_struct.path = NULL;
3778 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3779 (LONG_PTR)&tl_struct);
3781 if (tl_struct.path)
3783 LPWSTR help = NULL;
3784 LPCWSTR helpid;
3785 HRESULT res;
3787 helpid = MSI_RecordGetString(row,6);
3789 if (helpid) help = resolve_target_folder( package, helpid, FALSE, TRUE, NULL );
3790 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3791 msi_free(help);
3793 if (FAILED(res))
3794 ERR("Failed to register type library %s\n",
3795 debugstr_w(tl_struct.path));
3796 else
3797 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3799 ITypeLib_Release(tl_struct.ptLib);
3800 msi_free(tl_struct.path);
3802 else
3803 ERR("Failed to load type library %s\n",
3804 debugstr_w(tl_struct.source));
3806 FreeLibrary(module);
3807 msi_free(tl_struct.source);
3809 else
3811 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3812 if (FAILED(hr))
3814 ERR("Failed to load type library: %08x\n", hr);
3815 return ERROR_INSTALL_FAILURE;
3818 ITypeLib_Release(tlib);
3821 return ERROR_SUCCESS;
3824 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3827 * OK this is a bit confusing.. I am given a _Component key and I believe
3828 * that the file that is being registered as a type library is the "key file
3829 * of that component" which I interpret to mean "The file in the KeyPath of
3830 * that component".
3832 UINT rc;
3833 MSIQUERY * view;
3834 static const WCHAR Query[] =
3835 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3836 '`','T','y','p','e','L','i','b','`',0};
3838 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3839 if (rc != ERROR_SUCCESS)
3840 return ERROR_SUCCESS;
3842 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3843 msiobj_release(&view->hdr);
3844 return rc;
3847 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3849 MSIPACKAGE *package = param;
3850 LPCWSTR component, guid;
3851 MSICOMPONENT *comp;
3852 GUID libid;
3853 UINT version;
3854 LCID language;
3855 SYSKIND syskind;
3856 HRESULT hr;
3858 component = MSI_RecordGetString( row, 3 );
3859 comp = get_loaded_component( package, component );
3860 if (!comp)
3861 return ERROR_SUCCESS;
3863 if (!comp->Enabled)
3865 TRACE("component is disabled\n");
3866 return ERROR_SUCCESS;
3869 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3871 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3872 comp->Action = comp->Installed;
3873 return ERROR_SUCCESS;
3875 comp->Action = INSTALLSTATE_ABSENT;
3877 ui_actiondata( package, szUnregisterTypeLibraries, row );
3879 guid = MSI_RecordGetString( row, 1 );
3880 CLSIDFromString( (LPCWSTR)guid, &libid );
3881 version = MSI_RecordGetInteger( row, 4 );
3882 language = MSI_RecordGetInteger( row, 2 );
3884 #ifdef _WIN64
3885 syskind = SYS_WIN64;
3886 #else
3887 syskind = SYS_WIN32;
3888 #endif
3890 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3891 if (FAILED(hr))
3893 WARN("Failed to unregister typelib: %08x\n", hr);
3896 return ERROR_SUCCESS;
3899 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3901 UINT rc;
3902 MSIQUERY *view;
3903 static const WCHAR query[] =
3904 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3905 '`','T','y','p','e','L','i','b','`',0};
3907 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3908 if (rc != ERROR_SUCCESS)
3909 return ERROR_SUCCESS;
3911 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3912 msiobj_release( &view->hdr );
3913 return rc;
3916 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3918 static const WCHAR szlnk[] = {'.','l','n','k',0};
3919 LPCWSTR directory, extension;
3920 LPWSTR link_folder, link_file, filename;
3922 directory = MSI_RecordGetString( row, 2 );
3923 link_folder = resolve_target_folder( package, directory, FALSE, TRUE, NULL );
3925 /* may be needed because of a bug somewhere else */
3926 create_full_pathW( link_folder );
3928 filename = msi_dup_record_field( row, 3 );
3929 reduce_to_longfilename( filename );
3931 extension = strchrW( filename, '.' );
3932 if (!extension || strcmpiW( extension, szlnk ))
3934 int len = strlenW( filename );
3935 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3936 memcpy( filename + len, szlnk, sizeof(szlnk) );
3938 link_file = build_directory_name( 2, link_folder, filename );
3939 msi_free( link_folder );
3940 msi_free( filename );
3942 return link_file;
3945 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3947 MSIPACKAGE *package = param;
3948 LPWSTR link_file, deformated, path;
3949 LPCWSTR component, target;
3950 MSICOMPONENT *comp;
3951 IShellLinkW *sl = NULL;
3952 IPersistFile *pf = NULL;
3953 HRESULT res;
3955 component = MSI_RecordGetString(row, 4);
3956 comp = get_loaded_component(package, component);
3957 if (!comp)
3958 return ERROR_SUCCESS;
3960 if (!comp->Enabled)
3962 TRACE("component is disabled\n");
3963 return ERROR_SUCCESS;
3966 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3968 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3969 comp->Action = comp->Installed;
3970 return ERROR_SUCCESS;
3972 comp->Action = INSTALLSTATE_LOCAL;
3974 ui_actiondata(package,szCreateShortcuts,row);
3976 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3977 &IID_IShellLinkW, (LPVOID *) &sl );
3979 if (FAILED( res ))
3981 ERR("CLSID_ShellLink not available\n");
3982 goto err;
3985 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3986 if (FAILED( res ))
3988 ERR("QueryInterface(IID_IPersistFile) failed\n");
3989 goto err;
3992 target = MSI_RecordGetString(row, 5);
3993 if (strchrW(target, '['))
3995 deformat_string(package, target, &deformated);
3996 IShellLinkW_SetPath(sl,deformated);
3997 msi_free(deformated);
3999 else
4001 FIXME("poorly handled shortcut format, advertised shortcut\n");
4002 IShellLinkW_SetPath(sl,comp->FullKeypath);
4005 if (!MSI_RecordIsNull(row,6))
4007 LPCWSTR arguments = MSI_RecordGetString(row, 6);
4008 deformat_string(package, arguments, &deformated);
4009 IShellLinkW_SetArguments(sl,deformated);
4010 msi_free(deformated);
4013 if (!MSI_RecordIsNull(row,7))
4015 LPCWSTR description = MSI_RecordGetString(row, 7);
4016 IShellLinkW_SetDescription(sl, description);
4019 if (!MSI_RecordIsNull(row,8))
4020 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4022 if (!MSI_RecordIsNull(row,9))
4024 INT index;
4025 LPCWSTR icon = MSI_RecordGetString(row, 9);
4027 path = build_icon_path(package, icon);
4028 index = MSI_RecordGetInteger(row,10);
4030 /* no value means 0 */
4031 if (index == MSI_NULL_INTEGER)
4032 index = 0;
4034 IShellLinkW_SetIconLocation(sl, path, index);
4035 msi_free(path);
4038 if (!MSI_RecordIsNull(row,11))
4039 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4041 if (!MSI_RecordIsNull(row,12))
4043 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
4044 path = resolve_target_folder( package, wkdir, FALSE, TRUE, NULL );
4045 if (path)
4046 IShellLinkW_SetWorkingDirectory(sl, path);
4047 msi_free(path);
4050 link_file = get_link_file(package, row);
4052 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4053 IPersistFile_Save(pf, link_file, FALSE);
4055 msi_free(link_file);
4057 err:
4058 if (pf)
4059 IPersistFile_Release( pf );
4060 if (sl)
4061 IShellLinkW_Release( sl );
4063 return ERROR_SUCCESS;
4066 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4068 UINT rc;
4069 HRESULT res;
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 res = CoInitialize( NULL );
4081 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4082 msiobj_release(&view->hdr);
4084 if (SUCCEEDED(res))
4085 CoUninitialize();
4087 return rc;
4090 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4092 MSIPACKAGE *package = param;
4093 LPWSTR link_file;
4094 LPCWSTR component;
4095 MSICOMPONENT *comp;
4097 component = MSI_RecordGetString( row, 4 );
4098 comp = get_loaded_component( package, component );
4099 if (!comp)
4100 return ERROR_SUCCESS;
4102 if (!comp->Enabled)
4104 TRACE("component is disabled\n");
4105 return ERROR_SUCCESS;
4108 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4110 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4111 comp->Action = comp->Installed;
4112 return ERROR_SUCCESS;
4114 comp->Action = INSTALLSTATE_ABSENT;
4116 ui_actiondata( package, szRemoveShortcuts, row );
4118 link_file = get_link_file( package, row );
4120 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4121 if (!DeleteFileW( link_file ))
4123 WARN("Failed to remove shortcut file %u\n", GetLastError());
4125 msi_free( link_file );
4127 return ERROR_SUCCESS;
4130 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4132 UINT rc;
4133 MSIQUERY *view;
4134 static const WCHAR query[] =
4135 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4136 '`','S','h','o','r','t','c','u','t','`',0};
4138 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4139 if (rc != ERROR_SUCCESS)
4140 return ERROR_SUCCESS;
4142 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4143 msiobj_release( &view->hdr );
4145 return rc;
4148 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4150 MSIPACKAGE* package = param;
4151 HANDLE the_file;
4152 LPWSTR FilePath;
4153 LPCWSTR FileName;
4154 CHAR buffer[1024];
4155 DWORD sz;
4156 UINT rc;
4158 FileName = MSI_RecordGetString(row,1);
4159 if (!FileName)
4161 ERR("Unable to get FileName\n");
4162 return ERROR_SUCCESS;
4165 FilePath = build_icon_path(package,FileName);
4167 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4169 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4170 FILE_ATTRIBUTE_NORMAL, NULL);
4172 if (the_file == INVALID_HANDLE_VALUE)
4174 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4175 msi_free(FilePath);
4176 return ERROR_SUCCESS;
4181 DWORD write;
4182 sz = 1024;
4183 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4184 if (rc != ERROR_SUCCESS)
4186 ERR("Failed to get stream\n");
4187 CloseHandle(the_file);
4188 DeleteFileW(FilePath);
4189 break;
4191 WriteFile(the_file,buffer,sz,&write,NULL);
4192 } while (sz == 1024);
4194 msi_free(FilePath);
4195 CloseHandle(the_file);
4197 return ERROR_SUCCESS;
4200 static UINT msi_publish_icons(MSIPACKAGE *package)
4202 UINT r;
4203 MSIQUERY *view;
4205 static const WCHAR query[]= {
4206 'S','E','L','E','C','T',' ','*',' ',
4207 'F','R','O','M',' ','`','I','c','o','n','`',0};
4209 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4210 if (r == ERROR_SUCCESS)
4212 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4213 msiobj_release(&view->hdr);
4216 return ERROR_SUCCESS;
4219 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4221 UINT r;
4222 HKEY source;
4223 LPWSTR buffer;
4224 MSIMEDIADISK *disk;
4225 MSISOURCELISTINFO *info;
4227 r = RegCreateKeyW(hkey, szSourceList, &source);
4228 if (r != ERROR_SUCCESS)
4229 return r;
4231 RegCloseKey(source);
4233 buffer = strrchrW(package->PackagePath, '\\') + 1;
4234 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4235 package->Context, MSICODE_PRODUCT,
4236 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4237 if (r != ERROR_SUCCESS)
4238 return r;
4240 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4241 package->Context, MSICODE_PRODUCT,
4242 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4243 if (r != ERROR_SUCCESS)
4244 return r;
4246 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4247 package->Context, MSICODE_PRODUCT,
4248 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4249 if (r != ERROR_SUCCESS)
4250 return r;
4252 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4254 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4255 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4256 info->options, info->value);
4257 else
4258 MsiSourceListSetInfoW(package->ProductCode, NULL,
4259 info->context, info->options,
4260 info->property, info->value);
4263 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4265 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4266 disk->context, disk->options,
4267 disk->disk_id, disk->volume_label, disk->disk_prompt);
4270 return ERROR_SUCCESS;
4273 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4275 MSIHANDLE hdb, suminfo;
4276 WCHAR guids[MAX_PATH];
4277 WCHAR packcode[SQUISH_GUID_SIZE];
4278 LPWSTR buffer;
4279 LPWSTR ptr;
4280 DWORD langid;
4281 DWORD size;
4282 UINT r;
4284 static const WCHAR szProductLanguage[] =
4285 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4286 static const WCHAR szARPProductIcon[] =
4287 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4288 static const WCHAR szProductVersion[] =
4289 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4290 static const WCHAR szAssignment[] =
4291 {'A','s','s','i','g','n','m','e','n','t',0};
4292 static const WCHAR szAdvertiseFlags[] =
4293 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4294 static const WCHAR szClients[] =
4295 {'C','l','i','e','n','t','s',0};
4296 static const WCHAR szColon[] = {':',0};
4298 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4299 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4300 msi_free(buffer);
4302 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4303 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4305 /* FIXME */
4306 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4308 buffer = msi_dup_property(package->db, szARPProductIcon);
4309 if (buffer)
4311 LPWSTR path = build_icon_path(package,buffer);
4312 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4313 msi_free(path);
4314 msi_free(buffer);
4317 buffer = msi_dup_property(package->db, szProductVersion);
4318 if (buffer)
4320 DWORD verdword = msi_version_str_to_dword(buffer);
4321 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4322 msi_free(buffer);
4325 msi_reg_set_val_dword(hkey, szAssignment, 0);
4326 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4327 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4328 msi_reg_set_val_str(hkey, szClients, szColon);
4330 hdb = alloc_msihandle(&package->db->hdr);
4331 if (!hdb)
4332 return ERROR_NOT_ENOUGH_MEMORY;
4334 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4335 MsiCloseHandle(hdb);
4336 if (r != ERROR_SUCCESS)
4337 goto done;
4339 size = MAX_PATH;
4340 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4341 NULL, guids, &size);
4342 if (r != ERROR_SUCCESS)
4343 goto done;
4345 ptr = strchrW(guids, ';');
4346 if (ptr) *ptr = 0;
4347 squash_guid(guids, packcode);
4348 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4350 done:
4351 MsiCloseHandle(suminfo);
4352 return ERROR_SUCCESS;
4355 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4357 UINT r;
4358 HKEY hkey;
4359 LPWSTR upgrade;
4360 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4362 upgrade = msi_dup_property(package->db, szUpgradeCode);
4363 if (!upgrade)
4364 return ERROR_SUCCESS;
4366 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4368 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4369 if (r != ERROR_SUCCESS)
4370 goto done;
4372 else
4374 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4375 if (r != ERROR_SUCCESS)
4376 goto done;
4379 squash_guid(package->ProductCode, squashed_pc);
4380 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4382 RegCloseKey(hkey);
4384 done:
4385 msi_free(upgrade);
4386 return r;
4389 static BOOL msi_check_publish(MSIPACKAGE *package)
4391 MSIFEATURE *feature;
4393 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4395 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4396 return TRUE;
4399 return FALSE;
4402 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4404 MSIFEATURE *feature;
4406 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4408 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4409 return FALSE;
4412 return TRUE;
4415 static UINT msi_publish_patches( MSIPACKAGE *package )
4417 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4418 WCHAR patch_squashed[GUID_SIZE];
4419 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4420 LONG res;
4421 MSIPATCHINFO *patch;
4422 UINT r;
4423 WCHAR *p, *all_patches = NULL;
4424 DWORD len = 0;
4426 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4427 if (r != ERROR_SUCCESS)
4428 return ERROR_FUNCTION_FAILED;
4430 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4431 if (res != ERROR_SUCCESS)
4433 r = ERROR_FUNCTION_FAILED;
4434 goto done;
4437 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4438 if (r != ERROR_SUCCESS)
4439 goto done;
4441 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4443 squash_guid( patch->patchcode, patch_squashed );
4444 len += strlenW( patch_squashed ) + 1;
4447 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4448 if (!all_patches)
4449 goto done;
4451 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4453 HKEY patch_key;
4455 squash_guid( patch->patchcode, p );
4456 p += strlenW( p ) + 1;
4458 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4459 (const BYTE *)patch->transforms,
4460 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4461 if (res != ERROR_SUCCESS)
4462 goto done;
4464 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4465 if (r != ERROR_SUCCESS)
4466 goto done;
4468 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4469 (const BYTE *)patch->localfile,
4470 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4471 RegCloseKey( patch_key );
4472 if (res != ERROR_SUCCESS)
4473 goto done;
4475 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4476 if (res != ERROR_SUCCESS)
4477 goto done;
4479 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4480 RegCloseKey( patch_key );
4481 if (res != ERROR_SUCCESS)
4482 goto done;
4485 all_patches[len] = 0;
4486 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4487 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4488 if (res != ERROR_SUCCESS)
4489 goto done;
4491 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4492 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4493 if (res != ERROR_SUCCESS)
4494 r = ERROR_FUNCTION_FAILED;
4496 done:
4497 RegCloseKey( product_patches_key );
4498 RegCloseKey( patches_key );
4499 RegCloseKey( product_key );
4500 msi_free( all_patches );
4501 return r;
4505 * 99% of the work done here is only done for
4506 * advertised installs. However this is where the
4507 * Icon table is processed and written out
4508 * so that is what I am going to do here.
4510 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4512 UINT rc;
4513 HKEY hukey = NULL, hudkey = NULL;
4514 MSIRECORD *uirow;
4516 if (!list_empty(&package->patches))
4518 rc = msi_publish_patches(package);
4519 if (rc != ERROR_SUCCESS)
4520 goto end;
4523 /* FIXME: also need to publish if the product is in advertise mode */
4524 if (!msi_check_publish(package))
4525 return ERROR_SUCCESS;
4527 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4528 &hukey, TRUE);
4529 if (rc != ERROR_SUCCESS)
4530 goto end;
4532 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4533 NULL, &hudkey, TRUE);
4534 if (rc != ERROR_SUCCESS)
4535 goto end;
4537 rc = msi_publish_upgrade_code(package);
4538 if (rc != ERROR_SUCCESS)
4539 goto end;
4541 rc = msi_publish_product_properties(package, hukey);
4542 if (rc != ERROR_SUCCESS)
4543 goto end;
4545 rc = msi_publish_sourcelist(package, hukey);
4546 if (rc != ERROR_SUCCESS)
4547 goto end;
4549 rc = msi_publish_icons(package);
4551 end:
4552 uirow = MSI_CreateRecord( 1 );
4553 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4554 ui_actiondata( package, szPublishProduct, uirow );
4555 msiobj_release( &uirow->hdr );
4557 RegCloseKey(hukey);
4558 RegCloseKey(hudkey);
4560 return rc;
4563 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4565 WCHAR *filename, *ptr, *folder, *ret;
4566 const WCHAR *dirprop;
4568 filename = msi_dup_record_field( row, 2 );
4569 if (filename && (ptr = strchrW( filename, '|' )))
4570 ptr++;
4571 else
4572 ptr = filename;
4574 dirprop = MSI_RecordGetString( row, 3 );
4575 if (dirprop)
4577 folder = resolve_target_folder( package, dirprop, FALSE, TRUE, NULL );
4578 if (!folder)
4579 folder = msi_dup_property( package->db, dirprop );
4581 else
4582 folder = msi_dup_property( package->db, szWindowsFolder );
4584 if (!folder)
4586 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4587 msi_free( filename );
4588 return NULL;
4591 ret = build_directory_name( 2, folder, ptr );
4593 msi_free( filename );
4594 msi_free( folder );
4595 return ret;
4598 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4600 MSIPACKAGE *package = param;
4601 LPCWSTR component, section, key, value, identifier;
4602 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4603 MSIRECORD * uirow;
4604 INT action;
4605 MSICOMPONENT *comp;
4607 component = MSI_RecordGetString(row, 8);
4608 comp = get_loaded_component(package,component);
4609 if (!comp)
4610 return ERROR_SUCCESS;
4612 if (!comp->Enabled)
4614 TRACE("component is disabled\n");
4615 return ERROR_SUCCESS;
4618 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4620 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4621 comp->Action = comp->Installed;
4622 return ERROR_SUCCESS;
4624 comp->Action = INSTALLSTATE_LOCAL;
4626 identifier = MSI_RecordGetString(row,1);
4627 section = MSI_RecordGetString(row,4);
4628 key = MSI_RecordGetString(row,5);
4629 value = MSI_RecordGetString(row,6);
4630 action = MSI_RecordGetInteger(row,7);
4632 deformat_string(package,section,&deformated_section);
4633 deformat_string(package,key,&deformated_key);
4634 deformat_string(package,value,&deformated_value);
4636 fullname = get_ini_file_name(package, row);
4638 if (action == 0)
4640 TRACE("Adding value %s to section %s in %s\n",
4641 debugstr_w(deformated_key), debugstr_w(deformated_section),
4642 debugstr_w(fullname));
4643 WritePrivateProfileStringW(deformated_section, deformated_key,
4644 deformated_value, fullname);
4646 else if (action == 1)
4648 WCHAR returned[10];
4649 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4650 returned, 10, fullname);
4651 if (returned[0] == 0)
4653 TRACE("Adding value %s to section %s in %s\n",
4654 debugstr_w(deformated_key), debugstr_w(deformated_section),
4655 debugstr_w(fullname));
4657 WritePrivateProfileStringW(deformated_section, deformated_key,
4658 deformated_value, fullname);
4661 else if (action == 3)
4662 FIXME("Append to existing section not yet implemented\n");
4664 uirow = MSI_CreateRecord(4);
4665 MSI_RecordSetStringW(uirow,1,identifier);
4666 MSI_RecordSetStringW(uirow,2,deformated_section);
4667 MSI_RecordSetStringW(uirow,3,deformated_key);
4668 MSI_RecordSetStringW(uirow,4,deformated_value);
4669 ui_actiondata(package,szWriteIniValues,uirow);
4670 msiobj_release( &uirow->hdr );
4672 msi_free(fullname);
4673 msi_free(deformated_key);
4674 msi_free(deformated_value);
4675 msi_free(deformated_section);
4676 return ERROR_SUCCESS;
4679 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4681 UINT rc;
4682 MSIQUERY * view;
4683 static const WCHAR ExecSeqQuery[] =
4684 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4685 '`','I','n','i','F','i','l','e','`',0};
4687 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4688 if (rc != ERROR_SUCCESS)
4690 TRACE("no IniFile table\n");
4691 return ERROR_SUCCESS;
4694 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4695 msiobj_release(&view->hdr);
4696 return rc;
4699 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4701 MSIPACKAGE *package = param;
4702 LPCWSTR component, section, key, value, identifier;
4703 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4704 MSICOMPONENT *comp;
4705 MSIRECORD *uirow;
4706 INT action;
4708 component = MSI_RecordGetString( row, 8 );
4709 comp = get_loaded_component( package, component );
4710 if (!comp)
4711 return ERROR_SUCCESS;
4713 if (!comp->Enabled)
4715 TRACE("component is disabled\n");
4716 return ERROR_SUCCESS;
4719 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4721 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4722 comp->Action = comp->Installed;
4723 return ERROR_SUCCESS;
4725 comp->Action = INSTALLSTATE_ABSENT;
4727 identifier = MSI_RecordGetString( row, 1 );
4728 section = MSI_RecordGetString( row, 4 );
4729 key = MSI_RecordGetString( row, 5 );
4730 value = MSI_RecordGetString( row, 6 );
4731 action = MSI_RecordGetInteger( row, 7 );
4733 deformat_string( package, section, &deformated_section );
4734 deformat_string( package, key, &deformated_key );
4735 deformat_string( package, value, &deformated_value );
4737 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4739 filename = get_ini_file_name( package, row );
4741 TRACE("Removing key %s from section %s in %s\n",
4742 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4744 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4746 WARN("Unable to remove key %u\n", GetLastError());
4748 msi_free( filename );
4750 else
4751 FIXME("Unsupported action %d\n", action);
4754 uirow = MSI_CreateRecord( 4 );
4755 MSI_RecordSetStringW( uirow, 1, identifier );
4756 MSI_RecordSetStringW( uirow, 2, deformated_section );
4757 MSI_RecordSetStringW( uirow, 3, deformated_key );
4758 MSI_RecordSetStringW( uirow, 4, deformated_value );
4759 ui_actiondata( package, szRemoveIniValues, uirow );
4760 msiobj_release( &uirow->hdr );
4762 msi_free( deformated_key );
4763 msi_free( deformated_value );
4764 msi_free( deformated_section );
4765 return ERROR_SUCCESS;
4768 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4770 MSIPACKAGE *package = param;
4771 LPCWSTR component, section, key, value, identifier;
4772 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4773 MSICOMPONENT *comp;
4774 MSIRECORD *uirow;
4775 INT action;
4777 component = MSI_RecordGetString( row, 8 );
4778 comp = get_loaded_component( package, component );
4779 if (!comp)
4780 return ERROR_SUCCESS;
4782 if (!comp->Enabled)
4784 TRACE("component is disabled\n");
4785 return ERROR_SUCCESS;
4788 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4790 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4791 comp->Action = comp->Installed;
4792 return ERROR_SUCCESS;
4794 comp->Action = INSTALLSTATE_LOCAL;
4796 identifier = MSI_RecordGetString( row, 1 );
4797 section = MSI_RecordGetString( row, 4 );
4798 key = MSI_RecordGetString( row, 5 );
4799 value = MSI_RecordGetString( row, 6 );
4800 action = MSI_RecordGetInteger( row, 7 );
4802 deformat_string( package, section, &deformated_section );
4803 deformat_string( package, key, &deformated_key );
4804 deformat_string( package, value, &deformated_value );
4806 if (action == msidbIniFileActionRemoveLine)
4808 filename = get_ini_file_name( package, row );
4810 TRACE("Removing key %s from section %s in %s\n",
4811 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4813 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4815 WARN("Unable to remove key %u\n", GetLastError());
4817 msi_free( filename );
4819 else
4820 FIXME("Unsupported action %d\n", action);
4822 uirow = MSI_CreateRecord( 4 );
4823 MSI_RecordSetStringW( uirow, 1, identifier );
4824 MSI_RecordSetStringW( uirow, 2, deformated_section );
4825 MSI_RecordSetStringW( uirow, 3, deformated_key );
4826 MSI_RecordSetStringW( uirow, 4, deformated_value );
4827 ui_actiondata( package, szRemoveIniValues, uirow );
4828 msiobj_release( &uirow->hdr );
4830 msi_free( deformated_key );
4831 msi_free( deformated_value );
4832 msi_free( deformated_section );
4833 return ERROR_SUCCESS;
4836 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4838 UINT rc;
4839 MSIQUERY *view;
4840 static const WCHAR query[] =
4841 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4842 '`','I','n','i','F','i','l','e','`',0};
4843 static const WCHAR remove_query[] =
4844 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4845 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4847 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4848 if (rc == ERROR_SUCCESS)
4850 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4851 msiobj_release( &view->hdr );
4852 if (rc != ERROR_SUCCESS)
4853 return rc;
4856 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4857 if (rc == ERROR_SUCCESS)
4859 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4860 msiobj_release( &view->hdr );
4861 if (rc != ERROR_SUCCESS)
4862 return rc;
4865 return ERROR_SUCCESS;
4868 static void register_dll( const WCHAR *dll, BOOL unregister )
4870 HMODULE hmod;
4872 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4873 if (hmod)
4875 HRESULT (WINAPI *func_ptr)( void );
4876 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4878 func_ptr = (void *)GetProcAddress( hmod, func );
4879 if (func_ptr)
4881 HRESULT hr = func_ptr();
4882 if (FAILED( hr ))
4883 WARN("failed to register dll 0x%08x\n", hr);
4885 else
4886 WARN("entry point %s not found\n", func);
4887 FreeLibrary( hmod );
4888 return;
4890 WARN("failed to load library %u\n", GetLastError());
4893 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4895 MSIPACKAGE *package = param;
4896 LPCWSTR filename;
4897 MSIFILE *file;
4898 MSIRECORD *uirow;
4900 filename = MSI_RecordGetString(row,1);
4901 file = get_loaded_file( package, filename );
4903 if (!file)
4905 ERR("Unable to find file id %s\n",debugstr_w(filename));
4906 return ERROR_SUCCESS;
4909 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4911 register_dll( file->TargetPath, FALSE );
4913 uirow = MSI_CreateRecord( 2 );
4914 MSI_RecordSetStringW( uirow, 1, filename );
4915 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4916 ui_actiondata( package, szSelfRegModules, uirow );
4917 msiobj_release( &uirow->hdr );
4919 return ERROR_SUCCESS;
4922 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4924 UINT rc;
4925 MSIQUERY * view;
4926 static const WCHAR ExecSeqQuery[] =
4927 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4928 '`','S','e','l','f','R','e','g','`',0};
4930 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4931 if (rc != ERROR_SUCCESS)
4933 TRACE("no SelfReg table\n");
4934 return ERROR_SUCCESS;
4937 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4938 msiobj_release(&view->hdr);
4940 return ERROR_SUCCESS;
4943 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4945 MSIPACKAGE *package = param;
4946 LPCWSTR filename;
4947 MSIFILE *file;
4948 MSIRECORD *uirow;
4950 filename = MSI_RecordGetString( row, 1 );
4951 file = get_loaded_file( package, filename );
4953 if (!file)
4955 ERR("Unable to find file id %s\n", debugstr_w(filename));
4956 return ERROR_SUCCESS;
4959 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4961 register_dll( file->TargetPath, TRUE );
4963 uirow = MSI_CreateRecord( 2 );
4964 MSI_RecordSetStringW( uirow, 1, filename );
4965 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4966 ui_actiondata( package, szSelfUnregModules, uirow );
4967 msiobj_release( &uirow->hdr );
4969 return ERROR_SUCCESS;
4972 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4974 UINT rc;
4975 MSIQUERY *view;
4976 static const WCHAR query[] =
4977 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4978 '`','S','e','l','f','R','e','g','`',0};
4980 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4981 if (rc != ERROR_SUCCESS)
4983 TRACE("no SelfReg table\n");
4984 return ERROR_SUCCESS;
4987 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4988 msiobj_release( &view->hdr );
4990 return ERROR_SUCCESS;
4993 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4995 MSIFEATURE *feature;
4996 UINT rc;
4997 HKEY hkey = NULL, userdata = NULL;
4999 if (!msi_check_publish(package))
5000 return ERROR_SUCCESS;
5002 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5003 &hkey, TRUE);
5004 if (rc != ERROR_SUCCESS)
5005 goto end;
5007 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5008 &userdata, TRUE);
5009 if (rc != ERROR_SUCCESS)
5010 goto end;
5012 /* here the guids are base 85 encoded */
5013 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5015 ComponentList *cl;
5016 LPWSTR data = NULL;
5017 GUID clsid;
5018 INT size;
5019 BOOL absent = FALSE;
5020 MSIRECORD *uirow;
5022 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
5023 feature->ActionRequest != INSTALLSTATE_SOURCE &&
5024 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
5026 size = 1;
5027 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5029 size += 21;
5031 if (feature->Feature_Parent)
5032 size += strlenW( feature->Feature_Parent )+2;
5034 data = msi_alloc(size * sizeof(WCHAR));
5036 data[0] = 0;
5037 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5039 MSICOMPONENT* component = cl->component;
5040 WCHAR buf[21];
5042 buf[0] = 0;
5043 if (component->ComponentId)
5045 TRACE("From %s\n",debugstr_w(component->ComponentId));
5046 CLSIDFromString(component->ComponentId, &clsid);
5047 encode_base85_guid(&clsid,buf);
5048 TRACE("to %s\n",debugstr_w(buf));
5049 strcatW(data,buf);
5053 if (feature->Feature_Parent)
5055 static const WCHAR sep[] = {'\2',0};
5056 strcatW(data,sep);
5057 strcatW(data,feature->Feature_Parent);
5060 msi_reg_set_val_str( userdata, feature->Feature, data );
5061 msi_free(data);
5063 size = 0;
5064 if (feature->Feature_Parent)
5065 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5066 if (!absent)
5068 size += sizeof(WCHAR);
5069 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5070 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5072 else
5074 size += 2*sizeof(WCHAR);
5075 data = msi_alloc(size);
5076 data[0] = 0x6;
5077 data[1] = 0;
5078 if (feature->Feature_Parent)
5079 strcpyW( &data[1], feature->Feature_Parent );
5080 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5081 (LPBYTE)data,size);
5082 msi_free(data);
5085 /* the UI chunk */
5086 uirow = MSI_CreateRecord( 1 );
5087 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5088 ui_actiondata( package, szPublishFeatures, uirow);
5089 msiobj_release( &uirow->hdr );
5090 /* FIXME: call ui_progress? */
5093 end:
5094 RegCloseKey(hkey);
5095 RegCloseKey(userdata);
5096 return rc;
5099 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5101 UINT r;
5102 HKEY hkey;
5103 MSIRECORD *uirow;
5105 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5107 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5108 &hkey, FALSE);
5109 if (r == ERROR_SUCCESS)
5111 RegDeleteValueW(hkey, feature->Feature);
5112 RegCloseKey(hkey);
5115 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5116 &hkey, FALSE);
5117 if (r == ERROR_SUCCESS)
5119 RegDeleteValueW(hkey, feature->Feature);
5120 RegCloseKey(hkey);
5123 uirow = MSI_CreateRecord( 1 );
5124 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5125 ui_actiondata( package, szUnpublishFeatures, uirow );
5126 msiobj_release( &uirow->hdr );
5128 return ERROR_SUCCESS;
5131 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5133 MSIFEATURE *feature;
5135 if (!msi_check_unpublish(package))
5136 return ERROR_SUCCESS;
5138 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5140 msi_unpublish_feature(package, feature);
5143 return ERROR_SUCCESS;
5146 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5148 SYSTEMTIME systime;
5149 DWORD size, langid;
5150 WCHAR date[9], *val, *buffer;
5151 const WCHAR *prop, *key;
5153 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5154 static const WCHAR szWindowsInstaller[] =
5155 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
5156 static const WCHAR modpath_fmt[] =
5157 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5158 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5159 static const WCHAR szModifyPath[] =
5160 {'M','o','d','i','f','y','P','a','t','h',0};
5161 static const WCHAR szUninstallString[] =
5162 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5163 static const WCHAR szEstimatedSize[] =
5164 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5165 static const WCHAR szProductLanguage[] =
5166 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
5167 static const WCHAR szProductVersion[] =
5168 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
5169 static const WCHAR szDisplayVersion[] =
5170 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5171 static const WCHAR szInstallSource[] =
5172 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5173 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5174 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5175 static const WCHAR szAuthorizedCDFPrefix[] =
5176 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5177 static const WCHAR szARPCONTACT[] =
5178 {'A','R','P','C','O','N','T','A','C','T',0};
5179 static const WCHAR szContact[] =
5180 {'C','o','n','t','a','c','t',0};
5181 static const WCHAR szARPCOMMENTS[] =
5182 {'A','R','P','C','O','M','M','E','N','T','S',0};
5183 static const WCHAR szComments[] =
5184 {'C','o','m','m','e','n','t','s',0};
5185 static const WCHAR szProductName[] =
5186 {'P','r','o','d','u','c','t','N','a','m','e',0};
5187 static const WCHAR szDisplayName[] =
5188 {'D','i','s','p','l','a','y','N','a','m','e',0};
5189 static const WCHAR szARPHELPLINK[] =
5190 {'A','R','P','H','E','L','P','L','I','N','K',0};
5191 static const WCHAR szHelpLink[] =
5192 {'H','e','l','p','L','i','n','k',0};
5193 static const WCHAR szARPHELPTELEPHONE[] =
5194 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5195 static const WCHAR szHelpTelephone[] =
5196 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5197 static const WCHAR szARPINSTALLLOCATION[] =
5198 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5199 static const WCHAR szInstallLocation[] =
5200 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
5201 static const WCHAR szManufacturer[] =
5202 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5203 static const WCHAR szPublisher[] =
5204 {'P','u','b','l','i','s','h','e','r',0};
5205 static const WCHAR szARPREADME[] =
5206 {'A','R','P','R','E','A','D','M','E',0};
5207 static const WCHAR szReadme[] =
5208 {'R','e','a','d','M','e',0};
5209 static const WCHAR szARPSIZE[] =
5210 {'A','R','P','S','I','Z','E',0};
5211 static const WCHAR szSize[] =
5212 {'S','i','z','e',0};
5213 static const WCHAR szARPURLINFOABOUT[] =
5214 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5215 static const WCHAR szURLInfoAbout[] =
5216 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5217 static const WCHAR szARPURLUPDATEINFO[] =
5218 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5219 static const WCHAR szURLUpdateInfo[] =
5220 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5222 static const WCHAR *propval[] = {
5223 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5224 szARPCONTACT, szContact,
5225 szARPCOMMENTS, szComments,
5226 szProductName, szDisplayName,
5227 szARPHELPLINK, szHelpLink,
5228 szARPHELPTELEPHONE, szHelpTelephone,
5229 szARPINSTALLLOCATION, szInstallLocation,
5230 cszSourceDir, szInstallSource,
5231 szManufacturer, szPublisher,
5232 szARPREADME, szReadme,
5233 szARPSIZE, szSize,
5234 szARPURLINFOABOUT, szURLInfoAbout,
5235 szARPURLUPDATEINFO, szURLUpdateInfo,
5236 NULL
5238 const WCHAR **p = propval;
5240 while (*p)
5242 prop = *p++;
5243 key = *p++;
5244 val = msi_dup_property(package->db, prop);
5245 msi_reg_set_val_str(hkey, key, val);
5246 msi_free(val);
5249 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5251 size = deformat_string(package, modpath_fmt, &buffer);
5252 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5253 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5254 msi_free(buffer);
5256 /* FIXME: Write real Estimated Size when we have it */
5257 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5259 GetLocalTime(&systime);
5260 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5261 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5263 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5264 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5266 buffer = msi_dup_property(package->db, szProductVersion);
5267 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5268 if (buffer)
5270 DWORD verdword = msi_version_str_to_dword(buffer);
5272 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5273 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5274 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5275 msi_free(buffer);
5278 return ERROR_SUCCESS;
5281 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5283 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5284 MSIRECORD *uirow;
5285 LPWSTR upgrade_code;
5286 HKEY hkey, props;
5287 HKEY upgrade;
5288 UINT rc;
5290 /* FIXME: also need to publish if the product is in advertise mode */
5291 if (!msi_check_publish(package))
5292 return ERROR_SUCCESS;
5294 rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE);
5295 if (rc != ERROR_SUCCESS)
5296 return rc;
5298 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5299 NULL, &props, TRUE);
5300 if (rc != ERROR_SUCCESS)
5301 goto done;
5303 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
5304 msi_free( package->db->localfile );
5305 package->db->localfile = NULL;
5307 rc = msi_publish_install_properties(package, hkey);
5308 if (rc != ERROR_SUCCESS)
5309 goto done;
5311 rc = msi_publish_install_properties(package, props);
5312 if (rc != ERROR_SUCCESS)
5313 goto done;
5315 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5316 if (upgrade_code)
5318 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
5319 squash_guid(package->ProductCode, squashed_pc);
5320 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
5321 RegCloseKey(upgrade);
5322 msi_free(upgrade_code);
5325 done:
5326 uirow = MSI_CreateRecord( 1 );
5327 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5328 ui_actiondata( package, szRegisterProduct, uirow );
5329 msiobj_release( &uirow->hdr );
5331 RegCloseKey(hkey);
5332 return ERROR_SUCCESS;
5335 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5337 return execute_script(package,INSTALL_SCRIPT);
5340 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
5342 WCHAR *upgrade, **features;
5343 BOOL full_uninstall = TRUE;
5344 MSIFEATURE *feature;
5345 MSIPATCHINFO *patch;
5347 static const WCHAR szUpgradeCode[] =
5348 {'U','p','g','r','a','d','e','C','o','d','e',0};
5350 features = msi_split_string(remove, ',');
5351 if (!features)
5353 ERR("REMOVE feature list is empty!\n");
5354 return ERROR_FUNCTION_FAILED;
5357 if (!strcmpW( features[0], szAll ))
5358 full_uninstall = TRUE;
5359 else
5361 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5363 if (feature->Action != INSTALLSTATE_ABSENT)
5364 full_uninstall = FALSE;
5367 msi_free(features);
5369 if (!full_uninstall)
5370 return ERROR_SUCCESS;
5372 MSIREG_DeleteProductKey(package->ProductCode);
5373 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5374 MSIREG_DeleteUninstallKey(package);
5376 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5377 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5378 MSIREG_DeleteUserProductKey(package->ProductCode);
5379 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5381 upgrade = msi_dup_property(package->db, szUpgradeCode);
5382 if (upgrade)
5384 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5385 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5386 msi_free(upgrade);
5389 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5391 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5394 return ERROR_SUCCESS;
5397 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5399 UINT rc;
5400 WCHAR *remove;
5402 /* turn off scheduling */
5403 package->script->CurrentlyScripting= FALSE;
5405 /* first do the same as an InstallExecute */
5406 rc = ACTION_InstallExecute(package);
5407 if (rc != ERROR_SUCCESS)
5408 return rc;
5410 /* then handle Commit Actions */
5411 rc = execute_script(package,COMMIT_SCRIPT);
5412 if (rc != ERROR_SUCCESS)
5413 return rc;
5415 remove = msi_dup_property(package->db, szRemove);
5416 if (remove)
5417 rc = msi_unpublish_product(package, remove);
5419 msi_free(remove);
5420 return rc;
5423 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5425 static const WCHAR RunOnce[] = {
5426 'S','o','f','t','w','a','r','e','\\',
5427 'M','i','c','r','o','s','o','f','t','\\',
5428 'W','i','n','d','o','w','s','\\',
5429 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5430 'R','u','n','O','n','c','e',0};
5431 static const WCHAR InstallRunOnce[] = {
5432 'S','o','f','t','w','a','r','e','\\',
5433 'M','i','c','r','o','s','o','f','t','\\',
5434 'W','i','n','d','o','w','s','\\',
5435 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5436 'I','n','s','t','a','l','l','e','r','\\',
5437 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5439 static const WCHAR msiexec_fmt[] = {
5440 '%','s',
5441 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5442 '\"','%','s','\"',0};
5443 static const WCHAR install_fmt[] = {
5444 '/','I',' ','\"','%','s','\"',' ',
5445 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5446 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5447 WCHAR buffer[256], sysdir[MAX_PATH];
5448 HKEY hkey;
5449 WCHAR squished_pc[100];
5451 squash_guid(package->ProductCode,squished_pc);
5453 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5454 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5455 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5456 squished_pc);
5458 msi_reg_set_val_str( hkey, squished_pc, buffer );
5459 RegCloseKey(hkey);
5461 TRACE("Reboot command %s\n",debugstr_w(buffer));
5463 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5464 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5466 msi_reg_set_val_str( hkey, squished_pc, buffer );
5467 RegCloseKey(hkey);
5469 return ERROR_INSTALL_SUSPEND;
5472 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5474 DWORD attrib;
5475 UINT rc;
5478 * We are currently doing what should be done here in the top level Install
5479 * however for Administrative and uninstalls this step will be needed
5481 if (!package->PackagePath)
5482 return ERROR_SUCCESS;
5484 msi_set_sourcedir_props(package, TRUE);
5486 attrib = GetFileAttributesW(package->db->path);
5487 if (attrib == INVALID_FILE_ATTRIBUTES)
5489 LPWSTR prompt;
5490 LPWSTR msg;
5491 DWORD size = 0;
5493 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5494 package->Context, MSICODE_PRODUCT,
5495 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5496 if (rc == ERROR_MORE_DATA)
5498 prompt = msi_alloc(size * sizeof(WCHAR));
5499 MsiSourceListGetInfoW(package->ProductCode, NULL,
5500 package->Context, MSICODE_PRODUCT,
5501 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5503 else
5504 prompt = strdupW(package->db->path);
5506 msg = generate_error_string(package,1302,1,prompt);
5507 while(attrib == INVALID_FILE_ATTRIBUTES)
5509 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5510 if (rc == IDCANCEL)
5512 rc = ERROR_INSTALL_USEREXIT;
5513 break;
5515 attrib = GetFileAttributesW(package->db->path);
5517 msi_free(prompt);
5518 rc = ERROR_SUCCESS;
5520 else
5521 return ERROR_SUCCESS;
5523 return rc;
5526 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5528 HKEY hkey = 0;
5529 LPWSTR buffer, productid = NULL;
5530 UINT i, rc = ERROR_SUCCESS;
5531 MSIRECORD *uirow;
5533 static const WCHAR szPropKeys[][80] =
5535 {'P','r','o','d','u','c','t','I','D',0},
5536 {'U','S','E','R','N','A','M','E',0},
5537 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5538 {0},
5541 static const WCHAR szRegKeys[][80] =
5543 {'P','r','o','d','u','c','t','I','D',0},
5544 {'R','e','g','O','w','n','e','r',0},
5545 {'R','e','g','C','o','m','p','a','n','y',0},
5546 {0},
5549 if (msi_check_unpublish(package))
5551 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5552 goto end;
5555 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5556 if (!productid)
5557 goto end;
5559 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5560 NULL, &hkey, TRUE);
5561 if (rc != ERROR_SUCCESS)
5562 goto end;
5564 for( i = 0; szPropKeys[i][0]; i++ )
5566 buffer = msi_dup_property( package->db, szPropKeys[i] );
5567 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5568 msi_free( buffer );
5571 end:
5572 uirow = MSI_CreateRecord( 1 );
5573 MSI_RecordSetStringW( uirow, 1, productid );
5574 ui_actiondata( package, szRegisterUser, uirow );
5575 msiobj_release( &uirow->hdr );
5577 msi_free(productid);
5578 RegCloseKey(hkey);
5579 return rc;
5583 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5585 UINT rc;
5587 package->script->InWhatSequence |= SEQUENCE_EXEC;
5588 rc = ACTION_ProcessExecSequence(package,FALSE);
5589 return rc;
5593 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5595 MSIPACKAGE *package = param;
5596 LPCWSTR compgroupid, component, feature, qualifier, text;
5597 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5598 HKEY hkey = NULL;
5599 UINT rc;
5600 MSICOMPONENT *comp;
5601 MSIFEATURE *feat;
5602 DWORD sz;
5603 MSIRECORD *uirow;
5604 int len;
5606 feature = MSI_RecordGetString(rec, 5);
5607 feat = get_loaded_feature(package, feature);
5608 if (!feat)
5609 return ERROR_SUCCESS;
5611 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5612 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5613 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5615 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5616 feat->Action = feat->Installed;
5617 return ERROR_SUCCESS;
5620 component = MSI_RecordGetString(rec, 3);
5621 comp = get_loaded_component(package, component);
5622 if (!comp)
5623 return ERROR_SUCCESS;
5625 compgroupid = MSI_RecordGetString(rec,1);
5626 qualifier = MSI_RecordGetString(rec,2);
5628 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5629 if (rc != ERROR_SUCCESS)
5630 goto end;
5632 advertise = create_component_advertise_string( package, comp, feature );
5633 text = MSI_RecordGetString( rec, 4 );
5634 if (text)
5636 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5637 strcpyW( p, advertise );
5638 strcatW( p, text );
5639 msi_free( advertise );
5640 advertise = p;
5642 existing = msi_reg_get_val_str( hkey, qualifier );
5644 sz = strlenW( advertise ) + 1;
5645 if (existing)
5647 for (p = existing; *p; p += len)
5649 len = strlenW( p ) + 1;
5650 if (strcmpW( advertise, p )) sz += len;
5653 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5655 rc = ERROR_OUTOFMEMORY;
5656 goto end;
5658 q = output;
5659 if (existing)
5661 for (p = existing; *p; p += len)
5663 len = strlenW( p ) + 1;
5664 if (strcmpW( advertise, p ))
5666 memcpy( q, p, len * sizeof(WCHAR) );
5667 q += len;
5671 strcpyW( q, advertise );
5672 q[strlenW( q ) + 1] = 0;
5674 msi_reg_set_val_multi_str( hkey, qualifier, output );
5676 end:
5677 RegCloseKey(hkey);
5678 msi_free( output );
5679 msi_free( advertise );
5680 msi_free( existing );
5682 /* the UI chunk */
5683 uirow = MSI_CreateRecord( 2 );
5684 MSI_RecordSetStringW( uirow, 1, compgroupid );
5685 MSI_RecordSetStringW( uirow, 2, qualifier);
5686 ui_actiondata( package, szPublishComponents, uirow);
5687 msiobj_release( &uirow->hdr );
5688 /* FIXME: call ui_progress? */
5690 return rc;
5694 * At present I am ignorning the advertised components part of this and only
5695 * focusing on the qualified component sets
5697 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5699 UINT rc;
5700 MSIQUERY * view;
5701 static const WCHAR ExecSeqQuery[] =
5702 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5703 '`','P','u','b','l','i','s','h',
5704 'C','o','m','p','o','n','e','n','t','`',0};
5706 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5707 if (rc != ERROR_SUCCESS)
5708 return ERROR_SUCCESS;
5710 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5711 msiobj_release(&view->hdr);
5713 return rc;
5716 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5718 static const WCHAR szInstallerComponents[] = {
5719 'S','o','f','t','w','a','r','e','\\',
5720 'M','i','c','r','o','s','o','f','t','\\',
5721 'I','n','s','t','a','l','l','e','r','\\',
5722 'C','o','m','p','o','n','e','n','t','s','\\',0};
5724 MSIPACKAGE *package = param;
5725 LPCWSTR compgroupid, component, feature, qualifier;
5726 MSICOMPONENT *comp;
5727 MSIFEATURE *feat;
5728 MSIRECORD *uirow;
5729 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5730 LONG res;
5732 feature = MSI_RecordGetString( rec, 5 );
5733 feat = get_loaded_feature( package, feature );
5734 if (!feat)
5735 return ERROR_SUCCESS;
5737 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5739 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5740 feat->Action = feat->Installed;
5741 return ERROR_SUCCESS;
5744 component = MSI_RecordGetString( rec, 3 );
5745 comp = get_loaded_component( package, component );
5746 if (!comp)
5747 return ERROR_SUCCESS;
5749 compgroupid = MSI_RecordGetString( rec, 1 );
5750 qualifier = MSI_RecordGetString( rec, 2 );
5752 squash_guid( compgroupid, squashed );
5753 strcpyW( keypath, szInstallerComponents );
5754 strcatW( keypath, squashed );
5756 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5757 if (res != ERROR_SUCCESS)
5759 WARN("Unable to delete component key %d\n", res);
5762 uirow = MSI_CreateRecord( 2 );
5763 MSI_RecordSetStringW( uirow, 1, compgroupid );
5764 MSI_RecordSetStringW( uirow, 2, qualifier );
5765 ui_actiondata( package, szUnpublishComponents, uirow );
5766 msiobj_release( &uirow->hdr );
5768 return ERROR_SUCCESS;
5771 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5773 UINT rc;
5774 MSIQUERY *view;
5775 static const WCHAR query[] =
5776 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5777 '`','P','u','b','l','i','s','h',
5778 'C','o','m','p','o','n','e','n','t','`',0};
5780 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5781 if (rc != ERROR_SUCCESS)
5782 return ERROR_SUCCESS;
5784 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5785 msiobj_release( &view->hdr );
5787 return rc;
5790 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5792 MSIPACKAGE *package = param;
5793 MSIRECORD *row;
5794 MSIFILE *file;
5795 SC_HANDLE hscm, service = NULL;
5796 LPCWSTR comp, key;
5797 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5798 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5799 DWORD serv_type, start_type, err_control;
5800 SERVICE_DESCRIPTIONW sd = {NULL};
5802 static const WCHAR query[] =
5803 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5804 '`','C','o','m','p','o','n','e','n','t','`',' ',
5805 'W','H','E','R','E',' ',
5806 '`','C','o','m','p','o','n','e','n','t','`',' ',
5807 '=','\'','%','s','\'',0};
5809 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5810 if (!hscm)
5812 ERR("Failed to open the SC Manager!\n");
5813 goto done;
5816 comp = MSI_RecordGetString( rec, 12 );
5817 if (!get_loaded_component( package, comp ))
5818 goto done;
5820 start_type = MSI_RecordGetInteger(rec, 5);
5821 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5822 goto done;
5824 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5825 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5826 serv_type = MSI_RecordGetInteger(rec, 4);
5827 err_control = MSI_RecordGetInteger(rec, 6);
5828 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5829 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5830 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5831 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5832 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5833 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5835 /* fetch the service path */
5836 row = MSI_QueryGetRecord(package->db, query, comp);
5837 if (!row)
5839 ERR("Control query failed!\n");
5840 goto done;
5842 key = MSI_RecordGetString(row, 6);
5844 file = get_loaded_file(package, key);
5845 msiobj_release(&row->hdr);
5846 if (!file)
5848 ERR("Failed to load the service file\n");
5849 goto done;
5852 if (!args || !args[0]) image_path = file->TargetPath;
5853 else
5855 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5856 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5857 return ERROR_OUTOFMEMORY;
5859 strcpyW(image_path, file->TargetPath);
5860 strcatW(image_path, szSpace);
5861 strcatW(image_path, args);
5863 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5864 start_type, err_control, image_path, load_order,
5865 NULL, depends, serv_name, pass);
5867 if (!service)
5869 if (GetLastError() != ERROR_SERVICE_EXISTS)
5870 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5872 else if (sd.lpDescription)
5874 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5875 WARN("failed to set service description %u\n", GetLastError());
5878 if (image_path != file->TargetPath) msi_free(image_path);
5879 done:
5880 CloseServiceHandle(service);
5881 CloseServiceHandle(hscm);
5882 msi_free(name);
5883 msi_free(disp);
5884 msi_free(sd.lpDescription);
5885 msi_free(load_order);
5886 msi_free(serv_name);
5887 msi_free(pass);
5888 msi_free(depends);
5889 msi_free(args);
5891 return ERROR_SUCCESS;
5894 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5896 UINT rc;
5897 MSIQUERY * view;
5898 static const WCHAR ExecSeqQuery[] =
5899 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5900 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5902 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5903 if (rc != ERROR_SUCCESS)
5904 return ERROR_SUCCESS;
5906 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5907 msiobj_release(&view->hdr);
5909 return rc;
5912 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5913 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5915 LPCWSTR *vector, *temp_vector;
5916 LPWSTR p, q;
5917 DWORD sep_len;
5919 static const WCHAR separator[] = {'[','~',']',0};
5921 *numargs = 0;
5922 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5924 if (!args)
5925 return NULL;
5927 vector = msi_alloc(sizeof(LPWSTR));
5928 if (!vector)
5929 return NULL;
5931 p = args;
5934 (*numargs)++;
5935 vector[*numargs - 1] = p;
5937 if ((q = strstrW(p, separator)))
5939 *q = '\0';
5941 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5942 if (!temp_vector)
5944 msi_free(vector);
5945 return NULL;
5947 vector = temp_vector;
5949 p = q + sep_len;
5951 } while (q);
5953 return vector;
5956 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5958 MSIPACKAGE *package = param;
5959 MSICOMPONENT *comp;
5960 MSIRECORD *uirow;
5961 SC_HANDLE scm = NULL, service = NULL;
5962 LPCWSTR component, *vector = NULL;
5963 LPWSTR name, args, display_name = NULL;
5964 DWORD event, numargs, len;
5965 UINT r = ERROR_FUNCTION_FAILED;
5967 component = MSI_RecordGetString(rec, 6);
5968 comp = get_loaded_component(package, component);
5969 if (!comp)
5970 return ERROR_SUCCESS;
5972 if (!comp->Enabled)
5974 TRACE("component is disabled\n");
5975 return ERROR_SUCCESS;
5978 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5980 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5981 comp->Action = comp->Installed;
5982 return ERROR_SUCCESS;
5984 comp->Action = INSTALLSTATE_LOCAL;
5986 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5987 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5988 event = MSI_RecordGetInteger(rec, 3);
5990 if (!(event & msidbServiceControlEventStart))
5992 r = ERROR_SUCCESS;
5993 goto done;
5996 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5997 if (!scm)
5999 ERR("Failed to open the service control manager\n");
6000 goto done;
6003 len = 0;
6004 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6005 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6007 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6008 GetServiceDisplayNameW( scm, name, display_name, &len );
6011 service = OpenServiceW(scm, name, SERVICE_START);
6012 if (!service)
6014 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6015 goto done;
6018 vector = msi_service_args_to_vector(args, &numargs);
6020 if (!StartServiceW(service, numargs, vector) &&
6021 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6023 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6024 goto done;
6027 r = ERROR_SUCCESS;
6029 done:
6030 uirow = MSI_CreateRecord( 2 );
6031 MSI_RecordSetStringW( uirow, 1, display_name );
6032 MSI_RecordSetStringW( uirow, 2, name );
6033 ui_actiondata( package, szStartServices, uirow );
6034 msiobj_release( &uirow->hdr );
6036 CloseServiceHandle(service);
6037 CloseServiceHandle(scm);
6039 msi_free(name);
6040 msi_free(args);
6041 msi_free(vector);
6042 msi_free(display_name);
6043 return r;
6046 static UINT ACTION_StartServices( MSIPACKAGE *package )
6048 UINT rc;
6049 MSIQUERY *view;
6051 static const WCHAR query[] = {
6052 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6053 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6055 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6056 if (rc != ERROR_SUCCESS)
6057 return ERROR_SUCCESS;
6059 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6060 msiobj_release(&view->hdr);
6062 return rc;
6065 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6067 DWORD i, needed, count;
6068 ENUM_SERVICE_STATUSW *dependencies;
6069 SERVICE_STATUS ss;
6070 SC_HANDLE depserv;
6072 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6073 0, &needed, &count))
6074 return TRUE;
6076 if (GetLastError() != ERROR_MORE_DATA)
6077 return FALSE;
6079 dependencies = msi_alloc(needed);
6080 if (!dependencies)
6081 return FALSE;
6083 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6084 needed, &needed, &count))
6085 goto error;
6087 for (i = 0; i < count; i++)
6089 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6090 SERVICE_STOP | SERVICE_QUERY_STATUS);
6091 if (!depserv)
6092 goto error;
6094 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
6095 goto error;
6098 return TRUE;
6100 error:
6101 msi_free(dependencies);
6102 return FALSE;
6105 static UINT stop_service( LPCWSTR name )
6107 SC_HANDLE scm = NULL, service = NULL;
6108 SERVICE_STATUS status;
6109 SERVICE_STATUS_PROCESS ssp;
6110 DWORD needed;
6112 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6113 if (!scm)
6115 WARN("Failed to open the SCM: %d\n", GetLastError());
6116 goto done;
6119 service = OpenServiceW(scm, name,
6120 SERVICE_STOP |
6121 SERVICE_QUERY_STATUS |
6122 SERVICE_ENUMERATE_DEPENDENTS);
6123 if (!service)
6125 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6126 goto done;
6129 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6130 sizeof(SERVICE_STATUS_PROCESS), &needed))
6132 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6133 goto done;
6136 if (ssp.dwCurrentState == SERVICE_STOPPED)
6137 goto done;
6139 stop_service_dependents(scm, service);
6141 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6142 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6144 done:
6145 CloseServiceHandle(service);
6146 CloseServiceHandle(scm);
6148 return ERROR_SUCCESS;
6151 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6153 MSIPACKAGE *package = param;
6154 MSICOMPONENT *comp;
6155 MSIRECORD *uirow;
6156 LPCWSTR component;
6157 LPWSTR name = NULL, display_name = NULL;
6158 DWORD event, len;
6159 SC_HANDLE scm;
6161 event = MSI_RecordGetInteger( rec, 3 );
6162 if (!(event & msidbServiceControlEventStop))
6163 return ERROR_SUCCESS;
6165 component = MSI_RecordGetString( rec, 6 );
6166 comp = get_loaded_component( package, component );
6167 if (!comp)
6168 return ERROR_SUCCESS;
6170 if (!comp->Enabled)
6172 TRACE("component is disabled\n");
6173 return ERROR_SUCCESS;
6176 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6178 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6179 comp->Action = comp->Installed;
6180 return ERROR_SUCCESS;
6182 comp->Action = INSTALLSTATE_ABSENT;
6184 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6185 if (!scm)
6187 ERR("Failed to open the service control manager\n");
6188 goto done;
6191 len = 0;
6192 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6193 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6195 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6196 GetServiceDisplayNameW( scm, name, display_name, &len );
6198 CloseServiceHandle( scm );
6200 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6201 stop_service( name );
6203 done:
6204 uirow = MSI_CreateRecord( 2 );
6205 MSI_RecordSetStringW( uirow, 1, display_name );
6206 MSI_RecordSetStringW( uirow, 2, name );
6207 ui_actiondata( package, szStopServices, uirow );
6208 msiobj_release( &uirow->hdr );
6210 msi_free( name );
6211 msi_free( display_name );
6212 return ERROR_SUCCESS;
6215 static UINT ACTION_StopServices( MSIPACKAGE *package )
6217 UINT rc;
6218 MSIQUERY *view;
6220 static const WCHAR query[] = {
6221 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6222 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6224 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6225 if (rc != ERROR_SUCCESS)
6226 return ERROR_SUCCESS;
6228 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6229 msiobj_release(&view->hdr);
6231 return rc;
6234 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6236 MSIPACKAGE *package = param;
6237 MSICOMPONENT *comp;
6238 MSIRECORD *uirow;
6239 LPCWSTR component;
6240 LPWSTR name = NULL, display_name = NULL;
6241 DWORD event, len;
6242 SC_HANDLE scm = NULL, service = NULL;
6244 event = MSI_RecordGetInteger( rec, 3 );
6245 if (!(event & msidbServiceControlEventDelete))
6246 return ERROR_SUCCESS;
6248 component = MSI_RecordGetString(rec, 6);
6249 comp = get_loaded_component(package, component);
6250 if (!comp)
6251 return ERROR_SUCCESS;
6253 if (!comp->Enabled)
6255 TRACE("component is disabled\n");
6256 return ERROR_SUCCESS;
6259 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6261 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6262 comp->Action = comp->Installed;
6263 return ERROR_SUCCESS;
6265 comp->Action = INSTALLSTATE_ABSENT;
6267 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6268 stop_service( name );
6270 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6271 if (!scm)
6273 WARN("Failed to open the SCM: %d\n", GetLastError());
6274 goto done;
6277 len = 0;
6278 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6279 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6281 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6282 GetServiceDisplayNameW( scm, name, display_name, &len );
6285 service = OpenServiceW( scm, name, DELETE );
6286 if (!service)
6288 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6289 goto done;
6292 if (!DeleteService( service ))
6293 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6295 done:
6296 uirow = MSI_CreateRecord( 2 );
6297 MSI_RecordSetStringW( uirow, 1, display_name );
6298 MSI_RecordSetStringW( uirow, 2, name );
6299 ui_actiondata( package, szDeleteServices, uirow );
6300 msiobj_release( &uirow->hdr );
6302 CloseServiceHandle( service );
6303 CloseServiceHandle( scm );
6304 msi_free( name );
6305 msi_free( display_name );
6307 return ERROR_SUCCESS;
6310 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6312 UINT rc;
6313 MSIQUERY *view;
6315 static const WCHAR query[] = {
6316 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6317 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6319 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6320 if (rc != ERROR_SUCCESS)
6321 return ERROR_SUCCESS;
6323 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6324 msiobj_release( &view->hdr );
6326 return rc;
6329 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6331 MSIPACKAGE *package = param;
6332 LPWSTR driver, driver_path, ptr;
6333 WCHAR outpath[MAX_PATH];
6334 MSIFILE *driver_file = NULL, *setup_file = NULL;
6335 MSICOMPONENT *comp;
6336 MSIRECORD *uirow;
6337 LPCWSTR desc, file_key, component;
6338 DWORD len, usage;
6339 UINT r = ERROR_SUCCESS;
6341 static const WCHAR driver_fmt[] = {
6342 'D','r','i','v','e','r','=','%','s',0};
6343 static const WCHAR setup_fmt[] = {
6344 'S','e','t','u','p','=','%','s',0};
6345 static const WCHAR usage_fmt[] = {
6346 'F','i','l','e','U','s','a','g','e','=','1',0};
6348 component = MSI_RecordGetString( rec, 2 );
6349 comp = get_loaded_component( package, component );
6350 if (!comp)
6351 return ERROR_SUCCESS;
6353 if (!comp->Enabled)
6355 TRACE("component is disabled\n");
6356 return ERROR_SUCCESS;
6359 desc = MSI_RecordGetString(rec, 3);
6361 file_key = MSI_RecordGetString( rec, 4 );
6362 if (file_key) driver_file = get_loaded_file( package, file_key );
6364 file_key = MSI_RecordGetString( rec, 5 );
6365 if (file_key) setup_file = get_loaded_file( package, file_key );
6367 if (!driver_file)
6369 ERR("ODBC Driver entry not found!\n");
6370 return ERROR_FUNCTION_FAILED;
6373 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6374 if (setup_file)
6375 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6376 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6378 driver = msi_alloc(len * sizeof(WCHAR));
6379 if (!driver)
6380 return ERROR_OUTOFMEMORY;
6382 ptr = driver;
6383 lstrcpyW(ptr, desc);
6384 ptr += lstrlenW(ptr) + 1;
6386 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6387 ptr += len + 1;
6389 if (setup_file)
6391 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6392 ptr += len + 1;
6395 lstrcpyW(ptr, usage_fmt);
6396 ptr += lstrlenW(ptr) + 1;
6397 *ptr = '\0';
6399 driver_path = strdupW(driver_file->TargetPath);
6400 ptr = strrchrW(driver_path, '\\');
6401 if (ptr) *ptr = '\0';
6403 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6404 NULL, ODBC_INSTALL_COMPLETE, &usage))
6406 ERR("Failed to install SQL driver!\n");
6407 r = ERROR_FUNCTION_FAILED;
6410 uirow = MSI_CreateRecord( 5 );
6411 MSI_RecordSetStringW( uirow, 1, desc );
6412 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6413 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6414 ui_actiondata( package, szInstallODBC, uirow );
6415 msiobj_release( &uirow->hdr );
6417 msi_free(driver);
6418 msi_free(driver_path);
6420 return r;
6423 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6425 MSIPACKAGE *package = param;
6426 LPWSTR translator, translator_path, ptr;
6427 WCHAR outpath[MAX_PATH];
6428 MSIFILE *translator_file = NULL, *setup_file = NULL;
6429 MSICOMPONENT *comp;
6430 MSIRECORD *uirow;
6431 LPCWSTR desc, file_key, component;
6432 DWORD len, usage;
6433 UINT r = ERROR_SUCCESS;
6435 static const WCHAR translator_fmt[] = {
6436 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6437 static const WCHAR setup_fmt[] = {
6438 'S','e','t','u','p','=','%','s',0};
6440 component = MSI_RecordGetString( rec, 2 );
6441 comp = get_loaded_component( package, component );
6442 if (!comp)
6443 return ERROR_SUCCESS;
6445 if (!comp->Enabled)
6447 TRACE("component is disabled\n");
6448 return ERROR_SUCCESS;
6451 desc = MSI_RecordGetString(rec, 3);
6453 file_key = MSI_RecordGetString( rec, 4 );
6454 if (file_key) translator_file = get_loaded_file( package, file_key );
6456 file_key = MSI_RecordGetString( rec, 5 );
6457 if (file_key) setup_file = get_loaded_file( package, file_key );
6459 if (!translator_file)
6461 ERR("ODBC Translator entry not found!\n");
6462 return ERROR_FUNCTION_FAILED;
6465 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6466 if (setup_file)
6467 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6469 translator = msi_alloc(len * sizeof(WCHAR));
6470 if (!translator)
6471 return ERROR_OUTOFMEMORY;
6473 ptr = translator;
6474 lstrcpyW(ptr, desc);
6475 ptr += lstrlenW(ptr) + 1;
6477 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6478 ptr += len + 1;
6480 if (setup_file)
6482 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6483 ptr += len + 1;
6485 *ptr = '\0';
6487 translator_path = strdupW(translator_file->TargetPath);
6488 ptr = strrchrW(translator_path, '\\');
6489 if (ptr) *ptr = '\0';
6491 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6492 NULL, ODBC_INSTALL_COMPLETE, &usage))
6494 ERR("Failed to install SQL translator!\n");
6495 r = ERROR_FUNCTION_FAILED;
6498 uirow = MSI_CreateRecord( 5 );
6499 MSI_RecordSetStringW( uirow, 1, desc );
6500 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6501 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6502 ui_actiondata( package, szInstallODBC, uirow );
6503 msiobj_release( &uirow->hdr );
6505 msi_free(translator);
6506 msi_free(translator_path);
6508 return r;
6511 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6513 MSIPACKAGE *package = param;
6514 MSICOMPONENT *comp;
6515 LPWSTR attrs;
6516 LPCWSTR desc, driver, component;
6517 WORD request = ODBC_ADD_SYS_DSN;
6518 INT registration;
6519 DWORD len;
6520 UINT r = ERROR_SUCCESS;
6521 MSIRECORD *uirow;
6523 static const WCHAR attrs_fmt[] = {
6524 'D','S','N','=','%','s',0 };
6526 component = MSI_RecordGetString( rec, 2 );
6527 comp = get_loaded_component( package, component );
6528 if (!comp)
6529 return ERROR_SUCCESS;
6531 if (!comp->Enabled)
6533 TRACE("component is disabled\n");
6534 return ERROR_SUCCESS;
6537 desc = MSI_RecordGetString(rec, 3);
6538 driver = MSI_RecordGetString(rec, 4);
6539 registration = MSI_RecordGetInteger(rec, 5);
6541 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6542 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6544 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6545 attrs = msi_alloc(len * sizeof(WCHAR));
6546 if (!attrs)
6547 return ERROR_OUTOFMEMORY;
6549 len = sprintfW(attrs, attrs_fmt, desc);
6550 attrs[len + 1] = 0;
6552 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6554 ERR("Failed to install SQL data source!\n");
6555 r = ERROR_FUNCTION_FAILED;
6558 uirow = MSI_CreateRecord( 5 );
6559 MSI_RecordSetStringW( uirow, 1, desc );
6560 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6561 MSI_RecordSetInteger( uirow, 3, request );
6562 ui_actiondata( package, szInstallODBC, uirow );
6563 msiobj_release( &uirow->hdr );
6565 msi_free(attrs);
6567 return r;
6570 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6572 UINT rc;
6573 MSIQUERY *view;
6575 static const WCHAR driver_query[] = {
6576 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6577 'O','D','B','C','D','r','i','v','e','r',0 };
6579 static const WCHAR translator_query[] = {
6580 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6581 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6583 static const WCHAR source_query[] = {
6584 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6585 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6587 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6588 if (rc != ERROR_SUCCESS)
6589 return ERROR_SUCCESS;
6591 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6592 msiobj_release(&view->hdr);
6594 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6595 if (rc != ERROR_SUCCESS)
6596 return ERROR_SUCCESS;
6598 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6599 msiobj_release(&view->hdr);
6601 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6602 if (rc != ERROR_SUCCESS)
6603 return ERROR_SUCCESS;
6605 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6606 msiobj_release(&view->hdr);
6608 return rc;
6611 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6613 MSIPACKAGE *package = param;
6614 MSICOMPONENT *comp;
6615 MSIRECORD *uirow;
6616 DWORD usage;
6617 LPCWSTR desc, component;
6619 component = MSI_RecordGetString( rec, 2 );
6620 comp = get_loaded_component( package, component );
6621 if (!comp)
6622 return ERROR_SUCCESS;
6624 if (!comp->Enabled)
6626 TRACE("component is disabled\n");
6627 return ERROR_SUCCESS;
6630 desc = MSI_RecordGetString( rec, 3 );
6631 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6633 WARN("Failed to remove ODBC driver\n");
6635 else if (!usage)
6637 FIXME("Usage count reached 0\n");
6640 uirow = MSI_CreateRecord( 2 );
6641 MSI_RecordSetStringW( uirow, 1, desc );
6642 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6643 ui_actiondata( package, szRemoveODBC, uirow );
6644 msiobj_release( &uirow->hdr );
6646 return ERROR_SUCCESS;
6649 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6651 MSIPACKAGE *package = param;
6652 MSICOMPONENT *comp;
6653 MSIRECORD *uirow;
6654 DWORD usage;
6655 LPCWSTR desc, component;
6657 component = MSI_RecordGetString( rec, 2 );
6658 comp = get_loaded_component( package, component );
6659 if (!comp)
6660 return ERROR_SUCCESS;
6662 if (!comp->Enabled)
6664 TRACE("component is disabled\n");
6665 return ERROR_SUCCESS;
6668 desc = MSI_RecordGetString( rec, 3 );
6669 if (!SQLRemoveTranslatorW( desc, &usage ))
6671 WARN("Failed to remove ODBC translator\n");
6673 else if (!usage)
6675 FIXME("Usage count reached 0\n");
6678 uirow = MSI_CreateRecord( 2 );
6679 MSI_RecordSetStringW( uirow, 1, desc );
6680 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6681 ui_actiondata( package, szRemoveODBC, uirow );
6682 msiobj_release( &uirow->hdr );
6684 return ERROR_SUCCESS;
6687 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6689 MSIPACKAGE *package = param;
6690 MSICOMPONENT *comp;
6691 MSIRECORD *uirow;
6692 LPWSTR attrs;
6693 LPCWSTR desc, driver, component;
6694 WORD request = ODBC_REMOVE_SYS_DSN;
6695 INT registration;
6696 DWORD len;
6698 static const WCHAR attrs_fmt[] = {
6699 'D','S','N','=','%','s',0 };
6701 component = MSI_RecordGetString( rec, 2 );
6702 comp = get_loaded_component( package, component );
6703 if (!comp)
6704 return ERROR_SUCCESS;
6706 if (!comp->Enabled)
6708 TRACE("component is disabled\n");
6709 return ERROR_SUCCESS;
6712 desc = MSI_RecordGetString( rec, 3 );
6713 driver = MSI_RecordGetString( rec, 4 );
6714 registration = MSI_RecordGetInteger( rec, 5 );
6716 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6717 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6719 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6720 attrs = msi_alloc( len * sizeof(WCHAR) );
6721 if (!attrs)
6722 return ERROR_OUTOFMEMORY;
6724 FIXME("Use ODBCSourceAttribute table\n");
6726 len = sprintfW( attrs, attrs_fmt, desc );
6727 attrs[len + 1] = 0;
6729 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6731 WARN("Failed to remove ODBC data source\n");
6733 msi_free( attrs );
6735 uirow = MSI_CreateRecord( 3 );
6736 MSI_RecordSetStringW( uirow, 1, desc );
6737 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6738 MSI_RecordSetInteger( uirow, 3, request );
6739 ui_actiondata( package, szRemoveODBC, uirow );
6740 msiobj_release( &uirow->hdr );
6742 return ERROR_SUCCESS;
6745 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6747 UINT rc;
6748 MSIQUERY *view;
6750 static const WCHAR driver_query[] = {
6751 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6752 'O','D','B','C','D','r','i','v','e','r',0 };
6754 static const WCHAR translator_query[] = {
6755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6756 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6758 static const WCHAR source_query[] = {
6759 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6760 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6762 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6763 if (rc != ERROR_SUCCESS)
6764 return ERROR_SUCCESS;
6766 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6767 msiobj_release( &view->hdr );
6769 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6770 if (rc != ERROR_SUCCESS)
6771 return ERROR_SUCCESS;
6773 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6774 msiobj_release( &view->hdr );
6776 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6777 if (rc != ERROR_SUCCESS)
6778 return ERROR_SUCCESS;
6780 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6781 msiobj_release( &view->hdr );
6783 return rc;
6786 #define ENV_ACT_SETALWAYS 0x1
6787 #define ENV_ACT_SETABSENT 0x2
6788 #define ENV_ACT_REMOVE 0x4
6789 #define ENV_ACT_REMOVEMATCH 0x8
6791 #define ENV_MOD_MACHINE 0x20000000
6792 #define ENV_MOD_APPEND 0x40000000
6793 #define ENV_MOD_PREFIX 0x80000000
6794 #define ENV_MOD_MASK 0xC0000000
6796 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6798 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6800 LPCWSTR cptr = *name;
6802 static const WCHAR prefix[] = {'[','~',']',0};
6803 static const int prefix_len = 3;
6805 *flags = 0;
6806 while (*cptr)
6808 if (*cptr == '=')
6809 *flags |= ENV_ACT_SETALWAYS;
6810 else if (*cptr == '+')
6811 *flags |= ENV_ACT_SETABSENT;
6812 else if (*cptr == '-')
6813 *flags |= ENV_ACT_REMOVE;
6814 else if (*cptr == '!')
6815 *flags |= ENV_ACT_REMOVEMATCH;
6816 else if (*cptr == '*')
6817 *flags |= ENV_MOD_MACHINE;
6818 else
6819 break;
6821 cptr++;
6822 (*name)++;
6825 if (!*cptr)
6827 ERR("Missing environment variable\n");
6828 return ERROR_FUNCTION_FAILED;
6831 if (*value)
6833 LPCWSTR ptr = *value;
6834 if (!strncmpW(ptr, prefix, prefix_len))
6836 if (ptr[prefix_len] == szSemiColon[0])
6838 *flags |= ENV_MOD_APPEND;
6839 *value += lstrlenW(prefix);
6841 else
6843 *value = NULL;
6846 else if (lstrlenW(*value) >= prefix_len)
6848 ptr += lstrlenW(ptr) - prefix_len;
6849 if (!strcmpW( ptr, prefix ))
6851 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6853 *flags |= ENV_MOD_PREFIX;
6854 /* the "[~]" will be removed by deformat_string */;
6856 else
6858 *value = NULL;
6864 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6865 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6866 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6867 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6869 ERR("Invalid flags: %08x\n", *flags);
6870 return ERROR_FUNCTION_FAILED;
6873 if (!*flags)
6874 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6876 return ERROR_SUCCESS;
6879 static UINT open_env_key( DWORD flags, HKEY *key )
6881 static const WCHAR user_env[] =
6882 {'E','n','v','i','r','o','n','m','e','n','t',0};
6883 static const WCHAR machine_env[] =
6884 {'S','y','s','t','e','m','\\',
6885 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6886 'C','o','n','t','r','o','l','\\',
6887 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6888 'E','n','v','i','r','o','n','m','e','n','t',0};
6889 const WCHAR *env;
6890 HKEY root;
6891 LONG res;
6893 if (flags & ENV_MOD_MACHINE)
6895 env = machine_env;
6896 root = HKEY_LOCAL_MACHINE;
6898 else
6900 env = user_env;
6901 root = HKEY_CURRENT_USER;
6904 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6905 if (res != ERROR_SUCCESS)
6907 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6908 return ERROR_FUNCTION_FAILED;
6911 return ERROR_SUCCESS;
6914 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6916 MSIPACKAGE *package = param;
6917 LPCWSTR name, value, component;
6918 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6919 DWORD flags, type, size;
6920 UINT res;
6921 HKEY env = NULL;
6922 MSICOMPONENT *comp;
6923 MSIRECORD *uirow;
6924 int action = 0;
6926 component = MSI_RecordGetString(rec, 4);
6927 comp = get_loaded_component(package, component);
6928 if (!comp)
6929 return ERROR_SUCCESS;
6931 if (!comp->Enabled)
6933 TRACE("component is disabled\n");
6934 return ERROR_SUCCESS;
6937 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6939 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6940 comp->Action = comp->Installed;
6941 return ERROR_SUCCESS;
6943 comp->Action = INSTALLSTATE_LOCAL;
6945 name = MSI_RecordGetString(rec, 2);
6946 value = MSI_RecordGetString(rec, 3);
6948 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6950 res = env_parse_flags(&name, &value, &flags);
6951 if (res != ERROR_SUCCESS || !value)
6952 goto done;
6954 if (value && !deformat_string(package, value, &deformatted))
6956 res = ERROR_OUTOFMEMORY;
6957 goto done;
6960 value = deformatted;
6962 res = open_env_key( flags, &env );
6963 if (res != ERROR_SUCCESS)
6964 goto done;
6966 if (flags & ENV_MOD_MACHINE)
6967 action |= 0x20000000;
6969 size = 0;
6970 type = REG_SZ;
6971 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6972 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6973 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6974 goto done;
6976 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6978 action = 0x2;
6980 /* Nothing to do. */
6981 if (!value)
6983 res = ERROR_SUCCESS;
6984 goto done;
6987 /* If we are appending but the string was empty, strip ; */
6988 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6990 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6991 newval = strdupW(value);
6992 if (!newval)
6994 res = ERROR_OUTOFMEMORY;
6995 goto done;
6998 else
7000 action = 0x1;
7002 /* Contrary to MSDN, +-variable to [~];path works */
7003 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7005 res = ERROR_SUCCESS;
7006 goto done;
7009 data = msi_alloc(size);
7010 if (!data)
7012 RegCloseKey(env);
7013 return ERROR_OUTOFMEMORY;
7016 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7017 if (res != ERROR_SUCCESS)
7018 goto done;
7020 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7022 action = 0x4;
7023 res = RegDeleteValueW(env, name);
7024 if (res != ERROR_SUCCESS)
7025 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7026 goto done;
7029 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7030 if (flags & ENV_MOD_MASK)
7032 DWORD mod_size;
7033 int multiplier = 0;
7034 if (flags & ENV_MOD_APPEND) multiplier++;
7035 if (flags & ENV_MOD_PREFIX) multiplier++;
7036 mod_size = lstrlenW(value) * multiplier;
7037 size += mod_size * sizeof(WCHAR);
7040 newval = msi_alloc(size);
7041 ptr = newval;
7042 if (!newval)
7044 res = ERROR_OUTOFMEMORY;
7045 goto done;
7048 if (flags & ENV_MOD_PREFIX)
7050 lstrcpyW(newval, value);
7051 ptr = newval + lstrlenW(value);
7052 action |= 0x80000000;
7055 lstrcpyW(ptr, data);
7057 if (flags & ENV_MOD_APPEND)
7059 lstrcatW(newval, value);
7060 action |= 0x40000000;
7063 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7064 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7065 if (res)
7067 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7070 done:
7071 uirow = MSI_CreateRecord( 3 );
7072 MSI_RecordSetStringW( uirow, 1, name );
7073 MSI_RecordSetStringW( uirow, 2, newval );
7074 MSI_RecordSetInteger( uirow, 3, action );
7075 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7076 msiobj_release( &uirow->hdr );
7078 if (env) RegCloseKey(env);
7079 msi_free(deformatted);
7080 msi_free(data);
7081 msi_free(newval);
7082 return res;
7085 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7087 UINT rc;
7088 MSIQUERY * view;
7089 static const WCHAR ExecSeqQuery[] =
7090 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7091 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7092 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
7093 if (rc != ERROR_SUCCESS)
7094 return ERROR_SUCCESS;
7096 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7097 msiobj_release(&view->hdr);
7099 return rc;
7102 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7104 MSIPACKAGE *package = param;
7105 LPCWSTR name, value, component;
7106 LPWSTR deformatted = NULL;
7107 DWORD flags;
7108 HKEY env;
7109 MSICOMPONENT *comp;
7110 MSIRECORD *uirow;
7111 int action = 0;
7112 LONG res;
7113 UINT r;
7115 component = MSI_RecordGetString( rec, 4 );
7116 comp = get_loaded_component( package, component );
7117 if (!comp)
7118 return ERROR_SUCCESS;
7120 if (!comp->Enabled)
7122 TRACE("component is disabled\n");
7123 return ERROR_SUCCESS;
7126 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
7128 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
7129 comp->Action = comp->Installed;
7130 return ERROR_SUCCESS;
7132 comp->Action = INSTALLSTATE_ABSENT;
7134 name = MSI_RecordGetString( rec, 2 );
7135 value = MSI_RecordGetString( rec, 3 );
7137 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7139 r = env_parse_flags( &name, &value, &flags );
7140 if (r != ERROR_SUCCESS)
7141 return r;
7143 if (!(flags & ENV_ACT_REMOVE))
7145 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7146 return ERROR_SUCCESS;
7149 if (value && !deformat_string( package, value, &deformatted ))
7150 return ERROR_OUTOFMEMORY;
7152 value = deformatted;
7154 r = open_env_key( flags, &env );
7155 if (r != ERROR_SUCCESS)
7157 r = ERROR_SUCCESS;
7158 goto done;
7161 if (flags & ENV_MOD_MACHINE)
7162 action |= 0x20000000;
7164 TRACE("Removing %s\n", debugstr_w(name));
7166 res = RegDeleteValueW( env, name );
7167 if (res != ERROR_SUCCESS)
7169 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7170 r = ERROR_SUCCESS;
7173 done:
7174 uirow = MSI_CreateRecord( 3 );
7175 MSI_RecordSetStringW( uirow, 1, name );
7176 MSI_RecordSetStringW( uirow, 2, value );
7177 MSI_RecordSetInteger( uirow, 3, action );
7178 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7179 msiobj_release( &uirow->hdr );
7181 if (env) RegCloseKey( env );
7182 msi_free( deformatted );
7183 return r;
7186 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7188 UINT rc;
7189 MSIQUERY *view;
7190 static const WCHAR query[] =
7191 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7192 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7194 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7195 if (rc != ERROR_SUCCESS)
7196 return ERROR_SUCCESS;
7198 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7199 msiobj_release( &view->hdr );
7201 return rc;
7204 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7206 LPWSTR key, template, id;
7207 UINT r = ERROR_SUCCESS;
7209 id = msi_dup_property( package->db, szProductID );
7210 if (id)
7212 msi_free( id );
7213 return ERROR_SUCCESS;
7215 template = msi_dup_property( package->db, szPIDTemplate );
7216 key = msi_dup_property( package->db, szPIDKEY );
7218 if (key && template)
7220 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7221 r = msi_set_property( package->db, szProductID, key );
7223 msi_free( template );
7224 msi_free( key );
7225 return r;
7228 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7230 TRACE("\n");
7231 package->need_reboot = 1;
7232 return ERROR_SUCCESS;
7235 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7237 static const WCHAR szAvailableFreeReg[] =
7238 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7239 MSIRECORD *uirow;
7240 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7242 TRACE("%p %d kilobytes\n", package, space);
7244 uirow = MSI_CreateRecord( 1 );
7245 MSI_RecordSetInteger( uirow, 1, space );
7246 ui_actiondata( package, szAllocateRegistrySpace, uirow );
7247 msiobj_release( &uirow->hdr );
7249 return ERROR_SUCCESS;
7252 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7254 FIXME("%p\n", package);
7255 return ERROR_SUCCESS;
7258 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7260 FIXME("%p\n", package);
7261 return ERROR_SUCCESS;
7264 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7266 UINT r, count;
7267 MSIQUERY *view;
7269 static const WCHAR driver_query[] = {
7270 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7271 'O','D','B','C','D','r','i','v','e','r',0 };
7273 static const WCHAR translator_query[] = {
7274 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7275 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7277 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7278 if (r == ERROR_SUCCESS)
7280 count = 0;
7281 r = MSI_IterateRecords( view, &count, NULL, package );
7282 msiobj_release( &view->hdr );
7283 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7286 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7287 if (r == ERROR_SUCCESS)
7289 count = 0;
7290 r = MSI_IterateRecords( view, &count, NULL, package );
7291 msiobj_release( &view->hdr );
7292 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7295 return ERROR_SUCCESS;
7298 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7300 MSIPACKAGE *package = param;
7301 const WCHAR *property = MSI_RecordGetString( rec, 1 );
7302 WCHAR *value;
7304 if ((value = msi_dup_property( package->db, property )))
7306 FIXME("remove %s\n", debugstr_w(value));
7307 msi_free( value );
7309 return ERROR_SUCCESS;
7312 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7314 UINT r;
7315 MSIQUERY *view;
7317 static const WCHAR query[] =
7318 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7319 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7321 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7322 if (r == ERROR_SUCCESS)
7324 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7325 msiobj_release( &view->hdr );
7327 return ERROR_SUCCESS;
7330 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7332 MSIPACKAGE *package = param;
7333 int attributes = MSI_RecordGetInteger( rec, 5 );
7335 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7337 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7338 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7339 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7340 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7341 HKEY hkey;
7342 UINT r;
7344 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7346 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7347 if (r != ERROR_SUCCESS)
7348 return ERROR_SUCCESS;
7350 else
7352 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7353 if (r != ERROR_SUCCESS)
7354 return ERROR_SUCCESS;
7356 RegCloseKey( hkey );
7358 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7359 debugstr_w(upgrade_code), debugstr_w(version_min),
7360 debugstr_w(version_max), debugstr_w(language));
7362 return ERROR_SUCCESS;
7365 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7367 UINT r;
7368 MSIQUERY *view;
7370 static const WCHAR query[] =
7371 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7373 if (msi_get_property_int( package->db, szInstalled, 0 ))
7375 TRACE("product is installed, skipping action\n");
7376 return ERROR_SUCCESS;
7378 if (msi_get_property_int( package->db, szPreselected, 0 ))
7380 TRACE("Preselected property is set, not migrating feature states\n");
7381 return ERROR_SUCCESS;
7384 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7385 if (r == ERROR_SUCCESS)
7387 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7388 msiobj_release( &view->hdr );
7390 return ERROR_SUCCESS;
7393 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7394 LPCSTR action, LPCWSTR table )
7396 static const WCHAR query[] = {
7397 'S','E','L','E','C','T',' ','*',' ',
7398 'F','R','O','M',' ','`','%','s','`',0 };
7399 MSIQUERY *view = NULL;
7400 DWORD count = 0;
7401 UINT r;
7403 r = MSI_OpenQuery( package->db, &view, query, table );
7404 if (r == ERROR_SUCCESS)
7406 r = MSI_IterateRecords(view, &count, NULL, package);
7407 msiobj_release(&view->hdr);
7410 if (count)
7411 FIXME("%s -> %u ignored %s table values\n",
7412 action, count, debugstr_w(table));
7414 return ERROR_SUCCESS;
7417 static UINT ACTION_BindImage( MSIPACKAGE *package )
7419 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7420 return msi_unimplemented_action_stub( package, "BindImage", table );
7423 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7425 static const WCHAR table[] = {
7426 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7427 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7430 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7432 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7433 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7436 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7438 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7439 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7442 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7444 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7445 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7448 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7450 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7451 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7454 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7456 static const struct
7458 const WCHAR *action;
7459 UINT (*handler)(MSIPACKAGE *);
7461 StandardActions[] =
7463 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7464 { szAppSearch, ACTION_AppSearch },
7465 { szBindImage, ACTION_BindImage },
7466 { szCCPSearch, ACTION_CCPSearch },
7467 { szCostFinalize, ACTION_CostFinalize },
7468 { szCostInitialize, ACTION_CostInitialize },
7469 { szCreateFolders, ACTION_CreateFolders },
7470 { szCreateShortcuts, ACTION_CreateShortcuts },
7471 { szDeleteServices, ACTION_DeleteServices },
7472 { szDisableRollback, ACTION_DisableRollback },
7473 { szDuplicateFiles, ACTION_DuplicateFiles },
7474 { szExecuteAction, ACTION_ExecuteAction },
7475 { szFileCost, ACTION_FileCost },
7476 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7477 { szForceReboot, ACTION_ForceReboot },
7478 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7479 { szInstallExecute, ACTION_InstallExecute },
7480 { szInstallExecuteAgain, ACTION_InstallExecute },
7481 { szInstallFiles, ACTION_InstallFiles},
7482 { szInstallFinalize, ACTION_InstallFinalize },
7483 { szInstallInitialize, ACTION_InstallInitialize },
7484 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7485 { szInstallValidate, ACTION_InstallValidate },
7486 { szIsolateComponents, ACTION_IsolateComponents },
7487 { szLaunchConditions, ACTION_LaunchConditions },
7488 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7489 { szMoveFiles, ACTION_MoveFiles },
7490 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7491 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7492 { szInstallODBC, ACTION_InstallODBC },
7493 { szInstallServices, ACTION_InstallServices },
7494 { szPatchFiles, ACTION_PatchFiles },
7495 { szProcessComponents, ACTION_ProcessComponents },
7496 { szPublishComponents, ACTION_PublishComponents },
7497 { szPublishFeatures, ACTION_PublishFeatures },
7498 { szPublishProduct, ACTION_PublishProduct },
7499 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7500 { szRegisterComPlus, ACTION_RegisterComPlus},
7501 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7502 { szRegisterFonts, ACTION_RegisterFonts },
7503 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7504 { szRegisterProduct, ACTION_RegisterProduct },
7505 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7506 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7507 { szRegisterUser, ACTION_RegisterUser },
7508 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7509 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7510 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7511 { szRemoveFiles, ACTION_RemoveFiles },
7512 { szRemoveFolders, ACTION_RemoveFolders },
7513 { szRemoveIniValues, ACTION_RemoveIniValues },
7514 { szRemoveODBC, ACTION_RemoveODBC },
7515 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7516 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7517 { szResolveSource, ACTION_ResolveSource },
7518 { szRMCCPSearch, ACTION_RMCCPSearch },
7519 { szScheduleReboot, ACTION_ScheduleReboot },
7520 { szSelfRegModules, ACTION_SelfRegModules },
7521 { szSelfUnregModules, ACTION_SelfUnregModules },
7522 { szSetODBCFolders, ACTION_SetODBCFolders },
7523 { szStartServices, ACTION_StartServices },
7524 { szStopServices, ACTION_StopServices },
7525 { szUnpublishComponents, ACTION_UnpublishComponents },
7526 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7527 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7528 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7529 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7530 { szUnregisterFonts, ACTION_UnregisterFonts },
7531 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7532 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7533 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7534 { szValidateProductID, ACTION_ValidateProductID },
7535 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7536 { szWriteIniValues, ACTION_WriteIniValues },
7537 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7538 { NULL, NULL },
7541 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7543 BOOL ret = FALSE;
7544 UINT i;
7546 i = 0;
7547 while (StandardActions[i].action != NULL)
7549 if (!strcmpW( StandardActions[i].action, action ))
7551 ui_actionstart( package, action );
7552 if (StandardActions[i].handler)
7554 ui_actioninfo( package, action, TRUE, 0 );
7555 *rc = StandardActions[i].handler( package );
7556 ui_actioninfo( package, action, FALSE, *rc );
7558 else
7560 FIXME("unhandled standard action %s\n", debugstr_w(action));
7561 *rc = ERROR_SUCCESS;
7563 ret = TRUE;
7564 break;
7566 i++;
7568 return ret;
7571 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7573 UINT rc = ERROR_SUCCESS;
7574 BOOL handled;
7576 TRACE("Performing action (%s)\n", debugstr_w(action));
7578 handled = ACTION_HandleStandardAction(package, action, &rc);
7580 if (!handled)
7581 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7583 if (!handled)
7585 WARN("unhandled msi action %s\n", debugstr_w(action));
7586 rc = ERROR_FUNCTION_NOT_CALLED;
7589 return rc;
7592 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7594 UINT rc = ERROR_SUCCESS;
7595 BOOL handled = FALSE;
7597 TRACE("Performing action (%s)\n", debugstr_w(action));
7599 handled = ACTION_HandleStandardAction(package, action, &rc);
7601 if (!handled)
7602 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7604 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7605 handled = TRUE;
7607 if (!handled)
7609 WARN("unhandled msi action %s\n", debugstr_w(action));
7610 rc = ERROR_FUNCTION_NOT_CALLED;
7613 return rc;
7616 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7618 UINT rc = ERROR_SUCCESS;
7619 MSIRECORD *row;
7621 static const WCHAR ExecSeqQuery[] =
7622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7623 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7624 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7625 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7626 static const WCHAR UISeqQuery[] =
7627 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7628 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7629 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7630 ' ', '=',' ','%','i',0};
7632 if (needs_ui_sequence(package))
7633 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7634 else
7635 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7637 if (row)
7639 LPCWSTR action, cond;
7641 TRACE("Running the actions\n");
7643 /* check conditions */
7644 cond = MSI_RecordGetString(row, 2);
7646 /* this is a hack to skip errors in the condition code */
7647 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7649 msiobj_release(&row->hdr);
7650 return ERROR_SUCCESS;
7653 action = MSI_RecordGetString(row, 1);
7654 if (!action)
7656 ERR("failed to fetch action\n");
7657 msiobj_release(&row->hdr);
7658 return ERROR_FUNCTION_FAILED;
7661 if (needs_ui_sequence(package))
7662 rc = ACTION_PerformUIAction(package, action, -1);
7663 else
7664 rc = ACTION_PerformAction(package, action, -1);
7666 msiobj_release(&row->hdr);
7669 return rc;
7672 /****************************************************
7673 * TOP level entry points
7674 *****************************************************/
7676 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7677 LPCWSTR szCommandLine )
7679 UINT rc;
7680 BOOL ui_exists;
7682 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7683 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7685 msi_set_property( package->db, szAction, szInstall );
7687 package->script->InWhatSequence = SEQUENCE_INSTALL;
7689 if (szPackagePath)
7691 LPWSTR p, dir;
7692 LPCWSTR file;
7694 dir = strdupW(szPackagePath);
7695 p = strrchrW(dir, '\\');
7696 if (p)
7698 *(++p) = 0;
7699 file = szPackagePath + (p - dir);
7701 else
7703 msi_free(dir);
7704 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7705 GetCurrentDirectoryW(MAX_PATH, dir);
7706 lstrcatW(dir, szBackSlash);
7707 file = szPackagePath;
7710 msi_free( package->PackagePath );
7711 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7712 if (!package->PackagePath)
7714 msi_free(dir);
7715 return ERROR_OUTOFMEMORY;
7718 lstrcpyW(package->PackagePath, dir);
7719 lstrcatW(package->PackagePath, file);
7720 msi_free(dir);
7722 msi_set_sourcedir_props(package, FALSE);
7725 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7726 if (rc != ERROR_SUCCESS)
7727 return rc;
7729 msi_apply_transforms( package );
7730 msi_apply_patches( package );
7732 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7734 TRACE("setting reinstall property\n");
7735 msi_set_property( package->db, szReinstall, szAll );
7738 /* properties may have been added by a transform */
7739 msi_clone_properties( package );
7741 msi_parse_command_line( package, szCommandLine, FALSE );
7742 msi_adjust_privilege_properties( package );
7743 msi_set_context( package );
7745 if (needs_ui_sequence( package))
7747 package->script->InWhatSequence |= SEQUENCE_UI;
7748 rc = ACTION_ProcessUISequence(package);
7749 ui_exists = ui_sequence_exists(package);
7750 if (rc == ERROR_SUCCESS || !ui_exists)
7752 package->script->InWhatSequence |= SEQUENCE_EXEC;
7753 rc = ACTION_ProcessExecSequence(package, ui_exists);
7756 else
7757 rc = ACTION_ProcessExecSequence(package, FALSE);
7759 package->script->CurrentlyScripting = FALSE;
7761 /* process the ending type action */
7762 if (rc == ERROR_SUCCESS)
7763 ACTION_PerformActionSequence(package, -1);
7764 else if (rc == ERROR_INSTALL_USEREXIT)
7765 ACTION_PerformActionSequence(package, -2);
7766 else if (rc == ERROR_INSTALL_SUSPEND)
7767 ACTION_PerformActionSequence(package, -4);
7768 else /* failed */
7769 ACTION_PerformActionSequence(package, -3);
7771 /* finish up running custom actions */
7772 ACTION_FinishCustomActions(package);
7774 if (rc == ERROR_SUCCESS && package->need_reboot)
7775 return ERROR_SUCCESS_REBOOT_REQUIRED;
7777 return rc;