comctl32/treeview: Always return state from TVM_GETITEM handler.
[wine/multimedia.git] / dlls / msi / action.c
blob4a56485f75e153f92244302546bc0f2b7270809e
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);
47 static const WCHAR szCreateFolders[] =
48 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
49 static const WCHAR szCostFinalize[] =
50 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
51 static const WCHAR szWriteRegistryValues[] =
52 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
53 static const WCHAR szFileCost[] =
54 {'F','i','l','e','C','o','s','t',0};
55 static const WCHAR szInstallInitialize[] =
56 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
57 static const WCHAR szInstallValidate[] =
58 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
59 static const WCHAR szLaunchConditions[] =
60 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
61 static const WCHAR szProcessComponents[] =
62 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
63 static const WCHAR szRegisterTypeLibraries[] =
64 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
65 static const WCHAR szCreateShortcuts[] =
66 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
67 static const WCHAR szPublishProduct[] =
68 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
69 static const WCHAR szWriteIniValues[] =
70 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
71 static const WCHAR szSelfRegModules[] =
72 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
73 static const WCHAR szPublishFeatures[] =
74 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
75 static const WCHAR szRegisterProduct[] =
76 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
77 static const WCHAR szInstallExecute[] =
78 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
79 static const WCHAR szInstallExecuteAgain[] =
80 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
81 static const WCHAR szInstallFinalize[] =
82 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
83 static const WCHAR szForceReboot[] =
84 {'F','o','r','c','e','R','e','b','o','o','t',0};
85 static const WCHAR szResolveSource[] =
86 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
87 static const WCHAR szAllocateRegistrySpace[] =
88 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
89 static const WCHAR szBindImage[] =
90 {'B','i','n','d','I','m','a','g','e',0};
91 static const WCHAR szDeleteServices[] =
92 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
93 static const WCHAR szDisableRollback[] =
94 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
95 static const WCHAR szExecuteAction[] =
96 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
97 static const WCHAR szInstallAdminPackage[] =
98 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
99 static const WCHAR szInstallSFPCatalogFile[] =
100 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
101 static const WCHAR szIsolateComponents[] =
102 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
103 static const WCHAR szMigrateFeatureStates[] =
104 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
105 static const WCHAR szMsiUnpublishAssemblies[] =
106 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
107 static const WCHAR szInstallODBC[] =
108 {'I','n','s','t','a','l','l','O','D','B','C',0};
109 static const WCHAR szInstallServices[] =
110 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
111 static const WCHAR szPublishComponents[] =
112 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
113 static const WCHAR szRegisterComPlus[] =
114 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
115 static const WCHAR szRegisterUser[] =
116 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
117 static const WCHAR szRemoveEnvironmentStrings[] =
118 {'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};
119 static const WCHAR szRemoveExistingProducts[] =
120 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
121 static const WCHAR szRemoveFolders[] =
122 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
123 static const WCHAR szRemoveIniValues[] =
124 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
125 static const WCHAR szRemoveODBC[] =
126 {'R','e','m','o','v','e','O','D','B','C',0};
127 static const WCHAR szRemoveRegistryValues[] =
128 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
129 static const WCHAR szRemoveShortcuts[] =
130 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
131 static const WCHAR szRMCCPSearch[] =
132 {'R','M','C','C','P','S','e','a','r','c','h',0};
133 static const WCHAR szScheduleReboot[] =
134 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
135 static const WCHAR szSelfUnregModules[] =
136 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
137 static const WCHAR szSetODBCFolders[] =
138 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
139 static const WCHAR szStartServices[] =
140 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
141 static const WCHAR szStopServices[] =
142 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
143 static const WCHAR szUnpublishComponents[] =
144 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
145 static const WCHAR szUnpublishFeatures[] =
146 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
147 static const WCHAR szUnregisterComPlus[] =
148 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
149 static const WCHAR szUnregisterTypeLibraries[] =
150 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
151 static const WCHAR szValidateProductID[] =
152 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
153 static const WCHAR szWriteEnvironmentStrings[] =
154 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
156 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
158 static const WCHAR Query_t[] =
159 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
160 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
161 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
162 ' ','\'','%','s','\'',0};
163 MSIRECORD * row;
165 row = MSI_QueryGetRecord( package->db, Query_t, action );
166 if (!row)
167 return;
168 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
169 msiobj_release(&row->hdr);
172 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
173 UINT rc)
175 MSIRECORD * row;
176 static const WCHAR template_s[]=
177 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
178 '%','s', '.',0};
179 static const WCHAR template_e[]=
180 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
181 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
182 '%','i','.',0};
183 static const WCHAR format[] =
184 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
185 WCHAR message[1024];
186 WCHAR timet[0x100];
188 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
189 if (start)
190 sprintfW(message,template_s,timet,action);
191 else
192 sprintfW(message,template_e,timet,action,rc);
194 row = MSI_CreateRecord(1);
195 MSI_RecordSetStringW(row,1,message);
197 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
198 msiobj_release(&row->hdr);
201 enum parse_state
203 state_whitespace,
204 state_token,
205 state_quote
208 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
210 enum parse_state state = state_quote;
211 const WCHAR *p;
212 WCHAR *out = value;
213 int ignore, in_quotes = 0, count = 0, len = 0;
215 for (p = str; *p; p++)
217 ignore = 0;
218 switch (state)
220 case state_whitespace:
221 switch (*p)
223 case ' ':
224 if (!count) goto done;
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes) count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 if (!count) in_quotes = 0;
237 else in_quotes = 1;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = 1;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = 0;
259 else in_quotes = 1;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes) count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = 1;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = 0;
281 else in_quotes = 1;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
337 memcpy( prop, ptr, len * sizeof(WCHAR) );
338 prop[len] = 0;
339 if (!preserve_case) struprW( prop );
341 ptr2++;
342 while (*ptr2 == ' ') ptr2++;
344 num_quotes = 0;
345 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
346 len = parse_prop( ptr2, val, &num_quotes );
347 if (num_quotes % 2)
349 WARN("unbalanced quotes\n");
350 msi_free( val );
351 msi_free( prop );
352 return ERROR_INVALID_COMMAND_LINE;
354 remove_quotes( val );
355 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
357 r = msi_set_property( package->db, prop, val );
358 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
359 msi_reset_folders( package, TRUE );
361 msi_free( val );
362 msi_free( prop );
364 ptr = ptr2 + len;
367 return ERROR_SUCCESS;
370 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
372 LPCWSTR pc;
373 LPWSTR p, *ret = NULL;
374 UINT count = 0;
376 if (!str)
377 return ret;
379 /* count the number of substrings */
380 for ( pc = str, count = 0; pc; count++ )
382 pc = strchrW( pc, sep );
383 if (pc)
384 pc++;
387 /* allocate space for an array of substring pointers and the substrings */
388 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
389 (lstrlenW(str)+1) * sizeof(WCHAR) );
390 if (!ret)
391 return ret;
393 /* copy the string and set the pointers */
394 p = (LPWSTR) &ret[count+1];
395 lstrcpyW( p, str );
396 for( count = 0; (ret[count] = p); count++ )
398 p = strchrW( p, sep );
399 if (p)
400 *p++ = 0;
403 return ret;
406 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
408 static const WCHAR szSystemLanguageID[] =
409 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
411 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
412 UINT ret = ERROR_FUNCTION_FAILED;
414 prod_code = msi_dup_property( package->db, szProductCode );
415 patch_product = msi_get_suminfo_product( patch );
417 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
419 if ( strstrW( patch_product, prod_code ) )
421 MSISUMMARYINFO *si;
422 const WCHAR *p;
424 si = MSI_GetSummaryInformationW( patch, 0 );
425 if (!si)
427 ERR("no summary information!\n");
428 goto end;
431 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
432 if (!template)
434 ERR("no template property!\n");
435 msiobj_release( &si->hdr );
436 goto end;
439 if (!template[0])
441 ret = ERROR_SUCCESS;
442 msiobj_release( &si->hdr );
443 goto end;
446 langid = msi_dup_property( package->db, szSystemLanguageID );
447 if (!langid)
449 msiobj_release( &si->hdr );
450 goto end;
453 p = strchrW( template, ';' );
454 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
456 TRACE("applicable transform\n");
457 ret = ERROR_SUCCESS;
460 /* FIXME: check platform */
462 msiobj_release( &si->hdr );
465 end:
466 msi_free( patch_product );
467 msi_free( prod_code );
468 msi_free( template );
469 msi_free( langid );
471 return ret;
474 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
475 MSIDATABASE *patch_db, LPCWSTR name )
477 UINT ret = ERROR_FUNCTION_FAILED;
478 IStorage *stg = NULL;
479 HRESULT r;
481 TRACE("%p %s\n", package, debugstr_w(name) );
483 if (*name++ != ':')
485 ERR("expected a colon in %s\n", debugstr_w(name));
486 return ERROR_FUNCTION_FAILED;
489 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
490 if (SUCCEEDED(r))
492 ret = msi_check_transform_applicable( package, stg );
493 if (ret == ERROR_SUCCESS)
494 msi_table_apply_transform( package->db, stg );
495 else
496 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
497 IStorage_Release( stg );
499 else
500 ERR("failed to open substorage %s\n", debugstr_w(name));
502 return ERROR_SUCCESS;
505 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
507 LPWSTR guid_list, *guids, product_code;
508 UINT i, ret = ERROR_FUNCTION_FAILED;
510 product_code = msi_dup_property( package->db, szProductCode );
511 if (!product_code)
513 /* FIXME: the property ProductCode should be written into the DB somewhere */
514 ERR("no product code to check\n");
515 return ERROR_SUCCESS;
518 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
519 guids = msi_split_string( guid_list, ';' );
520 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
522 if (!strcmpW( guids[i], product_code ))
523 ret = ERROR_SUCCESS;
525 msi_free( guids );
526 msi_free( guid_list );
527 msi_free( product_code );
529 return ret;
532 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
534 MSIQUERY *view;
535 MSIRECORD *rec = NULL;
536 LPWSTR patch;
537 LPCWSTR prop;
538 UINT r;
540 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
541 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
542 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
543 '`','S','o','u','r','c','e','`',' ','I','S',' ',
544 'N','O','T',' ','N','U','L','L',0};
546 r = MSI_DatabaseOpenViewW(package->db, query, &view);
547 if (r != ERROR_SUCCESS)
548 return r;
550 r = MSI_ViewExecute(view, 0);
551 if (r != ERROR_SUCCESS)
552 goto done;
554 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
556 prop = MSI_RecordGetString(rec, 1);
557 patch = msi_dup_property(package->db, szPatch);
558 msi_set_property(package->db, prop, patch);
559 msi_free(patch);
562 done:
563 if (rec) msiobj_release(&rec->hdr);
564 msiobj_release(&view->hdr);
566 return r;
569 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
571 MSIPATCHINFO *pi;
572 UINT r = ERROR_SUCCESS;
573 WCHAR *p;
575 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
576 if (!pi)
577 return ERROR_OUTOFMEMORY;
579 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
580 if (!pi->patchcode)
582 msi_free( pi );
583 return ERROR_OUTOFMEMORY;
586 p = pi->patchcode;
587 if (*p != '{')
589 msi_free( pi->patchcode );
590 msi_free( pi );
591 return ERROR_PATCH_PACKAGE_INVALID;
594 p = strchrW( p + 1, '}' );
595 if (!p)
597 msi_free( pi->patchcode );
598 msi_free( pi );
599 return ERROR_PATCH_PACKAGE_INVALID;
602 if (p[1])
604 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
605 p[1] = 0;
608 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
610 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
611 if (!pi->transforms)
613 msi_free( pi->patchcode );
614 msi_free( pi );
615 return ERROR_OUTOFMEMORY;
618 *patch = pi;
619 return r;
622 struct msi_patch_offset
624 struct list entry;
625 LPWSTR Name;
626 UINT Sequence;
629 struct msi_patch_offset_list
631 struct list files;
632 UINT count, min, max;
633 UINT offset_to_apply;
636 static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
638 struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
639 list_init( &pos->files );
640 pos->count = pos->max = 0;
641 pos->min = 999999;
643 return pos;
646 static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
648 struct msi_patch_offset *po, *po2;
650 LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
652 msi_free( po->Name );
653 msi_free( po );
656 msi_free( pos );
659 static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
661 MSIQUERY *view;
662 MSIRECORD *rec;
663 UINT r;
664 static const WCHAR query_patch[] = {
665 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
666 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
667 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
669 r = MSI_DatabaseOpenViewW( db, query_patch, &view );
670 if (r != ERROR_SUCCESS)
671 return;
673 rec = MSI_CreateRecord( 1 );
674 MSI_RecordSetInteger(rec, 1, last_sequence);
676 r = MSI_ViewExecute( view, rec );
677 msiobj_release( &rec->hdr );
678 if (r != ERROR_SUCCESS)
679 return;
681 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
683 UINT sequence = MSI_RecordGetInteger( rec, 2 );
685 /* FIXME:
686 * We only use the max/min sequence numbers for now.
689 pos->min = min(pos->min, sequence);
690 pos->max = max(pos->max, sequence);
691 pos->count++;
693 msiobj_release( &rec->hdr );
696 msiobj_release( &view->hdr );
699 static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
701 MSIQUERY *view;
702 MSIRECORD *rec;
703 UINT r;
704 static const WCHAR query_files[] = {
705 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
706 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
707 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
709 r = MSI_DatabaseOpenViewW( db, query_files, &view );
710 if (r != ERROR_SUCCESS)
711 return;
713 rec = MSI_CreateRecord( 1 );
714 MSI_RecordSetInteger(rec, 1, last_sequence);
716 r = MSI_ViewExecute( view, rec );
717 msiobj_release( &rec->hdr );
718 if (r != ERROR_SUCCESS)
719 return;
721 while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
723 UINT attributes = MSI_RecordGetInteger( rec, 7 );
724 if (attributes & msidbFileAttributesPatchAdded)
726 struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
728 po->Name = msi_dup_record_field( rec, 1 );
729 po->Sequence = MSI_RecordGetInteger( rec, 8 );
731 pos->min = min(pos->min, po->Sequence);
732 pos->max = max(pos->max, po->Sequence);
734 list_add_tail( &pos->files, &po->entry );
735 pos->count++;
737 msiobj_release( &rec->hdr );
740 msiobj_release( &view->hdr );
743 static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
745 static const WCHAR query_files[] =
746 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
747 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
748 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
749 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
750 struct msi_patch_offset *po;
751 MSIQUERY *view;
752 MSIRECORD *rec;
753 UINT r;
755 r = MSI_DatabaseOpenViewW( db, query_files, &view );
756 if (r != ERROR_SUCCESS)
757 return ERROR_SUCCESS;
759 rec = MSI_CreateRecord( 2 );
760 MSI_RecordSetInteger( rec, 1, pos->min );
761 MSI_RecordSetInteger( rec, 2, pos->max );
763 r = MSI_ViewExecute( view, rec );
764 msiobj_release( &rec->hdr );
765 if (r != ERROR_SUCCESS)
766 goto done;
768 LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
770 UINT r_fetch;
771 while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
773 LPCWSTR file = MSI_RecordGetString( rec, 1 );
774 UINT seq;
776 if (!strcmpiW(file, po->Name))
778 /* Update record */
779 seq = MSI_RecordGetInteger( rec, 8 );
780 MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
781 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
782 if (r != ERROR_SUCCESS)
783 ERR("Failed to update offset for file %s.\n", debugstr_w(file));
785 msiobj_release( &rec->hdr );
786 break;
789 msiobj_release( &rec->hdr );
792 if (r_fetch != ERROR_SUCCESS)
793 break;
796 done:
797 msiobj_release( &view->hdr );
799 return ERROR_SUCCESS;
802 static const WCHAR patch_media_query[] = {
803 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
804 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
805 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
806 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
808 struct patch_media
810 struct list entry;
811 UINT disk_id;
812 UINT last_sequence;
813 WCHAR *prompt;
814 WCHAR *cabinet;
815 WCHAR *volume;
816 WCHAR *source;
819 static UINT msi_add_patch_media( MSIPACKAGE *package, IStorage *patch )
821 static const WCHAR delete_query[] = {
822 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
823 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
824 static const WCHAR insert_query[] = {
825 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
826 '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
827 '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
828 '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
829 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
830 MSIQUERY *view;
831 MSIRECORD *rec = NULL;
832 UINT r, disk_id;
833 struct list media_list;
834 struct patch_media *media, *next;
836 r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
837 if (r != ERROR_SUCCESS) return r;
839 r = MSI_ViewExecute( view, 0 );
840 if (r != ERROR_SUCCESS)
842 msiobj_release( &view->hdr );
843 TRACE("query failed %u\n", r);
844 return r;
847 list_init( &media_list );
848 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
850 disk_id = MSI_RecordGetInteger( rec, 1 );
851 TRACE("disk_id %u\n", disk_id);
852 if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
854 msiobj_release( &rec->hdr );
855 continue;
857 if (!(media = msi_alloc( sizeof( *media )))) goto done;
858 media->disk_id = disk_id;
859 media->last_sequence = MSI_RecordGetInteger( rec, 2 );
860 media->prompt = msi_dup_record_field( rec, 3 );
861 media->cabinet = msi_dup_record_field( rec, 4 );
862 media->volume = msi_dup_record_field( rec, 5 );
863 media->source = msi_dup_record_field( rec, 6 );
865 list_add_tail( &media_list, &media->entry );
866 msiobj_release( &rec->hdr );
868 LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
870 MSIQUERY *delete_view, *insert_view;
872 r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
873 if (r != ERROR_SUCCESS) goto done;
875 rec = MSI_CreateRecord( 1 );
876 MSI_RecordSetInteger( rec, 1, media->disk_id );
878 r = MSI_ViewExecute( delete_view, rec );
879 msiobj_release( &delete_view->hdr );
880 msiobj_release( &rec->hdr );
881 if (r != ERROR_SUCCESS) goto done;
883 r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
884 if (r != ERROR_SUCCESS) goto done;
886 disk_id = package->db->media_transform_disk_id;
887 TRACE("disk id %u\n", disk_id);
888 TRACE("last sequence %u\n", media->last_sequence);
889 TRACE("prompt %s\n", debugstr_w(media->prompt));
890 TRACE("cabinet %s\n", debugstr_w(media->cabinet));
891 TRACE("volume %s\n", debugstr_w(media->volume));
892 TRACE("source %s\n", debugstr_w(media->source));
894 rec = MSI_CreateRecord( 6 );
895 MSI_RecordSetInteger( rec, 1, disk_id );
896 MSI_RecordSetInteger( rec, 2, media->last_sequence );
897 MSI_RecordSetStringW( rec, 3, media->prompt );
898 MSI_RecordSetStringW( rec, 4, media->cabinet );
899 MSI_RecordSetStringW( rec, 5, media->volume );
900 MSI_RecordSetStringW( rec, 6, media->source );
902 r = MSI_ViewExecute( insert_view, rec );
903 msiobj_release( &insert_view->hdr );
904 msiobj_release( &rec->hdr );
905 if (r != ERROR_SUCCESS) goto done;
907 r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
908 if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
909 package->db->media_transform_disk_id++;
912 done:
913 msiobj_release( &view->hdr );
914 LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
916 list_remove( &media->entry );
917 msi_free( media->prompt );
918 msi_free( media->cabinet );
919 msi_free( media->volume );
920 msi_free( media->source );
921 msi_free( media );
923 return r;
926 static UINT msi_set_patch_offsets(MSIDATABASE *db)
928 MSIQUERY *view;
929 MSIRECORD *rec = NULL;
930 UINT r;
932 r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
933 if (r != ERROR_SUCCESS)
934 return r;
936 r = MSI_ViewExecute( view, 0 );
937 if (r != ERROR_SUCCESS)
938 goto done;
940 while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
942 UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
943 struct msi_patch_offset_list *pos;
945 /* FIXME: Set/Check Source field instead? */
946 if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
948 msiobj_release( &rec->hdr );
949 continue;
952 pos = msi_patch_offset_list_create();
954 msi_patch_offset_get_files( db, last_sequence, pos );
955 msi_patch_offset_get_patches( db, last_sequence, pos );
957 if (pos->count)
959 UINT offset = db->media_transform_offset - pos->min;
960 last_sequence = offset + pos->max;
962 /* FIXME:
963 * This is for the patch table, which is not yet properly transformed.
965 last_sequence += pos->min;
967 pos->offset_to_apply = offset;
968 msi_patch_offset_modify_db( db, pos );
970 MSI_RecordSetInteger( rec, 2, last_sequence );
971 r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
972 if (r != ERROR_SUCCESS)
973 ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
975 db->media_transform_offset = last_sequence + 1;
978 msi_patch_offset_list_free( pos );
979 msiobj_release( &rec->hdr );
982 done:
983 msiobj_release( &view->hdr );
984 return r;
987 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
989 UINT i, r = ERROR_SUCCESS;
990 WCHAR **substorage;
992 /* apply substorage transforms */
993 substorage = msi_split_string( patch->transforms, ';' );
994 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
996 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
997 if (r == ERROR_SUCCESS)
999 msi_add_patch_media( package, patch_db->storage );
1000 msi_set_patch_offsets( package->db );
1004 msi_free( substorage );
1005 if (r != ERROR_SUCCESS)
1006 return r;
1008 msi_set_media_source_prop( package );
1010 patch->state = MSIPATCHSTATE_APPLIED;
1011 list_add_tail( &package->patches, &patch->entry );
1012 return ERROR_SUCCESS;
1015 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
1017 static const WCHAR dotmsp[] = {'.','m','s','p',0};
1018 MSIDATABASE *patch_db = NULL;
1019 WCHAR localfile[MAX_PATH];
1020 MSISUMMARYINFO *si;
1021 MSIPATCHINFO *patch = NULL;
1022 UINT r = ERROR_SUCCESS;
1024 TRACE("%p %s\n", package, debugstr_w( file ) );
1026 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
1027 if ( r != ERROR_SUCCESS )
1029 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
1030 return r;
1033 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
1034 if (!si)
1036 msiobj_release( &patch_db->hdr );
1037 return ERROR_FUNCTION_FAILED;
1040 r = msi_check_patch_applicable( package, si );
1041 if (r != ERROR_SUCCESS)
1043 TRACE("patch not applicable\n");
1044 r = ERROR_SUCCESS;
1045 goto done;
1048 r = msi_parse_patch_summary( si, &patch );
1049 if ( r != ERROR_SUCCESS )
1050 goto done;
1052 r = msi_get_local_package_name( localfile, dotmsp );
1053 if ( r != ERROR_SUCCESS )
1054 goto done;
1056 TRACE("copying to local package %s\n", debugstr_w(localfile));
1058 if (!CopyFileW( file, localfile, FALSE ))
1060 ERR("Unable to copy package (%s -> %s) (error %u)\n",
1061 debugstr_w(file), debugstr_w(localfile), GetLastError());
1062 r = GetLastError();
1063 goto done;
1065 patch->localfile = strdupW( localfile );
1067 r = msi_apply_patch_db( package, patch_db, patch );
1068 if ( r != ERROR_SUCCESS )
1069 WARN("patch failed to apply %u\n", r);
1071 done:
1072 msiobj_release( &si->hdr );
1073 msiobj_release( &patch_db->hdr );
1074 if (patch && r != ERROR_SUCCESS)
1076 if (patch->localfile)
1077 DeleteFileW( patch->localfile );
1079 msi_free( patch->patchcode );
1080 msi_free( patch->transforms );
1081 msi_free( patch->localfile );
1082 msi_free( patch );
1084 return r;
1087 /* get the PATCH property, and apply all the patches it specifies */
1088 static UINT msi_apply_patches( MSIPACKAGE *package )
1090 LPWSTR patch_list, *patches;
1091 UINT i, r = ERROR_SUCCESS;
1093 patch_list = msi_dup_property( package->db, szPatch );
1095 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
1097 patches = msi_split_string( patch_list, ';' );
1098 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
1099 r = msi_apply_patch_package( package, patches[i] );
1101 msi_free( patches );
1102 msi_free( patch_list );
1104 return r;
1107 static UINT msi_apply_transforms( MSIPACKAGE *package )
1109 static const WCHAR szTransforms[] = {
1110 'T','R','A','N','S','F','O','R','M','S',0 };
1111 LPWSTR xform_list, *xforms;
1112 UINT i, r = ERROR_SUCCESS;
1114 xform_list = msi_dup_property( package->db, szTransforms );
1115 xforms = msi_split_string( xform_list, ';' );
1117 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
1119 if (xforms[i][0] == ':')
1120 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
1121 else
1123 WCHAR *transform;
1125 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
1126 else
1128 WCHAR *p = strrchrW( package->PackagePath, '\\' );
1129 DWORD len = p - package->PackagePath + 1;
1131 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
1133 msi_free( xforms );
1134 msi_free( xform_list );
1135 return ERROR_OUTOFMEMORY;
1137 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
1138 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
1140 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
1141 if (transform != xforms[i]) msi_free( transform );
1145 msi_free( xforms );
1146 msi_free( xform_list );
1148 return r;
1151 static BOOL ui_sequence_exists( MSIPACKAGE *package )
1153 MSIQUERY *view;
1154 UINT rc;
1156 static const WCHAR ExecSeqQuery [] =
1157 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1158 '`','I','n','s','t','a','l','l',
1159 'U','I','S','e','q','u','e','n','c','e','`',
1160 ' ','W','H','E','R','E',' ',
1161 '`','S','e','q','u','e','n','c','e','`',' ',
1162 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1163 '`','S','e','q','u','e','n','c','e','`',0};
1165 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1166 if (rc == ERROR_SUCCESS)
1168 msiobj_release(&view->hdr);
1169 return TRUE;
1172 return FALSE;
1175 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
1177 LPWSTR source, check;
1179 if (msi_get_property_int( package->db, szInstalled, 0 ))
1181 HKEY hkey;
1183 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
1184 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
1185 RegCloseKey( hkey );
1187 else
1189 LPWSTR p, db;
1190 DWORD len;
1192 db = msi_dup_property( package->db, szOriginalDatabase );
1193 if (!db)
1194 return ERROR_OUTOFMEMORY;
1196 p = strrchrW( db, '\\' );
1197 if (!p)
1199 p = strrchrW( db, '/' );
1200 if (!p)
1202 msi_free(db);
1203 return ERROR_SUCCESS;
1207 len = p - db + 2;
1208 source = msi_alloc( len * sizeof(WCHAR) );
1209 lstrcpynW( source, db, len );
1210 msi_free( db );
1213 check = msi_dup_property( package->db, szSourceDir );
1214 if (!check || replace)
1216 UINT r = msi_set_property( package->db, szSourceDir, source );
1217 if (r == ERROR_SUCCESS)
1218 msi_reset_folders( package, TRUE );
1220 msi_free( check );
1222 check = msi_dup_property( package->db, szSOURCEDIR );
1223 if (!check || replace)
1224 msi_set_property( package->db, szSOURCEDIR, source );
1226 msi_free( check );
1227 msi_free( source );
1229 return ERROR_SUCCESS;
1232 static BOOL needs_ui_sequence(MSIPACKAGE *package)
1234 INT level = msi_get_property_int(package->db, szUILevel, 0);
1235 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
1238 UINT msi_set_context(MSIPACKAGE *package)
1240 int num;
1242 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
1244 num = msi_get_property_int(package->db, szAllUsers, 0);
1245 if (num == 1 || num == 2)
1246 package->Context = MSIINSTALLCONTEXT_MACHINE;
1248 return ERROR_SUCCESS;
1251 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
1253 UINT rc;
1254 LPCWSTR cond, action;
1255 MSIPACKAGE *package = param;
1257 action = MSI_RecordGetString(row,1);
1258 if (!action)
1260 ERR("Error is retrieving action name\n");
1261 return ERROR_FUNCTION_FAILED;
1264 /* check conditions */
1265 cond = MSI_RecordGetString(row,2);
1267 /* this is a hack to skip errors in the condition code */
1268 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
1270 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
1271 return ERROR_SUCCESS;
1274 if (needs_ui_sequence(package))
1275 rc = ACTION_PerformUIAction(package, action, -1);
1276 else
1277 rc = ACTION_PerformAction(package, action, -1);
1279 msi_dialog_check_messages( NULL );
1281 if (package->CurrentInstallState != ERROR_SUCCESS)
1282 rc = package->CurrentInstallState;
1284 if (rc == ERROR_FUNCTION_NOT_CALLED)
1285 rc = ERROR_SUCCESS;
1287 if (rc != ERROR_SUCCESS)
1288 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
1290 return rc;
1293 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
1295 MSIQUERY * view;
1296 UINT r;
1297 static const WCHAR query[] =
1298 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1299 '`','%','s','`',
1300 ' ','W','H','E','R','E',' ',
1301 '`','S','e','q','u','e','n','c','e','`',' ',
1302 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1303 '`','S','e','q','u','e','n','c','e','`',0};
1305 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1307 r = MSI_OpenQuery( package->db, &view, query, szTable );
1308 if (r == ERROR_SUCCESS)
1310 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
1311 msiobj_release(&view->hdr);
1314 return r;
1317 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1319 MSIQUERY * view;
1320 UINT rc;
1321 static const WCHAR ExecSeqQuery[] =
1322 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1323 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1324 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1325 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1326 'O','R','D','E','R',' ', 'B','Y',' ',
1327 '`','S','e','q','u','e','n','c','e','`',0 };
1328 static const WCHAR IVQuery[] =
1329 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1330 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1331 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1332 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1333 ' ','\'', 'I','n','s','t','a','l','l',
1334 'V','a','l','i','d','a','t','e','\'', 0};
1335 INT seq = 0;
1337 if (package->script->ExecuteSequenceRun)
1339 TRACE("Execute Sequence already Run\n");
1340 return ERROR_SUCCESS;
1343 package->script->ExecuteSequenceRun = TRUE;
1345 /* get the sequence number */
1346 if (UIran)
1348 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
1349 if( !row )
1350 return ERROR_FUNCTION_FAILED;
1351 seq = MSI_RecordGetInteger(row,1);
1352 msiobj_release(&row->hdr);
1355 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1356 if (rc == ERROR_SUCCESS)
1358 TRACE("Running the actions\n");
1360 msi_set_property(package->db, szSourceDir, NULL);
1362 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1363 msiobj_release(&view->hdr);
1366 return rc;
1369 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1371 MSIQUERY * view;
1372 UINT rc;
1373 static const WCHAR ExecSeqQuery [] =
1374 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1375 '`','I','n','s','t','a','l','l',
1376 'U','I','S','e','q','u','e','n','c','e','`',
1377 ' ','W','H','E','R','E',' ',
1378 '`','S','e','q','u','e','n','c','e','`',' ',
1379 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1380 '`','S','e','q','u','e','n','c','e','`',0};
1382 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1383 if (rc == ERROR_SUCCESS)
1385 TRACE("Running the actions\n");
1387 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1388 msiobj_release(&view->hdr);
1391 return rc;
1394 /********************************************************
1395 * ACTION helper functions and functions that perform the actions
1396 *******************************************************/
1397 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1398 UINT* rc, UINT script, BOOL force )
1400 BOOL ret=FALSE;
1401 UINT arc;
1403 arc = ACTION_CustomAction(package, action, script, force);
1405 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1407 *rc = arc;
1408 ret = TRUE;
1410 return ret;
1413 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
1415 MSICOMPONENT *comp;
1417 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1419 if (!strcmpW( Component, comp->Component )) return comp;
1421 return NULL;
1424 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
1426 MSIFEATURE *feature;
1428 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1430 if (!strcmpW( Feature, feature->Feature )) return feature;
1432 return NULL;
1435 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
1437 MSIFILE *file;
1439 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1441 if (!strcmpW( key, file->File )) return file;
1443 return NULL;
1446 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
1448 MSIFILEPATCH *patch;
1450 /* FIXME: There might be more than one patch */
1451 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
1453 if (!strcmpW( key, patch->File->File )) return patch;
1455 return NULL;
1458 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
1460 MSIFOLDER *folder;
1462 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
1464 if (!strcmpW( dir, folder->Directory )) return folder;
1466 return NULL;
1470 * Recursively create all directories in the path.
1471 * shamelessly stolen from setupapi/queue.c
1473 BOOL msi_create_full_path( const WCHAR *path )
1475 BOOL ret = TRUE;
1476 WCHAR *new_path;
1477 int len;
1479 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
1480 strcpyW( new_path, path );
1482 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
1483 new_path[len - 1] = 0;
1485 while (!CreateDirectoryW( new_path, NULL ))
1487 WCHAR *slash;
1488 DWORD last_error = GetLastError();
1489 if (last_error == ERROR_ALREADY_EXISTS) break;
1490 if (last_error != ERROR_PATH_NOT_FOUND)
1492 ret = FALSE;
1493 break;
1495 if (!(slash = strrchrW( new_path, '\\' )))
1497 ret = FALSE;
1498 break;
1500 len = slash - new_path;
1501 new_path[len] = 0;
1502 if (!msi_create_full_path( new_path ))
1504 ret = FALSE;
1505 break;
1507 new_path[len] = '\\';
1509 msi_free( new_path );
1510 return ret;
1513 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
1515 MSIRECORD *row;
1517 row = MSI_CreateRecord( 4 );
1518 MSI_RecordSetInteger( row, 1, a );
1519 MSI_RecordSetInteger( row, 2, b );
1520 MSI_RecordSetInteger( row, 3, c );
1521 MSI_RecordSetInteger( row, 4, d );
1522 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
1523 msiobj_release( &row->hdr );
1525 msi_dialog_check_messages( NULL );
1528 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
1530 static const WCHAR query[] =
1531 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1532 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
1533 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
1534 WCHAR message[1024];
1535 MSIRECORD *row = 0;
1536 DWORD size;
1538 if (!package->LastAction || strcmpW( package->LastAction, action ))
1540 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
1542 if (MSI_RecordIsNull( row, 3 ))
1544 msiobj_release( &row->hdr );
1545 return;
1547 /* update the cached action format */
1548 msi_free( package->ActionFormat );
1549 package->ActionFormat = msi_dup_record_field( row, 3 );
1550 msi_free( package->LastAction );
1551 package->LastAction = strdupW( action );
1552 msiobj_release( &row->hdr );
1554 size = 1024;
1555 MSI_RecordSetStringW( record, 0, package->ActionFormat );
1556 MSI_FormatRecordW( package, record, message, &size );
1557 row = MSI_CreateRecord( 1 );
1558 MSI_RecordSetStringW( row, 1, message );
1559 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
1560 msiobj_release( &row->hdr );
1563 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1565 MSIPACKAGE *package = param;
1566 LPCWSTR dir, component, full_path;
1567 MSIRECORD *uirow;
1568 MSIFOLDER *folder;
1569 MSICOMPONENT *comp;
1571 component = MSI_RecordGetString(row, 2);
1572 if (!component)
1573 return ERROR_SUCCESS;
1575 comp = msi_get_loaded_component(package, component);
1576 if (!comp)
1577 return ERROR_SUCCESS;
1579 if (!comp->Enabled)
1581 TRACE("component is disabled\n");
1582 return ERROR_SUCCESS;
1585 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1587 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1588 comp->Action = comp->Installed;
1589 return ERROR_SUCCESS;
1591 comp->Action = INSTALLSTATE_LOCAL;
1593 dir = MSI_RecordGetString(row,1);
1594 if (!dir)
1596 ERR("Unable to get folder id\n");
1597 return ERROR_SUCCESS;
1600 uirow = MSI_CreateRecord(1);
1601 MSI_RecordSetStringW(uirow, 1, dir);
1602 msi_ui_actiondata(package, szCreateFolders, uirow);
1603 msiobj_release(&uirow->hdr);
1605 full_path = msi_get_target_folder( package, dir );
1606 if (!full_path)
1608 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
1609 return ERROR_SUCCESS;
1611 TRACE("folder is %s\n", debugstr_w(full_path));
1613 folder = msi_get_loaded_folder( package, dir );
1614 if (folder->State == 0) msi_create_full_path( full_path );
1615 folder->State = 3;
1616 return ERROR_SUCCESS;
1619 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1621 static const WCHAR query[] =
1622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1623 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1624 UINT rc;
1625 MSIQUERY *view;
1627 /* create all the empty folders specified in the CreateFolder table */
1628 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1629 if (rc != ERROR_SUCCESS)
1630 return ERROR_SUCCESS;
1632 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1633 msiobj_release(&view->hdr);
1635 return rc;
1638 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1640 MSIPACKAGE *package = param;
1641 LPCWSTR dir, component, full_path;
1642 MSIRECORD *uirow;
1643 MSIFOLDER *folder;
1644 MSICOMPONENT *comp;
1646 component = MSI_RecordGetString(row, 2);
1647 if (!component)
1648 return ERROR_SUCCESS;
1650 comp = msi_get_loaded_component(package, component);
1651 if (!comp)
1652 return ERROR_SUCCESS;
1654 if (!comp->Enabled)
1656 TRACE("component is disabled\n");
1657 return ERROR_SUCCESS;
1660 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1662 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1663 comp->Action = comp->Installed;
1664 return ERROR_SUCCESS;
1666 comp->Action = INSTALLSTATE_ABSENT;
1668 dir = MSI_RecordGetString( row, 1 );
1669 if (!dir)
1671 ERR("Unable to get folder id\n");
1672 return ERROR_SUCCESS;
1675 full_path = msi_get_target_folder( package, dir );
1676 if (!full_path)
1678 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
1679 return ERROR_SUCCESS;
1681 TRACE("folder is %s\n", debugstr_w(full_path));
1683 uirow = MSI_CreateRecord( 1 );
1684 MSI_RecordSetStringW( uirow, 1, dir );
1685 msi_ui_actiondata( package, szRemoveFolders, uirow );
1686 msiobj_release( &uirow->hdr );
1688 RemoveDirectoryW( full_path );
1689 folder = msi_get_loaded_folder( package, dir );
1690 folder->State = 0;
1691 return ERROR_SUCCESS;
1694 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1696 static const WCHAR query[] =
1697 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1698 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1700 MSIQUERY *view;
1701 UINT rc;
1703 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1704 if (rc != ERROR_SUCCESS)
1705 return ERROR_SUCCESS;
1707 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1708 msiobj_release( &view->hdr );
1710 return rc;
1713 static UINT load_component( MSIRECORD *row, LPVOID param )
1715 MSIPACKAGE *package = param;
1716 MSICOMPONENT *comp;
1718 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1719 if (!comp)
1720 return ERROR_FUNCTION_FAILED;
1722 list_add_tail( &package->components, &comp->entry );
1724 /* fill in the data */
1725 comp->Component = msi_dup_record_field( row, 1 );
1727 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1729 comp->ComponentId = msi_dup_record_field( row, 2 );
1730 comp->Directory = msi_dup_record_field( row, 3 );
1731 comp->Attributes = MSI_RecordGetInteger(row,4);
1732 comp->Condition = msi_dup_record_field( row, 5 );
1733 comp->KeyPath = msi_dup_record_field( row, 6 );
1735 comp->Installed = INSTALLSTATE_UNKNOWN;
1736 comp->Action = INSTALLSTATE_UNKNOWN;
1737 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1739 comp->assembly = msi_load_assembly( package, comp );
1740 return ERROR_SUCCESS;
1743 static UINT load_all_components( MSIPACKAGE *package )
1745 static const WCHAR query[] = {
1746 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1747 '`','C','o','m','p','o','n','e','n','t','`',0 };
1748 MSIQUERY *view;
1749 UINT r;
1751 if (!list_empty(&package->components))
1752 return ERROR_SUCCESS;
1754 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1755 if (r != ERROR_SUCCESS)
1756 return r;
1758 if (!msi_init_assembly_caches( package ))
1760 ERR("can't initialize assembly caches\n");
1761 msiobj_release( &view->hdr );
1762 return ERROR_FUNCTION_FAILED;
1765 r = MSI_IterateRecords(view, NULL, load_component, package);
1766 msiobj_release(&view->hdr);
1768 msi_destroy_assembly_caches( package );
1769 return r;
1772 typedef struct {
1773 MSIPACKAGE *package;
1774 MSIFEATURE *feature;
1775 } _ilfs;
1777 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1779 ComponentList *cl;
1781 cl = msi_alloc( sizeof (*cl) );
1782 if ( !cl )
1783 return ERROR_NOT_ENOUGH_MEMORY;
1784 cl->component = comp;
1785 list_add_tail( &feature->Components, &cl->entry );
1787 return ERROR_SUCCESS;
1790 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1792 FeatureList *fl;
1794 fl = msi_alloc( sizeof(*fl) );
1795 if ( !fl )
1796 return ERROR_NOT_ENOUGH_MEMORY;
1797 fl->feature = child;
1798 list_add_tail( &parent->Children, &fl->entry );
1800 return ERROR_SUCCESS;
1803 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1805 _ilfs* ilfs = param;
1806 LPCWSTR component;
1807 MSICOMPONENT *comp;
1809 component = MSI_RecordGetString(row,1);
1811 /* check to see if the component is already loaded */
1812 comp = msi_get_loaded_component( ilfs->package, component );
1813 if (!comp)
1815 ERR("unknown component %s\n", debugstr_w(component));
1816 return ERROR_FUNCTION_FAILED;
1819 add_feature_component( ilfs->feature, comp );
1820 comp->Enabled = TRUE;
1822 return ERROR_SUCCESS;
1825 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1827 MSIFEATURE *feature;
1829 if ( !name )
1830 return NULL;
1832 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1834 if ( !strcmpW( feature->Feature, name ) )
1835 return feature;
1838 return NULL;
1841 static UINT load_feature(MSIRECORD * row, LPVOID param)
1843 MSIPACKAGE* package = param;
1844 MSIFEATURE* feature;
1845 static const WCHAR Query1[] =
1846 {'S','E','L','E','C','T',' ',
1847 '`','C','o','m','p','o','n','e','n','t','_','`',
1848 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1849 'C','o','m','p','o','n','e','n','t','s','`',' ',
1850 'W','H','E','R','E',' ',
1851 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1852 MSIQUERY * view;
1853 UINT rc;
1854 _ilfs ilfs;
1856 /* fill in the data */
1858 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1859 if (!feature)
1860 return ERROR_NOT_ENOUGH_MEMORY;
1862 list_init( &feature->Children );
1863 list_init( &feature->Components );
1865 feature->Feature = msi_dup_record_field( row, 1 );
1867 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1869 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1870 feature->Title = msi_dup_record_field( row, 3 );
1871 feature->Description = msi_dup_record_field( row, 4 );
1873 if (!MSI_RecordIsNull(row,5))
1874 feature->Display = MSI_RecordGetInteger(row,5);
1876 feature->Level= MSI_RecordGetInteger(row,6);
1877 feature->Directory = msi_dup_record_field( row, 7 );
1878 feature->Attributes = MSI_RecordGetInteger(row,8);
1880 feature->Installed = INSTALLSTATE_UNKNOWN;
1881 feature->Action = INSTALLSTATE_UNKNOWN;
1882 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1884 list_add_tail( &package->features, &feature->entry );
1886 /* load feature components */
1888 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1889 if (rc != ERROR_SUCCESS)
1890 return ERROR_SUCCESS;
1892 ilfs.package = package;
1893 ilfs.feature = feature;
1895 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1896 msiobj_release(&view->hdr);
1898 return ERROR_SUCCESS;
1901 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1903 MSIPACKAGE* package = param;
1904 MSIFEATURE *parent, *child;
1906 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1907 if (!child)
1908 return ERROR_FUNCTION_FAILED;
1910 if (!child->Feature_Parent)
1911 return ERROR_SUCCESS;
1913 parent = find_feature_by_name( package, child->Feature_Parent );
1914 if (!parent)
1915 return ERROR_FUNCTION_FAILED;
1917 add_feature_child( parent, child );
1918 return ERROR_SUCCESS;
1921 static UINT load_all_features( MSIPACKAGE *package )
1923 static const WCHAR query[] = {
1924 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1925 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1926 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1927 MSIQUERY *view;
1928 UINT r;
1930 if (!list_empty(&package->features))
1931 return ERROR_SUCCESS;
1933 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1934 if (r != ERROR_SUCCESS)
1935 return r;
1937 r = MSI_IterateRecords( view, NULL, load_feature, package );
1938 if (r != ERROR_SUCCESS)
1939 return r;
1941 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1942 msiobj_release( &view->hdr );
1944 return r;
1947 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1949 if (!p)
1950 return p;
1951 p = strchrW(p, ch);
1952 if (!p)
1953 return p;
1954 *p = 0;
1955 return p+1;
1958 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1960 static const WCHAR query[] = {
1961 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1962 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1963 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1964 MSIQUERY *view = NULL;
1965 MSIRECORD *row = NULL;
1966 UINT r;
1968 TRACE("%s\n", debugstr_w(file->File));
1970 r = MSI_OpenQuery(package->db, &view, query, file->File);
1971 if (r != ERROR_SUCCESS)
1972 goto done;
1974 r = MSI_ViewExecute(view, NULL);
1975 if (r != ERROR_SUCCESS)
1976 goto done;
1978 r = MSI_ViewFetch(view, &row);
1979 if (r != ERROR_SUCCESS)
1980 goto done;
1982 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1983 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1984 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1985 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1986 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1988 done:
1989 if (view) msiobj_release(&view->hdr);
1990 if (row) msiobj_release(&row->hdr);
1991 return r;
1994 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1996 MSIRECORD *row;
1997 static const WCHAR query[] = {
1998 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1999 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2000 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
2002 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
2003 if (!row)
2005 WARN("query failed\n");
2006 return ERROR_FUNCTION_FAILED;
2009 file->disk_id = MSI_RecordGetInteger( row, 1 );
2010 msiobj_release( &row->hdr );
2011 return ERROR_SUCCESS;
2014 static UINT load_file(MSIRECORD *row, LPVOID param)
2016 MSIPACKAGE* package = param;
2017 LPCWSTR component;
2018 MSIFILE *file;
2020 /* fill in the data */
2022 file = msi_alloc_zero( sizeof (MSIFILE) );
2023 if (!file)
2024 return ERROR_NOT_ENOUGH_MEMORY;
2026 file->File = msi_dup_record_field( row, 1 );
2028 component = MSI_RecordGetString( row, 2 );
2029 file->Component = msi_get_loaded_component( package, component );
2031 if (!file->Component)
2033 WARN("Component not found: %s\n", debugstr_w(component));
2034 msi_free(file->File);
2035 msi_free(file);
2036 return ERROR_SUCCESS;
2039 file->FileName = msi_dup_record_field( row, 3 );
2040 msi_reduce_to_long_filename( file->FileName );
2042 file->ShortName = msi_dup_record_field( row, 3 );
2043 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
2045 file->FileSize = MSI_RecordGetInteger( row, 4 );
2046 file->Version = msi_dup_record_field( row, 5 );
2047 file->Language = msi_dup_record_field( row, 6 );
2048 file->Attributes = MSI_RecordGetInteger( row, 7 );
2049 file->Sequence = MSI_RecordGetInteger( row, 8 );
2051 file->state = msifs_invalid;
2053 /* if the compressed bits are not set in the file attributes,
2054 * then read the information from the package word count property
2056 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
2058 file->IsCompressed = FALSE;
2060 else if (file->Attributes &
2061 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
2063 file->IsCompressed = TRUE;
2065 else if (file->Attributes & msidbFileAttributesNoncompressed)
2067 file->IsCompressed = FALSE;
2069 else
2071 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
2074 load_file_hash(package, file);
2075 load_file_disk_id(package, file);
2077 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
2079 list_add_tail( &package->files, &file->entry );
2081 return ERROR_SUCCESS;
2084 static UINT load_all_files(MSIPACKAGE *package)
2086 MSIQUERY * view;
2087 UINT rc;
2088 static const WCHAR Query[] =
2089 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2090 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
2091 '`','S','e','q','u','e','n','c','e','`', 0};
2093 if (!list_empty(&package->files))
2094 return ERROR_SUCCESS;
2096 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2097 if (rc != ERROR_SUCCESS)
2098 return ERROR_SUCCESS;
2100 rc = MSI_IterateRecords(view, NULL, load_file, package);
2101 msiobj_release(&view->hdr);
2103 return ERROR_SUCCESS;
2106 static UINT load_media( MSIRECORD *row, LPVOID param )
2108 MSIPACKAGE *package = param;
2109 UINT disk_id = MSI_RecordGetInteger( row, 1 );
2110 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
2112 /* FIXME: load external cabinets and directory sources too */
2113 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
2114 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
2115 return ERROR_SUCCESS;
2118 static UINT load_all_media( MSIPACKAGE *package )
2120 static const WCHAR query[] =
2121 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
2122 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
2123 MSIQUERY *view;
2124 UINT r;
2126 if (!list_empty( &package->cabinet_streams )) return ERROR_SUCCESS;
2128 r = MSI_DatabaseOpenViewW( package->db, query, &view );
2129 if (r != ERROR_SUCCESS) return ERROR_SUCCESS;
2131 MSI_IterateRecords( view, NULL, load_media, package );
2132 msiobj_release( &view->hdr );
2133 return ERROR_SUCCESS;
2136 static UINT load_patch(MSIRECORD *row, LPVOID param)
2138 MSIPACKAGE *package = param;
2139 MSIFILEPATCH *patch;
2140 LPWSTR file_key;
2142 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
2143 if (!patch)
2144 return ERROR_NOT_ENOUGH_MEMORY;
2146 file_key = msi_dup_record_field( row, 1 );
2147 patch->File = msi_get_loaded_file( package, file_key );
2148 msi_free(file_key);
2150 if( !patch->File )
2152 ERR("Failed to find target for patch in File table\n");
2153 msi_free(patch);
2154 return ERROR_FUNCTION_FAILED;
2157 patch->Sequence = MSI_RecordGetInteger( row, 2 );
2159 /* FIXME: The database should be properly transformed */
2160 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
2162 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
2163 patch->Attributes = MSI_RecordGetInteger( row, 4 );
2164 patch->IsApplied = FALSE;
2166 /* FIXME:
2167 * Header field - for patch validation.
2168 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
2171 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
2173 list_add_tail( &package->filepatches, &patch->entry );
2175 return ERROR_SUCCESS;
2178 static UINT load_all_patches(MSIPACKAGE *package)
2180 MSIQUERY *view;
2181 UINT rc;
2182 static const WCHAR Query[] =
2183 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2184 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
2185 '`','S','e','q','u','e','n','c','e','`',0};
2187 if (!list_empty(&package->filepatches))
2188 return ERROR_SUCCESS;
2190 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2191 if (rc != ERROR_SUCCESS)
2192 return ERROR_SUCCESS;
2194 rc = MSI_IterateRecords(view, NULL, load_patch, package);
2195 msiobj_release(&view->hdr);
2197 return ERROR_SUCCESS;
2200 static UINT load_folder( MSIRECORD *row, LPVOID param )
2202 MSIPACKAGE *package = param;
2203 static WCHAR szEmpty[] = { 0 };
2204 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
2205 MSIFOLDER *folder;
2207 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
2208 list_init( &folder->children );
2209 folder->Directory = msi_dup_record_field( row, 1 );
2210 folder->Parent = msi_dup_record_field( row, 2 );
2211 p = msi_dup_record_field(row, 3);
2213 TRACE("%s\n", debugstr_w(folder->Directory));
2215 /* split src and target dir */
2216 tgt_short = p;
2217 src_short = folder_split_path( p, ':' );
2219 /* split the long and short paths */
2220 tgt_long = folder_split_path( tgt_short, '|' );
2221 src_long = folder_split_path( src_short, '|' );
2223 /* check for no-op dirs */
2224 if (tgt_short && !strcmpW( szDot, tgt_short ))
2225 tgt_short = szEmpty;
2226 if (src_short && !strcmpW( szDot, src_short ))
2227 src_short = szEmpty;
2229 if (!tgt_long)
2230 tgt_long = tgt_short;
2232 if (!src_short) {
2233 src_short = tgt_short;
2234 src_long = tgt_long;
2237 if (!src_long)
2238 src_long = src_short;
2240 /* FIXME: use the target short path too */
2241 folder->TargetDefault = strdupW(tgt_long);
2242 folder->SourceShortPath = strdupW(src_short);
2243 folder->SourceLongPath = strdupW(src_long);
2244 msi_free(p);
2246 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
2247 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
2248 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
2250 list_add_tail( &package->folders, &folder->entry );
2251 return ERROR_SUCCESS;
2254 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
2256 FolderList *fl;
2258 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
2259 fl->folder = child;
2260 list_add_tail( &parent->children, &fl->entry );
2261 return ERROR_SUCCESS;
2264 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
2266 MSIPACKAGE *package = param;
2267 MSIFOLDER *parent, *child;
2269 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
2270 return ERROR_FUNCTION_FAILED;
2272 if (!child->Parent) return ERROR_SUCCESS;
2274 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
2275 return ERROR_FUNCTION_FAILED;
2277 return add_folder_child( parent, child );
2280 static UINT load_all_folders( MSIPACKAGE *package )
2282 static const WCHAR query[] = {
2283 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
2284 '`','D','i','r','e','c','t','o','r','y','`',0 };
2285 MSIQUERY *view;
2286 UINT r;
2288 if (!list_empty(&package->folders))
2289 return ERROR_SUCCESS;
2291 r = MSI_DatabaseOpenViewW( package->db, query, &view );
2292 if (r != ERROR_SUCCESS)
2293 return r;
2295 r = MSI_IterateRecords( view, NULL, load_folder, package );
2296 if (r != ERROR_SUCCESS)
2298 msiobj_release( &view->hdr );
2299 return r;
2301 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
2302 msiobj_release( &view->hdr );
2303 return r;
2306 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
2308 msi_set_property( package->db, szCostingComplete, szZero );
2309 msi_set_property( package->db, szRootDrive, szCRoot );
2311 load_all_folders( package );
2312 load_all_components( package );
2313 load_all_features( package );
2314 load_all_files( package );
2315 load_all_patches( package );
2316 load_all_media( package );
2318 return ERROR_SUCCESS;
2321 static UINT execute_script(MSIPACKAGE *package, UINT script )
2323 UINT i;
2324 UINT rc = ERROR_SUCCESS;
2326 TRACE("Executing Script %i\n",script);
2328 if (!package->script)
2330 ERR("no script!\n");
2331 return ERROR_FUNCTION_FAILED;
2334 for (i = 0; i < package->script->ActionCount[script]; i++)
2336 LPWSTR action;
2337 action = package->script->Actions[script][i];
2338 ui_actionstart(package, action);
2339 TRACE("Executing Action (%s)\n",debugstr_w(action));
2340 rc = ACTION_PerformAction(package, action, script);
2341 if (rc != ERROR_SUCCESS)
2342 break;
2344 msi_free_action_script(package, script);
2345 return rc;
2348 static UINT ACTION_FileCost(MSIPACKAGE *package)
2350 return ERROR_SUCCESS;
2353 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
2355 MSICOMPONENT *comp;
2356 UINT r;
2358 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
2360 if (!comp->ComponentId) continue;
2362 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2363 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
2364 &comp->Installed );
2365 if (r != ERROR_SUCCESS)
2366 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2367 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
2368 &comp->Installed );
2369 if (r != ERROR_SUCCESS)
2370 r = MsiQueryComponentStateW( package->ProductCode, NULL,
2371 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
2372 &comp->Installed );
2373 if (r != ERROR_SUCCESS)
2374 comp->Installed = INSTALLSTATE_ABSENT;
2378 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
2380 MSIFEATURE *feature;
2382 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2384 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
2386 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
2387 feature->Installed = INSTALLSTATE_ABSENT;
2388 else
2389 feature->Installed = state;
2393 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
2395 return (feature->Level > 0 && feature->Level <= level);
2398 static BOOL process_state_property(MSIPACKAGE* package, int level,
2399 LPCWSTR property, INSTALLSTATE state)
2401 LPWSTR override;
2402 MSIFEATURE *feature;
2404 override = msi_dup_property( package->db, property );
2405 if (!override)
2406 return FALSE;
2408 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2410 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
2411 continue;
2413 if (!strcmpW(property, szReinstall)) state = feature->Installed;
2415 if (!strcmpiW( override, szAll ))
2417 if (feature->Installed != state)
2419 feature->Action = state;
2420 feature->ActionRequest = state;
2423 else
2425 LPWSTR ptr = override;
2426 LPWSTR ptr2 = strchrW(override,',');
2428 while (ptr)
2430 int len = ptr2 - ptr;
2432 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
2433 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
2435 if (feature->Installed != state)
2437 feature->Action = state;
2438 feature->ActionRequest = state;
2440 break;
2442 if (ptr2)
2444 ptr=ptr2+1;
2445 ptr2 = strchrW(ptr,',');
2447 else
2448 break;
2452 msi_free(override);
2453 return TRUE;
2456 static BOOL process_overrides( MSIPACKAGE *package, int level )
2458 static const WCHAR szAddLocal[] =
2459 {'A','D','D','L','O','C','A','L',0};
2460 static const WCHAR szAddSource[] =
2461 {'A','D','D','S','O','U','R','C','E',0};
2462 static const WCHAR szAdvertise[] =
2463 {'A','D','V','E','R','T','I','S','E',0};
2464 BOOL ret = FALSE;
2466 /* all these activation/deactivation things happen in order and things
2467 * later on the list override things earlier on the list.
2469 * 0 INSTALLLEVEL processing
2470 * 1 ADDLOCAL
2471 * 2 REMOVE
2472 * 3 ADDSOURCE
2473 * 4 ADDDEFAULT
2474 * 5 REINSTALL
2475 * 6 ADVERTISE
2476 * 7 COMPADDLOCAL
2477 * 8 COMPADDSOURCE
2478 * 9 FILEADDLOCAL
2479 * 10 FILEADDSOURCE
2480 * 11 FILEADDDEFAULT
2482 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
2483 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
2484 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
2485 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
2486 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
2488 if (ret)
2489 msi_set_property( package->db, szPreselected, szOne );
2491 return ret;
2494 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
2496 int level;
2497 MSICOMPONENT* component;
2498 MSIFEATURE *feature;
2500 TRACE("Checking Install Level\n");
2502 level = msi_get_property_int(package->db, szInstallLevel, 1);
2504 if (!msi_get_property_int( package->db, szPreselected, 0 ))
2506 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2508 if (!is_feature_selected( feature, level )) continue;
2510 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2512 if (feature->Attributes & msidbFeatureAttributesFavorSource)
2514 feature->Action = INSTALLSTATE_SOURCE;
2515 feature->ActionRequest = INSTALLSTATE_SOURCE;
2517 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2519 feature->Action = INSTALLSTATE_ADVERTISED;
2520 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
2522 else
2524 feature->Action = INSTALLSTATE_LOCAL;
2525 feature->ActionRequest = INSTALLSTATE_LOCAL;
2529 /* disable child features of unselected parent or follow parent */
2530 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2532 FeatureList *fl;
2534 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2536 if (!is_feature_selected( feature, level ))
2538 fl->feature->Action = INSTALLSTATE_UNKNOWN;
2539 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2541 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
2543 fl->feature->Action = feature->Action;
2544 fl->feature->ActionRequest = feature->ActionRequest;
2549 else /* preselected */
2551 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2553 if (!is_feature_selected( feature, level )) continue;
2555 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
2557 if (feature->Installed == INSTALLSTATE_ABSENT)
2559 feature->Action = INSTALLSTATE_UNKNOWN;
2560 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
2562 else
2564 feature->Action = feature->Installed;
2565 feature->ActionRequest = feature->Installed;
2569 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2571 FeatureList *fl;
2573 if (!is_feature_selected( feature, level )) continue;
2575 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
2577 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
2579 fl->feature->Action = feature->Action;
2580 fl->feature->ActionRequest = feature->ActionRequest;
2586 /* now we want to set component state based based on feature state */
2587 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2589 ComponentList *cl;
2591 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
2592 debugstr_w(feature->Feature), feature->Level, feature->Installed,
2593 feature->ActionRequest, feature->Action);
2595 if (!is_feature_selected( feature, level )) continue;
2597 /* features with components that have compressed files are made local */
2598 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2600 if (cl->component->ForceLocalState &&
2601 feature->ActionRequest == INSTALLSTATE_SOURCE)
2603 feature->Action = INSTALLSTATE_LOCAL;
2604 feature->ActionRequest = INSTALLSTATE_LOCAL;
2605 break;
2609 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2611 component = cl->component;
2613 switch (feature->ActionRequest)
2615 case INSTALLSTATE_ABSENT:
2616 component->anyAbsent = 1;
2617 break;
2618 case INSTALLSTATE_ADVERTISED:
2619 component->hasAdvertiseFeature = 1;
2620 break;
2621 case INSTALLSTATE_SOURCE:
2622 component->hasSourceFeature = 1;
2623 break;
2624 case INSTALLSTATE_LOCAL:
2625 component->hasLocalFeature = 1;
2626 break;
2627 case INSTALLSTATE_DEFAULT:
2628 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2629 component->hasAdvertiseFeature = 1;
2630 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2631 component->hasSourceFeature = 1;
2632 else
2633 component->hasLocalFeature = 1;
2634 break;
2635 default:
2636 break;
2641 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2643 /* check if it's local or source */
2644 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2645 (component->hasLocalFeature || component->hasSourceFeature))
2647 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2648 !component->ForceLocalState)
2650 component->Action = INSTALLSTATE_SOURCE;
2651 component->ActionRequest = INSTALLSTATE_SOURCE;
2653 else
2655 component->Action = INSTALLSTATE_LOCAL;
2656 component->ActionRequest = INSTALLSTATE_LOCAL;
2658 continue;
2661 /* if any feature is local, the component must be local too */
2662 if (component->hasLocalFeature)
2664 component->Action = INSTALLSTATE_LOCAL;
2665 component->ActionRequest = INSTALLSTATE_LOCAL;
2666 continue;
2668 if (component->hasSourceFeature)
2670 component->Action = INSTALLSTATE_SOURCE;
2671 component->ActionRequest = INSTALLSTATE_SOURCE;
2672 continue;
2674 if (component->hasAdvertiseFeature)
2676 component->Action = INSTALLSTATE_ADVERTISED;
2677 component->ActionRequest = INSTALLSTATE_ADVERTISED;
2678 continue;
2680 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2681 if (component->anyAbsent &&
2682 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
2684 component->Action = INSTALLSTATE_ABSENT;
2685 component->ActionRequest = INSTALLSTATE_ABSENT;
2689 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2691 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2693 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2694 component->Action = INSTALLSTATE_LOCAL;
2695 component->ActionRequest = INSTALLSTATE_LOCAL;
2698 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2699 component->Installed == INSTALLSTATE_SOURCE &&
2700 component->hasSourceFeature)
2702 component->Action = INSTALLSTATE_UNKNOWN;
2703 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2706 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2707 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2710 return ERROR_SUCCESS;
2713 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2715 MSIPACKAGE *package = param;
2716 LPCWSTR name;
2717 MSIFEATURE *feature;
2719 name = MSI_RecordGetString( row, 1 );
2721 feature = msi_get_loaded_feature( package, name );
2722 if (!feature)
2723 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2724 else
2726 LPCWSTR Condition;
2727 Condition = MSI_RecordGetString(row,3);
2729 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2731 int level = MSI_RecordGetInteger(row,2);
2732 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2733 feature->Level = level;
2736 return ERROR_SUCCESS;
2739 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2741 static const WCHAR name[] = {'\\',0};
2742 VS_FIXEDFILEINFO *ptr, *ret;
2743 LPVOID version;
2744 DWORD versize, handle;
2745 UINT sz;
2747 versize = GetFileVersionInfoSizeW( filename, &handle );
2748 if (!versize)
2749 return NULL;
2751 version = msi_alloc( versize );
2752 if (!version)
2753 return NULL;
2755 GetFileVersionInfoW( filename, 0, versize, version );
2757 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2759 msi_free( version );
2760 return NULL;
2763 ret = msi_alloc( sz );
2764 memcpy( ret, ptr, sz );
2766 msi_free( version );
2767 return ret;
2770 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2772 DWORD ms, ls;
2774 msi_parse_version_string( version, &ms, &ls );
2776 if (fi->dwFileVersionMS > ms) return 1;
2777 else if (fi->dwFileVersionMS < ms) return -1;
2778 else if (fi->dwFileVersionLS > ls) return 1;
2779 else if (fi->dwFileVersionLS < ls) return -1;
2780 return 0;
2783 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2785 DWORD ms1, ms2;
2787 msi_parse_version_string( ver1, &ms1, NULL );
2788 msi_parse_version_string( ver2, &ms2, NULL );
2790 if (ms1 > ms2) return 1;
2791 else if (ms1 < ms2) return -1;
2792 return 0;
2795 DWORD msi_get_disk_file_size( LPCWSTR filename )
2797 HANDLE file;
2798 DWORD size;
2800 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2801 if (file == INVALID_HANDLE_VALUE)
2802 return INVALID_FILE_SIZE;
2804 size = GetFileSize( file, NULL );
2805 TRACE("size is %u\n", size);
2806 CloseHandle( file );
2807 return size;
2810 BOOL msi_file_hash_matches( MSIFILE *file )
2812 UINT r;
2813 MSIFILEHASHINFO hash;
2815 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2816 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2817 if (r != ERROR_SUCCESS)
2818 return FALSE;
2820 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2823 static WCHAR *get_temp_dir( void )
2825 static UINT id;
2826 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2828 GetTempPathW( MAX_PATH, tmp );
2829 for (;;)
2831 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2832 if (CreateDirectoryW( dir, NULL )) break;
2834 return strdupW( dir );
2838 * msi_build_directory_name()
2840 * This function is to save messing round with directory names
2841 * It handles adding backslashes between path segments,
2842 * and can add \ at the end of the directory name if told to.
2844 * It takes a variable number of arguments.
2845 * It always allocates a new string for the result, so make sure
2846 * to free the return value when finished with it.
2848 * The first arg is the number of path segments that follow.
2849 * The arguments following count are a list of path segments.
2850 * A path segment may be NULL.
2852 * Path segments will be added with a \ separating them.
2853 * A \ will not be added after the last segment, however if the
2854 * last segment is NULL, then the last character will be a \
2856 WCHAR *msi_build_directory_name( DWORD count, ... )
2858 DWORD sz = 1, i;
2859 WCHAR *dir;
2860 va_list va;
2862 va_start( va, count );
2863 for (i = 0; i < count; i++)
2865 const WCHAR *str = va_arg( va, const WCHAR * );
2866 if (str) sz += strlenW( str ) + 1;
2868 va_end( va );
2870 dir = msi_alloc( sz * sizeof(WCHAR) );
2871 dir[0] = 0;
2873 va_start( va, count );
2874 for (i = 0; i < count; i++)
2876 const WCHAR *str = va_arg( va, const WCHAR * );
2877 if (!str) continue;
2878 strcatW( dir, str );
2879 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2881 va_end( va );
2882 return dir;
2885 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2887 MSIASSEMBLY *assembly = file->Component->assembly;
2889 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2891 msi_free( file->TargetPath );
2892 if (assembly && !assembly->application)
2894 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2895 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2896 msi_track_tempfile( package, file->TargetPath );
2898 else
2900 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2901 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2904 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2907 static UINT calculate_file_cost( MSIPACKAGE *package )
2909 VS_FIXEDFILEINFO *file_version;
2910 WCHAR *font_version;
2911 MSIFILE *file;
2913 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2915 MSICOMPONENT *comp = file->Component;
2916 DWORD file_size;
2918 if (!comp->Enabled) continue;
2920 if (file->IsCompressed)
2921 comp->ForceLocalState = TRUE;
2923 set_target_path( package, file );
2925 if ((comp->assembly && !comp->assembly->installed) ||
2926 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2928 comp->Cost += file->FileSize;
2929 continue;
2931 file_size = msi_get_disk_file_size( file->TargetPath );
2933 if (file->Version)
2935 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2937 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2939 comp->Cost += file->FileSize - file_size;
2941 msi_free( file_version );
2942 continue;
2944 else if ((font_version = font_version_from_file( file->TargetPath )))
2946 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2948 comp->Cost += file->FileSize - file_size;
2950 msi_free( font_version );
2951 continue;
2954 if (file_size != file->FileSize)
2956 comp->Cost += file->FileSize - file_size;
2959 return ERROR_SUCCESS;
2962 void msi_clean_path( WCHAR *p )
2964 WCHAR *q = p;
2965 int n, len = 0;
2967 while (1)
2969 /* copy until the end of the string or a space */
2970 while (*p != ' ' && (*q = *p))
2972 p++, len++;
2973 /* reduce many backslashes to one */
2974 if (*p != '\\' || *q != '\\')
2975 q++;
2978 /* quit at the end of the string */
2979 if (!*p)
2980 break;
2982 /* count the number of spaces */
2983 n = 0;
2984 while (p[n] == ' ')
2985 n++;
2987 /* if it's leading or trailing space, skip it */
2988 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2989 p += n;
2990 else /* copy n spaces */
2991 while (n && (*q++ = *p++)) n--;
2995 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2997 FolderList *fl;
2998 MSIFOLDER *folder, *parent, *child;
2999 WCHAR *path;
3001 TRACE("resolving %s\n", debugstr_w(name));
3003 if (!(folder = msi_get_loaded_folder( package, name ))) return;
3005 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
3007 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
3009 path = msi_dup_property( package->db, szRootDrive );
3012 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
3014 parent = msi_get_loaded_folder( package, folder->Parent );
3015 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
3017 msi_clean_path( path );
3018 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
3020 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
3021 msi_free( path );
3022 return;
3024 msi_set_property( package->db, folder->Directory, path );
3025 msi_free( folder->ResolvedTarget );
3026 folder->ResolvedTarget = path;
3028 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
3030 child = fl->folder;
3031 msi_resolve_target_folder( package, child->Directory, load_prop );
3033 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
3036 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
3038 static const WCHAR condition_query[] =
3039 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','C','o','n','d','i','t','i','o','n','`',0};
3040 static const WCHAR szOutOfDiskSpace[] =
3041 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
3042 MSICOMPONENT *comp;
3043 UINT rc = ERROR_SUCCESS;
3044 MSIQUERY * view;
3045 LPWSTR level;
3047 TRACE("Building directory properties\n");
3048 msi_resolve_target_folder( package, szTargetDir, TRUE );
3050 TRACE("Evaluating component conditions\n");
3051 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3053 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
3055 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
3056 comp->Enabled = FALSE;
3058 else
3059 comp->Enabled = TRUE;
3062 /* read components states from the registry */
3063 ACTION_GetComponentInstallStates(package);
3064 ACTION_GetFeatureInstallStates(package);
3066 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
3068 TRACE("Evaluating feature conditions\n");
3070 rc = MSI_DatabaseOpenViewW( package->db, condition_query, &view );
3071 if (rc == ERROR_SUCCESS)
3073 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
3074 msiobj_release( &view->hdr );
3078 TRACE("Calculating file cost\n");
3079 calculate_file_cost( package );
3081 msi_set_property( package->db, szCostingComplete, szOne );
3082 /* set default run level if not set */
3083 level = msi_dup_property( package->db, szInstallLevel );
3084 if (!level)
3085 msi_set_property( package->db, szInstallLevel, szOne );
3086 msi_free(level);
3088 /* FIXME: check volume disk space */
3089 msi_set_property( package->db, szOutOfDiskSpace, szZero );
3091 return MSI_SetFeatureStates(package);
3094 /* OK this value is "interpreted" and then formatted based on the
3095 first few characters */
3096 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
3097 DWORD *size)
3099 LPSTR data = NULL;
3101 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
3103 if (value[1]=='x')
3105 LPWSTR ptr;
3106 CHAR byte[5];
3107 LPWSTR deformated = NULL;
3108 int count;
3110 deformat_string(package, &value[2], &deformated);
3112 /* binary value type */
3113 ptr = deformated;
3114 *type = REG_BINARY;
3115 if (strlenW(ptr)%2)
3116 *size = (strlenW(ptr)/2)+1;
3117 else
3118 *size = strlenW(ptr)/2;
3120 data = msi_alloc(*size);
3122 byte[0] = '0';
3123 byte[1] = 'x';
3124 byte[4] = 0;
3125 count = 0;
3126 /* if uneven pad with a zero in front */
3127 if (strlenW(ptr)%2)
3129 byte[2]= '0';
3130 byte[3]= *ptr;
3131 ptr++;
3132 data[count] = (BYTE)strtol(byte,NULL,0);
3133 count ++;
3134 TRACE("Uneven byte count\n");
3136 while (*ptr)
3138 byte[2]= *ptr;
3139 ptr++;
3140 byte[3]= *ptr;
3141 ptr++;
3142 data[count] = (BYTE)strtol(byte,NULL,0);
3143 count ++;
3145 msi_free(deformated);
3147 TRACE("Data %i bytes(%i)\n",*size,count);
3149 else
3151 LPWSTR deformated;
3152 LPWSTR p;
3153 DWORD d = 0;
3154 deformat_string(package, &value[1], &deformated);
3156 *type=REG_DWORD;
3157 *size = sizeof(DWORD);
3158 data = msi_alloc(*size);
3159 p = deformated;
3160 if (*p == '-')
3161 p++;
3162 while (*p)
3164 if ( (*p < '0') || (*p > '9') )
3165 break;
3166 d *= 10;
3167 d += (*p - '0');
3168 p++;
3170 if (deformated[0] == '-')
3171 d = -d;
3172 *(LPDWORD)data = d;
3173 TRACE("DWORD %i\n",*(LPDWORD)data);
3175 msi_free(deformated);
3178 else
3180 static const WCHAR szMulti[] = {'[','~',']',0};
3181 LPCWSTR ptr;
3182 *type=REG_SZ;
3184 if (value[0]=='#')
3186 if (value[1]=='%')
3188 ptr = &value[2];
3189 *type=REG_EXPAND_SZ;
3191 else
3192 ptr = &value[1];
3194 else
3195 ptr=value;
3197 if (strstrW(value, szMulti))
3198 *type = REG_MULTI_SZ;
3200 /* remove initial delimiter */
3201 if (!strncmpW(value, szMulti, 3))
3202 ptr = value + 3;
3204 *size = deformat_string(package, ptr,(LPWSTR*)&data);
3206 /* add double NULL terminator */
3207 if (*type == REG_MULTI_SZ)
3209 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
3210 data = msi_realloc_zero(data, *size);
3213 return data;
3216 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
3218 const WCHAR *ret;
3220 switch (root)
3222 case -1:
3223 if (msi_get_property_int( package->db, szAllUsers, 0 ))
3225 *root_key = HKEY_LOCAL_MACHINE;
3226 ret = szHLM;
3228 else
3230 *root_key = HKEY_CURRENT_USER;
3231 ret = szHCU;
3233 break;
3234 case 0:
3235 *root_key = HKEY_CLASSES_ROOT;
3236 ret = szHCR;
3237 break;
3238 case 1:
3239 *root_key = HKEY_CURRENT_USER;
3240 ret = szHCU;
3241 break;
3242 case 2:
3243 *root_key = HKEY_LOCAL_MACHINE;
3244 ret = szHLM;
3245 break;
3246 case 3:
3247 *root_key = HKEY_USERS;
3248 ret = szHU;
3249 break;
3250 default:
3251 ERR("Unknown root %i\n", root);
3252 return NULL;
3255 return ret;
3258 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
3260 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
3261 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
3263 if (is_64bit && package->platform == PLATFORM_INTEL &&
3264 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
3266 UINT size;
3267 WCHAR *path_32node;
3269 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
3270 if (!(path_32node = msi_alloc( size ))) return NULL;
3272 memcpy( path_32node, path, len * sizeof(WCHAR) );
3273 strcpyW( path_32node + len, szWow6432Node );
3274 strcatW( path_32node, szBackSlash );
3275 strcatW( path_32node, path + len );
3276 return path_32node;
3279 return strdupW( path );
3282 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
3284 MSIPACKAGE *package = param;
3285 LPSTR value_data = NULL;
3286 HKEY root_key, hkey;
3287 DWORD type,size;
3288 LPWSTR deformated, uikey, keypath;
3289 LPCWSTR szRoot, component, name, key, value;
3290 MSICOMPONENT *comp;
3291 MSIRECORD * uirow;
3292 INT root;
3293 BOOL check_first = FALSE;
3294 UINT rc;
3296 msi_ui_progress( package, 2, 0, 0, 0 );
3298 component = MSI_RecordGetString(row, 6);
3299 comp = msi_get_loaded_component(package,component);
3300 if (!comp)
3301 return ERROR_SUCCESS;
3303 if (!comp->Enabled)
3305 TRACE("component is disabled\n");
3306 return ERROR_SUCCESS;
3309 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3311 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3312 comp->Action = comp->Installed;
3313 return ERROR_SUCCESS;
3315 comp->Action = INSTALLSTATE_LOCAL;
3317 name = MSI_RecordGetString(row, 4);
3318 if( MSI_RecordIsNull(row,5) && name )
3320 /* null values can have special meanings */
3321 if (name[0]=='-' && name[1] == 0)
3322 return ERROR_SUCCESS;
3323 else if ((name[0]=='+' && name[1] == 0) ||
3324 (name[0] == '*' && name[1] == 0))
3325 name = NULL;
3326 check_first = TRUE;
3329 root = MSI_RecordGetInteger(row,2);
3330 key = MSI_RecordGetString(row, 3);
3332 szRoot = get_root_key( package, root, &root_key );
3333 if (!szRoot)
3334 return ERROR_SUCCESS;
3336 deformat_string(package, key , &deformated);
3337 size = strlenW(deformated) + strlenW(szRoot) + 1;
3338 uikey = msi_alloc(size*sizeof(WCHAR));
3339 strcpyW(uikey,szRoot);
3340 strcatW(uikey,deformated);
3342 keypath = get_keypath( package, root_key, deformated );
3343 msi_free( deformated );
3344 if (RegCreateKeyW( root_key, keypath, &hkey ))
3346 ERR("Could not create key %s\n", debugstr_w(keypath));
3347 msi_free(uikey);
3348 msi_free(keypath);
3349 return ERROR_SUCCESS;
3352 value = MSI_RecordGetString(row,5);
3353 if (value)
3354 value_data = parse_value(package, value, &type, &size);
3355 else
3357 value_data = (LPSTR)strdupW(szEmpty);
3358 size = sizeof(szEmpty);
3359 type = REG_SZ;
3362 deformat_string(package, name, &deformated);
3364 if (!check_first)
3366 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
3367 debugstr_w(uikey));
3368 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
3370 else
3372 DWORD sz = 0;
3373 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
3374 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
3376 TRACE("value %s of %s checked already exists\n",
3377 debugstr_w(deformated), debugstr_w(uikey));
3379 else
3381 TRACE("Checked and setting value %s of %s\n",
3382 debugstr_w(deformated), debugstr_w(uikey));
3383 if (deformated || size)
3384 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
3387 RegCloseKey(hkey);
3389 uirow = MSI_CreateRecord(3);
3390 MSI_RecordSetStringW(uirow,2,deformated);
3391 MSI_RecordSetStringW(uirow,1,uikey);
3392 if (type == REG_SZ || type == REG_EXPAND_SZ)
3393 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
3394 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
3395 msiobj_release( &uirow->hdr );
3397 msi_free(value_data);
3398 msi_free(deformated);
3399 msi_free(uikey);
3400 msi_free(keypath);
3402 return ERROR_SUCCESS;
3405 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
3407 UINT rc;
3408 MSIQUERY * view;
3409 static const WCHAR ExecSeqQuery[] =
3410 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3411 '`','R','e','g','i','s','t','r','y','`',0 };
3413 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3414 if (rc != ERROR_SUCCESS)
3415 return ERROR_SUCCESS;
3417 /* increment progress bar each time action data is sent */
3418 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3420 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
3421 msiobj_release(&view->hdr);
3422 return rc;
3425 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
3427 LONG res;
3428 HKEY hkey;
3429 DWORD num_subkeys, num_values;
3431 if (delete_key)
3433 if ((res = RegDeleteTreeW( hkey_root, key )))
3435 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
3437 return;
3440 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
3442 if ((res = RegDeleteValueW( hkey, value )))
3444 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
3446 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3447 NULL, NULL, NULL, NULL );
3448 RegCloseKey( hkey );
3449 if (!res && !num_subkeys && !num_values)
3451 TRACE("Removing empty key %s\n", debugstr_w(key));
3452 RegDeleteKeyW( hkey_root, key );
3454 return;
3456 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
3460 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3462 MSIPACKAGE *package = param;
3463 LPCWSTR component, name, key_str, root_key_str;
3464 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3465 MSICOMPONENT *comp;
3466 MSIRECORD *uirow;
3467 BOOL delete_key = FALSE;
3468 HKEY hkey_root;
3469 UINT size;
3470 INT root;
3472 msi_ui_progress( package, 2, 0, 0, 0 );
3474 component = MSI_RecordGetString( row, 6 );
3475 comp = msi_get_loaded_component( package, component );
3476 if (!comp)
3477 return ERROR_SUCCESS;
3479 if (!comp->Enabled)
3481 TRACE("component is disabled\n");
3482 return ERROR_SUCCESS;
3485 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3487 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
3488 comp->Action = comp->Installed;
3489 return ERROR_SUCCESS;
3491 comp->Action = INSTALLSTATE_ABSENT;
3493 name = MSI_RecordGetString( row, 4 );
3494 if (MSI_RecordIsNull( row, 5 ) && name )
3496 if (name[0] == '+' && !name[1])
3497 return ERROR_SUCCESS;
3498 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
3500 delete_key = TRUE;
3501 name = NULL;
3505 root = MSI_RecordGetInteger( row, 2 );
3506 key_str = MSI_RecordGetString( row, 3 );
3508 root_key_str = get_root_key( package, root, &hkey_root );
3509 if (!root_key_str)
3510 return ERROR_SUCCESS;
3512 deformat_string( package, key_str, &deformated_key );
3513 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3514 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3515 strcpyW( ui_key_str, root_key_str );
3516 strcatW( ui_key_str, deformated_key );
3518 deformat_string( package, name, &deformated_name );
3520 keypath = get_keypath( package, hkey_root, deformated_key );
3521 msi_free( deformated_key );
3522 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3523 msi_free( keypath );
3525 uirow = MSI_CreateRecord( 2 );
3526 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3527 MSI_RecordSetStringW( uirow, 2, deformated_name );
3529 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3530 msiobj_release( &uirow->hdr );
3532 msi_free( ui_key_str );
3533 msi_free( deformated_name );
3534 return ERROR_SUCCESS;
3537 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3539 MSIPACKAGE *package = param;
3540 LPCWSTR component, name, key_str, root_key_str;
3541 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3542 MSICOMPONENT *comp;
3543 MSIRECORD *uirow;
3544 BOOL delete_key = FALSE;
3545 HKEY hkey_root;
3546 UINT size;
3547 INT root;
3549 msi_ui_progress( package, 2, 0, 0, 0 );
3551 component = MSI_RecordGetString( row, 5 );
3552 comp = msi_get_loaded_component( package, component );
3553 if (!comp)
3554 return ERROR_SUCCESS;
3556 if (!comp->Enabled)
3558 TRACE("component is disabled\n");
3559 return ERROR_SUCCESS;
3562 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3564 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3565 comp->Action = comp->Installed;
3566 return ERROR_SUCCESS;
3568 comp->Action = INSTALLSTATE_LOCAL;
3570 if ((name = MSI_RecordGetString( row, 4 )))
3572 if (name[0] == '-' && !name[1])
3574 delete_key = TRUE;
3575 name = NULL;
3579 root = MSI_RecordGetInteger( row, 2 );
3580 key_str = MSI_RecordGetString( row, 3 );
3582 root_key_str = get_root_key( package, root, &hkey_root );
3583 if (!root_key_str)
3584 return ERROR_SUCCESS;
3586 deformat_string( package, key_str, &deformated_key );
3587 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3588 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3589 strcpyW( ui_key_str, root_key_str );
3590 strcatW( ui_key_str, deformated_key );
3592 deformat_string( package, name, &deformated_name );
3594 keypath = get_keypath( package, hkey_root, deformated_key );
3595 msi_free( deformated_key );
3596 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
3597 msi_free( keypath );
3599 uirow = MSI_CreateRecord( 2 );
3600 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3601 MSI_RecordSetStringW( uirow, 2, deformated_name );
3603 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3604 msiobj_release( &uirow->hdr );
3606 msi_free( ui_key_str );
3607 msi_free( deformated_name );
3608 return ERROR_SUCCESS;
3611 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3613 UINT rc;
3614 MSIQUERY *view;
3615 static const WCHAR registry_query[] =
3616 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3617 '`','R','e','g','i','s','t','r','y','`',0 };
3618 static const WCHAR remove_registry_query[] =
3619 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3620 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
3622 /* increment progress bar each time action data is sent */
3623 msi_ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
3625 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3626 if (rc == ERROR_SUCCESS)
3628 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3629 msiobj_release( &view->hdr );
3630 if (rc != ERROR_SUCCESS)
3631 return rc;
3634 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3635 if (rc == ERROR_SUCCESS)
3637 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3638 msiobj_release( &view->hdr );
3639 if (rc != ERROR_SUCCESS)
3640 return rc;
3643 return ERROR_SUCCESS;
3646 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3648 package->script->CurrentlyScripting = TRUE;
3650 return ERROR_SUCCESS;
3654 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3656 MSICOMPONENT *comp;
3657 DWORD progress = 0;
3658 DWORD total = 0;
3659 static const WCHAR q1[]=
3660 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3661 '`','R','e','g','i','s','t','r','y','`',0};
3662 UINT rc;
3663 MSIQUERY * view;
3664 MSIFEATURE *feature;
3665 MSIFILE *file;
3667 TRACE("InstallValidate\n");
3669 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
3670 if (rc == ERROR_SUCCESS)
3672 MSI_IterateRecords( view, &progress, NULL, package );
3673 msiobj_release( &view->hdr );
3674 total += progress * REG_PROGRESS_VALUE;
3677 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3678 total += COMPONENT_PROGRESS_VALUE;
3680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3681 total += file->FileSize;
3683 msi_ui_progress( package, 0, total, 0, 0 );
3685 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3687 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3688 debugstr_w(feature->Feature), feature->Installed,
3689 feature->ActionRequest, feature->Action);
3692 return ERROR_SUCCESS;
3695 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3697 MSIPACKAGE* package = param;
3698 LPCWSTR cond = NULL;
3699 LPCWSTR message = NULL;
3700 UINT r;
3702 static const WCHAR title[]=
3703 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3705 cond = MSI_RecordGetString(row,1);
3707 r = MSI_EvaluateConditionW(package,cond);
3708 if (r == MSICONDITION_FALSE)
3710 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3712 LPWSTR deformated;
3713 message = MSI_RecordGetString(row,2);
3714 deformat_string(package,message,&deformated);
3715 MessageBoxW(NULL,deformated,title,MB_OK);
3716 msi_free(deformated);
3719 return ERROR_INSTALL_FAILURE;
3722 return ERROR_SUCCESS;
3725 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3727 UINT rc;
3728 MSIQUERY * view = NULL;
3729 static const WCHAR ExecSeqQuery[] =
3730 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3731 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3733 TRACE("Checking launch conditions\n");
3735 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3736 if (rc != ERROR_SUCCESS)
3737 return ERROR_SUCCESS;
3739 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3740 msiobj_release(&view->hdr);
3742 return rc;
3745 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3748 if (!cmp->KeyPath)
3749 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3751 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3753 MSIRECORD * row = 0;
3754 UINT root,len;
3755 LPWSTR deformated,buffer,deformated_name;
3756 LPCWSTR key,name;
3757 static const WCHAR ExecSeqQuery[] =
3758 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3759 '`','R','e','g','i','s','t','r','y','`',' ',
3760 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3761 ' ','=',' ' ,'\'','%','s','\'',0 };
3762 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3763 static const WCHAR fmt2[]=
3764 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3766 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3767 if (!row)
3768 return NULL;
3770 root = MSI_RecordGetInteger(row,2);
3771 key = MSI_RecordGetString(row, 3);
3772 name = MSI_RecordGetString(row, 4);
3773 deformat_string(package, key , &deformated);
3774 deformat_string(package, name, &deformated_name);
3776 len = strlenW(deformated) + 6;
3777 if (deformated_name)
3778 len+=strlenW(deformated_name);
3780 buffer = msi_alloc( len *sizeof(WCHAR));
3782 if (deformated_name)
3783 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3784 else
3785 sprintfW(buffer,fmt,root,deformated);
3787 msi_free(deformated);
3788 msi_free(deformated_name);
3789 msiobj_release(&row->hdr);
3791 return buffer;
3793 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3795 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3796 return NULL;
3798 else
3800 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3802 if (file)
3803 return strdupW( file->TargetPath );
3805 return NULL;
3808 static HKEY openSharedDLLsKey(void)
3810 HKEY hkey=0;
3811 static const WCHAR path[] =
3812 {'S','o','f','t','w','a','r','e','\\',
3813 'M','i','c','r','o','s','o','f','t','\\',
3814 'W','i','n','d','o','w','s','\\',
3815 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3816 'S','h','a','r','e','d','D','L','L','s',0};
3818 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3819 return hkey;
3822 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3824 HKEY hkey;
3825 DWORD count=0;
3826 DWORD type;
3827 DWORD sz = sizeof(count);
3828 DWORD rc;
3830 hkey = openSharedDLLsKey();
3831 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3832 if (rc != ERROR_SUCCESS)
3833 count = 0;
3834 RegCloseKey(hkey);
3835 return count;
3838 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3840 HKEY hkey;
3842 hkey = openSharedDLLsKey();
3843 if (count > 0)
3844 msi_reg_set_val_dword( hkey, path, count );
3845 else
3846 RegDeleteValueW(hkey,path);
3847 RegCloseKey(hkey);
3848 return count;
3851 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3853 MSIFEATURE *feature;
3854 INT count = 0;
3855 BOOL write = FALSE;
3857 /* only refcount DLLs */
3858 if (comp->KeyPath == NULL ||
3859 comp->assembly ||
3860 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3861 comp->Attributes & msidbComponentAttributesODBCDataSource)
3862 write = FALSE;
3863 else
3865 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3866 write = (count > 0);
3868 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3869 write = TRUE;
3872 /* increment counts */
3873 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3875 ComponentList *cl;
3877 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3878 continue;
3880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3882 if ( cl->component == comp )
3883 count++;
3887 /* decrement counts */
3888 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3890 ComponentList *cl;
3892 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3893 continue;
3895 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3897 if ( cl->component == comp )
3898 count--;
3902 /* ref count all the files in the component */
3903 if (write)
3905 MSIFILE *file;
3907 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3909 if (file->Component == comp)
3910 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3914 /* add a count for permanent */
3915 if (comp->Attributes & msidbComponentAttributesPermanent)
3916 count ++;
3918 comp->RefCount = count;
3920 if (write)
3921 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3924 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3926 WCHAR squished_pc[GUID_SIZE];
3927 WCHAR squished_cc[GUID_SIZE];
3928 UINT rc;
3929 MSICOMPONENT *comp;
3930 HKEY hkey;
3932 TRACE("\n");
3934 squash_guid(package->ProductCode,squished_pc);
3935 msi_ui_progress( package, 1, COMPONENT_PROGRESS_VALUE, 1, 0 );
3937 msi_set_sourcedir_props(package, FALSE);
3939 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3941 MSIRECORD * uirow;
3943 msi_ui_progress( package, 2, 0, 0, 0 );
3944 if (!comp->ComponentId)
3945 continue;
3947 squash_guid(comp->ComponentId,squished_cc);
3949 msi_free(comp->FullKeypath);
3950 if (comp->assembly)
3952 const WCHAR prefixW[] = {'<','\\',0};
3953 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3955 comp->FullKeypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3956 if (comp->FullKeypath)
3958 strcpyW( comp->FullKeypath, prefixW );
3959 strcatW( comp->FullKeypath, comp->assembly->display_name );
3962 else comp->FullKeypath = resolve_keypath( package, comp );
3964 ACTION_RefCountComponent( package, comp );
3966 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3967 debugstr_w(comp->Component),
3968 debugstr_w(squished_cc),
3969 debugstr_w(comp->FullKeypath),
3970 comp->RefCount,
3971 comp->ActionRequest);
3973 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3974 comp->ActionRequest == INSTALLSTATE_SOURCE)
3976 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3977 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3978 else
3979 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3981 if (rc != ERROR_SUCCESS)
3982 continue;
3984 if (comp->Attributes & msidbComponentAttributesPermanent)
3986 static const WCHAR szPermKey[] =
3987 { '0','0','0','0','0','0','0','0','0','0','0','0',
3988 '0','0','0','0','0','0','0','0','0','0','0','0',
3989 '0','0','0','0','0','0','0','0',0 };
3991 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3994 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3995 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3996 else
3998 MSIFILE *file;
3999 MSIRECORD *row;
4000 LPWSTR ptr, ptr2;
4001 WCHAR source[MAX_PATH];
4002 WCHAR base[MAX_PATH];
4003 LPWSTR sourcepath;
4005 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
4006 static const WCHAR query[] = {
4007 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
4008 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
4009 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
4010 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
4011 '`','D','i','s','k','I','d','`',0};
4013 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
4014 continue;
4016 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
4017 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
4018 ptr2 = strrchrW(source, '\\') + 1;
4019 msiobj_release(&row->hdr);
4021 lstrcpyW(base, package->PackagePath);
4022 ptr = strrchrW(base, '\\');
4023 *(ptr + 1) = '\0';
4025 sourcepath = msi_resolve_file_source(package, file);
4026 ptr = sourcepath + lstrlenW(base);
4027 lstrcpyW(ptr2, ptr);
4028 msi_free(sourcepath);
4030 msi_reg_set_val_str(hkey, squished_pc, source);
4032 RegCloseKey(hkey);
4034 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
4036 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4037 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
4038 else
4039 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
4041 comp->Action = comp->ActionRequest;
4043 /* UI stuff */
4044 uirow = MSI_CreateRecord(3);
4045 MSI_RecordSetStringW(uirow,1,package->ProductCode);
4046 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
4047 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
4048 msi_ui_actiondata( package, szProcessComponents, uirow );
4049 msiobj_release( &uirow->hdr );
4052 return ERROR_SUCCESS;
4055 typedef struct {
4056 CLSID clsid;
4057 LPWSTR source;
4059 LPWSTR path;
4060 ITypeLib *ptLib;
4061 } typelib_struct;
4063 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
4064 LPWSTR lpszName, LONG_PTR lParam)
4066 TLIBATTR *attr;
4067 typelib_struct *tl_struct = (typelib_struct*) lParam;
4068 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
4069 int sz;
4070 HRESULT res;
4072 if (!IS_INTRESOURCE(lpszName))
4074 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
4075 return TRUE;
4078 sz = strlenW(tl_struct->source)+4;
4079 sz *= sizeof(WCHAR);
4081 if ((INT_PTR)lpszName == 1)
4082 tl_struct->path = strdupW(tl_struct->source);
4083 else
4085 tl_struct->path = msi_alloc(sz);
4086 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
4089 TRACE("trying %s\n", debugstr_w(tl_struct->path));
4090 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
4091 if (FAILED(res))
4093 msi_free(tl_struct->path);
4094 tl_struct->path = NULL;
4096 return TRUE;
4099 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
4100 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
4102 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
4103 return FALSE;
4106 msi_free(tl_struct->path);
4107 tl_struct->path = NULL;
4109 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
4110 ITypeLib_Release(tl_struct->ptLib);
4112 return TRUE;
4115 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
4117 MSIPACKAGE* package = param;
4118 LPCWSTR component;
4119 MSICOMPONENT *comp;
4120 MSIFILE *file;
4121 typelib_struct tl_struct;
4122 ITypeLib *tlib;
4123 HMODULE module;
4124 HRESULT hr;
4126 component = MSI_RecordGetString(row,3);
4127 comp = msi_get_loaded_component(package,component);
4128 if (!comp)
4129 return ERROR_SUCCESS;
4131 if (!comp->Enabled)
4133 TRACE("component is disabled\n");
4134 return ERROR_SUCCESS;
4137 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4139 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
4140 comp->Action = comp->Installed;
4141 return ERROR_SUCCESS;
4143 comp->Action = INSTALLSTATE_LOCAL;
4145 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
4147 TRACE("component has no key path\n");
4148 return ERROR_SUCCESS;
4150 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
4152 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
4153 if (module)
4155 LPCWSTR guid;
4156 guid = MSI_RecordGetString(row,1);
4157 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
4158 tl_struct.source = strdupW( file->TargetPath );
4159 tl_struct.path = NULL;
4161 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
4162 (LONG_PTR)&tl_struct);
4164 if (tl_struct.path)
4166 LPCWSTR helpid, help_path = NULL;
4167 HRESULT res;
4169 helpid = MSI_RecordGetString(row,6);
4171 if (helpid) help_path = msi_get_target_folder( package, helpid );
4172 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
4174 if (FAILED(res))
4175 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
4176 else
4177 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
4179 ITypeLib_Release(tl_struct.ptLib);
4180 msi_free(tl_struct.path);
4182 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
4184 FreeLibrary(module);
4185 msi_free(tl_struct.source);
4187 else
4189 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
4190 if (FAILED(hr))
4192 ERR("Failed to load type library: %08x\n", hr);
4193 return ERROR_INSTALL_FAILURE;
4196 ITypeLib_Release(tlib);
4199 return ERROR_SUCCESS;
4202 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
4205 * OK this is a bit confusing.. I am given a _Component key and I believe
4206 * that the file that is being registered as a type library is the "key file
4207 * of that component" which I interpret to mean "The file in the KeyPath of
4208 * that component".
4210 UINT rc;
4211 MSIQUERY * view;
4212 static const WCHAR Query[] =
4213 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4214 '`','T','y','p','e','L','i','b','`',0};
4216 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4217 if (rc != ERROR_SUCCESS)
4218 return ERROR_SUCCESS;
4220 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
4221 msiobj_release(&view->hdr);
4222 return rc;
4225 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
4227 MSIPACKAGE *package = param;
4228 LPCWSTR component, guid;
4229 MSICOMPONENT *comp;
4230 GUID libid;
4231 UINT version;
4232 LCID language;
4233 SYSKIND syskind;
4234 HRESULT hr;
4236 component = MSI_RecordGetString( row, 3 );
4237 comp = msi_get_loaded_component( package, component );
4238 if (!comp)
4239 return ERROR_SUCCESS;
4241 if (!comp->Enabled)
4243 TRACE("component is disabled\n");
4244 return ERROR_SUCCESS;
4247 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4249 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4250 comp->Action = comp->Installed;
4251 return ERROR_SUCCESS;
4253 comp->Action = INSTALLSTATE_ABSENT;
4255 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
4257 guid = MSI_RecordGetString( row, 1 );
4258 CLSIDFromString( (LPCWSTR)guid, &libid );
4259 version = MSI_RecordGetInteger( row, 4 );
4260 language = MSI_RecordGetInteger( row, 2 );
4262 #ifdef _WIN64
4263 syskind = SYS_WIN64;
4264 #else
4265 syskind = SYS_WIN32;
4266 #endif
4268 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
4269 if (FAILED(hr))
4271 WARN("Failed to unregister typelib: %08x\n", hr);
4274 return ERROR_SUCCESS;
4277 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
4279 UINT rc;
4280 MSIQUERY *view;
4281 static const WCHAR query[] =
4282 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4283 '`','T','y','p','e','L','i','b','`',0};
4285 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4286 if (rc != ERROR_SUCCESS)
4287 return ERROR_SUCCESS;
4289 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
4290 msiobj_release( &view->hdr );
4291 return rc;
4294 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
4296 static const WCHAR szlnk[] = {'.','l','n','k',0};
4297 LPCWSTR directory, extension, link_folder;
4298 LPWSTR link_file, filename;
4300 directory = MSI_RecordGetString( row, 2 );
4301 link_folder = msi_get_target_folder( package, directory );
4303 /* may be needed because of a bug somewhere else */
4304 msi_create_full_path( link_folder );
4306 filename = msi_dup_record_field( row, 3 );
4307 msi_reduce_to_long_filename( filename );
4309 extension = strchrW( filename, '.' );
4310 if (!extension || strcmpiW( extension, szlnk ))
4312 int len = strlenW( filename );
4313 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
4314 memcpy( filename + len, szlnk, sizeof(szlnk) );
4316 link_file = msi_build_directory_name( 2, link_folder, filename );
4317 msi_free( filename );
4319 return link_file;
4322 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
4324 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
4325 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
4326 WCHAR *folder, *dest, *path;
4328 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4329 folder = msi_dup_property( package->db, szWindowsFolder );
4330 else
4332 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
4333 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
4334 msi_free( appdata );
4336 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
4337 msi_create_full_path( dest );
4338 path = msi_build_directory_name( 2, dest, icon_name );
4339 msi_free( folder );
4340 msi_free( dest );
4341 return path;
4344 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
4346 MSIPACKAGE *package = param;
4347 LPWSTR link_file, deformated, path;
4348 LPCWSTR component, target;
4349 MSICOMPONENT *comp;
4350 IShellLinkW *sl = NULL;
4351 IPersistFile *pf = NULL;
4352 HRESULT res;
4354 component = MSI_RecordGetString(row, 4);
4355 comp = msi_get_loaded_component(package, component);
4356 if (!comp)
4357 return ERROR_SUCCESS;
4359 if (!comp->Enabled)
4361 TRACE("component is disabled\n");
4362 return ERROR_SUCCESS;
4365 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4367 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4368 comp->Action = comp->Installed;
4369 return ERROR_SUCCESS;
4371 comp->Action = INSTALLSTATE_LOCAL;
4373 msi_ui_actiondata( package, szCreateShortcuts, row );
4375 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
4376 &IID_IShellLinkW, (LPVOID *) &sl );
4378 if (FAILED( res ))
4380 ERR("CLSID_ShellLink not available\n");
4381 goto err;
4384 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
4385 if (FAILED( res ))
4387 ERR("QueryInterface(IID_IPersistFile) failed\n");
4388 goto err;
4391 target = MSI_RecordGetString(row, 5);
4392 if (strchrW(target, '['))
4394 deformat_string(package, target, &deformated);
4395 IShellLinkW_SetPath(sl,deformated);
4396 msi_free(deformated);
4398 else
4400 FIXME("poorly handled shortcut format, advertised shortcut\n");
4401 IShellLinkW_SetPath(sl,comp->FullKeypath);
4404 if (!MSI_RecordIsNull(row,6))
4406 LPCWSTR arguments = MSI_RecordGetString(row, 6);
4407 deformat_string(package, arguments, &deformated);
4408 IShellLinkW_SetArguments(sl,deformated);
4409 msi_free(deformated);
4412 if (!MSI_RecordIsNull(row,7))
4414 LPCWSTR description = MSI_RecordGetString(row, 7);
4415 IShellLinkW_SetDescription(sl, description);
4418 if (!MSI_RecordIsNull(row,8))
4419 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4421 if (!MSI_RecordIsNull(row,9))
4423 INT index;
4424 LPCWSTR icon = MSI_RecordGetString(row, 9);
4426 path = msi_build_icon_path(package, icon);
4427 index = MSI_RecordGetInteger(row,10);
4429 /* no value means 0 */
4430 if (index == MSI_NULL_INTEGER)
4431 index = 0;
4433 IShellLinkW_SetIconLocation(sl, path, index);
4434 msi_free(path);
4437 if (!MSI_RecordIsNull(row,11))
4438 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4440 if (!MSI_RecordIsNull(row,12))
4442 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4443 full_path = msi_get_target_folder( package, wkdir );
4444 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4446 link_file = get_link_file(package, row);
4448 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4449 IPersistFile_Save(pf, link_file, FALSE);
4450 msi_free(link_file);
4452 err:
4453 if (pf)
4454 IPersistFile_Release( pf );
4455 if (sl)
4456 IShellLinkW_Release( sl );
4458 return ERROR_SUCCESS;
4461 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4463 UINT rc;
4464 HRESULT res;
4465 MSIQUERY * view;
4466 static const WCHAR Query[] =
4467 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4468 '`','S','h','o','r','t','c','u','t','`',0};
4470 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
4471 if (rc != ERROR_SUCCESS)
4472 return ERROR_SUCCESS;
4474 res = CoInitialize( NULL );
4476 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4477 msiobj_release(&view->hdr);
4479 if (SUCCEEDED(res))
4480 CoUninitialize();
4482 return rc;
4485 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4487 MSIPACKAGE *package = param;
4488 LPWSTR link_file;
4489 LPCWSTR component;
4490 MSICOMPONENT *comp;
4492 component = MSI_RecordGetString( row, 4 );
4493 comp = msi_get_loaded_component( package, component );
4494 if (!comp)
4495 return ERROR_SUCCESS;
4497 if (!comp->Enabled)
4499 TRACE("component is disabled\n");
4500 return ERROR_SUCCESS;
4503 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4505 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4506 comp->Action = comp->Installed;
4507 return ERROR_SUCCESS;
4509 comp->Action = INSTALLSTATE_ABSENT;
4511 msi_ui_actiondata( package, szRemoveShortcuts, row );
4513 link_file = get_link_file( package, row );
4515 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4516 if (!DeleteFileW( link_file ))
4518 WARN("Failed to remove shortcut file %u\n", GetLastError());
4520 msi_free( link_file );
4522 return ERROR_SUCCESS;
4525 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4527 UINT rc;
4528 MSIQUERY *view;
4529 static const WCHAR query[] =
4530 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4531 '`','S','h','o','r','t','c','u','t','`',0};
4533 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4534 if (rc != ERROR_SUCCESS)
4535 return ERROR_SUCCESS;
4537 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4538 msiobj_release( &view->hdr );
4540 return rc;
4543 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4545 MSIPACKAGE* package = param;
4546 HANDLE the_file;
4547 LPWSTR FilePath;
4548 LPCWSTR FileName;
4549 CHAR buffer[1024];
4550 DWORD sz;
4551 UINT rc;
4553 FileName = MSI_RecordGetString(row,1);
4554 if (!FileName)
4556 ERR("Unable to get FileName\n");
4557 return ERROR_SUCCESS;
4560 FilePath = msi_build_icon_path(package, FileName);
4562 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4564 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4565 FILE_ATTRIBUTE_NORMAL, NULL);
4567 if (the_file == INVALID_HANDLE_VALUE)
4569 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4570 msi_free(FilePath);
4571 return ERROR_SUCCESS;
4576 DWORD write;
4577 sz = 1024;
4578 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4579 if (rc != ERROR_SUCCESS)
4581 ERR("Failed to get stream\n");
4582 CloseHandle(the_file);
4583 DeleteFileW(FilePath);
4584 break;
4586 WriteFile(the_file,buffer,sz,&write,NULL);
4587 } while (sz == 1024);
4589 msi_free(FilePath);
4590 CloseHandle(the_file);
4592 return ERROR_SUCCESS;
4595 static UINT msi_publish_icons(MSIPACKAGE *package)
4597 UINT r;
4598 MSIQUERY *view;
4600 static const WCHAR query[]= {
4601 'S','E','L','E','C','T',' ','*',' ',
4602 'F','R','O','M',' ','`','I','c','o','n','`',0};
4604 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4605 if (r == ERROR_SUCCESS)
4607 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4608 msiobj_release(&view->hdr);
4611 return ERROR_SUCCESS;
4614 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4616 UINT r;
4617 HKEY source;
4618 LPWSTR buffer;
4619 MSIMEDIADISK *disk;
4620 MSISOURCELISTINFO *info;
4622 r = RegCreateKeyW(hkey, szSourceList, &source);
4623 if (r != ERROR_SUCCESS)
4624 return r;
4626 RegCloseKey(source);
4628 buffer = strrchrW(package->PackagePath, '\\') + 1;
4629 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4630 package->Context, MSICODE_PRODUCT,
4631 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4632 if (r != ERROR_SUCCESS)
4633 return r;
4635 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4636 package->Context, MSICODE_PRODUCT,
4637 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4638 if (r != ERROR_SUCCESS)
4639 return r;
4641 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4642 package->Context, MSICODE_PRODUCT,
4643 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4644 if (r != ERROR_SUCCESS)
4645 return r;
4647 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4649 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4650 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4651 info->options, info->value);
4652 else
4653 MsiSourceListSetInfoW(package->ProductCode, NULL,
4654 info->context, info->options,
4655 info->property, info->value);
4658 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4660 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4661 disk->context, disk->options,
4662 disk->disk_id, disk->volume_label, disk->disk_prompt);
4665 return ERROR_SUCCESS;
4668 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4670 MSIHANDLE hdb, suminfo;
4671 WCHAR guids[MAX_PATH];
4672 WCHAR packcode[SQUISH_GUID_SIZE];
4673 LPWSTR buffer;
4674 LPWSTR ptr;
4675 DWORD langid;
4676 DWORD size;
4677 UINT r;
4679 static const WCHAR szARPProductIcon[] =
4680 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4681 static const WCHAR szAssignment[] =
4682 {'A','s','s','i','g','n','m','e','n','t',0};
4683 static const WCHAR szAdvertiseFlags[] =
4684 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4685 static const WCHAR szClients[] =
4686 {'C','l','i','e','n','t','s',0};
4687 static const WCHAR szColon[] = {':',0};
4689 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4690 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4691 msi_free(buffer);
4693 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4694 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4696 /* FIXME */
4697 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4699 buffer = msi_dup_property(package->db, szARPProductIcon);
4700 if (buffer)
4702 LPWSTR path = msi_build_icon_path(package, buffer);
4703 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4704 msi_free(path);
4705 msi_free(buffer);
4708 buffer = msi_dup_property(package->db, szProductVersion);
4709 if (buffer)
4711 DWORD verdword = msi_version_str_to_dword(buffer);
4712 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4713 msi_free(buffer);
4716 msi_reg_set_val_dword(hkey, szAssignment, 0);
4717 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4718 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4719 msi_reg_set_val_str(hkey, szClients, szColon);
4721 hdb = alloc_msihandle(&package->db->hdr);
4722 if (!hdb)
4723 return ERROR_NOT_ENOUGH_MEMORY;
4725 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4726 MsiCloseHandle(hdb);
4727 if (r != ERROR_SUCCESS)
4728 goto done;
4730 size = MAX_PATH;
4731 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4732 NULL, guids, &size);
4733 if (r != ERROR_SUCCESS)
4734 goto done;
4736 ptr = strchrW(guids, ';');
4737 if (ptr) *ptr = 0;
4738 squash_guid(guids, packcode);
4739 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4741 done:
4742 MsiCloseHandle(suminfo);
4743 return ERROR_SUCCESS;
4746 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4748 UINT r;
4749 HKEY hkey;
4750 LPWSTR upgrade;
4751 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4753 upgrade = msi_dup_property(package->db, szUpgradeCode);
4754 if (!upgrade)
4755 return ERROR_SUCCESS;
4757 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4758 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4759 else
4760 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4762 if (r != ERROR_SUCCESS)
4764 WARN("failed to open upgrade code key\n");
4765 msi_free(upgrade);
4766 return ERROR_SUCCESS;
4768 squash_guid(package->ProductCode, squashed_pc);
4769 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4770 RegCloseKey(hkey);
4771 msi_free(upgrade);
4772 return ERROR_SUCCESS;
4775 static BOOL msi_check_publish(MSIPACKAGE *package)
4777 MSIFEATURE *feature;
4779 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4781 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4782 return TRUE;
4785 return FALSE;
4788 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4790 MSIFEATURE *feature;
4792 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4794 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4795 return FALSE;
4798 return TRUE;
4801 static UINT msi_publish_patches( MSIPACKAGE *package )
4803 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4804 WCHAR patch_squashed[GUID_SIZE];
4805 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4806 LONG res;
4807 MSIPATCHINFO *patch;
4808 UINT r;
4809 WCHAR *p, *all_patches = NULL;
4810 DWORD len = 0;
4812 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4813 if (r != ERROR_SUCCESS)
4814 return ERROR_FUNCTION_FAILED;
4816 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4817 if (res != ERROR_SUCCESS)
4819 r = ERROR_FUNCTION_FAILED;
4820 goto done;
4823 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4824 if (r != ERROR_SUCCESS)
4825 goto done;
4827 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4829 squash_guid( patch->patchcode, patch_squashed );
4830 len += strlenW( patch_squashed ) + 1;
4833 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4834 if (!all_patches)
4835 goto done;
4837 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4839 HKEY patch_key;
4841 squash_guid( patch->patchcode, p );
4842 p += strlenW( p ) + 1;
4844 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4845 (const BYTE *)patch->transforms,
4846 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4847 if (res != ERROR_SUCCESS)
4848 goto done;
4850 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4851 if (r != ERROR_SUCCESS)
4852 goto done;
4854 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4855 (const BYTE *)patch->localfile,
4856 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4857 RegCloseKey( patch_key );
4858 if (res != ERROR_SUCCESS)
4859 goto done;
4861 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4862 if (res != ERROR_SUCCESS)
4863 goto done;
4865 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4866 RegCloseKey( patch_key );
4867 if (res != ERROR_SUCCESS)
4868 goto done;
4871 all_patches[len] = 0;
4872 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4873 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4874 if (res != ERROR_SUCCESS)
4875 goto done;
4877 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4878 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4879 if (res != ERROR_SUCCESS)
4880 r = ERROR_FUNCTION_FAILED;
4882 done:
4883 RegCloseKey( product_patches_key );
4884 RegCloseKey( patches_key );
4885 RegCloseKey( product_key );
4886 msi_free( all_patches );
4887 return r;
4891 * 99% of the work done here is only done for
4892 * advertised installs. However this is where the
4893 * Icon table is processed and written out
4894 * so that is what I am going to do here.
4896 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4898 UINT rc;
4899 HKEY hukey = NULL, hudkey = NULL;
4900 MSIRECORD *uirow;
4902 if (!list_empty(&package->patches))
4904 rc = msi_publish_patches(package);
4905 if (rc != ERROR_SUCCESS)
4906 goto end;
4909 /* FIXME: also need to publish if the product is in advertise mode */
4910 if (!msi_check_publish(package))
4911 return ERROR_SUCCESS;
4913 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4914 &hukey, TRUE);
4915 if (rc != ERROR_SUCCESS)
4916 goto end;
4918 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4919 NULL, &hudkey, TRUE);
4920 if (rc != ERROR_SUCCESS)
4921 goto end;
4923 rc = msi_publish_upgrade_code(package);
4924 if (rc != ERROR_SUCCESS)
4925 goto end;
4927 rc = msi_publish_product_properties(package, hukey);
4928 if (rc != ERROR_SUCCESS)
4929 goto end;
4931 rc = msi_publish_sourcelist(package, hukey);
4932 if (rc != ERROR_SUCCESS)
4933 goto end;
4935 rc = msi_publish_icons(package);
4937 end:
4938 uirow = MSI_CreateRecord( 1 );
4939 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4940 msi_ui_actiondata( package, szPublishProduct, uirow );
4941 msiobj_release( &uirow->hdr );
4943 RegCloseKey(hukey);
4944 RegCloseKey(hudkey);
4946 return rc;
4949 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4951 WCHAR *filename, *ptr, *folder, *ret;
4952 const WCHAR *dirprop;
4954 filename = msi_dup_record_field( row, 2 );
4955 if (filename && (ptr = strchrW( filename, '|' )))
4956 ptr++;
4957 else
4958 ptr = filename;
4960 dirprop = MSI_RecordGetString( row, 3 );
4961 if (dirprop)
4963 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4964 if (!folder) folder = msi_dup_property( package->db, dirprop );
4966 else
4967 folder = msi_dup_property( package->db, szWindowsFolder );
4969 if (!folder)
4971 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4972 msi_free( filename );
4973 return NULL;
4976 ret = msi_build_directory_name( 2, folder, ptr );
4978 msi_free( filename );
4979 msi_free( folder );
4980 return ret;
4983 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4985 MSIPACKAGE *package = param;
4986 LPCWSTR component, section, key, value, identifier;
4987 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4988 MSIRECORD * uirow;
4989 INT action;
4990 MSICOMPONENT *comp;
4992 component = MSI_RecordGetString(row, 8);
4993 comp = msi_get_loaded_component(package,component);
4994 if (!comp)
4995 return ERROR_SUCCESS;
4997 if (!comp->Enabled)
4999 TRACE("component is disabled\n");
5000 return ERROR_SUCCESS;
5003 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5005 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
5006 comp->Action = comp->Installed;
5007 return ERROR_SUCCESS;
5009 comp->Action = INSTALLSTATE_LOCAL;
5011 identifier = MSI_RecordGetString(row,1);
5012 section = MSI_RecordGetString(row,4);
5013 key = MSI_RecordGetString(row,5);
5014 value = MSI_RecordGetString(row,6);
5015 action = MSI_RecordGetInteger(row,7);
5017 deformat_string(package,section,&deformated_section);
5018 deformat_string(package,key,&deformated_key);
5019 deformat_string(package,value,&deformated_value);
5021 fullname = get_ini_file_name(package, row);
5023 if (action == 0)
5025 TRACE("Adding value %s to section %s in %s\n",
5026 debugstr_w(deformated_key), debugstr_w(deformated_section),
5027 debugstr_w(fullname));
5028 WritePrivateProfileStringW(deformated_section, deformated_key,
5029 deformated_value, fullname);
5031 else if (action == 1)
5033 WCHAR returned[10];
5034 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
5035 returned, 10, fullname);
5036 if (returned[0] == 0)
5038 TRACE("Adding value %s to section %s in %s\n",
5039 debugstr_w(deformated_key), debugstr_w(deformated_section),
5040 debugstr_w(fullname));
5042 WritePrivateProfileStringW(deformated_section, deformated_key,
5043 deformated_value, fullname);
5046 else if (action == 3)
5047 FIXME("Append to existing section not yet implemented\n");
5049 uirow = MSI_CreateRecord(4);
5050 MSI_RecordSetStringW(uirow,1,identifier);
5051 MSI_RecordSetStringW(uirow,2,deformated_section);
5052 MSI_RecordSetStringW(uirow,3,deformated_key);
5053 MSI_RecordSetStringW(uirow,4,deformated_value);
5054 msi_ui_actiondata( package, szWriteIniValues, uirow );
5055 msiobj_release( &uirow->hdr );
5057 msi_free(fullname);
5058 msi_free(deformated_key);
5059 msi_free(deformated_value);
5060 msi_free(deformated_section);
5061 return ERROR_SUCCESS;
5064 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
5066 UINT rc;
5067 MSIQUERY * view;
5068 static const WCHAR ExecSeqQuery[] =
5069 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5070 '`','I','n','i','F','i','l','e','`',0};
5072 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5073 if (rc != ERROR_SUCCESS)
5075 TRACE("no IniFile table\n");
5076 return ERROR_SUCCESS;
5079 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
5080 msiobj_release(&view->hdr);
5081 return rc;
5084 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
5086 MSIPACKAGE *package = param;
5087 LPCWSTR component, section, key, value, identifier;
5088 LPWSTR deformated_section, deformated_key, deformated_value, filename;
5089 MSICOMPONENT *comp;
5090 MSIRECORD *uirow;
5091 INT action;
5093 component = MSI_RecordGetString( row, 8 );
5094 comp = msi_get_loaded_component( package, component );
5095 if (!comp)
5096 return ERROR_SUCCESS;
5098 if (!comp->Enabled)
5100 TRACE("component is disabled\n");
5101 return ERROR_SUCCESS;
5104 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5106 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
5107 comp->Action = comp->Installed;
5108 return ERROR_SUCCESS;
5110 comp->Action = INSTALLSTATE_ABSENT;
5112 identifier = MSI_RecordGetString( row, 1 );
5113 section = MSI_RecordGetString( row, 4 );
5114 key = MSI_RecordGetString( row, 5 );
5115 value = MSI_RecordGetString( row, 6 );
5116 action = MSI_RecordGetInteger( row, 7 );
5118 deformat_string( package, section, &deformated_section );
5119 deformat_string( package, key, &deformated_key );
5120 deformat_string( package, value, &deformated_value );
5122 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
5124 filename = get_ini_file_name( package, row );
5126 TRACE("Removing key %s from section %s in %s\n",
5127 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
5129 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
5131 WARN("Unable to remove key %u\n", GetLastError());
5133 msi_free( filename );
5135 else
5136 FIXME("Unsupported action %d\n", action);
5139 uirow = MSI_CreateRecord( 4 );
5140 MSI_RecordSetStringW( uirow, 1, identifier );
5141 MSI_RecordSetStringW( uirow, 2, deformated_section );
5142 MSI_RecordSetStringW( uirow, 3, deformated_key );
5143 MSI_RecordSetStringW( uirow, 4, deformated_value );
5144 msi_ui_actiondata( package, szRemoveIniValues, uirow );
5145 msiobj_release( &uirow->hdr );
5147 msi_free( deformated_key );
5148 msi_free( deformated_value );
5149 msi_free( deformated_section );
5150 return ERROR_SUCCESS;
5153 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
5155 MSIPACKAGE *package = param;
5156 LPCWSTR component, section, key, value, identifier;
5157 LPWSTR deformated_section, deformated_key, deformated_value, filename;
5158 MSICOMPONENT *comp;
5159 MSIRECORD *uirow;
5160 INT action;
5162 component = MSI_RecordGetString( row, 8 );
5163 comp = msi_get_loaded_component( package, component );
5164 if (!comp)
5165 return ERROR_SUCCESS;
5167 if (!comp->Enabled)
5169 TRACE("component is disabled\n");
5170 return ERROR_SUCCESS;
5173 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5175 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
5176 comp->Action = comp->Installed;
5177 return ERROR_SUCCESS;
5179 comp->Action = INSTALLSTATE_LOCAL;
5181 identifier = MSI_RecordGetString( row, 1 );
5182 section = MSI_RecordGetString( row, 4 );
5183 key = MSI_RecordGetString( row, 5 );
5184 value = MSI_RecordGetString( row, 6 );
5185 action = MSI_RecordGetInteger( row, 7 );
5187 deformat_string( package, section, &deformated_section );
5188 deformat_string( package, key, &deformated_key );
5189 deformat_string( package, value, &deformated_value );
5191 if (action == msidbIniFileActionRemoveLine)
5193 filename = get_ini_file_name( package, row );
5195 TRACE("Removing key %s from section %s in %s\n",
5196 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
5198 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
5200 WARN("Unable to remove key %u\n", GetLastError());
5202 msi_free( filename );
5204 else
5205 FIXME("Unsupported action %d\n", action);
5207 uirow = MSI_CreateRecord( 4 );
5208 MSI_RecordSetStringW( uirow, 1, identifier );
5209 MSI_RecordSetStringW( uirow, 2, deformated_section );
5210 MSI_RecordSetStringW( uirow, 3, deformated_key );
5211 MSI_RecordSetStringW( uirow, 4, deformated_value );
5212 msi_ui_actiondata( package, szRemoveIniValues, uirow );
5213 msiobj_release( &uirow->hdr );
5215 msi_free( deformated_key );
5216 msi_free( deformated_value );
5217 msi_free( deformated_section );
5218 return ERROR_SUCCESS;
5221 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5223 UINT rc;
5224 MSIQUERY *view;
5225 static const WCHAR query[] =
5226 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5227 '`','I','n','i','F','i','l','e','`',0};
5228 static const WCHAR remove_query[] =
5229 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5230 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
5232 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5233 if (rc == ERROR_SUCCESS)
5235 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
5236 msiobj_release( &view->hdr );
5237 if (rc != ERROR_SUCCESS)
5238 return rc;
5241 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
5242 if (rc == ERROR_SUCCESS)
5244 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
5245 msiobj_release( &view->hdr );
5246 if (rc != ERROR_SUCCESS)
5247 return rc;
5250 return ERROR_SUCCESS;
5253 static void register_dll( const WCHAR *dll, BOOL unregister )
5255 HMODULE hmod;
5257 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
5258 if (hmod)
5260 HRESULT (WINAPI *func_ptr)( void );
5261 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
5263 func_ptr = (void *)GetProcAddress( hmod, func );
5264 if (func_ptr)
5266 HRESULT hr = func_ptr();
5267 if (FAILED( hr ))
5268 WARN("failed to register dll 0x%08x\n", hr);
5270 else
5271 WARN("entry point %s not found\n", func);
5272 FreeLibrary( hmod );
5273 return;
5275 WARN("failed to load library %u\n", GetLastError());
5278 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
5280 MSIPACKAGE *package = param;
5281 LPCWSTR filename;
5282 MSIFILE *file;
5283 MSIRECORD *uirow;
5285 filename = MSI_RecordGetString(row,1);
5286 file = msi_get_loaded_file( package, filename );
5288 if (!file)
5290 ERR("Unable to find file id %s\n",debugstr_w(filename));
5291 return ERROR_SUCCESS;
5294 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
5296 register_dll( file->TargetPath, FALSE );
5298 uirow = MSI_CreateRecord( 2 );
5299 MSI_RecordSetStringW( uirow, 1, filename );
5300 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
5301 msi_ui_actiondata( package, szSelfRegModules, uirow );
5302 msiobj_release( &uirow->hdr );
5304 return ERROR_SUCCESS;
5307 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
5309 UINT rc;
5310 MSIQUERY * view;
5311 static const WCHAR ExecSeqQuery[] =
5312 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5313 '`','S','e','l','f','R','e','g','`',0};
5315 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5316 if (rc != ERROR_SUCCESS)
5318 TRACE("no SelfReg table\n");
5319 return ERROR_SUCCESS;
5322 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
5323 msiobj_release(&view->hdr);
5325 return ERROR_SUCCESS;
5328 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
5330 MSIPACKAGE *package = param;
5331 LPCWSTR filename;
5332 MSIFILE *file;
5333 MSIRECORD *uirow;
5335 filename = MSI_RecordGetString( row, 1 );
5336 file = msi_get_loaded_file( package, filename );
5338 if (!file)
5340 ERR("Unable to find file id %s\n", debugstr_w(filename));
5341 return ERROR_SUCCESS;
5344 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
5346 register_dll( file->TargetPath, TRUE );
5348 uirow = MSI_CreateRecord( 2 );
5349 MSI_RecordSetStringW( uirow, 1, filename );
5350 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
5351 msi_ui_actiondata( package, szSelfUnregModules, uirow );
5352 msiobj_release( &uirow->hdr );
5354 return ERROR_SUCCESS;
5357 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5359 UINT rc;
5360 MSIQUERY *view;
5361 static const WCHAR query[] =
5362 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5363 '`','S','e','l','f','R','e','g','`',0};
5365 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5366 if (rc != ERROR_SUCCESS)
5368 TRACE("no SelfReg table\n");
5369 return ERROR_SUCCESS;
5372 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
5373 msiobj_release( &view->hdr );
5375 return ERROR_SUCCESS;
5378 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
5380 MSIFEATURE *feature;
5381 UINT rc;
5382 HKEY hkey = NULL, userdata = NULL;
5384 if (!msi_check_publish(package))
5385 return ERROR_SUCCESS;
5387 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5388 &hkey, TRUE);
5389 if (rc != ERROR_SUCCESS)
5390 goto end;
5392 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5393 &userdata, TRUE);
5394 if (rc != ERROR_SUCCESS)
5395 goto end;
5397 /* here the guids are base 85 encoded */
5398 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5400 ComponentList *cl;
5401 LPWSTR data = NULL;
5402 GUID clsid;
5403 INT size;
5404 BOOL absent = FALSE;
5405 MSIRECORD *uirow;
5407 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
5408 feature->ActionRequest != INSTALLSTATE_SOURCE &&
5409 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
5411 size = 1;
5412 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5414 size += 21;
5416 if (feature->Feature_Parent)
5417 size += strlenW( feature->Feature_Parent )+2;
5419 data = msi_alloc(size * sizeof(WCHAR));
5421 data[0] = 0;
5422 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5424 MSICOMPONENT* component = cl->component;
5425 WCHAR buf[21];
5427 buf[0] = 0;
5428 if (component->ComponentId)
5430 TRACE("From %s\n",debugstr_w(component->ComponentId));
5431 CLSIDFromString(component->ComponentId, &clsid);
5432 encode_base85_guid(&clsid,buf);
5433 TRACE("to %s\n",debugstr_w(buf));
5434 strcatW(data,buf);
5438 if (feature->Feature_Parent)
5440 static const WCHAR sep[] = {'\2',0};
5441 strcatW(data,sep);
5442 strcatW(data,feature->Feature_Parent);
5445 msi_reg_set_val_str( userdata, feature->Feature, data );
5446 msi_free(data);
5448 size = 0;
5449 if (feature->Feature_Parent)
5450 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5451 if (!absent)
5453 size += sizeof(WCHAR);
5454 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5455 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5457 else
5459 size += 2*sizeof(WCHAR);
5460 data = msi_alloc(size);
5461 data[0] = 0x6;
5462 data[1] = 0;
5463 if (feature->Feature_Parent)
5464 strcpyW( &data[1], feature->Feature_Parent );
5465 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5466 (LPBYTE)data,size);
5467 msi_free(data);
5470 /* the UI chunk */
5471 uirow = MSI_CreateRecord( 1 );
5472 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5473 msi_ui_actiondata( package, szPublishFeatures, uirow );
5474 msiobj_release( &uirow->hdr );
5475 /* FIXME: call msi_ui_progress? */
5478 end:
5479 RegCloseKey(hkey);
5480 RegCloseKey(userdata);
5481 return rc;
5484 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5486 UINT r;
5487 HKEY hkey;
5488 MSIRECORD *uirow;
5490 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5492 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
5493 &hkey, FALSE);
5494 if (r == ERROR_SUCCESS)
5496 RegDeleteValueW(hkey, feature->Feature);
5497 RegCloseKey(hkey);
5500 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
5501 &hkey, FALSE);
5502 if (r == ERROR_SUCCESS)
5504 RegDeleteValueW(hkey, feature->Feature);
5505 RegCloseKey(hkey);
5508 uirow = MSI_CreateRecord( 1 );
5509 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5510 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5511 msiobj_release( &uirow->hdr );
5513 return ERROR_SUCCESS;
5516 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5518 MSIFEATURE *feature;
5520 if (!msi_check_unpublish(package))
5521 return ERROR_SUCCESS;
5523 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5525 msi_unpublish_feature(package, feature);
5528 return ERROR_SUCCESS;
5531 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5533 SYSTEMTIME systime;
5534 DWORD size, langid;
5535 WCHAR date[9], *val, *buffer;
5536 const WCHAR *prop, *key;
5538 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5539 static const WCHAR modpath_fmt[] =
5540 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5541 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5542 static const WCHAR szModifyPath[] =
5543 {'M','o','d','i','f','y','P','a','t','h',0};
5544 static const WCHAR szUninstallString[] =
5545 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5546 static const WCHAR szEstimatedSize[] =
5547 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5548 static const WCHAR szDisplayVersion[] =
5549 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5550 static const WCHAR szInstallSource[] =
5551 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5552 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5553 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5554 static const WCHAR szAuthorizedCDFPrefix[] =
5555 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5556 static const WCHAR szARPCONTACT[] =
5557 {'A','R','P','C','O','N','T','A','C','T',0};
5558 static const WCHAR szContact[] =
5559 {'C','o','n','t','a','c','t',0};
5560 static const WCHAR szARPCOMMENTS[] =
5561 {'A','R','P','C','O','M','M','E','N','T','S',0};
5562 static const WCHAR szComments[] =
5563 {'C','o','m','m','e','n','t','s',0};
5564 static const WCHAR szProductName[] =
5565 {'P','r','o','d','u','c','t','N','a','m','e',0};
5566 static const WCHAR szDisplayName[] =
5567 {'D','i','s','p','l','a','y','N','a','m','e',0};
5568 static const WCHAR szARPHELPLINK[] =
5569 {'A','R','P','H','E','L','P','L','I','N','K',0};
5570 static const WCHAR szHelpLink[] =
5571 {'H','e','l','p','L','i','n','k',0};
5572 static const WCHAR szARPHELPTELEPHONE[] =
5573 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5574 static const WCHAR szHelpTelephone[] =
5575 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5576 static const WCHAR szARPINSTALLLOCATION[] =
5577 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5578 static const WCHAR szInstallLocation[] =
5579 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
5580 static const WCHAR szManufacturer[] =
5581 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5582 static const WCHAR szPublisher[] =
5583 {'P','u','b','l','i','s','h','e','r',0};
5584 static const WCHAR szARPREADME[] =
5585 {'A','R','P','R','E','A','D','M','E',0};
5586 static const WCHAR szReadme[] =
5587 {'R','e','a','d','M','e',0};
5588 static const WCHAR szARPSIZE[] =
5589 {'A','R','P','S','I','Z','E',0};
5590 static const WCHAR szSize[] =
5591 {'S','i','z','e',0};
5592 static const WCHAR szARPURLINFOABOUT[] =
5593 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5594 static const WCHAR szURLInfoAbout[] =
5595 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5596 static const WCHAR szARPURLUPDATEINFO[] =
5597 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5598 static const WCHAR szURLUpdateInfo[] =
5599 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5601 static const WCHAR *propval[] = {
5602 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5603 szARPCONTACT, szContact,
5604 szARPCOMMENTS, szComments,
5605 szProductName, szDisplayName,
5606 szARPHELPLINK, szHelpLink,
5607 szARPHELPTELEPHONE, szHelpTelephone,
5608 szARPINSTALLLOCATION, szInstallLocation,
5609 szSourceDir, szInstallSource,
5610 szManufacturer, szPublisher,
5611 szARPREADME, szReadme,
5612 szARPSIZE, szSize,
5613 szARPURLINFOABOUT, szURLInfoAbout,
5614 szARPURLUPDATEINFO, szURLUpdateInfo,
5615 NULL
5617 const WCHAR **p = propval;
5619 while (*p)
5621 prop = *p++;
5622 key = *p++;
5623 val = msi_dup_property(package->db, prop);
5624 msi_reg_set_val_str(hkey, key, val);
5625 msi_free(val);
5628 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5630 size = deformat_string(package, modpath_fmt, &buffer);
5631 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5632 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5633 msi_free(buffer);
5635 /* FIXME: Write real Estimated Size when we have it */
5636 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5638 GetLocalTime(&systime);
5639 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5640 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5642 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5643 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5645 buffer = msi_dup_property(package->db, szProductVersion);
5646 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5647 if (buffer)
5649 DWORD verdword = msi_version_str_to_dword(buffer);
5651 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5652 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5653 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5654 msi_free(buffer);
5657 return ERROR_SUCCESS;
5660 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5662 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5663 MSIRECORD *uirow;
5664 LPWSTR upgrade_code;
5665 HKEY hkey, props, upgrade_key;
5666 UINT rc;
5668 /* FIXME: also need to publish if the product is in advertise mode */
5669 if (!msi_check_publish(package))
5670 return ERROR_SUCCESS;
5672 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5673 if (rc != ERROR_SUCCESS)
5674 return rc;
5676 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5677 NULL, &props, TRUE);
5678 if (rc != ERROR_SUCCESS)
5679 goto done;
5681 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
5682 msi_free( package->db->localfile );
5683 package->db->localfile = NULL;
5685 rc = msi_publish_install_properties(package, hkey);
5686 if (rc != ERROR_SUCCESS)
5687 goto done;
5689 rc = msi_publish_install_properties(package, props);
5690 if (rc != ERROR_SUCCESS)
5691 goto done;
5693 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5694 if (upgrade_code)
5696 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5697 if (rc == ERROR_SUCCESS)
5699 squash_guid( package->ProductCode, squashed_pc );
5700 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5701 RegCloseKey( upgrade_key );
5703 msi_free( upgrade_code );
5706 done:
5707 uirow = MSI_CreateRecord( 1 );
5708 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5709 msi_ui_actiondata( package, szRegisterProduct, uirow );
5710 msiobj_release( &uirow->hdr );
5712 RegCloseKey(hkey);
5713 return ERROR_SUCCESS;
5716 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5718 return execute_script(package,INSTALL_SCRIPT);
5721 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
5723 WCHAR *upgrade, **features;
5724 BOOL full_uninstall = TRUE;
5725 MSIFEATURE *feature;
5726 MSIPATCHINFO *patch;
5728 static const WCHAR szUpgradeCode[] =
5729 {'U','p','g','r','a','d','e','C','o','d','e',0};
5731 features = msi_split_string(remove, ',');
5732 if (!features)
5734 ERR("REMOVE feature list is empty!\n");
5735 return ERROR_FUNCTION_FAILED;
5738 if (!strcmpW( features[0], szAll ))
5739 full_uninstall = TRUE;
5740 else
5742 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5744 if (feature->Action != INSTALLSTATE_ABSENT)
5745 full_uninstall = FALSE;
5748 msi_free(features);
5750 if (!full_uninstall)
5751 return ERROR_SUCCESS;
5753 MSIREG_DeleteProductKey(package->ProductCode);
5754 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5755 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5757 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5758 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5759 MSIREG_DeleteUserProductKey(package->ProductCode);
5760 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5762 upgrade = msi_dup_property(package->db, szUpgradeCode);
5763 if (upgrade)
5765 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5766 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5767 msi_free(upgrade);
5770 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5772 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5775 return ERROR_SUCCESS;
5778 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5780 UINT rc;
5781 WCHAR *remove;
5783 /* turn off scheduling */
5784 package->script->CurrentlyScripting= FALSE;
5786 /* first do the same as an InstallExecute */
5787 rc = ACTION_InstallExecute(package);
5788 if (rc != ERROR_SUCCESS)
5789 return rc;
5791 /* then handle Commit Actions */
5792 rc = execute_script(package,COMMIT_SCRIPT);
5793 if (rc != ERROR_SUCCESS)
5794 return rc;
5796 remove = msi_dup_property(package->db, szRemove);
5797 if (remove)
5798 rc = msi_unpublish_product(package, remove);
5800 msi_free(remove);
5801 return rc;
5804 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5806 static const WCHAR RunOnce[] = {
5807 'S','o','f','t','w','a','r','e','\\',
5808 'M','i','c','r','o','s','o','f','t','\\',
5809 'W','i','n','d','o','w','s','\\',
5810 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5811 'R','u','n','O','n','c','e',0};
5812 static const WCHAR InstallRunOnce[] = {
5813 'S','o','f','t','w','a','r','e','\\',
5814 'M','i','c','r','o','s','o','f','t','\\',
5815 'W','i','n','d','o','w','s','\\',
5816 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5817 'I','n','s','t','a','l','l','e','r','\\',
5818 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5820 static const WCHAR msiexec_fmt[] = {
5821 '%','s',
5822 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5823 '\"','%','s','\"',0};
5824 static const WCHAR install_fmt[] = {
5825 '/','I',' ','\"','%','s','\"',' ',
5826 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5827 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5828 WCHAR buffer[256], sysdir[MAX_PATH];
5829 HKEY hkey;
5830 WCHAR squished_pc[100];
5832 squash_guid(package->ProductCode,squished_pc);
5834 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5835 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5836 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5837 squished_pc);
5839 msi_reg_set_val_str( hkey, squished_pc, buffer );
5840 RegCloseKey(hkey);
5842 TRACE("Reboot command %s\n",debugstr_w(buffer));
5844 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5845 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5847 msi_reg_set_val_str( hkey, squished_pc, buffer );
5848 RegCloseKey(hkey);
5850 return ERROR_INSTALL_SUSPEND;
5853 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5855 static const WCHAR query[] =
5856 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5857 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5858 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5859 MSIRECORD *rec, *row;
5860 DWORD i, size = 0;
5861 va_list va;
5862 const WCHAR *str;
5863 WCHAR *data;
5865 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5867 rec = MSI_CreateRecord( count + 2 );
5868 str = MSI_RecordGetString( row, 1 );
5869 MSI_RecordSetStringW( rec, 0, str );
5870 msiobj_release( &row->hdr );
5871 MSI_RecordSetInteger( rec, 1, error );
5873 va_start( va, count );
5874 for (i = 0; i < count; i++)
5876 str = va_arg( va, const WCHAR *);
5877 MSI_RecordSetStringW( rec, i + 2, str );
5879 va_end( va );
5881 MSI_FormatRecordW( package, rec, NULL, &size );
5882 size++;
5883 data = msi_alloc( size * sizeof(WCHAR) );
5884 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5885 else data[0] = 0;
5886 msiobj_release( &rec->hdr );
5887 return data;
5890 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5892 DWORD attrib;
5893 UINT rc;
5896 * We are currently doing what should be done here in the top level Install
5897 * however for Administrative and uninstalls this step will be needed
5899 if (!package->PackagePath)
5900 return ERROR_SUCCESS;
5902 msi_set_sourcedir_props(package, TRUE);
5904 attrib = GetFileAttributesW(package->db->path);
5905 if (attrib == INVALID_FILE_ATTRIBUTES)
5907 LPWSTR prompt;
5908 LPWSTR msg;
5909 DWORD size = 0;
5911 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5912 package->Context, MSICODE_PRODUCT,
5913 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5914 if (rc == ERROR_MORE_DATA)
5916 prompt = msi_alloc(size * sizeof(WCHAR));
5917 MsiSourceListGetInfoW(package->ProductCode, NULL,
5918 package->Context, MSICODE_PRODUCT,
5919 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5921 else
5922 prompt = strdupW(package->db->path);
5924 msg = msi_build_error_string(package, 1302, 1, prompt);
5925 while(attrib == INVALID_FILE_ATTRIBUTES)
5927 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5928 if (rc == IDCANCEL)
5930 rc = ERROR_INSTALL_USEREXIT;
5931 break;
5933 attrib = GetFileAttributesW(package->db->path);
5935 msi_free(prompt);
5936 rc = ERROR_SUCCESS;
5938 else
5939 return ERROR_SUCCESS;
5941 return rc;
5944 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5946 HKEY hkey = 0;
5947 LPWSTR buffer, productid = NULL;
5948 UINT i, rc = ERROR_SUCCESS;
5949 MSIRECORD *uirow;
5951 static const WCHAR szPropKeys[][80] =
5953 {'P','r','o','d','u','c','t','I','D',0},
5954 {'U','S','E','R','N','A','M','E',0},
5955 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5956 {0},
5959 static const WCHAR szRegKeys[][80] =
5961 {'P','r','o','d','u','c','t','I','D',0},
5962 {'R','e','g','O','w','n','e','r',0},
5963 {'R','e','g','C','o','m','p','a','n','y',0},
5964 {0},
5967 if (msi_check_unpublish(package))
5969 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5970 goto end;
5973 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5974 if (!productid)
5975 goto end;
5977 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5978 NULL, &hkey, TRUE);
5979 if (rc != ERROR_SUCCESS)
5980 goto end;
5982 for( i = 0; szPropKeys[i][0]; i++ )
5984 buffer = msi_dup_property( package->db, szPropKeys[i] );
5985 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5986 msi_free( buffer );
5989 end:
5990 uirow = MSI_CreateRecord( 1 );
5991 MSI_RecordSetStringW( uirow, 1, productid );
5992 msi_ui_actiondata( package, szRegisterUser, uirow );
5993 msiobj_release( &uirow->hdr );
5995 msi_free(productid);
5996 RegCloseKey(hkey);
5997 return rc;
6001 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
6003 UINT rc;
6005 package->script->InWhatSequence |= SEQUENCE_EXEC;
6006 rc = ACTION_ProcessExecSequence(package,FALSE);
6007 return rc;
6010 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
6012 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
6013 WCHAR productid_85[21], component_85[21], *ret;
6014 GUID clsid;
6015 DWORD sz;
6017 /* > is used if there is a component GUID and < if not. */
6019 productid_85[0] = 0;
6020 component_85[0] = 0;
6021 CLSIDFromString( package->ProductCode, &clsid );
6023 encode_base85_guid( &clsid, productid_85 );
6024 if (component)
6026 CLSIDFromString( component->ComponentId, &clsid );
6027 encode_base85_guid( &clsid, component_85 );
6030 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
6031 debugstr_w(component_85));
6033 sz = 20 + strlenW( feature ) + 20 + 3;
6034 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
6035 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
6036 return ret;
6039 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
6041 MSIPACKAGE *package = param;
6042 LPCWSTR compgroupid, component, feature, qualifier, text;
6043 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
6044 HKEY hkey = NULL;
6045 UINT rc;
6046 MSICOMPONENT *comp;
6047 MSIFEATURE *feat;
6048 DWORD sz;
6049 MSIRECORD *uirow;
6050 int len;
6052 feature = MSI_RecordGetString(rec, 5);
6053 feat = msi_get_loaded_feature(package, feature);
6054 if (!feat)
6055 return ERROR_SUCCESS;
6057 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
6058 feat->ActionRequest != INSTALLSTATE_SOURCE &&
6059 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
6061 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
6062 feat->Action = feat->Installed;
6063 return ERROR_SUCCESS;
6066 component = MSI_RecordGetString(rec, 3);
6067 comp = msi_get_loaded_component(package, component);
6068 if (!comp)
6069 return ERROR_SUCCESS;
6071 compgroupid = MSI_RecordGetString(rec,1);
6072 qualifier = MSI_RecordGetString(rec,2);
6074 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
6075 if (rc != ERROR_SUCCESS)
6076 goto end;
6078 advertise = msi_create_component_advertise_string( package, comp, feature );
6079 text = MSI_RecordGetString( rec, 4 );
6080 if (text)
6082 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
6083 strcpyW( p, advertise );
6084 strcatW( p, text );
6085 msi_free( advertise );
6086 advertise = p;
6088 existing = msi_reg_get_val_str( hkey, qualifier );
6090 sz = strlenW( advertise ) + 1;
6091 if (existing)
6093 for (p = existing; *p; p += len)
6095 len = strlenW( p ) + 1;
6096 if (strcmpW( advertise, p )) sz += len;
6099 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
6101 rc = ERROR_OUTOFMEMORY;
6102 goto end;
6104 q = output;
6105 if (existing)
6107 for (p = existing; *p; p += len)
6109 len = strlenW( p ) + 1;
6110 if (strcmpW( advertise, p ))
6112 memcpy( q, p, len * sizeof(WCHAR) );
6113 q += len;
6117 strcpyW( q, advertise );
6118 q[strlenW( q ) + 1] = 0;
6120 msi_reg_set_val_multi_str( hkey, qualifier, output );
6122 end:
6123 RegCloseKey(hkey);
6124 msi_free( output );
6125 msi_free( advertise );
6126 msi_free( existing );
6128 /* the UI chunk */
6129 uirow = MSI_CreateRecord( 2 );
6130 MSI_RecordSetStringW( uirow, 1, compgroupid );
6131 MSI_RecordSetStringW( uirow, 2, qualifier);
6132 msi_ui_actiondata( package, szPublishComponents, uirow );
6133 msiobj_release( &uirow->hdr );
6134 /* FIXME: call ui_progress? */
6136 return rc;
6140 * At present I am ignorning the advertised components part of this and only
6141 * focusing on the qualified component sets
6143 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
6145 UINT rc;
6146 MSIQUERY * view;
6147 static const WCHAR ExecSeqQuery[] =
6148 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6149 '`','P','u','b','l','i','s','h',
6150 'C','o','m','p','o','n','e','n','t','`',0};
6152 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6153 if (rc != ERROR_SUCCESS)
6154 return ERROR_SUCCESS;
6156 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
6157 msiobj_release(&view->hdr);
6159 return rc;
6162 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
6164 static const WCHAR szInstallerComponents[] = {
6165 'S','o','f','t','w','a','r','e','\\',
6166 'M','i','c','r','o','s','o','f','t','\\',
6167 'I','n','s','t','a','l','l','e','r','\\',
6168 'C','o','m','p','o','n','e','n','t','s','\\',0};
6170 MSIPACKAGE *package = param;
6171 LPCWSTR compgroupid, component, feature, qualifier;
6172 MSICOMPONENT *comp;
6173 MSIFEATURE *feat;
6174 MSIRECORD *uirow;
6175 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
6176 LONG res;
6178 feature = MSI_RecordGetString( rec, 5 );
6179 feat = msi_get_loaded_feature( package, feature );
6180 if (!feat)
6181 return ERROR_SUCCESS;
6183 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
6185 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
6186 feat->Action = feat->Installed;
6187 return ERROR_SUCCESS;
6190 component = MSI_RecordGetString( rec, 3 );
6191 comp = msi_get_loaded_component( package, component );
6192 if (!comp)
6193 return ERROR_SUCCESS;
6195 compgroupid = MSI_RecordGetString( rec, 1 );
6196 qualifier = MSI_RecordGetString( rec, 2 );
6198 squash_guid( compgroupid, squashed );
6199 strcpyW( keypath, szInstallerComponents );
6200 strcatW( keypath, squashed );
6202 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
6203 if (res != ERROR_SUCCESS)
6205 WARN("Unable to delete component key %d\n", res);
6208 uirow = MSI_CreateRecord( 2 );
6209 MSI_RecordSetStringW( uirow, 1, compgroupid );
6210 MSI_RecordSetStringW( uirow, 2, qualifier );
6211 msi_ui_actiondata( package, szUnpublishComponents, uirow );
6212 msiobj_release( &uirow->hdr );
6214 return ERROR_SUCCESS;
6217 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6219 UINT rc;
6220 MSIQUERY *view;
6221 static const WCHAR query[] =
6222 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6223 '`','P','u','b','l','i','s','h',
6224 'C','o','m','p','o','n','e','n','t','`',0};
6226 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6227 if (rc != ERROR_SUCCESS)
6228 return ERROR_SUCCESS;
6230 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
6231 msiobj_release( &view->hdr );
6233 return rc;
6236 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
6238 static const WCHAR query[] =
6239 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6240 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
6241 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
6242 MSIPACKAGE *package = param;
6243 MSICOMPONENT *component;
6244 MSIRECORD *row;
6245 MSIFILE *file;
6246 SC_HANDLE hscm = NULL, service = NULL;
6247 LPCWSTR comp, key;
6248 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
6249 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
6250 DWORD serv_type, start_type, err_control;
6251 SERVICE_DESCRIPTIONW sd = {NULL};
6253 comp = MSI_RecordGetString( rec, 12 );
6254 component = msi_get_loaded_component( package, comp );
6255 if (!component)
6257 WARN("service component not found\n");
6258 goto done;
6260 if (!component->Enabled)
6262 TRACE("service component disabled\n");
6263 goto done;
6265 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
6266 if (!hscm)
6268 ERR("Failed to open the SC Manager!\n");
6269 goto done;
6272 start_type = MSI_RecordGetInteger(rec, 5);
6273 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
6274 goto done;
6276 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6277 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6278 serv_type = MSI_RecordGetInteger(rec, 4);
6279 err_control = MSI_RecordGetInteger(rec, 6);
6280 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6281 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6282 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
6283 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6284 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6285 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6287 /* fetch the service path */
6288 row = MSI_QueryGetRecord(package->db, query, comp);
6289 if (!row)
6291 ERR("Query failed\n");
6292 goto done;
6294 key = MSI_RecordGetString(row, 6);
6295 file = msi_get_loaded_file(package, key);
6296 msiobj_release(&row->hdr);
6297 if (!file)
6299 ERR("Failed to load the service file\n");
6300 goto done;
6303 if (!args || !args[0]) image_path = file->TargetPath;
6304 else
6306 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6307 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6308 return ERROR_OUTOFMEMORY;
6310 strcpyW(image_path, file->TargetPath);
6311 strcatW(image_path, szSpace);
6312 strcatW(image_path, args);
6314 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6315 start_type, err_control, image_path, load_order,
6316 NULL, depends, serv_name, pass);
6318 if (!service)
6320 if (GetLastError() != ERROR_SERVICE_EXISTS)
6321 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6323 else if (sd.lpDescription)
6325 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6326 WARN("failed to set service description %u\n", GetLastError());
6329 if (image_path != file->TargetPath) msi_free(image_path);
6330 done:
6331 CloseServiceHandle(service);
6332 CloseServiceHandle(hscm);
6333 msi_free(name);
6334 msi_free(disp);
6335 msi_free(sd.lpDescription);
6336 msi_free(load_order);
6337 msi_free(serv_name);
6338 msi_free(pass);
6339 msi_free(depends);
6340 msi_free(args);
6342 return ERROR_SUCCESS;
6345 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6347 UINT rc;
6348 MSIQUERY * view;
6349 static const WCHAR ExecSeqQuery[] =
6350 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6351 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6353 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6354 if (rc != ERROR_SUCCESS)
6355 return ERROR_SUCCESS;
6357 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6358 msiobj_release(&view->hdr);
6360 return rc;
6363 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6364 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6366 LPCWSTR *vector, *temp_vector;
6367 LPWSTR p, q;
6368 DWORD sep_len;
6370 static const WCHAR separator[] = {'[','~',']',0};
6372 *numargs = 0;
6373 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6375 if (!args)
6376 return NULL;
6378 vector = msi_alloc(sizeof(LPWSTR));
6379 if (!vector)
6380 return NULL;
6382 p = args;
6385 (*numargs)++;
6386 vector[*numargs - 1] = p;
6388 if ((q = strstrW(p, separator)))
6390 *q = '\0';
6392 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6393 if (!temp_vector)
6395 msi_free(vector);
6396 return NULL;
6398 vector = temp_vector;
6400 p = q + sep_len;
6402 } while (q);
6404 return vector;
6407 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6409 MSIPACKAGE *package = param;
6410 MSICOMPONENT *comp;
6411 MSIRECORD *uirow;
6412 SC_HANDLE scm = NULL, service = NULL;
6413 LPCWSTR component, *vector = NULL;
6414 LPWSTR name, args, display_name = NULL;
6415 DWORD event, numargs, len;
6416 UINT r = ERROR_FUNCTION_FAILED;
6418 component = MSI_RecordGetString(rec, 6);
6419 comp = msi_get_loaded_component(package, component);
6420 if (!comp)
6421 return ERROR_SUCCESS;
6423 if (!comp->Enabled)
6425 TRACE("component is disabled\n");
6426 return ERROR_SUCCESS;
6429 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6431 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6432 comp->Action = comp->Installed;
6433 return ERROR_SUCCESS;
6435 comp->Action = INSTALLSTATE_LOCAL;
6437 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6438 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6439 event = MSI_RecordGetInteger(rec, 3);
6441 if (!(event & msidbServiceControlEventStart))
6443 r = ERROR_SUCCESS;
6444 goto done;
6447 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6448 if (!scm)
6450 ERR("Failed to open the service control manager\n");
6451 goto done;
6454 len = 0;
6455 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6456 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6458 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6459 GetServiceDisplayNameW( scm, name, display_name, &len );
6462 service = OpenServiceW(scm, name, SERVICE_START);
6463 if (!service)
6465 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6466 goto done;
6469 vector = msi_service_args_to_vector(args, &numargs);
6471 if (!StartServiceW(service, numargs, vector) &&
6472 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6474 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6475 goto done;
6478 r = ERROR_SUCCESS;
6480 done:
6481 uirow = MSI_CreateRecord( 2 );
6482 MSI_RecordSetStringW( uirow, 1, display_name );
6483 MSI_RecordSetStringW( uirow, 2, name );
6484 msi_ui_actiondata( package, szStartServices, uirow );
6485 msiobj_release( &uirow->hdr );
6487 CloseServiceHandle(service);
6488 CloseServiceHandle(scm);
6490 msi_free(name);
6491 msi_free(args);
6492 msi_free(vector);
6493 msi_free(display_name);
6494 return r;
6497 static UINT ACTION_StartServices( MSIPACKAGE *package )
6499 UINT rc;
6500 MSIQUERY *view;
6502 static const WCHAR query[] = {
6503 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6504 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6506 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6507 if (rc != ERROR_SUCCESS)
6508 return ERROR_SUCCESS;
6510 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6511 msiobj_release(&view->hdr);
6513 return rc;
6516 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6518 DWORD i, needed, count;
6519 ENUM_SERVICE_STATUSW *dependencies;
6520 SERVICE_STATUS ss;
6521 SC_HANDLE depserv;
6523 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6524 0, &needed, &count))
6525 return TRUE;
6527 if (GetLastError() != ERROR_MORE_DATA)
6528 return FALSE;
6530 dependencies = msi_alloc(needed);
6531 if (!dependencies)
6532 return FALSE;
6534 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6535 needed, &needed, &count))
6536 goto error;
6538 for (i = 0; i < count; i++)
6540 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6541 SERVICE_STOP | SERVICE_QUERY_STATUS);
6542 if (!depserv)
6543 goto error;
6545 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
6546 goto error;
6549 return TRUE;
6551 error:
6552 msi_free(dependencies);
6553 return FALSE;
6556 static UINT stop_service( LPCWSTR name )
6558 SC_HANDLE scm = NULL, service = NULL;
6559 SERVICE_STATUS status;
6560 SERVICE_STATUS_PROCESS ssp;
6561 DWORD needed;
6563 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6564 if (!scm)
6566 WARN("Failed to open the SCM: %d\n", GetLastError());
6567 goto done;
6570 service = OpenServiceW(scm, name,
6571 SERVICE_STOP |
6572 SERVICE_QUERY_STATUS |
6573 SERVICE_ENUMERATE_DEPENDENTS);
6574 if (!service)
6576 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6577 goto done;
6580 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6581 sizeof(SERVICE_STATUS_PROCESS), &needed))
6583 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6584 goto done;
6587 if (ssp.dwCurrentState == SERVICE_STOPPED)
6588 goto done;
6590 stop_service_dependents(scm, service);
6592 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6593 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6595 done:
6596 CloseServiceHandle(service);
6597 CloseServiceHandle(scm);
6599 return ERROR_SUCCESS;
6602 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6604 MSIPACKAGE *package = param;
6605 MSICOMPONENT *comp;
6606 MSIRECORD *uirow;
6607 LPCWSTR component;
6608 LPWSTR name = NULL, display_name = NULL;
6609 DWORD event, len;
6610 SC_HANDLE scm;
6612 event = MSI_RecordGetInteger( rec, 3 );
6613 if (!(event & msidbServiceControlEventStop))
6614 return ERROR_SUCCESS;
6616 component = MSI_RecordGetString( rec, 6 );
6617 comp = msi_get_loaded_component( package, component );
6618 if (!comp)
6619 return ERROR_SUCCESS;
6621 if (!comp->Enabled)
6623 TRACE("component is disabled\n");
6624 return ERROR_SUCCESS;
6627 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6629 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6630 comp->Action = comp->Installed;
6631 return ERROR_SUCCESS;
6633 comp->Action = INSTALLSTATE_ABSENT;
6635 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6636 if (!scm)
6638 ERR("Failed to open the service control manager\n");
6639 goto done;
6642 len = 0;
6643 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6644 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6646 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6647 GetServiceDisplayNameW( scm, name, display_name, &len );
6649 CloseServiceHandle( scm );
6651 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6652 stop_service( name );
6654 done:
6655 uirow = MSI_CreateRecord( 2 );
6656 MSI_RecordSetStringW( uirow, 1, display_name );
6657 MSI_RecordSetStringW( uirow, 2, name );
6658 msi_ui_actiondata( package, szStopServices, uirow );
6659 msiobj_release( &uirow->hdr );
6661 msi_free( name );
6662 msi_free( display_name );
6663 return ERROR_SUCCESS;
6666 static UINT ACTION_StopServices( MSIPACKAGE *package )
6668 UINT rc;
6669 MSIQUERY *view;
6671 static const WCHAR query[] = {
6672 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6673 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6675 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6676 if (rc != ERROR_SUCCESS)
6677 return ERROR_SUCCESS;
6679 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6680 msiobj_release(&view->hdr);
6682 return rc;
6685 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6687 MSIPACKAGE *package = param;
6688 MSICOMPONENT *comp;
6689 MSIRECORD *uirow;
6690 LPCWSTR component;
6691 LPWSTR name = NULL, display_name = NULL;
6692 DWORD event, len;
6693 SC_HANDLE scm = NULL, service = NULL;
6695 event = MSI_RecordGetInteger( rec, 3 );
6696 if (!(event & msidbServiceControlEventDelete))
6697 return ERROR_SUCCESS;
6699 component = MSI_RecordGetString(rec, 6);
6700 comp = msi_get_loaded_component(package, component);
6701 if (!comp)
6702 return ERROR_SUCCESS;
6704 if (!comp->Enabled)
6706 TRACE("component is disabled\n");
6707 return ERROR_SUCCESS;
6710 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6712 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6713 comp->Action = comp->Installed;
6714 return ERROR_SUCCESS;
6716 comp->Action = INSTALLSTATE_ABSENT;
6718 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6719 stop_service( name );
6721 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6722 if (!scm)
6724 WARN("Failed to open the SCM: %d\n", GetLastError());
6725 goto done;
6728 len = 0;
6729 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6730 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6732 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6733 GetServiceDisplayNameW( scm, name, display_name, &len );
6736 service = OpenServiceW( scm, name, DELETE );
6737 if (!service)
6739 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6740 goto done;
6743 if (!DeleteService( service ))
6744 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6746 done:
6747 uirow = MSI_CreateRecord( 2 );
6748 MSI_RecordSetStringW( uirow, 1, display_name );
6749 MSI_RecordSetStringW( uirow, 2, name );
6750 msi_ui_actiondata( package, szDeleteServices, uirow );
6751 msiobj_release( &uirow->hdr );
6753 CloseServiceHandle( service );
6754 CloseServiceHandle( scm );
6755 msi_free( name );
6756 msi_free( display_name );
6758 return ERROR_SUCCESS;
6761 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6763 UINT rc;
6764 MSIQUERY *view;
6766 static const WCHAR query[] = {
6767 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6768 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6770 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6771 if (rc != ERROR_SUCCESS)
6772 return ERROR_SUCCESS;
6774 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6775 msiobj_release( &view->hdr );
6777 return rc;
6780 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6782 MSIPACKAGE *package = param;
6783 LPWSTR driver, driver_path, ptr;
6784 WCHAR outpath[MAX_PATH];
6785 MSIFILE *driver_file = NULL, *setup_file = NULL;
6786 MSICOMPONENT *comp;
6787 MSIRECORD *uirow;
6788 LPCWSTR desc, file_key, component;
6789 DWORD len, usage;
6790 UINT r = ERROR_SUCCESS;
6792 static const WCHAR driver_fmt[] = {
6793 'D','r','i','v','e','r','=','%','s',0};
6794 static const WCHAR setup_fmt[] = {
6795 'S','e','t','u','p','=','%','s',0};
6796 static const WCHAR usage_fmt[] = {
6797 'F','i','l','e','U','s','a','g','e','=','1',0};
6799 component = MSI_RecordGetString( rec, 2 );
6800 comp = msi_get_loaded_component( package, component );
6801 if (!comp)
6802 return ERROR_SUCCESS;
6804 if (!comp->Enabled)
6806 TRACE("component is disabled\n");
6807 return ERROR_SUCCESS;
6810 desc = MSI_RecordGetString(rec, 3);
6812 file_key = MSI_RecordGetString( rec, 4 );
6813 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6815 file_key = MSI_RecordGetString( rec, 5 );
6816 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6818 if (!driver_file)
6820 ERR("ODBC Driver entry not found!\n");
6821 return ERROR_FUNCTION_FAILED;
6824 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6825 if (setup_file)
6826 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6827 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6829 driver = msi_alloc(len * sizeof(WCHAR));
6830 if (!driver)
6831 return ERROR_OUTOFMEMORY;
6833 ptr = driver;
6834 lstrcpyW(ptr, desc);
6835 ptr += lstrlenW(ptr) + 1;
6837 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6838 ptr += len + 1;
6840 if (setup_file)
6842 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6843 ptr += len + 1;
6846 lstrcpyW(ptr, usage_fmt);
6847 ptr += lstrlenW(ptr) + 1;
6848 *ptr = '\0';
6850 driver_path = strdupW(driver_file->TargetPath);
6851 ptr = strrchrW(driver_path, '\\');
6852 if (ptr) *ptr = '\0';
6854 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6855 NULL, ODBC_INSTALL_COMPLETE, &usage))
6857 ERR("Failed to install SQL driver!\n");
6858 r = ERROR_FUNCTION_FAILED;
6861 uirow = MSI_CreateRecord( 5 );
6862 MSI_RecordSetStringW( uirow, 1, desc );
6863 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6864 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6865 msi_ui_actiondata( package, szInstallODBC, uirow );
6866 msiobj_release( &uirow->hdr );
6868 msi_free(driver);
6869 msi_free(driver_path);
6871 return r;
6874 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6876 MSIPACKAGE *package = param;
6877 LPWSTR translator, translator_path, ptr;
6878 WCHAR outpath[MAX_PATH];
6879 MSIFILE *translator_file = NULL, *setup_file = NULL;
6880 MSICOMPONENT *comp;
6881 MSIRECORD *uirow;
6882 LPCWSTR desc, file_key, component;
6883 DWORD len, usage;
6884 UINT r = ERROR_SUCCESS;
6886 static const WCHAR translator_fmt[] = {
6887 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6888 static const WCHAR setup_fmt[] = {
6889 'S','e','t','u','p','=','%','s',0};
6891 component = MSI_RecordGetString( rec, 2 );
6892 comp = msi_get_loaded_component( package, component );
6893 if (!comp)
6894 return ERROR_SUCCESS;
6896 if (!comp->Enabled)
6898 TRACE("component is disabled\n");
6899 return ERROR_SUCCESS;
6902 desc = MSI_RecordGetString(rec, 3);
6904 file_key = MSI_RecordGetString( rec, 4 );
6905 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6907 file_key = MSI_RecordGetString( rec, 5 );
6908 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6910 if (!translator_file)
6912 ERR("ODBC Translator entry not found!\n");
6913 return ERROR_FUNCTION_FAILED;
6916 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6917 if (setup_file)
6918 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6920 translator = msi_alloc(len * sizeof(WCHAR));
6921 if (!translator)
6922 return ERROR_OUTOFMEMORY;
6924 ptr = translator;
6925 lstrcpyW(ptr, desc);
6926 ptr += lstrlenW(ptr) + 1;
6928 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6929 ptr += len + 1;
6931 if (setup_file)
6933 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6934 ptr += len + 1;
6936 *ptr = '\0';
6938 translator_path = strdupW(translator_file->TargetPath);
6939 ptr = strrchrW(translator_path, '\\');
6940 if (ptr) *ptr = '\0';
6942 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6943 NULL, ODBC_INSTALL_COMPLETE, &usage))
6945 ERR("Failed to install SQL translator!\n");
6946 r = ERROR_FUNCTION_FAILED;
6949 uirow = MSI_CreateRecord( 5 );
6950 MSI_RecordSetStringW( uirow, 1, desc );
6951 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6952 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6953 msi_ui_actiondata( package, szInstallODBC, uirow );
6954 msiobj_release( &uirow->hdr );
6956 msi_free(translator);
6957 msi_free(translator_path);
6959 return r;
6962 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6964 MSIPACKAGE *package = param;
6965 MSICOMPONENT *comp;
6966 LPWSTR attrs;
6967 LPCWSTR desc, driver, component;
6968 WORD request = ODBC_ADD_SYS_DSN;
6969 INT registration;
6970 DWORD len;
6971 UINT r = ERROR_SUCCESS;
6972 MSIRECORD *uirow;
6974 static const WCHAR attrs_fmt[] = {
6975 'D','S','N','=','%','s',0 };
6977 component = MSI_RecordGetString( rec, 2 );
6978 comp = msi_get_loaded_component( package, component );
6979 if (!comp)
6980 return ERROR_SUCCESS;
6982 if (!comp->Enabled)
6984 TRACE("component is disabled\n");
6985 return ERROR_SUCCESS;
6988 desc = MSI_RecordGetString(rec, 3);
6989 driver = MSI_RecordGetString(rec, 4);
6990 registration = MSI_RecordGetInteger(rec, 5);
6992 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6993 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6995 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6996 attrs = msi_alloc(len * sizeof(WCHAR));
6997 if (!attrs)
6998 return ERROR_OUTOFMEMORY;
7000 len = sprintfW(attrs, attrs_fmt, desc);
7001 attrs[len + 1] = 0;
7003 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
7005 ERR("Failed to install SQL data source!\n");
7006 r = ERROR_FUNCTION_FAILED;
7009 uirow = MSI_CreateRecord( 5 );
7010 MSI_RecordSetStringW( uirow, 1, desc );
7011 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7012 MSI_RecordSetInteger( uirow, 3, request );
7013 msi_ui_actiondata( package, szInstallODBC, uirow );
7014 msiobj_release( &uirow->hdr );
7016 msi_free(attrs);
7018 return r;
7021 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
7023 UINT rc;
7024 MSIQUERY *view;
7026 static const WCHAR driver_query[] = {
7027 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7028 'O','D','B','C','D','r','i','v','e','r',0 };
7030 static const WCHAR translator_query[] = {
7031 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7032 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7034 static const WCHAR source_query[] = {
7035 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7036 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
7038 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
7039 if (rc != ERROR_SUCCESS)
7040 return ERROR_SUCCESS;
7042 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
7043 msiobj_release(&view->hdr);
7045 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
7046 if (rc != ERROR_SUCCESS)
7047 return ERROR_SUCCESS;
7049 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
7050 msiobj_release(&view->hdr);
7052 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
7053 if (rc != ERROR_SUCCESS)
7054 return ERROR_SUCCESS;
7056 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
7057 msiobj_release(&view->hdr);
7059 return rc;
7062 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
7064 MSIPACKAGE *package = param;
7065 MSICOMPONENT *comp;
7066 MSIRECORD *uirow;
7067 DWORD usage;
7068 LPCWSTR desc, component;
7070 component = MSI_RecordGetString( rec, 2 );
7071 comp = msi_get_loaded_component( package, component );
7072 if (!comp)
7073 return ERROR_SUCCESS;
7075 if (!comp->Enabled)
7077 TRACE("component is disabled\n");
7078 return ERROR_SUCCESS;
7081 desc = MSI_RecordGetString( rec, 3 );
7082 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
7084 WARN("Failed to remove ODBC driver\n");
7086 else if (!usage)
7088 FIXME("Usage count reached 0\n");
7091 uirow = MSI_CreateRecord( 2 );
7092 MSI_RecordSetStringW( uirow, 1, desc );
7093 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7094 msi_ui_actiondata( package, szRemoveODBC, uirow );
7095 msiobj_release( &uirow->hdr );
7097 return ERROR_SUCCESS;
7100 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
7102 MSIPACKAGE *package = param;
7103 MSICOMPONENT *comp;
7104 MSIRECORD *uirow;
7105 DWORD usage;
7106 LPCWSTR desc, component;
7108 component = MSI_RecordGetString( rec, 2 );
7109 comp = msi_get_loaded_component( package, component );
7110 if (!comp)
7111 return ERROR_SUCCESS;
7113 if (!comp->Enabled)
7115 TRACE("component is disabled\n");
7116 return ERROR_SUCCESS;
7119 desc = MSI_RecordGetString( rec, 3 );
7120 if (!SQLRemoveTranslatorW( desc, &usage ))
7122 WARN("Failed to remove ODBC translator\n");
7124 else if (!usage)
7126 FIXME("Usage count reached 0\n");
7129 uirow = MSI_CreateRecord( 2 );
7130 MSI_RecordSetStringW( uirow, 1, desc );
7131 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7132 msi_ui_actiondata( package, szRemoveODBC, uirow );
7133 msiobj_release( &uirow->hdr );
7135 return ERROR_SUCCESS;
7138 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
7140 MSIPACKAGE *package = param;
7141 MSICOMPONENT *comp;
7142 MSIRECORD *uirow;
7143 LPWSTR attrs;
7144 LPCWSTR desc, driver, component;
7145 WORD request = ODBC_REMOVE_SYS_DSN;
7146 INT registration;
7147 DWORD len;
7149 static const WCHAR attrs_fmt[] = {
7150 'D','S','N','=','%','s',0 };
7152 component = MSI_RecordGetString( rec, 2 );
7153 comp = msi_get_loaded_component( package, component );
7154 if (!comp)
7155 return ERROR_SUCCESS;
7157 if (!comp->Enabled)
7159 TRACE("component is disabled\n");
7160 return ERROR_SUCCESS;
7163 desc = MSI_RecordGetString( rec, 3 );
7164 driver = MSI_RecordGetString( rec, 4 );
7165 registration = MSI_RecordGetInteger( rec, 5 );
7167 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
7168 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
7170 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
7171 attrs = msi_alloc( len * sizeof(WCHAR) );
7172 if (!attrs)
7173 return ERROR_OUTOFMEMORY;
7175 FIXME("Use ODBCSourceAttribute table\n");
7177 len = sprintfW( attrs, attrs_fmt, desc );
7178 attrs[len + 1] = 0;
7180 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
7182 WARN("Failed to remove ODBC data source\n");
7184 msi_free( attrs );
7186 uirow = MSI_CreateRecord( 3 );
7187 MSI_RecordSetStringW( uirow, 1, desc );
7188 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7189 MSI_RecordSetInteger( uirow, 3, request );
7190 msi_ui_actiondata( package, szRemoveODBC, uirow );
7191 msiobj_release( &uirow->hdr );
7193 return ERROR_SUCCESS;
7196 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
7198 UINT rc;
7199 MSIQUERY *view;
7201 static const WCHAR driver_query[] = {
7202 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7203 'O','D','B','C','D','r','i','v','e','r',0 };
7205 static const WCHAR translator_query[] = {
7206 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7207 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7209 static const WCHAR source_query[] = {
7210 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7211 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
7213 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7214 if (rc != ERROR_SUCCESS)
7215 return ERROR_SUCCESS;
7217 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
7218 msiobj_release( &view->hdr );
7220 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7221 if (rc != ERROR_SUCCESS)
7222 return ERROR_SUCCESS;
7224 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
7225 msiobj_release( &view->hdr );
7227 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7228 if (rc != ERROR_SUCCESS)
7229 return ERROR_SUCCESS;
7231 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
7232 msiobj_release( &view->hdr );
7234 return rc;
7237 #define ENV_ACT_SETALWAYS 0x1
7238 #define ENV_ACT_SETABSENT 0x2
7239 #define ENV_ACT_REMOVE 0x4
7240 #define ENV_ACT_REMOVEMATCH 0x8
7242 #define ENV_MOD_MACHINE 0x20000000
7243 #define ENV_MOD_APPEND 0x40000000
7244 #define ENV_MOD_PREFIX 0x80000000
7245 #define ENV_MOD_MASK 0xC0000000
7247 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
7249 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7251 LPCWSTR cptr = *name;
7253 static const WCHAR prefix[] = {'[','~',']',0};
7254 static const int prefix_len = 3;
7256 *flags = 0;
7257 while (*cptr)
7259 if (*cptr == '=')
7260 *flags |= ENV_ACT_SETALWAYS;
7261 else if (*cptr == '+')
7262 *flags |= ENV_ACT_SETABSENT;
7263 else if (*cptr == '-')
7264 *flags |= ENV_ACT_REMOVE;
7265 else if (*cptr == '!')
7266 *flags |= ENV_ACT_REMOVEMATCH;
7267 else if (*cptr == '*')
7268 *flags |= ENV_MOD_MACHINE;
7269 else
7270 break;
7272 cptr++;
7273 (*name)++;
7276 if (!*cptr)
7278 ERR("Missing environment variable\n");
7279 return ERROR_FUNCTION_FAILED;
7282 if (*value)
7284 LPCWSTR ptr = *value;
7285 if (!strncmpW(ptr, prefix, prefix_len))
7287 if (ptr[prefix_len] == szSemiColon[0])
7289 *flags |= ENV_MOD_APPEND;
7290 *value += lstrlenW(prefix);
7292 else
7294 *value = NULL;
7297 else if (lstrlenW(*value) >= prefix_len)
7299 ptr += lstrlenW(ptr) - prefix_len;
7300 if (!strcmpW( ptr, prefix ))
7302 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7304 *flags |= ENV_MOD_PREFIX;
7305 /* the "[~]" will be removed by deformat_string */;
7307 else
7309 *value = NULL;
7315 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7316 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7317 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7318 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7320 ERR("Invalid flags: %08x\n", *flags);
7321 return ERROR_FUNCTION_FAILED;
7324 if (!*flags)
7325 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7327 return ERROR_SUCCESS;
7330 static UINT open_env_key( DWORD flags, HKEY *key )
7332 static const WCHAR user_env[] =
7333 {'E','n','v','i','r','o','n','m','e','n','t',0};
7334 static const WCHAR machine_env[] =
7335 {'S','y','s','t','e','m','\\',
7336 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7337 'C','o','n','t','r','o','l','\\',
7338 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7339 'E','n','v','i','r','o','n','m','e','n','t',0};
7340 const WCHAR *env;
7341 HKEY root;
7342 LONG res;
7344 if (flags & ENV_MOD_MACHINE)
7346 env = machine_env;
7347 root = HKEY_LOCAL_MACHINE;
7349 else
7351 env = user_env;
7352 root = HKEY_CURRENT_USER;
7355 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7356 if (res != ERROR_SUCCESS)
7358 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7359 return ERROR_FUNCTION_FAILED;
7362 return ERROR_SUCCESS;
7365 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7367 MSIPACKAGE *package = param;
7368 LPCWSTR name, value, component;
7369 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
7370 DWORD flags, type, size;
7371 UINT res;
7372 HKEY env = NULL;
7373 MSICOMPONENT *comp;
7374 MSIRECORD *uirow;
7375 int action = 0;
7377 component = MSI_RecordGetString(rec, 4);
7378 comp = msi_get_loaded_component(package, component);
7379 if (!comp)
7380 return ERROR_SUCCESS;
7382 if (!comp->Enabled)
7384 TRACE("component is disabled\n");
7385 return ERROR_SUCCESS;
7388 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
7390 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
7391 comp->Action = comp->Installed;
7392 return ERROR_SUCCESS;
7394 comp->Action = INSTALLSTATE_LOCAL;
7396 name = MSI_RecordGetString(rec, 2);
7397 value = MSI_RecordGetString(rec, 3);
7399 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7401 res = env_parse_flags(&name, &value, &flags);
7402 if (res != ERROR_SUCCESS || !value)
7403 goto done;
7405 if (value && !deformat_string(package, value, &deformatted))
7407 res = ERROR_OUTOFMEMORY;
7408 goto done;
7411 value = deformatted;
7413 res = open_env_key( flags, &env );
7414 if (res != ERROR_SUCCESS)
7415 goto done;
7417 if (flags & ENV_MOD_MACHINE)
7418 action |= 0x20000000;
7420 size = 0;
7421 type = REG_SZ;
7422 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7423 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7424 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7425 goto done;
7427 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7429 action = 0x2;
7431 /* Nothing to do. */
7432 if (!value)
7434 res = ERROR_SUCCESS;
7435 goto done;
7438 /* If we are appending but the string was empty, strip ; */
7439 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
7441 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7442 newval = strdupW(value);
7443 if (!newval)
7445 res = ERROR_OUTOFMEMORY;
7446 goto done;
7449 else
7451 action = 0x1;
7453 /* Contrary to MSDN, +-variable to [~];path works */
7454 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7456 res = ERROR_SUCCESS;
7457 goto done;
7460 data = msi_alloc(size);
7461 if (!data)
7463 RegCloseKey(env);
7464 return ERROR_OUTOFMEMORY;
7467 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7468 if (res != ERROR_SUCCESS)
7469 goto done;
7471 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7473 action = 0x4;
7474 res = RegDeleteValueW(env, name);
7475 if (res != ERROR_SUCCESS)
7476 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7477 goto done;
7480 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7481 if (flags & ENV_MOD_MASK)
7483 DWORD mod_size;
7484 int multiplier = 0;
7485 if (flags & ENV_MOD_APPEND) multiplier++;
7486 if (flags & ENV_MOD_PREFIX) multiplier++;
7487 mod_size = lstrlenW(value) * multiplier;
7488 size += mod_size * sizeof(WCHAR);
7491 newval = msi_alloc(size);
7492 ptr = newval;
7493 if (!newval)
7495 res = ERROR_OUTOFMEMORY;
7496 goto done;
7499 if (flags & ENV_MOD_PREFIX)
7501 lstrcpyW(newval, value);
7502 ptr = newval + lstrlenW(value);
7503 action |= 0x80000000;
7506 lstrcpyW(ptr, data);
7508 if (flags & ENV_MOD_APPEND)
7510 lstrcatW(newval, value);
7511 action |= 0x40000000;
7514 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7515 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7516 if (res)
7518 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7521 done:
7522 uirow = MSI_CreateRecord( 3 );
7523 MSI_RecordSetStringW( uirow, 1, name );
7524 MSI_RecordSetStringW( uirow, 2, newval );
7525 MSI_RecordSetInteger( uirow, 3, action );
7526 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7527 msiobj_release( &uirow->hdr );
7529 if (env) RegCloseKey(env);
7530 msi_free(deformatted);
7531 msi_free(data);
7532 msi_free(newval);
7533 return res;
7536 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7538 UINT rc;
7539 MSIQUERY * view;
7540 static const WCHAR ExecSeqQuery[] =
7541 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7542 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7543 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
7544 if (rc != ERROR_SUCCESS)
7545 return ERROR_SUCCESS;
7547 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7548 msiobj_release(&view->hdr);
7550 return rc;
7553 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7555 MSIPACKAGE *package = param;
7556 LPCWSTR name, value, component;
7557 LPWSTR deformatted = NULL;
7558 DWORD flags;
7559 HKEY env;
7560 MSICOMPONENT *comp;
7561 MSIRECORD *uirow;
7562 int action = 0;
7563 LONG res;
7564 UINT r;
7566 component = MSI_RecordGetString( rec, 4 );
7567 comp = msi_get_loaded_component( package, component );
7568 if (!comp)
7569 return ERROR_SUCCESS;
7571 if (!comp->Enabled)
7573 TRACE("component is disabled\n");
7574 return ERROR_SUCCESS;
7577 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
7579 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
7580 comp->Action = comp->Installed;
7581 return ERROR_SUCCESS;
7583 comp->Action = INSTALLSTATE_ABSENT;
7585 name = MSI_RecordGetString( rec, 2 );
7586 value = MSI_RecordGetString( rec, 3 );
7588 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7590 r = env_parse_flags( &name, &value, &flags );
7591 if (r != ERROR_SUCCESS)
7592 return r;
7594 if (!(flags & ENV_ACT_REMOVE))
7596 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7597 return ERROR_SUCCESS;
7600 if (value && !deformat_string( package, value, &deformatted ))
7601 return ERROR_OUTOFMEMORY;
7603 value = deformatted;
7605 r = open_env_key( flags, &env );
7606 if (r != ERROR_SUCCESS)
7608 r = ERROR_SUCCESS;
7609 goto done;
7612 if (flags & ENV_MOD_MACHINE)
7613 action |= 0x20000000;
7615 TRACE("Removing %s\n", debugstr_w(name));
7617 res = RegDeleteValueW( env, name );
7618 if (res != ERROR_SUCCESS)
7620 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7621 r = ERROR_SUCCESS;
7624 done:
7625 uirow = MSI_CreateRecord( 3 );
7626 MSI_RecordSetStringW( uirow, 1, name );
7627 MSI_RecordSetStringW( uirow, 2, value );
7628 MSI_RecordSetInteger( uirow, 3, action );
7629 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7630 msiobj_release( &uirow->hdr );
7632 if (env) RegCloseKey( env );
7633 msi_free( deformatted );
7634 return r;
7637 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7639 UINT rc;
7640 MSIQUERY *view;
7641 static const WCHAR query[] =
7642 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7643 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7645 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7646 if (rc != ERROR_SUCCESS)
7647 return ERROR_SUCCESS;
7649 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7650 msiobj_release( &view->hdr );
7652 return rc;
7655 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7657 LPWSTR key, template, id;
7658 UINT r = ERROR_SUCCESS;
7660 id = msi_dup_property( package->db, szProductID );
7661 if (id)
7663 msi_free( id );
7664 return ERROR_SUCCESS;
7666 template = msi_dup_property( package->db, szPIDTemplate );
7667 key = msi_dup_property( package->db, szPIDKEY );
7669 if (key && template)
7671 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7672 r = msi_set_property( package->db, szProductID, key );
7674 msi_free( template );
7675 msi_free( key );
7676 return r;
7679 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7681 TRACE("\n");
7682 package->need_reboot = 1;
7683 return ERROR_SUCCESS;
7686 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7688 static const WCHAR szAvailableFreeReg[] =
7689 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7690 MSIRECORD *uirow;
7691 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7693 TRACE("%p %d kilobytes\n", package, space);
7695 uirow = MSI_CreateRecord( 1 );
7696 MSI_RecordSetInteger( uirow, 1, space );
7697 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7698 msiobj_release( &uirow->hdr );
7700 return ERROR_SUCCESS;
7703 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7705 FIXME("%p\n", package);
7706 return ERROR_SUCCESS;
7709 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7711 FIXME("%p\n", package);
7712 return ERROR_SUCCESS;
7715 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7717 UINT r, count;
7718 MSIQUERY *view;
7720 static const WCHAR driver_query[] = {
7721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7722 'O','D','B','C','D','r','i','v','e','r',0 };
7724 static const WCHAR translator_query[] = {
7725 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7726 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
7728 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7729 if (r == ERROR_SUCCESS)
7731 count = 0;
7732 r = MSI_IterateRecords( view, &count, NULL, package );
7733 msiobj_release( &view->hdr );
7734 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7737 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7738 if (r == ERROR_SUCCESS)
7740 count = 0;
7741 r = MSI_IterateRecords( view, &count, NULL, package );
7742 msiobj_release( &view->hdr );
7743 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7746 return ERROR_SUCCESS;
7749 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7751 MSIPACKAGE *package = param;
7752 const WCHAR *property = MSI_RecordGetString( rec, 1 );
7753 WCHAR *value;
7755 if ((value = msi_dup_property( package->db, property )))
7757 FIXME("remove %s\n", debugstr_w(value));
7758 msi_free( value );
7760 return ERROR_SUCCESS;
7763 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7765 UINT r;
7766 MSIQUERY *view;
7768 static const WCHAR query[] =
7769 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
7770 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7772 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7773 if (r == ERROR_SUCCESS)
7775 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7776 msiobj_release( &view->hdr );
7778 return ERROR_SUCCESS;
7781 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7783 MSIPACKAGE *package = param;
7784 int attributes = MSI_RecordGetInteger( rec, 5 );
7786 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7788 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7789 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7790 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7791 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7792 HKEY hkey;
7793 UINT r;
7795 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7797 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7798 if (r != ERROR_SUCCESS)
7799 return ERROR_SUCCESS;
7801 else
7803 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7804 if (r != ERROR_SUCCESS)
7805 return ERROR_SUCCESS;
7807 RegCloseKey( hkey );
7809 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7810 debugstr_w(upgrade_code), debugstr_w(version_min),
7811 debugstr_w(version_max), debugstr_w(language));
7813 return ERROR_SUCCESS;
7816 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7818 UINT r;
7819 MSIQUERY *view;
7821 static const WCHAR query[] =
7822 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7824 if (msi_get_property_int( package->db, szInstalled, 0 ))
7826 TRACE("product is installed, skipping action\n");
7827 return ERROR_SUCCESS;
7829 if (msi_get_property_int( package->db, szPreselected, 0 ))
7831 TRACE("Preselected property is set, not migrating feature states\n");
7832 return ERROR_SUCCESS;
7835 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7836 if (r == ERROR_SUCCESS)
7838 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7839 msiobj_release( &view->hdr );
7841 return ERROR_SUCCESS;
7844 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7845 LPCSTR action, LPCWSTR table )
7847 static const WCHAR query[] = {
7848 'S','E','L','E','C','T',' ','*',' ',
7849 'F','R','O','M',' ','`','%','s','`',0 };
7850 MSIQUERY *view = NULL;
7851 DWORD count = 0;
7852 UINT r;
7854 r = MSI_OpenQuery( package->db, &view, query, table );
7855 if (r == ERROR_SUCCESS)
7857 r = MSI_IterateRecords(view, &count, NULL, package);
7858 msiobj_release(&view->hdr);
7861 if (count)
7862 FIXME("%s -> %u ignored %s table values\n",
7863 action, count, debugstr_w(table));
7865 return ERROR_SUCCESS;
7868 static UINT ACTION_BindImage( MSIPACKAGE *package )
7870 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7871 return msi_unimplemented_action_stub( package, "BindImage", table );
7874 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7876 static const WCHAR table[] = {
7877 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7878 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7881 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7883 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7884 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7887 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7889 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7890 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7893 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7895 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7896 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7899 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7901 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7902 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7905 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7907 static const struct
7909 const WCHAR *action;
7910 UINT (*handler)(MSIPACKAGE *);
7912 StandardActions[] =
7914 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7915 { szAppSearch, ACTION_AppSearch },
7916 { szBindImage, ACTION_BindImage },
7917 { szCCPSearch, ACTION_CCPSearch },
7918 { szCostFinalize, ACTION_CostFinalize },
7919 { szCostInitialize, ACTION_CostInitialize },
7920 { szCreateFolders, ACTION_CreateFolders },
7921 { szCreateShortcuts, ACTION_CreateShortcuts },
7922 { szDeleteServices, ACTION_DeleteServices },
7923 { szDisableRollback, ACTION_DisableRollback },
7924 { szDuplicateFiles, ACTION_DuplicateFiles },
7925 { szExecuteAction, ACTION_ExecuteAction },
7926 { szFileCost, ACTION_FileCost },
7927 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7928 { szForceReboot, ACTION_ForceReboot },
7929 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7930 { szInstallExecute, ACTION_InstallExecute },
7931 { szInstallExecuteAgain, ACTION_InstallExecute },
7932 { szInstallFiles, ACTION_InstallFiles},
7933 { szInstallFinalize, ACTION_InstallFinalize },
7934 { szInstallInitialize, ACTION_InstallInitialize },
7935 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7936 { szInstallValidate, ACTION_InstallValidate },
7937 { szIsolateComponents, ACTION_IsolateComponents },
7938 { szLaunchConditions, ACTION_LaunchConditions },
7939 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7940 { szMoveFiles, ACTION_MoveFiles },
7941 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7942 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7943 { szInstallODBC, ACTION_InstallODBC },
7944 { szInstallServices, ACTION_InstallServices },
7945 { szPatchFiles, ACTION_PatchFiles },
7946 { szProcessComponents, ACTION_ProcessComponents },
7947 { szPublishComponents, ACTION_PublishComponents },
7948 { szPublishFeatures, ACTION_PublishFeatures },
7949 { szPublishProduct, ACTION_PublishProduct },
7950 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7951 { szRegisterComPlus, ACTION_RegisterComPlus},
7952 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7953 { szRegisterFonts, ACTION_RegisterFonts },
7954 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7955 { szRegisterProduct, ACTION_RegisterProduct },
7956 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7957 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7958 { szRegisterUser, ACTION_RegisterUser },
7959 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7960 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7961 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7962 { szRemoveFiles, ACTION_RemoveFiles },
7963 { szRemoveFolders, ACTION_RemoveFolders },
7964 { szRemoveIniValues, ACTION_RemoveIniValues },
7965 { szRemoveODBC, ACTION_RemoveODBC },
7966 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7967 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7968 { szResolveSource, ACTION_ResolveSource },
7969 { szRMCCPSearch, ACTION_RMCCPSearch },
7970 { szScheduleReboot, ACTION_ScheduleReboot },
7971 { szSelfRegModules, ACTION_SelfRegModules },
7972 { szSelfUnregModules, ACTION_SelfUnregModules },
7973 { szSetODBCFolders, ACTION_SetODBCFolders },
7974 { szStartServices, ACTION_StartServices },
7975 { szStopServices, ACTION_StopServices },
7976 { szUnpublishComponents, ACTION_UnpublishComponents },
7977 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7978 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7979 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7980 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7981 { szUnregisterFonts, ACTION_UnregisterFonts },
7982 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7983 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7984 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7985 { szValidateProductID, ACTION_ValidateProductID },
7986 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7987 { szWriteIniValues, ACTION_WriteIniValues },
7988 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7989 { NULL, NULL },
7992 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7994 BOOL ret = FALSE;
7995 UINT i;
7997 i = 0;
7998 while (StandardActions[i].action != NULL)
8000 if (!strcmpW( StandardActions[i].action, action ))
8002 ui_actionstart( package, action );
8003 if (StandardActions[i].handler)
8005 ui_actioninfo( package, action, TRUE, 0 );
8006 *rc = StandardActions[i].handler( package );
8007 ui_actioninfo( package, action, FALSE, *rc );
8009 else
8011 FIXME("unhandled standard action %s\n", debugstr_w(action));
8012 *rc = ERROR_SUCCESS;
8014 ret = TRUE;
8015 break;
8017 i++;
8019 return ret;
8022 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
8024 UINT rc = ERROR_SUCCESS;
8025 BOOL handled;
8027 TRACE("Performing action (%s)\n", debugstr_w(action));
8029 handled = ACTION_HandleStandardAction(package, action, &rc);
8031 if (!handled)
8032 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
8034 if (!handled)
8036 WARN("unhandled msi action %s\n", debugstr_w(action));
8037 rc = ERROR_FUNCTION_NOT_CALLED;
8040 return rc;
8043 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
8045 UINT rc = ERROR_SUCCESS;
8046 BOOL handled = FALSE;
8048 TRACE("Performing action (%s)\n", debugstr_w(action));
8050 handled = ACTION_HandleStandardAction(package, action, &rc);
8052 if (!handled)
8053 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
8055 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
8056 handled = TRUE;
8058 if (!handled)
8060 WARN("unhandled msi action %s\n", debugstr_w(action));
8061 rc = ERROR_FUNCTION_NOT_CALLED;
8064 return rc;
8067 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8069 UINT rc = ERROR_SUCCESS;
8070 MSIRECORD *row;
8072 static const WCHAR ExecSeqQuery[] =
8073 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8074 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
8075 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
8076 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8077 static const WCHAR UISeqQuery[] =
8078 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8079 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
8080 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
8081 ' ', '=',' ','%','i',0};
8083 if (needs_ui_sequence(package))
8084 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
8085 else
8086 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
8088 if (row)
8090 LPCWSTR action, cond;
8092 TRACE("Running the actions\n");
8094 /* check conditions */
8095 cond = MSI_RecordGetString(row, 2);
8097 /* this is a hack to skip errors in the condition code */
8098 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8100 msiobj_release(&row->hdr);
8101 return ERROR_SUCCESS;
8104 action = MSI_RecordGetString(row, 1);
8105 if (!action)
8107 ERR("failed to fetch action\n");
8108 msiobj_release(&row->hdr);
8109 return ERROR_FUNCTION_FAILED;
8112 if (needs_ui_sequence(package))
8113 rc = ACTION_PerformUIAction(package, action, -1);
8114 else
8115 rc = ACTION_PerformAction(package, action, -1);
8117 msiobj_release(&row->hdr);
8120 return rc;
8123 /****************************************************
8124 * TOP level entry points
8125 *****************************************************/
8127 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8128 LPCWSTR szCommandLine )
8130 UINT rc;
8131 BOOL ui_exists;
8133 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8134 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
8136 msi_set_property( package->db, szAction, szInstall );
8138 package->script->InWhatSequence = SEQUENCE_INSTALL;
8140 if (szPackagePath)
8142 LPWSTR p, dir;
8143 LPCWSTR file;
8145 dir = strdupW(szPackagePath);
8146 p = strrchrW(dir, '\\');
8147 if (p)
8149 *(++p) = 0;
8150 file = szPackagePath + (p - dir);
8152 else
8154 msi_free(dir);
8155 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8156 GetCurrentDirectoryW(MAX_PATH, dir);
8157 lstrcatW(dir, szBackSlash);
8158 file = szPackagePath;
8161 msi_free( package->PackagePath );
8162 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8163 if (!package->PackagePath)
8165 msi_free(dir);
8166 return ERROR_OUTOFMEMORY;
8169 lstrcpyW(package->PackagePath, dir);
8170 lstrcatW(package->PackagePath, file);
8171 msi_free(dir);
8173 msi_set_sourcedir_props(package, FALSE);
8176 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8177 if (rc != ERROR_SUCCESS)
8178 return rc;
8180 msi_apply_transforms( package );
8181 msi_apply_patches( package );
8183 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
8185 TRACE("setting reinstall property\n");
8186 msi_set_property( package->db, szReinstall, szAll );
8189 /* properties may have been added by a transform */
8190 msi_clone_properties( package );
8192 msi_parse_command_line( package, szCommandLine, FALSE );
8193 msi_adjust_privilege_properties( package );
8194 msi_set_context( package );
8196 if (needs_ui_sequence( package))
8198 package->script->InWhatSequence |= SEQUENCE_UI;
8199 rc = ACTION_ProcessUISequence(package);
8200 ui_exists = ui_sequence_exists(package);
8201 if (rc == ERROR_SUCCESS || !ui_exists)
8203 package->script->InWhatSequence |= SEQUENCE_EXEC;
8204 rc = ACTION_ProcessExecSequence(package, ui_exists);
8207 else
8208 rc = ACTION_ProcessExecSequence(package, FALSE);
8210 package->script->CurrentlyScripting = FALSE;
8212 /* process the ending type action */
8213 if (rc == ERROR_SUCCESS)
8214 ACTION_PerformActionSequence(package, -1);
8215 else if (rc == ERROR_INSTALL_USEREXIT)
8216 ACTION_PerformActionSequence(package, -2);
8217 else if (rc == ERROR_INSTALL_SUSPEND)
8218 ACTION_PerformActionSequence(package, -4);
8219 else /* failed */
8220 ACTION_PerformActionSequence(package, -3);
8222 /* finish up running custom actions */
8223 ACTION_FinishCustomActions(package);
8225 if (rc == ERROR_SUCCESS && package->need_reboot)
8226 return ERROR_SUCCESS_REBOOT_REQUIRED;
8228 return rc;