sync msi with wine 1.1.35
[reactos.git] / reactos / dll / win32 / msi / action.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
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 "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
42
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47
48 /*
49 * consts and values used
50 */
51 static const WCHAR c_colon[] = {'C',':','\\',0};
52
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings[] =
140 {'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};
141 static const WCHAR szRemoveExistingProducts[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
187
188 /********************************************************
189 * helper functions
190 ********************************************************/
191
192 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
193 {
194 static const WCHAR Query_t[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
199 MSIRECORD * row;
200
201 row = MSI_QueryGetRecord( package->db, Query_t, action );
202 if (!row)
203 return;
204 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
205 msiobj_release(&row->hdr);
206 }
207
208 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
209 UINT rc)
210 {
211 MSIRECORD * row;
212 static const WCHAR template_s[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
214 '%','s', '.',0};
215 static const WCHAR template_e[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
218 '%','i','.',0};
219 static const WCHAR format[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
221 WCHAR message[1024];
222 WCHAR timet[0x100];
223
224 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
225 if (start)
226 sprintfW(message,template_s,timet,action);
227 else
228 sprintfW(message,template_e,timet,action,rc);
229
230 row = MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row,1,message);
232
233 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
234 msiobj_release(&row->hdr);
235 }
236
237 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
238 BOOL preserve_case )
239 {
240 LPCWSTR ptr,ptr2;
241 BOOL quote;
242 DWORD len;
243 LPWSTR prop = NULL, val = NULL;
244
245 if (!szCommandLine)
246 return ERROR_SUCCESS;
247
248 ptr = szCommandLine;
249
250 while (*ptr)
251 {
252 if (*ptr==' ')
253 {
254 ptr++;
255 continue;
256 }
257
258 TRACE("Looking at %s\n",debugstr_w(ptr));
259
260 ptr2 = strchrW(ptr,'=');
261 if (!ptr2)
262 {
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
264 break;
265 }
266
267 quote = FALSE;
268
269 len = ptr2-ptr;
270 prop = msi_alloc((len+1)*sizeof(WCHAR));
271 memcpy(prop,ptr,len*sizeof(WCHAR));
272 prop[len]=0;
273
274 if (!preserve_case)
275 struprW(prop);
276
277 ptr2++;
278
279 len = 0;
280 ptr = ptr2;
281 while (*ptr && (quote || (!quote && *ptr!=' ')))
282 {
283 if (*ptr == '"')
284 quote = !quote;
285 ptr++;
286 len++;
287 }
288
289 if (*ptr2=='"')
290 {
291 ptr2++;
292 len -= 2;
293 }
294 val = msi_alloc((len+1)*sizeof(WCHAR));
295 memcpy(val,ptr2,len*sizeof(WCHAR));
296 val[len] = 0;
297
298 if (lstrlenW(prop) > 0)
299 {
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop), debugstr_w(val));
302 MSI_SetPropertyW(package,prop,val);
303 }
304 msi_free(val);
305 msi_free(prop);
306 }
307
308 return ERROR_SUCCESS;
309 }
310
311
312 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
313 {
314 LPCWSTR pc;
315 LPWSTR p, *ret = NULL;
316 UINT count = 0;
317
318 if (!str)
319 return ret;
320
321 /* count the number of substrings */
322 for ( pc = str, count = 0; pc; count++ )
323 {
324 pc = strchrW( pc, sep );
325 if (pc)
326 pc++;
327 }
328
329 /* allocate space for an array of substring pointers and the substrings */
330 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
331 (lstrlenW(str)+1) * sizeof(WCHAR) );
332 if (!ret)
333 return ret;
334
335 /* copy the string and set the pointers */
336 p = (LPWSTR) &ret[count+1];
337 lstrcpyW( p, str );
338 for( count = 0; (ret[count] = p); count++ )
339 {
340 p = strchrW( p, sep );
341 if (p)
342 *p++ = 0;
343 }
344
345 return ret;
346 }
347
348 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
349 {
350 static const WCHAR szSystemLanguageID[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
352
353 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
354 UINT ret = ERROR_FUNCTION_FAILED;
355
356 prod_code = msi_dup_property( package, szProductCode );
357 patch_product = msi_get_suminfo_product( patch );
358
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
360
361 if ( strstrW( patch_product, prod_code ) )
362 {
363 MSISUMMARYINFO *si;
364 const WCHAR *p;
365
366 si = MSI_GetSummaryInformationW( patch, 0 );
367 if (!si)
368 {
369 ERR("no summary information!\n");
370 goto end;
371 }
372
373 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
374 if (!template)
375 {
376 ERR("no template property!\n");
377 msiobj_release( &si->hdr );
378 goto end;
379 }
380
381 if (!template[0])
382 {
383 ret = ERROR_SUCCESS;
384 msiobj_release( &si->hdr );
385 goto end;
386 }
387
388 langid = msi_dup_property( package, szSystemLanguageID );
389 if (!langid)
390 {
391 msiobj_release( &si->hdr );
392 goto end;
393 }
394
395 p = strchrW( template, ';' );
396 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
397 {
398 TRACE("applicable transform\n");
399 ret = ERROR_SUCCESS;
400 }
401
402 /* FIXME: check platform */
403
404 msiobj_release( &si->hdr );
405 }
406
407 end:
408 msi_free( patch_product );
409 msi_free( prod_code );
410 msi_free( template );
411 msi_free( langid );
412
413 return ret;
414 }
415
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
418 {
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
421 HRESULT r;
422
423 TRACE("%p %s\n", package, debugstr_w(name) );
424
425 if (*name++ != ':')
426 {
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
429 }
430
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 if (SUCCEEDED(r))
433 {
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
437 else
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
440 }
441 else
442 ERR("failed to open substorage %s\n", debugstr_w(name));
443
444 return ERROR_SUCCESS;
445 }
446
447 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
448 {
449 LPWSTR guid_list, *guids, product_code;
450 UINT i, ret = ERROR_FUNCTION_FAILED;
451
452 product_code = msi_dup_property( package, szProductCode );
453 if (!product_code)
454 {
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS;
458 }
459
460 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
461 guids = msi_split_string( guid_list, ';' );
462 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
463 {
464 if (!lstrcmpW( guids[i], product_code ))
465 ret = ERROR_SUCCESS;
466 }
467 msi_free( guids );
468 msi_free( guid_list );
469 msi_free( product_code );
470
471 return ret;
472 }
473
474 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
475 {
476 MSIQUERY *view;
477 MSIRECORD *rec = NULL;
478 LPWSTR patch;
479 LPCWSTR prop;
480 UINT r;
481
482 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
487
488 r = MSI_DatabaseOpenViewW(package->db, query, &view);
489 if (r != ERROR_SUCCESS)
490 return r;
491
492 r = MSI_ViewExecute(view, 0);
493 if (r != ERROR_SUCCESS)
494 goto done;
495
496 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
497 {
498 prop = MSI_RecordGetString(rec, 1);
499 patch = msi_dup_property(package, szPatch);
500 MSI_SetPropertyW(package, prop, patch);
501 msi_free(patch);
502 }
503
504 done:
505 if (rec) msiobj_release(&rec->hdr);
506 msiobj_release(&view->hdr);
507
508 return r;
509 }
510
511 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
512 {
513 MSISUMMARYINFO *si;
514 LPWSTR str, *substorage;
515 UINT i, r = ERROR_SUCCESS;
516
517 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
518 if (!si)
519 return ERROR_FUNCTION_FAILED;
520
521 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
522 {
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS;
525 }
526
527 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
528 if (!package->patch)
529 return ERROR_OUTOFMEMORY;
530
531 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
532 if (!package->patch->patchcode)
533 return ERROR_OUTOFMEMORY;
534
535 /* enumerate the substorage */
536 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 package->patch->transforms = str;
538
539 substorage = msi_split_string( str, ';' );
540 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
541 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
542
543 msi_free( substorage );
544 msiobj_release( &si->hdr );
545
546 msi_set_media_source_prop(package);
547
548 return r;
549 }
550
551 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
552 {
553 MSIDATABASE *patch_db = NULL;
554 UINT r;
555
556 TRACE("%p %s\n", package, debugstr_w( file ) );
557
558 /* FIXME:
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
562 */
563 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
564 if ( r != ERROR_SUCCESS )
565 {
566 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
567 return r;
568 }
569
570 msi_parse_patch_summary( package, patch_db );
571
572 /*
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
575 */
576 append_storage_to_db( package->db, patch_db->storage );
577
578 msiobj_release( &patch_db->hdr );
579
580 return ERROR_SUCCESS;
581 }
582
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT msi_apply_patches( MSIPACKAGE *package )
585 {
586 LPWSTR patch_list, *patches;
587 UINT i, r = ERROR_SUCCESS;
588
589 patch_list = msi_dup_property( package, szPatch );
590
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
592
593 patches = msi_split_string( patch_list, ';' );
594 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
595 r = msi_apply_patch_package( package, patches[i] );
596
597 msi_free( patches );
598 msi_free( patch_list );
599
600 return r;
601 }
602
603 static UINT msi_apply_transforms( MSIPACKAGE *package )
604 {
605 static const WCHAR szTransforms[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list, *xforms;
608 UINT i, r = ERROR_SUCCESS;
609
610 xform_list = msi_dup_property( package, szTransforms );
611 xforms = msi_split_string( xform_list, ';' );
612
613 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
614 {
615 if (xforms[i][0] == ':')
616 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
617 else
618 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
619 }
620
621 msi_free( xforms );
622 msi_free( xform_list );
623
624 return r;
625 }
626
627 static BOOL ui_sequence_exists( MSIPACKAGE *package )
628 {
629 MSIQUERY *view;
630 UINT rc;
631
632 static const WCHAR ExecSeqQuery [] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
640
641 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642 if (rc == ERROR_SUCCESS)
643 {
644 msiobj_release(&view->hdr);
645 return TRUE;
646 }
647
648 return FALSE;
649 }
650
651 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
652 {
653 LPWSTR p, db;
654 LPWSTR source, check;
655 DWORD len;
656
657 static const WCHAR szOriginalDatabase[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
659
660 db = msi_dup_property( package, szOriginalDatabase );
661 if (!db)
662 return ERROR_OUTOFMEMORY;
663
664 p = strrchrW( db, '\\' );
665 if (!p)
666 {
667 p = strrchrW( db, '/' );
668 if (!p)
669 {
670 msi_free(db);
671 return ERROR_SUCCESS;
672 }
673 }
674
675 len = p - db + 2;
676 source = msi_alloc( len * sizeof(WCHAR) );
677 lstrcpynW( source, db, len );
678
679 check = msi_dup_property( package, cszSourceDir );
680 if (!check || replace)
681 MSI_SetPropertyW( package, cszSourceDir, source );
682
683 msi_free( check );
684
685 check = msi_dup_property( package, cszSOURCEDIR );
686 if (!check || replace)
687 MSI_SetPropertyW( package, cszSOURCEDIR, source );
688
689 msi_free( check );
690 msi_free( source );
691 msi_free( db );
692
693 return ERROR_SUCCESS;
694 }
695
696 static BOOL needs_ui_sequence(MSIPACKAGE *package)
697 {
698 INT level = msi_get_property_int(package, szUILevel, 0);
699 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
700 }
701
702 static UINT msi_set_context(MSIPACKAGE *package)
703 {
704 WCHAR val[10];
705 DWORD sz = 10;
706 DWORD num;
707 UINT r;
708
709 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
710
711 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
712 if (r == ERROR_SUCCESS)
713 {
714 num = atolW(val);
715 if (num == 1 || num == 2)
716 package->Context = MSIINSTALLCONTEXT_MACHINE;
717 }
718
719 return ERROR_SUCCESS;
720 }
721
722 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
723 {
724 UINT rc;
725 LPCWSTR cond, action;
726 MSIPACKAGE *package = param;
727
728 action = MSI_RecordGetString(row,1);
729 if (!action)
730 {
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED;
733 }
734
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
737
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
740 {
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
742 return ERROR_SUCCESS;
743 }
744
745 if (needs_ui_sequence(package))
746 rc = ACTION_PerformUIAction(package, action, -1);
747 else
748 rc = ACTION_PerformAction(package, action, -1, FALSE);
749
750 msi_dialog_check_messages( NULL );
751
752 if (package->CurrentInstallState != ERROR_SUCCESS)
753 rc = package->CurrentInstallState;
754
755 if (rc == ERROR_FUNCTION_NOT_CALLED)
756 rc = ERROR_SUCCESS;
757
758 if (rc != ERROR_SUCCESS)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
760
761 return rc;
762 }
763
764 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
765 {
766 MSIQUERY * view;
767 UINT r;
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','%','s','`',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
775
776 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
777
778 r = MSI_OpenQuery( package->db, &view, query, szTable );
779 if (r == ERROR_SUCCESS)
780 {
781 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
782 msiobj_release(&view->hdr);
783 }
784
785 return r;
786 }
787
788 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
789 {
790 MSIQUERY * view;
791 UINT rc;
792 static const WCHAR ExecSeqQuery[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
806 INT seq = 0;
807
808 if (package->script->ExecuteSequenceRun)
809 {
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS;
812 }
813
814 package->script->ExecuteSequenceRun = TRUE;
815
816 /* get the sequence number */
817 if (UIran)
818 {
819 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
820 if( !row )
821 return ERROR_FUNCTION_FAILED;
822 seq = MSI_RecordGetInteger(row,1);
823 msiobj_release(&row->hdr);
824 }
825
826 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
827 if (rc == ERROR_SUCCESS)
828 {
829 TRACE("Running the actions\n");
830
831 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
832 msiobj_release(&view->hdr);
833 }
834
835 return rc;
836 }
837
838 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
839 {
840 MSIQUERY * view;
841 UINT rc;
842 static const WCHAR ExecSeqQuery [] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
850
851 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
852 if (rc == ERROR_SUCCESS)
853 {
854 TRACE("Running the actions\n");
855
856 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
857 msiobj_release(&view->hdr);
858 }
859
860 return rc;
861 }
862
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
867 UINT* rc, UINT script, BOOL force )
868 {
869 BOOL ret=FALSE;
870 UINT arc;
871
872 arc = ACTION_CustomAction(package, action, script, force);
873
874 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
875 {
876 *rc = arc;
877 ret = TRUE;
878 }
879 return ret;
880 }
881
882 /*
883 * Actual Action Handlers
884 */
885
886 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
887 {
888 MSIPACKAGE *package = param;
889 LPCWSTR dir;
890 LPWSTR full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
893
894 dir = MSI_RecordGetString(row,1);
895 if (!dir)
896 {
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS;
899 }
900
901 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
902 if (!full_path)
903 {
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
905 return ERROR_SUCCESS;
906 }
907
908 TRACE("Folder is %s\n",debugstr_w(full_path));
909
910 /* UI stuff */
911 uirow = MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow,1,full_path);
913 ui_actiondata(package,szCreateFolders,uirow);
914 msiobj_release( &uirow->hdr );
915
916 if (folder->State == 0)
917 create_full_pathW(full_path);
918
919 folder->State = 3;
920
921 msi_free(full_path);
922 return ERROR_SUCCESS;
923 }
924
925 /* FIXME: probably should merge this with the above function */
926 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
927 {
928 UINT rc = ERROR_SUCCESS;
929 MSIFOLDER *folder;
930 LPWSTR install_path;
931
932 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
933 if (!install_path)
934 return ERROR_FUNCTION_FAILED;
935
936 /* create the path */
937 if (folder->State == 0)
938 {
939 create_full_pathW(install_path);
940 folder->State = 2;
941 }
942 msi_free(install_path);
943
944 return rc;
945 }
946
947 UINT msi_create_component_directories( MSIPACKAGE *package )
948 {
949 MSICOMPONENT *comp;
950
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
953 {
954 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
955 continue;
956 msi_create_directory( package, comp->Directory );
957 }
958
959 return ERROR_SUCCESS;
960 }
961
962 /*
963 * Also we cannot enable/disable components either, so for now I am just going
964 * to do all the directories for all the components.
965 */
966 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
967 {
968 static const WCHAR ExecSeqQuery[] =
969 {'S','E','L','E','C','T',' ',
970 '`','D','i','r','e','c','t','o','r','y','_','`',
971 ' ','F','R','O','M',' ',
972 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
973 UINT rc;
974 MSIQUERY *view;
975
976 /* create all the empty folders specified in the CreateFolder table */
977 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
978 if (rc != ERROR_SUCCESS)
979 return ERROR_SUCCESS;
980
981 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
982 msiobj_release(&view->hdr);
983
984 msi_create_component_directories( package );
985
986 return rc;
987 }
988
989 static UINT load_component( MSIRECORD *row, LPVOID param )
990 {
991 MSIPACKAGE *package = param;
992 MSICOMPONENT *comp;
993
994 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
995 if (!comp)
996 return ERROR_FUNCTION_FAILED;
997
998 list_add_tail( &package->components, &comp->entry );
999
1000 /* fill in the data */
1001 comp->Component = msi_dup_record_field( row, 1 );
1002
1003 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1004
1005 comp->ComponentId = msi_dup_record_field( row, 2 );
1006 comp->Directory = msi_dup_record_field( row, 3 );
1007 comp->Attributes = MSI_RecordGetInteger(row,4);
1008 comp->Condition = msi_dup_record_field( row, 5 );
1009 comp->KeyPath = msi_dup_record_field( row, 6 );
1010
1011 comp->Installed = INSTALLSTATE_UNKNOWN;
1012 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1013
1014 return ERROR_SUCCESS;
1015 }
1016
1017 static UINT load_all_components( MSIPACKAGE *package )
1018 {
1019 static const WCHAR query[] = {
1020 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1021 '`','C','o','m','p','o','n','e','n','t','`',0 };
1022 MSIQUERY *view;
1023 UINT r;
1024
1025 if (!list_empty(&package->components))
1026 return ERROR_SUCCESS;
1027
1028 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1029 if (r != ERROR_SUCCESS)
1030 return r;
1031
1032 r = MSI_IterateRecords(view, NULL, load_component, package);
1033 msiobj_release(&view->hdr);
1034 return r;
1035 }
1036
1037 typedef struct {
1038 MSIPACKAGE *package;
1039 MSIFEATURE *feature;
1040 } _ilfs;
1041
1042 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1043 {
1044 ComponentList *cl;
1045
1046 cl = msi_alloc( sizeof (*cl) );
1047 if ( !cl )
1048 return ERROR_NOT_ENOUGH_MEMORY;
1049 cl->component = comp;
1050 list_add_tail( &feature->Components, &cl->entry );
1051
1052 return ERROR_SUCCESS;
1053 }
1054
1055 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1056 {
1057 FeatureList *fl;
1058
1059 fl = msi_alloc( sizeof(*fl) );
1060 if ( !fl )
1061 return ERROR_NOT_ENOUGH_MEMORY;
1062 fl->feature = child;
1063 list_add_tail( &parent->Children, &fl->entry );
1064
1065 return ERROR_SUCCESS;
1066 }
1067
1068 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1069 {
1070 _ilfs* ilfs = param;
1071 LPCWSTR component;
1072 MSICOMPONENT *comp;
1073
1074 component = MSI_RecordGetString(row,1);
1075
1076 /* check to see if the component is already loaded */
1077 comp = get_loaded_component( ilfs->package, component );
1078 if (!comp)
1079 {
1080 ERR("unknown component %s\n", debugstr_w(component));
1081 return ERROR_FUNCTION_FAILED;
1082 }
1083
1084 add_feature_component( ilfs->feature, comp );
1085 comp->Enabled = TRUE;
1086
1087 return ERROR_SUCCESS;
1088 }
1089
1090 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1091 {
1092 MSIFEATURE *feature;
1093
1094 if ( !name )
1095 return NULL;
1096
1097 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1098 {
1099 if ( !lstrcmpW( feature->Feature, name ) )
1100 return feature;
1101 }
1102
1103 return NULL;
1104 }
1105
1106 static UINT load_feature(MSIRECORD * row, LPVOID param)
1107 {
1108 MSIPACKAGE* package = param;
1109 MSIFEATURE* feature;
1110 static const WCHAR Query1[] =
1111 {'S','E','L','E','C','T',' ',
1112 '`','C','o','m','p','o','n','e','n','t','_','`',
1113 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1114 'C','o','m','p','o','n','e','n','t','s','`',' ',
1115 'W','H','E','R','E',' ',
1116 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1117 MSIQUERY * view;
1118 UINT rc;
1119 _ilfs ilfs;
1120
1121 /* fill in the data */
1122
1123 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1124 if (!feature)
1125 return ERROR_NOT_ENOUGH_MEMORY;
1126
1127 list_init( &feature->Children );
1128 list_init( &feature->Components );
1129
1130 feature->Feature = msi_dup_record_field( row, 1 );
1131
1132 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1133
1134 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1135 feature->Title = msi_dup_record_field( row, 3 );
1136 feature->Description = msi_dup_record_field( row, 4 );
1137
1138 if (!MSI_RecordIsNull(row,5))
1139 feature->Display = MSI_RecordGetInteger(row,5);
1140
1141 feature->Level= MSI_RecordGetInteger(row,6);
1142 feature->Directory = msi_dup_record_field( row, 7 );
1143 feature->Attributes = MSI_RecordGetInteger(row,8);
1144
1145 feature->Installed = INSTALLSTATE_UNKNOWN;
1146 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1147
1148 list_add_tail( &package->features, &feature->entry );
1149
1150 /* load feature components */
1151
1152 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1153 if (rc != ERROR_SUCCESS)
1154 return ERROR_SUCCESS;
1155
1156 ilfs.package = package;
1157 ilfs.feature = feature;
1158
1159 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1160 msiobj_release(&view->hdr);
1161
1162 return ERROR_SUCCESS;
1163 }
1164
1165 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1166 {
1167 MSIPACKAGE* package = param;
1168 MSIFEATURE *parent, *child;
1169
1170 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1171 if (!child)
1172 return ERROR_FUNCTION_FAILED;
1173
1174 if (!child->Feature_Parent)
1175 return ERROR_SUCCESS;
1176
1177 parent = find_feature_by_name( package, child->Feature_Parent );
1178 if (!parent)
1179 return ERROR_FUNCTION_FAILED;
1180
1181 add_feature_child( parent, child );
1182 return ERROR_SUCCESS;
1183 }
1184
1185 static UINT load_all_features( MSIPACKAGE *package )
1186 {
1187 static const WCHAR query[] = {
1188 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1189 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1190 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1191 MSIQUERY *view;
1192 UINT r;
1193
1194 if (!list_empty(&package->features))
1195 return ERROR_SUCCESS;
1196
1197 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1198 if (r != ERROR_SUCCESS)
1199 return r;
1200
1201 r = MSI_IterateRecords( view, NULL, load_feature, package );
1202 if (r != ERROR_SUCCESS)
1203 return r;
1204
1205 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1206 msiobj_release( &view->hdr );
1207
1208 return r;
1209 }
1210
1211 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1212 {
1213 if (!p)
1214 return p;
1215 p = strchrW(p, ch);
1216 if (!p)
1217 return p;
1218 *p = 0;
1219 return p+1;
1220 }
1221
1222 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1223 {
1224 static const WCHAR query[] = {
1225 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1226 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1227 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1228 MSIQUERY *view = NULL;
1229 MSIRECORD *row = NULL;
1230 UINT r;
1231
1232 TRACE("%s\n", debugstr_w(file->File));
1233
1234 r = MSI_OpenQuery(package->db, &view, query, file->File);
1235 if (r != ERROR_SUCCESS)
1236 goto done;
1237
1238 r = MSI_ViewExecute(view, NULL);
1239 if (r != ERROR_SUCCESS)
1240 goto done;
1241
1242 r = MSI_ViewFetch(view, &row);
1243 if (r != ERROR_SUCCESS)
1244 goto done;
1245
1246 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1247 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1248 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1249 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1250 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1251
1252 done:
1253 if (view) msiobj_release(&view->hdr);
1254 if (row) msiobj_release(&row->hdr);
1255 return r;
1256 }
1257
1258 static UINT load_file(MSIRECORD *row, LPVOID param)
1259 {
1260 MSIPACKAGE* package = param;
1261 LPCWSTR component;
1262 MSIFILE *file;
1263
1264 /* fill in the data */
1265
1266 file = msi_alloc_zero( sizeof (MSIFILE) );
1267 if (!file)
1268 return ERROR_NOT_ENOUGH_MEMORY;
1269
1270 file->File = msi_dup_record_field( row, 1 );
1271
1272 component = MSI_RecordGetString( row, 2 );
1273 file->Component = get_loaded_component( package, component );
1274
1275 if (!file->Component)
1276 {
1277 WARN("Component not found: %s\n", debugstr_w(component));
1278 msi_free(file->File);
1279 msi_free(file);
1280 return ERROR_SUCCESS;
1281 }
1282
1283 file->FileName = msi_dup_record_field( row, 3 );
1284 reduce_to_longfilename( file->FileName );
1285
1286 file->ShortName = msi_dup_record_field( row, 3 );
1287 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1288
1289 file->FileSize = MSI_RecordGetInteger( row, 4 );
1290 file->Version = msi_dup_record_field( row, 5 );
1291 file->Language = msi_dup_record_field( row, 6 );
1292 file->Attributes = MSI_RecordGetInteger( row, 7 );
1293 file->Sequence = MSI_RecordGetInteger( row, 8 );
1294
1295 file->state = msifs_invalid;
1296
1297 /* if the compressed bits are not set in the file attributes,
1298 * then read the information from the package word count property
1299 */
1300 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1301 {
1302 file->IsCompressed = FALSE;
1303 }
1304 else if (file->Attributes &
1305 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1306 {
1307 file->IsCompressed = TRUE;
1308 }
1309 else if (file->Attributes & msidbFileAttributesNoncompressed)
1310 {
1311 file->IsCompressed = FALSE;
1312 }
1313 else
1314 {
1315 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1316 }
1317
1318 load_file_hash(package, file);
1319
1320 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1321
1322 list_add_tail( &package->files, &file->entry );
1323
1324 return ERROR_SUCCESS;
1325 }
1326
1327 static UINT load_all_files(MSIPACKAGE *package)
1328 {
1329 MSIQUERY * view;
1330 UINT rc;
1331 static const WCHAR Query[] =
1332 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1333 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1334 '`','S','e','q','u','e','n','c','e','`', 0};
1335
1336 if (!list_empty(&package->files))
1337 return ERROR_SUCCESS;
1338
1339 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1340 if (rc != ERROR_SUCCESS)
1341 return ERROR_SUCCESS;
1342
1343 rc = MSI_IterateRecords(view, NULL, load_file, package);
1344 msiobj_release(&view->hdr);
1345
1346 return ERROR_SUCCESS;
1347 }
1348
1349 static UINT load_folder( MSIRECORD *row, LPVOID param )
1350 {
1351 MSIPACKAGE *package = param;
1352 static WCHAR szEmpty[] = { 0 };
1353 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1354 MSIFOLDER *folder;
1355
1356 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1357 if (!folder)
1358 return ERROR_NOT_ENOUGH_MEMORY;
1359
1360 folder->Directory = msi_dup_record_field( row, 1 );
1361
1362 TRACE("%s\n", debugstr_w(folder->Directory));
1363
1364 p = msi_dup_record_field(row, 3);
1365
1366 /* split src and target dir */
1367 tgt_short = p;
1368 src_short = folder_split_path( p, ':' );
1369
1370 /* split the long and short paths */
1371 tgt_long = folder_split_path( tgt_short, '|' );
1372 src_long = folder_split_path( src_short, '|' );
1373
1374 /* check for no-op dirs */
1375 if (!lstrcmpW(szDot, tgt_short))
1376 tgt_short = szEmpty;
1377 if (!lstrcmpW(szDot, src_short))
1378 src_short = szEmpty;
1379
1380 if (!tgt_long)
1381 tgt_long = tgt_short;
1382
1383 if (!src_short) {
1384 src_short = tgt_short;
1385 src_long = tgt_long;
1386 }
1387
1388 if (!src_long)
1389 src_long = src_short;
1390
1391 /* FIXME: use the target short path too */
1392 folder->TargetDefault = strdupW(tgt_long);
1393 folder->SourceShortPath = strdupW(src_short);
1394 folder->SourceLongPath = strdupW(src_long);
1395 msi_free(p);
1396
1397 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1398 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1399 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1400
1401 folder->Parent = msi_dup_record_field( row, 2 );
1402
1403 folder->Property = msi_dup_property( package, folder->Directory );
1404
1405 list_add_tail( &package->folders, &folder->entry );
1406
1407 TRACE("returning %p\n", folder);
1408
1409 return ERROR_SUCCESS;
1410 }
1411
1412 static UINT load_all_folders( MSIPACKAGE *package )
1413 {
1414 static const WCHAR query[] = {
1415 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1416 '`','D','i','r','e','c','t','o','r','y','`',0 };
1417 MSIQUERY *view;
1418 UINT r;
1419
1420 if (!list_empty(&package->folders))
1421 return ERROR_SUCCESS;
1422
1423 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1424 if (r != ERROR_SUCCESS)
1425 return r;
1426
1427 r = MSI_IterateRecords(view, NULL, load_folder, package);
1428 msiobj_release(&view->hdr);
1429 return r;
1430 }
1431
1432 /*
1433 * I am not doing any of the costing functionality yet.
1434 * Mostly looking at doing the Component and Feature loading
1435 *
1436 * The native MSI does A LOT of modification to tables here. Mostly adding
1437 * a lot of temporary columns to the Feature and Component tables.
1438 *
1439 * note: Native msi also tracks the short filename. But I am only going to
1440 * track the long ones. Also looking at this directory table
1441 * it appears that the directory table does not get the parents
1442 * resolved base on property only based on their entries in the
1443 * directory table.
1444 */
1445 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1446 {
1447 static const WCHAR szCosting[] =
1448 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1449
1450 MSI_SetPropertyW(package, szCosting, szZero);
1451 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1452
1453 load_all_folders( package );
1454 load_all_components( package );
1455 load_all_features( package );
1456 load_all_files( package );
1457
1458 return ERROR_SUCCESS;
1459 }
1460
1461 static UINT execute_script(MSIPACKAGE *package, UINT script )
1462 {
1463 UINT i;
1464 UINT rc = ERROR_SUCCESS;
1465
1466 TRACE("Executing Script %i\n",script);
1467
1468 if (!package->script)
1469 {
1470 ERR("no script!\n");
1471 return ERROR_FUNCTION_FAILED;
1472 }
1473
1474 for (i = 0; i < package->script->ActionCount[script]; i++)
1475 {
1476 LPWSTR action;
1477 action = package->script->Actions[script][i];
1478 ui_actionstart(package, action);
1479 TRACE("Executing Action (%s)\n",debugstr_w(action));
1480 rc = ACTION_PerformAction(package, action, script, TRUE);
1481 if (rc != ERROR_SUCCESS)
1482 break;
1483 }
1484 msi_free_action_script(package, script);
1485 return rc;
1486 }
1487
1488 static UINT ACTION_FileCost(MSIPACKAGE *package)
1489 {
1490 return ERROR_SUCCESS;
1491 }
1492
1493 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1494 {
1495 MSICOMPONENT *comp;
1496 INSTALLSTATE state;
1497 UINT r;
1498
1499 state = MsiQueryProductStateW(package->ProductCode);
1500
1501 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1502 {
1503 if (!comp->ComponentId)
1504 continue;
1505
1506 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1507 comp->Installed = INSTALLSTATE_ABSENT;
1508 else
1509 {
1510 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1511 package->Context, comp->ComponentId,
1512 &comp->Installed);
1513 if (r != ERROR_SUCCESS)
1514 comp->Installed = INSTALLSTATE_ABSENT;
1515 }
1516 }
1517 }
1518
1519 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1520 {
1521 MSIFEATURE *feature;
1522 INSTALLSTATE state;
1523
1524 state = MsiQueryProductStateW(package->ProductCode);
1525
1526 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1527 {
1528 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1529 feature->Installed = INSTALLSTATE_ABSENT;
1530 else
1531 {
1532 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1533 feature->Feature);
1534 }
1535 }
1536 }
1537
1538 static BOOL process_state_property(MSIPACKAGE* package, int level,
1539 LPCWSTR property, INSTALLSTATE state)
1540 {
1541 LPWSTR override;
1542 MSIFEATURE *feature;
1543
1544 override = msi_dup_property( package, property );
1545 if (!override)
1546 return FALSE;
1547
1548 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1549 {
1550 if (lstrcmpW(property, szRemove) &&
1551 (feature->Level <= 0 || feature->Level > level))
1552 continue;
1553
1554 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1555
1556 if (strcmpiW(override, szAll)==0)
1557 msi_feature_set_state(package, feature, state);
1558 else
1559 {
1560 LPWSTR ptr = override;
1561 LPWSTR ptr2 = strchrW(override,',');
1562
1563 while (ptr)
1564 {
1565 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1566 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1567 {
1568 msi_feature_set_state(package, feature, state);
1569 break;
1570 }
1571 if (ptr2)
1572 {
1573 ptr=ptr2+1;
1574 ptr2 = strchrW(ptr,',');
1575 }
1576 else
1577 break;
1578 }
1579 }
1580 }
1581 msi_free(override);
1582
1583 return TRUE;
1584 }
1585
1586 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1587 {
1588 int level;
1589 static const WCHAR szlevel[] =
1590 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1591 static const WCHAR szAddLocal[] =
1592 {'A','D','D','L','O','C','A','L',0};
1593 static const WCHAR szAddSource[] =
1594 {'A','D','D','S','O','U','R','C','E',0};
1595 static const WCHAR szAdvertise[] =
1596 {'A','D','V','E','R','T','I','S','E',0};
1597 BOOL override = FALSE;
1598 MSICOMPONENT* component;
1599 MSIFEATURE *feature;
1600
1601
1602 /* I do not know if this is where it should happen.. but */
1603
1604 TRACE("Checking Install Level\n");
1605
1606 level = msi_get_property_int(package, szlevel, 1);
1607
1608 /* ok here is the _real_ rub
1609 * all these activation/deactivation things happen in order and things
1610 * later on the list override things earlier on the list.
1611 * 0) INSTALLLEVEL processing
1612 * 1) ADDLOCAL
1613 * 2) REMOVE
1614 * 3) ADDSOURCE
1615 * 4) ADDDEFAULT
1616 * 5) REINSTALL
1617 * 6) ADVERTISE
1618 * 7) COMPADDLOCAL
1619 * 8) COMPADDSOURCE
1620 * 9) FILEADDLOCAL
1621 * 10) FILEADDSOURCE
1622 * 11) FILEADDDEFAULT
1623 *
1624 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1625 * REMOVE are the big ones, since we don't handle administrative installs
1626 * yet anyway.
1627 */
1628 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1629 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1630 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1631 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1632 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1633
1634 if (!override)
1635 {
1636 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1637 {
1638 BOOL feature_state = ((feature->Level > 0) &&
1639 (feature->Level <= level));
1640
1641 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1642 {
1643 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1644 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1645 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1646 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1647 else
1648 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1649 }
1650 }
1651
1652 /* disable child features of unselected parent features */
1653 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1654 {
1655 FeatureList *fl;
1656
1657 if (feature->Level > 0 && feature->Level <= level)
1658 continue;
1659
1660 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1661 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1662 }
1663 }
1664 else
1665 MSI_SetPropertyW(package, szPreselected, szOne);
1666
1667 /*
1668 * now we want to enable or disable components base on feature
1669 */
1670
1671 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1672 {
1673 ComponentList *cl;
1674
1675 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1676 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1677
1678 if (!feature->Level)
1679 continue;
1680
1681 /* features with components that have compressed files are made local */
1682 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1683 {
1684 if (cl->component->Enabled &&
1685 cl->component->ForceLocalState &&
1686 feature->Action == INSTALLSTATE_SOURCE)
1687 {
1688 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1689 break;
1690 }
1691 }
1692
1693 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1694 {
1695 component = cl->component;
1696
1697 if (!component->Enabled)
1698 continue;
1699
1700 switch (feature->Action)
1701 {
1702 case INSTALLSTATE_ABSENT:
1703 component->anyAbsent = 1;
1704 break;
1705 case INSTALLSTATE_ADVERTISED:
1706 component->hasAdvertiseFeature = 1;
1707 break;
1708 case INSTALLSTATE_SOURCE:
1709 component->hasSourceFeature = 1;
1710 break;
1711 case INSTALLSTATE_LOCAL:
1712 component->hasLocalFeature = 1;
1713 break;
1714 case INSTALLSTATE_DEFAULT:
1715 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1716 component->hasAdvertiseFeature = 1;
1717 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1718 component->hasSourceFeature = 1;
1719 else
1720 component->hasLocalFeature = 1;
1721 break;
1722 default:
1723 break;
1724 }
1725 }
1726 }
1727
1728 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1729 {
1730 /* if the component isn't enabled, leave it alone */
1731 if (!component->Enabled)
1732 continue;
1733
1734 /* check if it's local or source */
1735 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1736 (component->hasLocalFeature || component->hasSourceFeature))
1737 {
1738 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1739 !component->ForceLocalState)
1740 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1741 else
1742 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1743 continue;
1744 }
1745
1746 /* if any feature is local, the component must be local too */
1747 if (component->hasLocalFeature)
1748 {
1749 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1750 continue;
1751 }
1752
1753 if (component->hasSourceFeature)
1754 {
1755 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1756 continue;
1757 }
1758
1759 if (component->hasAdvertiseFeature)
1760 {
1761 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1762 continue;
1763 }
1764
1765 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1766 if (component->anyAbsent)
1767 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1768 }
1769
1770 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1771 {
1772 if (component->Action == INSTALLSTATE_DEFAULT)
1773 {
1774 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1775 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1776 }
1777
1778 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1779 debugstr_w(component->Component), component->Installed, component->Action);
1780 }
1781
1782
1783 return ERROR_SUCCESS;
1784 }
1785
1786 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1787 {
1788 MSIPACKAGE *package = param;
1789 LPCWSTR name;
1790 LPWSTR path;
1791 MSIFOLDER *f;
1792
1793 name = MSI_RecordGetString(row,1);
1794
1795 f = get_loaded_folder(package, name);
1796 if (!f) return ERROR_SUCCESS;
1797
1798 /* reset the ResolvedTarget */
1799 msi_free(f->ResolvedTarget);
1800 f->ResolvedTarget = NULL;
1801
1802 /* This helper function now does ALL the work */
1803 TRACE("Dir %s ...\n",debugstr_w(name));
1804 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1805 TRACE("resolves to %s\n",debugstr_w(path));
1806 msi_free(path);
1807
1808 return ERROR_SUCCESS;
1809 }
1810
1811 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1812 {
1813 MSIPACKAGE *package = param;
1814 LPCWSTR name;
1815 MSIFEATURE *feature;
1816
1817 name = MSI_RecordGetString( row, 1 );
1818
1819 feature = get_loaded_feature( package, name );
1820 if (!feature)
1821 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1822 else
1823 {
1824 LPCWSTR Condition;
1825 Condition = MSI_RecordGetString(row,3);
1826
1827 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1828 {
1829 int level = MSI_RecordGetInteger(row,2);
1830 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1831 feature->Level = level;
1832 }
1833 }
1834 return ERROR_SUCCESS;
1835 }
1836
1837 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1838 {
1839 static const WCHAR name_fmt[] =
1840 {'%','u','.','%','u','.','%','u','.','%','u',0};
1841 static const WCHAR name[] = {'\\',0};
1842 VS_FIXEDFILEINFO *lpVer;
1843 WCHAR filever[0x100];
1844 LPVOID version;
1845 DWORD versize;
1846 DWORD handle;
1847 UINT sz;
1848
1849 TRACE("%s\n", debugstr_w(filename));
1850
1851 versize = GetFileVersionInfoSizeW( filename, &handle );
1852 if (!versize)
1853 return NULL;
1854
1855 version = msi_alloc( versize );
1856 GetFileVersionInfoW( filename, 0, versize, version );
1857
1858 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1859 {
1860 msi_free( version );
1861 return NULL;
1862 }
1863
1864 sprintfW( filever, name_fmt,
1865 HIWORD(lpVer->dwFileVersionMS),
1866 LOWORD(lpVer->dwFileVersionMS),
1867 HIWORD(lpVer->dwFileVersionLS),
1868 LOWORD(lpVer->dwFileVersionLS));
1869
1870 msi_free( version );
1871
1872 return strdupW( filever );
1873 }
1874
1875 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1876 {
1877 LPWSTR file_version;
1878 MSIFILE *file;
1879
1880 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1881 {
1882 MSICOMPONENT* comp = file->Component;
1883 LPWSTR p;
1884
1885 if (!comp)
1886 continue;
1887
1888 if (file->IsCompressed)
1889 comp->ForceLocalState = TRUE;
1890
1891 /* calculate target */
1892 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1893
1894 msi_free(file->TargetPath);
1895
1896 TRACE("file %s is named %s\n",
1897 debugstr_w(file->File), debugstr_w(file->FileName));
1898
1899 file->TargetPath = build_directory_name(2, p, file->FileName);
1900
1901 msi_free(p);
1902
1903 TRACE("file %s resolves to %s\n",
1904 debugstr_w(file->File), debugstr_w(file->TargetPath));
1905
1906 /* don't check files of components that aren't installed */
1907 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1908 comp->Installed == INSTALLSTATE_ABSENT)
1909 {
1910 file->state = msifs_missing; /* assume files are missing */
1911 continue;
1912 }
1913
1914 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1915 {
1916 file->state = msifs_missing;
1917 comp->Cost += file->FileSize;
1918 continue;
1919 }
1920
1921 if (file->Version &&
1922 (file_version = msi_get_disk_file_version( file->TargetPath )))
1923 {
1924 TRACE("new %s old %s\n", debugstr_w(file->Version),
1925 debugstr_w(file_version));
1926 /* FIXME: seems like a bad way to compare version numbers */
1927 if (lstrcmpiW(file_version, file->Version)<0)
1928 {
1929 file->state = msifs_overwrite;
1930 comp->Cost += file->FileSize;
1931 }
1932 else
1933 file->state = msifs_present;
1934 msi_free( file_version );
1935 }
1936 else
1937 file->state = msifs_present;
1938 }
1939
1940 return ERROR_SUCCESS;
1941 }
1942
1943 /*
1944 * A lot is done in this function aside from just the costing.
1945 * The costing needs to be implemented at some point but for now I am going
1946 * to focus on the directory building
1947 *
1948 */
1949 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1950 {
1951 static const WCHAR ExecSeqQuery[] =
1952 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1953 '`','D','i','r','e','c','t','o','r','y','`',0};
1954 static const WCHAR ConditionQuery[] =
1955 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1956 '`','C','o','n','d','i','t','i','o','n','`',0};
1957 static const WCHAR szCosting[] =
1958 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1959 static const WCHAR szlevel[] =
1960 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1961 static const WCHAR szOutOfDiskSpace[] =
1962 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
1963 MSICOMPONENT *comp;
1964 UINT rc;
1965 MSIQUERY * view;
1966 LPWSTR level;
1967
1968 TRACE("Building Directory properties\n");
1969
1970 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1971 if (rc == ERROR_SUCCESS)
1972 {
1973 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1974 package);
1975 msiobj_release(&view->hdr);
1976 }
1977
1978 /* read components states from the registry */
1979 ACTION_GetComponentInstallStates(package);
1980 ACTION_GetFeatureInstallStates(package);
1981
1982 TRACE("File calculations\n");
1983 msi_check_file_install_states( package );
1984
1985 TRACE("Evaluating Condition Table\n");
1986
1987 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
1988 if (rc == ERROR_SUCCESS)
1989 {
1990 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
1991 package);
1992 msiobj_release(&view->hdr);
1993 }
1994
1995 TRACE("Enabling or Disabling Components\n");
1996 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1997 {
1998 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
1999 {
2000 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2001 comp->Enabled = FALSE;
2002 }
2003 else
2004 comp->Enabled = TRUE;
2005 }
2006
2007 MSI_SetPropertyW(package,szCosting,szOne);
2008 /* set default run level if not set */
2009 level = msi_dup_property( package, szlevel );
2010 if (!level)
2011 MSI_SetPropertyW(package,szlevel, szOne);
2012 msi_free(level);
2013
2014 /* FIXME: check volume disk space */
2015 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2016
2017 return MSI_SetFeatureStates(package);
2018 }
2019
2020 /* OK this value is "interpreted" and then formatted based on the
2021 first few characters */
2022 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2023 DWORD *size)
2024 {
2025 LPSTR data = NULL;
2026
2027 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2028 {
2029 if (value[1]=='x')
2030 {
2031 LPWSTR ptr;
2032 CHAR byte[5];
2033 LPWSTR deformated = NULL;
2034 int count;
2035
2036 deformat_string(package, &value[2], &deformated);
2037
2038 /* binary value type */
2039 ptr = deformated;
2040 *type = REG_BINARY;
2041 if (strlenW(ptr)%2)
2042 *size = (strlenW(ptr)/2)+1;
2043 else
2044 *size = strlenW(ptr)/2;
2045
2046 data = msi_alloc(*size);
2047
2048 byte[0] = '0';
2049 byte[1] = 'x';
2050 byte[4] = 0;
2051 count = 0;
2052 /* if uneven pad with a zero in front */
2053 if (strlenW(ptr)%2)
2054 {
2055 byte[2]= '0';
2056 byte[3]= *ptr;
2057 ptr++;
2058 data[count] = (BYTE)strtol(byte,NULL,0);
2059 count ++;
2060 TRACE("Uneven byte count\n");
2061 }
2062 while (*ptr)
2063 {
2064 byte[2]= *ptr;
2065 ptr++;
2066 byte[3]= *ptr;
2067 ptr++;
2068 data[count] = (BYTE)strtol(byte,NULL,0);
2069 count ++;
2070 }
2071 msi_free(deformated);
2072
2073 TRACE("Data %i bytes(%i)\n",*size,count);
2074 }
2075 else
2076 {
2077 LPWSTR deformated;
2078 LPWSTR p;
2079 DWORD d = 0;
2080 deformat_string(package, &value[1], &deformated);
2081
2082 *type=REG_DWORD;
2083 *size = sizeof(DWORD);
2084 data = msi_alloc(*size);
2085 p = deformated;
2086 if (*p == '-')
2087 p++;
2088 while (*p)
2089 {
2090 if ( (*p < '0') || (*p > '9') )
2091 break;
2092 d *= 10;
2093 d += (*p - '0');
2094 p++;
2095 }
2096 if (deformated[0] == '-')
2097 d = -d;
2098 *(LPDWORD)data = d;
2099 TRACE("DWORD %i\n",*(LPDWORD)data);
2100
2101 msi_free(deformated);
2102 }
2103 }
2104 else
2105 {
2106 static const WCHAR szMulti[] = {'[','~',']',0};
2107 LPCWSTR ptr;
2108 *type=REG_SZ;
2109
2110 if (value[0]=='#')
2111 {
2112 if (value[1]=='%')
2113 {
2114 ptr = &value[2];
2115 *type=REG_EXPAND_SZ;
2116 }
2117 else
2118 ptr = &value[1];
2119 }
2120 else
2121 ptr=value;
2122
2123 if (strstrW(value,szMulti))
2124 *type = REG_MULTI_SZ;
2125
2126 /* remove initial delimiter */
2127 if (!strncmpW(value, szMulti, 3))
2128 ptr = value + 3;
2129
2130 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2131
2132 /* add double NULL terminator */
2133 if (*type == REG_MULTI_SZ)
2134 {
2135 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2136 data = msi_realloc_zero(data, *size);
2137 }
2138 }
2139 return data;
2140 }
2141
2142 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2143 {
2144 MSIPACKAGE *package = param;
2145 static const WCHAR szHCR[] =
2146 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2147 'R','O','O','T','\\',0};
2148 static const WCHAR szHCU[] =
2149 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2150 'U','S','E','R','\\',0};
2151 static const WCHAR szHLM[] =
2152 {'H','K','E','Y','_','L','O','C','A','L','_',
2153 'M','A','C','H','I','N','E','\\',0};
2154 static const WCHAR szHU[] =
2155 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2156
2157 LPSTR value_data = NULL;
2158 HKEY root_key, hkey;
2159 DWORD type,size;
2160 LPWSTR deformated;
2161 LPCWSTR szRoot, component, name, key, value;
2162 MSICOMPONENT *comp;
2163 MSIRECORD * uirow;
2164 LPWSTR uikey;
2165 INT root;
2166 BOOL check_first = FALSE;
2167 UINT rc;
2168
2169 ui_progress(package,2,0,0,0);
2170
2171 value = NULL;
2172 key = NULL;
2173 uikey = NULL;
2174 name = NULL;
2175
2176 component = MSI_RecordGetString(row, 6);
2177 comp = get_loaded_component(package,component);
2178 if (!comp)
2179 return ERROR_SUCCESS;
2180
2181 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2182 {
2183 TRACE("Skipping write due to disabled component %s\n",
2184 debugstr_w(component));
2185
2186 comp->Action = comp->Installed;
2187
2188 return ERROR_SUCCESS;
2189 }
2190
2191 comp->Action = INSTALLSTATE_LOCAL;
2192
2193 name = MSI_RecordGetString(row, 4);
2194 if( MSI_RecordIsNull(row,5) && name )
2195 {
2196 /* null values can have special meanings */
2197 if (name[0]=='-' && name[1] == 0)
2198 return ERROR_SUCCESS;
2199 else if ((name[0]=='+' && name[1] == 0) ||
2200 (name[0] == '*' && name[1] == 0))
2201 name = NULL;
2202 check_first = TRUE;
2203 }
2204
2205 root = MSI_RecordGetInteger(row,2);
2206 key = MSI_RecordGetString(row, 3);
2207
2208 /* get the root key */
2209 switch (root)
2210 {
2211 case -1:
2212 {
2213 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2214 if (all_users && all_users[0] == '1')
2215 {
2216 root_key = HKEY_LOCAL_MACHINE;
2217 szRoot = szHLM;
2218 }
2219 else
2220 {
2221 root_key = HKEY_CURRENT_USER;
2222 szRoot = szHCU;
2223 }
2224 msi_free(all_users);
2225 }
2226 break;
2227 case 0: root_key = HKEY_CLASSES_ROOT;
2228 szRoot = szHCR;
2229 break;
2230 case 1: root_key = HKEY_CURRENT_USER;
2231 szRoot = szHCU;
2232 break;
2233 case 2: root_key = HKEY_LOCAL_MACHINE;
2234 szRoot = szHLM;
2235 break;
2236 case 3: root_key = HKEY_USERS;
2237 szRoot = szHU;
2238 break;
2239 default:
2240 ERR("Unknown root %i\n",root);
2241 root_key=NULL;
2242 szRoot = NULL;
2243 break;
2244 }
2245 if (!root_key)
2246 return ERROR_SUCCESS;
2247
2248 deformat_string(package, key , &deformated);
2249 size = strlenW(deformated) + strlenW(szRoot) + 1;
2250 uikey = msi_alloc(size*sizeof(WCHAR));
2251 strcpyW(uikey,szRoot);
2252 strcatW(uikey,deformated);
2253
2254 if (RegCreateKeyW( root_key, deformated, &hkey))
2255 {
2256 ERR("Could not create key %s\n",debugstr_w(deformated));
2257 msi_free(deformated);
2258 msi_free(uikey);
2259 return ERROR_SUCCESS;
2260 }
2261 msi_free(deformated);
2262
2263 value = MSI_RecordGetString(row,5);
2264 if (value)
2265 value_data = parse_value(package, value, &type, &size);
2266 else
2267 {
2268 value_data = (LPSTR)strdupW(szEmpty);
2269 size = sizeof(szEmpty);
2270 type = REG_SZ;
2271 }
2272
2273 deformat_string(package, name, &deformated);
2274
2275 if (!check_first)
2276 {
2277 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2278 debugstr_w(uikey));
2279 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2280 }
2281 else
2282 {
2283 DWORD sz = 0;
2284 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2285 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2286 {
2287 TRACE("value %s of %s checked already exists\n",
2288 debugstr_w(deformated), debugstr_w(uikey));
2289 }
2290 else
2291 {
2292 TRACE("Checked and setting value %s of %s\n",
2293 debugstr_w(deformated), debugstr_w(uikey));
2294 if (deformated || size)
2295 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2296 }
2297 }
2298 RegCloseKey(hkey);
2299
2300 uirow = MSI_CreateRecord(3);
2301 MSI_RecordSetStringW(uirow,2,deformated);
2302 MSI_RecordSetStringW(uirow,1,uikey);
2303
2304 if (type == REG_SZ)
2305 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2306 else
2307 MSI_RecordSetStringW(uirow,3,value);
2308
2309 ui_actiondata(package,szWriteRegistryValues,uirow);
2310 msiobj_release( &uirow->hdr );
2311
2312 msi_free(value_data);
2313 msi_free(deformated);
2314 msi_free(uikey);
2315
2316 return ERROR_SUCCESS;
2317 }
2318
2319 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2320 {
2321 UINT rc;
2322 MSIQUERY * view;
2323 static const WCHAR ExecSeqQuery[] =
2324 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2325 '`','R','e','g','i','s','t','r','y','`',0 };
2326
2327 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2328 if (rc != ERROR_SUCCESS)
2329 return ERROR_SUCCESS;
2330
2331 /* increment progress bar each time action data is sent */
2332 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2333
2334 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2335
2336 msiobj_release(&view->hdr);
2337 return rc;
2338 }
2339
2340 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2341 {
2342 package->script->CurrentlyScripting = TRUE;
2343
2344 return ERROR_SUCCESS;
2345 }
2346
2347
2348 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2349 {
2350 MSICOMPONENT *comp;
2351 DWORD progress = 0;
2352 DWORD total = 0;
2353 static const WCHAR q1[]=
2354 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2355 '`','R','e','g','i','s','t','r','y','`',0};
2356 UINT rc;
2357 MSIQUERY * view;
2358 MSIFEATURE *feature;
2359 MSIFILE *file;
2360
2361 TRACE("InstallValidate\n");
2362
2363 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2364 if (rc == ERROR_SUCCESS)
2365 {
2366 MSI_IterateRecords( view, &progress, NULL, package );
2367 msiobj_release( &view->hdr );
2368 total += progress * REG_PROGRESS_VALUE;
2369 }
2370
2371 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2372 total += COMPONENT_PROGRESS_VALUE;
2373
2374 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2375 total += file->FileSize;
2376
2377 ui_progress(package,0,total,0,0);
2378
2379 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2380 {
2381 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2382 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2383 feature->ActionRequest);
2384 }
2385
2386 return ERROR_SUCCESS;
2387 }
2388
2389 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2390 {
2391 MSIPACKAGE* package = param;
2392 LPCWSTR cond = NULL;
2393 LPCWSTR message = NULL;
2394 UINT r;
2395
2396 static const WCHAR title[]=
2397 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2398
2399 cond = MSI_RecordGetString(row,1);
2400
2401 r = MSI_EvaluateConditionW(package,cond);
2402 if (r == MSICONDITION_FALSE)
2403 {
2404 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2405 {
2406 LPWSTR deformated;
2407 message = MSI_RecordGetString(row,2);
2408 deformat_string(package,message,&deformated);
2409 MessageBoxW(NULL,deformated,title,MB_OK);
2410 msi_free(deformated);
2411 }
2412
2413 return ERROR_INSTALL_FAILURE;
2414 }
2415
2416 return ERROR_SUCCESS;
2417 }
2418
2419 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2420 {
2421 UINT rc;
2422 MSIQUERY * view = NULL;
2423 static const WCHAR ExecSeqQuery[] =
2424 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2425 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2426
2427 TRACE("Checking launch conditions\n");
2428
2429 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2430 if (rc != ERROR_SUCCESS)
2431 return ERROR_SUCCESS;
2432
2433 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2434 msiobj_release(&view->hdr);
2435
2436 return rc;
2437 }
2438
2439 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2440 {
2441
2442 if (!cmp->KeyPath)
2443 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2444
2445 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2446 {
2447 MSIRECORD * row = 0;
2448 UINT root,len;
2449 LPWSTR deformated,buffer,deformated_name;
2450 LPCWSTR key,name;
2451 static const WCHAR ExecSeqQuery[] =
2452 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2453 '`','R','e','g','i','s','t','r','y','`',' ',
2454 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2455 ' ','=',' ' ,'\'','%','s','\'',0 };
2456 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2457 static const WCHAR fmt2[]=
2458 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2459
2460 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2461 if (!row)
2462 return NULL;
2463
2464 root = MSI_RecordGetInteger(row,2);
2465 key = MSI_RecordGetString(row, 3);
2466 name = MSI_RecordGetString(row, 4);
2467 deformat_string(package, key , &deformated);
2468 deformat_string(package, name, &deformated_name);
2469
2470 len = strlenW(deformated) + 6;
2471 if (deformated_name)
2472 len+=strlenW(deformated_name);
2473
2474 buffer = msi_alloc( len *sizeof(WCHAR));
2475
2476 if (deformated_name)
2477 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2478 else
2479 sprintfW(buffer,fmt,root,deformated);
2480
2481 msi_free(deformated);
2482 msi_free(deformated_name);
2483 msiobj_release(&row->hdr);
2484
2485 return buffer;
2486 }
2487 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2488 {
2489 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2490 return NULL;
2491 }
2492 else
2493 {
2494 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2495
2496 if (file)
2497 return strdupW( file->TargetPath );
2498 }
2499 return NULL;
2500 }
2501
2502 static HKEY openSharedDLLsKey(void)
2503 {
2504 HKEY hkey=0;
2505 static const WCHAR path[] =
2506 {'S','o','f','t','w','a','r','e','\\',
2507 'M','i','c','r','o','s','o','f','t','\\',
2508 'W','i','n','d','o','w','s','\\',
2509 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2510 'S','h','a','r','e','d','D','L','L','s',0};
2511
2512 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2513 return hkey;
2514 }
2515
2516 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2517 {
2518 HKEY hkey;
2519 DWORD count=0;
2520 DWORD type;
2521 DWORD sz = sizeof(count);
2522 DWORD rc;
2523
2524 hkey = openSharedDLLsKey();
2525 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2526 if (rc != ERROR_SUCCESS)
2527 count = 0;
2528 RegCloseKey(hkey);
2529 return count;
2530 }
2531
2532 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2533 {
2534 HKEY hkey;
2535
2536 hkey = openSharedDLLsKey();
2537 if (count > 0)
2538 msi_reg_set_val_dword( hkey, path, count );
2539 else
2540 RegDeleteValueW(hkey,path);
2541 RegCloseKey(hkey);
2542 return count;
2543 }
2544
2545 /*
2546 * Return TRUE if the count should be written out and FALSE if not
2547 */
2548 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2549 {
2550 MSIFEATURE *feature;
2551 INT count = 0;
2552 BOOL write = FALSE;
2553
2554 /* only refcount DLLs */
2555 if (comp->KeyPath == NULL ||
2556 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2557 comp->Attributes & msidbComponentAttributesODBCDataSource)
2558 write = FALSE;
2559 else
2560 {
2561 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2562 write = (count > 0);
2563
2564 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2565 write = TRUE;
2566 }
2567
2568 /* increment counts */
2569 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2570 {
2571 ComponentList *cl;
2572
2573 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2574 continue;
2575
2576 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2577 {
2578 if ( cl->component == comp )
2579 count++;
2580 }
2581 }
2582
2583 /* decrement counts */
2584 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2585 {
2586 ComponentList *cl;
2587
2588 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2589 continue;
2590
2591 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2592 {
2593 if ( cl->component == comp )
2594 count--;
2595 }
2596 }
2597
2598 /* ref count all the files in the component */
2599 if (write)
2600 {
2601 MSIFILE *file;
2602
2603 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2604 {
2605 if (file->Component == comp)
2606 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2607 }
2608 }
2609
2610 /* add a count for permanent */
2611 if (comp->Attributes & msidbComponentAttributesPermanent)
2612 count ++;
2613
2614 comp->RefCount = count;
2615
2616 if (write)
2617 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2618 }
2619
2620 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2621 {
2622 WCHAR squished_pc[GUID_SIZE];
2623 WCHAR squished_cc[GUID_SIZE];
2624 UINT rc;
2625 MSICOMPONENT *comp;
2626 HKEY hkey;
2627
2628 TRACE("\n");
2629
2630 squash_guid(package->ProductCode,squished_pc);
2631 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2632
2633 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2634 {
2635 MSIRECORD * uirow;
2636
2637 ui_progress(package,2,0,0,0);
2638 if (!comp->ComponentId)
2639 continue;
2640
2641 squash_guid(comp->ComponentId,squished_cc);
2642
2643 msi_free(comp->FullKeypath);
2644 comp->FullKeypath = resolve_keypath( package, comp );
2645
2646 ACTION_RefCountComponent( package, comp );
2647
2648 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2649 debugstr_w(comp->Component),
2650 debugstr_w(squished_cc),
2651 debugstr_w(comp->FullKeypath),
2652 comp->RefCount);
2653
2654 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2655 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2656 {
2657 if (!comp->FullKeypath)
2658 continue;
2659
2660 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2661 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2662 &hkey, TRUE);
2663 else
2664 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2665 &hkey, TRUE);
2666
2667 if (rc != ERROR_SUCCESS)
2668 continue;
2669
2670 if (comp->Attributes & msidbComponentAttributesPermanent)
2671 {
2672 static const WCHAR szPermKey[] =
2673 { '0','0','0','0','0','0','0','0','0','0','0','0',
2674 '0','0','0','0','0','0','0','0','0','0','0','0',
2675 '0','0','0','0','0','0','0','0',0 };
2676
2677 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2678 }
2679
2680 if (comp->Action == INSTALLSTATE_LOCAL)
2681 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2682 else
2683 {
2684 MSIFILE *file;
2685 MSIRECORD *row;
2686 LPWSTR ptr, ptr2;
2687 WCHAR source[MAX_PATH];
2688 WCHAR base[MAX_PATH];
2689 LPWSTR sourcepath;
2690
2691 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2692 static const WCHAR query[] = {
2693 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2694 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2695 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2696 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2697 '`','D','i','s','k','I','d','`',0};
2698
2699 file = get_loaded_file(package, comp->KeyPath);
2700 if (!file)
2701 continue;
2702
2703 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2704 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2705 ptr2 = strrchrW(source, '\\') + 1;
2706 msiobj_release(&row->hdr);
2707
2708 lstrcpyW(base, package->PackagePath);
2709 ptr = strrchrW(base, '\\');
2710 *(ptr + 1) = '\0';
2711
2712 sourcepath = resolve_file_source(package, file);
2713 ptr = sourcepath + lstrlenW(base);
2714 lstrcpyW(ptr2, ptr);
2715 msi_free(sourcepath);
2716
2717 msi_reg_set_val_str(hkey, squished_pc, source);
2718 }
2719 RegCloseKey(hkey);
2720 }
2721 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2722 {
2723 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2724 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2725 else
2726 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2727 }
2728
2729 /* UI stuff */
2730 uirow = MSI_CreateRecord(3);
2731 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2732 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2733 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2734 ui_actiondata(package,szProcessComponents,uirow);
2735 msiobj_release( &uirow->hdr );
2736 }
2737
2738 return ERROR_SUCCESS;
2739 }
2740
2741 typedef struct {
2742 CLSID clsid;
2743 LPWSTR source;
2744