-sync msi with wine 1.1.32
[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 MSI_SetPropertyW(package, szAllUsers, szOne);
720 return ERROR_SUCCESS;
721 }
722
723 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
724 {
725 UINT rc;
726 LPCWSTR cond, action;
727 MSIPACKAGE *package = param;
728
729 action = MSI_RecordGetString(row,1);
730 if (!action)
731 {
732 ERR("Error is retrieving action name\n");
733 return ERROR_FUNCTION_FAILED;
734 }
735
736 /* check conditions */
737 cond = MSI_RecordGetString(row,2);
738
739 /* this is a hack to skip errors in the condition code */
740 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
741 {
742 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
743 return ERROR_SUCCESS;
744 }
745
746 if (needs_ui_sequence(package))
747 rc = ACTION_PerformUIAction(package, action, -1);
748 else
749 rc = ACTION_PerformAction(package, action, -1, FALSE);
750
751 msi_dialog_check_messages( NULL );
752
753 if (package->CurrentInstallState != ERROR_SUCCESS)
754 rc = package->CurrentInstallState;
755
756 if (rc == ERROR_FUNCTION_NOT_CALLED)
757 rc = ERROR_SUCCESS;
758
759 if (rc != ERROR_SUCCESS)
760 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
761
762 return rc;
763 }
764
765 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
766 {
767 MSIQUERY * view;
768 UINT r;
769 static const WCHAR query[] =
770 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
771 '`','%','s','`',
772 ' ','W','H','E','R','E',' ',
773 '`','S','e','q','u','e','n','c','e','`',' ',
774 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
775 '`','S','e','q','u','e','n','c','e','`',0};
776
777 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
778
779 r = MSI_OpenQuery( package->db, &view, query, szTable );
780 if (r == ERROR_SUCCESS)
781 {
782 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
783 msiobj_release(&view->hdr);
784 }
785
786 return r;
787 }
788
789 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
790 {
791 MSIQUERY * view;
792 UINT rc;
793 static const WCHAR ExecSeqQuery[] =
794 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
795 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
796 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
797 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
798 'O','R','D','E','R',' ', 'B','Y',' ',
799 '`','S','e','q','u','e','n','c','e','`',0 };
800 static const WCHAR IVQuery[] =
801 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
802 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
803 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
804 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
805 ' ','\'', 'I','n','s','t','a','l','l',
806 'V','a','l','i','d','a','t','e','\'', 0};
807 INT seq = 0;
808
809 if (package->script->ExecuteSequenceRun)
810 {
811 TRACE("Execute Sequence already Run\n");
812 return ERROR_SUCCESS;
813 }
814
815 package->script->ExecuteSequenceRun = TRUE;
816
817 /* get the sequence number */
818 if (UIran)
819 {
820 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
821 if( !row )
822 return ERROR_FUNCTION_FAILED;
823 seq = MSI_RecordGetInteger(row,1);
824 msiobj_release(&row->hdr);
825 }
826
827 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
828 if (rc == ERROR_SUCCESS)
829 {
830 TRACE("Running the actions\n");
831
832 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
833 msiobj_release(&view->hdr);
834 }
835
836 return rc;
837 }
838
839 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
840 {
841 MSIQUERY * view;
842 UINT rc;
843 static const WCHAR ExecSeqQuery [] =
844 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
845 '`','I','n','s','t','a','l','l',
846 'U','I','S','e','q','u','e','n','c','e','`',
847 ' ','W','H','E','R','E',' ',
848 '`','S','e','q','u','e','n','c','e','`',' ',
849 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
850 '`','S','e','q','u','e','n','c','e','`',0};
851
852 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
853 if (rc == ERROR_SUCCESS)
854 {
855 TRACE("Running the actions\n");
856
857 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
858 msiobj_release(&view->hdr);
859 }
860
861 return rc;
862 }
863
864 /********************************************************
865 * ACTION helper functions and functions that perform the actions
866 *******************************************************/
867 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
868 UINT* rc, UINT script, BOOL force )
869 {
870 BOOL ret=FALSE;
871 UINT arc;
872
873 arc = ACTION_CustomAction(package, action, script, force);
874
875 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
876 {
877 *rc = arc;
878 ret = TRUE;
879 }
880 return ret;
881 }
882
883 /*
884 * Actual Action Handlers
885 */
886
887 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
888 {
889 MSIPACKAGE *package = param;
890 LPCWSTR dir;
891 LPWSTR full_path;
892 MSIRECORD *uirow;
893 MSIFOLDER *folder;
894
895 dir = MSI_RecordGetString(row,1);
896 if (!dir)
897 {
898 ERR("Unable to get folder id\n");
899 return ERROR_SUCCESS;
900 }
901
902 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
903 if (!full_path)
904 {
905 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
906 return ERROR_SUCCESS;
907 }
908
909 TRACE("Folder is %s\n",debugstr_w(full_path));
910
911 /* UI stuff */
912 uirow = MSI_CreateRecord(1);
913 MSI_RecordSetStringW(uirow,1,full_path);
914 ui_actiondata(package,szCreateFolders,uirow);
915 msiobj_release( &uirow->hdr );
916
917 if (folder->State == 0)
918 create_full_pathW(full_path);
919
920 folder->State = 3;
921
922 msi_free(full_path);
923 return ERROR_SUCCESS;
924 }
925
926 /* FIXME: probably should merge this with the above function */
927 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
928 {
929 UINT rc = ERROR_SUCCESS;
930 MSIFOLDER *folder;
931 LPWSTR install_path;
932
933 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
934 if (!install_path)
935 return ERROR_FUNCTION_FAILED;
936
937 /* create the path */
938 if (folder->State == 0)
939 {
940 create_full_pathW(install_path);
941 folder->State = 2;
942 }
943 msi_free(install_path);
944
945 return rc;
946 }
947
948 UINT msi_create_component_directories( MSIPACKAGE *package )
949 {
950 MSICOMPONENT *comp;
951
952 /* create all the folders required by the components are going to install */
953 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
954 {
955 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
956 continue;
957 msi_create_directory( package, comp->Directory );
958 }
959
960 return ERROR_SUCCESS;
961 }
962
963 /*
964 * Also we cannot enable/disable components either, so for now I am just going
965 * to do all the directories for all the components.
966 */
967 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
968 {
969 static const WCHAR ExecSeqQuery[] =
970 {'S','E','L','E','C','T',' ',
971 '`','D','i','r','e','c','t','o','r','y','_','`',
972 ' ','F','R','O','M',' ',
973 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
974 UINT rc;
975 MSIQUERY *view;
976
977 /* create all the empty folders specified in the CreateFolder table */
978 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
979 if (rc != ERROR_SUCCESS)
980 return ERROR_SUCCESS;
981
982 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
983 msiobj_release(&view->hdr);
984
985 msi_create_component_directories( package );
986
987 return rc;
988 }
989
990 static UINT load_component( MSIRECORD *row, LPVOID param )
991 {
992 MSIPACKAGE *package = param;
993 MSICOMPONENT *comp;
994
995 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
996 if (!comp)
997 return ERROR_FUNCTION_FAILED;
998
999 list_add_tail( &package->components, &comp->entry );
1000
1001 /* fill in the data */
1002 comp->Component = msi_dup_record_field( row, 1 );
1003
1004 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1005
1006 comp->ComponentId = msi_dup_record_field( row, 2 );
1007 comp->Directory = msi_dup_record_field( row, 3 );
1008 comp->Attributes = MSI_RecordGetInteger(row,4);
1009 comp->Condition = msi_dup_record_field( row, 5 );
1010 comp->KeyPath = msi_dup_record_field( row, 6 );
1011
1012 comp->Installed = INSTALLSTATE_UNKNOWN;
1013 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1014
1015 return ERROR_SUCCESS;
1016 }
1017
1018 static UINT load_all_components( MSIPACKAGE *package )
1019 {
1020 static const WCHAR query[] = {
1021 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1022 '`','C','o','m','p','o','n','e','n','t','`',0 };
1023 MSIQUERY *view;
1024 UINT r;
1025
1026 if (!list_empty(&package->components))
1027 return ERROR_SUCCESS;
1028
1029 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1030 if (r != ERROR_SUCCESS)
1031 return r;
1032
1033 r = MSI_IterateRecords(view, NULL, load_component, package);
1034 msiobj_release(&view->hdr);
1035 return r;
1036 }
1037
1038 typedef struct {
1039 MSIPACKAGE *package;
1040 MSIFEATURE *feature;
1041 } _ilfs;
1042
1043 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1044 {
1045 ComponentList *cl;
1046
1047 cl = msi_alloc( sizeof (*cl) );
1048 if ( !cl )
1049 return ERROR_NOT_ENOUGH_MEMORY;
1050 cl->component = comp;
1051 list_add_tail( &feature->Components, &cl->entry );
1052
1053 return ERROR_SUCCESS;
1054 }
1055
1056 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1057 {
1058 FeatureList *fl;
1059
1060 fl = msi_alloc( sizeof(*fl) );
1061 if ( !fl )
1062 return ERROR_NOT_ENOUGH_MEMORY;
1063 fl->feature = child;
1064 list_add_tail( &parent->Children, &fl->entry );
1065
1066 return ERROR_SUCCESS;
1067 }
1068
1069 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1070 {
1071 _ilfs* ilfs = param;
1072 LPCWSTR component;
1073 MSICOMPONENT *comp;
1074
1075 component = MSI_RecordGetString(row,1);
1076
1077 /* check to see if the component is already loaded */
1078 comp = get_loaded_component( ilfs->package, component );
1079 if (!comp)
1080 {
1081 ERR("unknown component %s\n", debugstr_w(component));
1082 return ERROR_FUNCTION_FAILED;
1083 }
1084
1085 add_feature_component( ilfs->feature, comp );
1086 comp->Enabled = TRUE;
1087
1088 return ERROR_SUCCESS;
1089 }
1090
1091 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1092 {
1093 MSIFEATURE *feature;
1094
1095 if ( !name )
1096 return NULL;
1097
1098 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1099 {
1100 if ( !lstrcmpW( feature->Feature, name ) )
1101 return feature;
1102 }
1103
1104 return NULL;
1105 }
1106
1107 static UINT load_feature(MSIRECORD * row, LPVOID param)
1108 {
1109 MSIPACKAGE* package = param;
1110 MSIFEATURE* feature;
1111 static const WCHAR Query1[] =
1112 {'S','E','L','E','C','T',' ',
1113 '`','C','o','m','p','o','n','e','n','t','_','`',
1114 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1115 'C','o','m','p','o','n','e','n','t','s','`',' ',
1116 'W','H','E','R','E',' ',
1117 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1118 MSIQUERY * view;
1119 UINT rc;
1120 _ilfs ilfs;
1121
1122 /* fill in the data */
1123
1124 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1125 if (!feature)
1126 return ERROR_NOT_ENOUGH_MEMORY;
1127
1128 list_init( &feature->Children );
1129 list_init( &feature->Components );
1130
1131 feature->Feature = msi_dup_record_field( row, 1 );
1132
1133 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1134
1135 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1136 feature->Title = msi_dup_record_field( row, 3 );
1137 feature->Description = msi_dup_record_field( row, 4 );
1138
1139 if (!MSI_RecordIsNull(row,5))
1140 feature->Display = MSI_RecordGetInteger(row,5);
1141
1142 feature->Level= MSI_RecordGetInteger(row,6);
1143 feature->Directory = msi_dup_record_field( row, 7 );
1144 feature->Attributes = MSI_RecordGetInteger(row,8);
1145
1146 feature->Installed = INSTALLSTATE_UNKNOWN;
1147 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1148
1149 list_add_tail( &package->features, &feature->entry );
1150
1151 /* load feature components */
1152
1153 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1154 if (rc != ERROR_SUCCESS)
1155 return ERROR_SUCCESS;
1156
1157 ilfs.package = package;
1158 ilfs.feature = feature;
1159
1160 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1161 msiobj_release(&view->hdr);
1162
1163 return ERROR_SUCCESS;
1164 }
1165
1166 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1167 {
1168 MSIPACKAGE* package = param;
1169 MSIFEATURE *parent, *child;
1170
1171 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1172 if (!child)
1173 return ERROR_FUNCTION_FAILED;
1174
1175 if (!child->Feature_Parent)
1176 return ERROR_SUCCESS;
1177
1178 parent = find_feature_by_name( package, child->Feature_Parent );
1179 if (!parent)
1180 return ERROR_FUNCTION_FAILED;
1181
1182 add_feature_child( parent, child );
1183 return ERROR_SUCCESS;
1184 }
1185
1186 static UINT load_all_features( MSIPACKAGE *package )
1187 {
1188 static const WCHAR query[] = {
1189 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1190 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1191 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1192 MSIQUERY *view;
1193 UINT r;
1194
1195 if (!list_empty(&package->features))
1196 return ERROR_SUCCESS;
1197
1198 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1199 if (r != ERROR_SUCCESS)
1200 return r;
1201
1202 r = MSI_IterateRecords( view, NULL, load_feature, package );
1203 if (r != ERROR_SUCCESS)
1204 return r;
1205
1206 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1207 msiobj_release( &view->hdr );
1208
1209 return r;
1210 }
1211
1212 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1213 {
1214 if (!p)
1215 return p;
1216 p = strchrW(p, ch);
1217 if (!p)
1218 return p;
1219 *p = 0;
1220 return p+1;
1221 }
1222
1223 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1224 {
1225 static const WCHAR query[] = {
1226 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1227 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1228 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1229 MSIQUERY *view = NULL;
1230 MSIRECORD *row = NULL;
1231 UINT r;
1232
1233 TRACE("%s\n", debugstr_w(file->File));
1234
1235 r = MSI_OpenQuery(package->db, &view, query, file->File);
1236 if (r != ERROR_SUCCESS)
1237 goto done;
1238
1239 r = MSI_ViewExecute(view, NULL);
1240 if (r != ERROR_SUCCESS)
1241 goto done;
1242
1243 r = MSI_ViewFetch(view, &row);
1244 if (r != ERROR_SUCCESS)
1245 goto done;
1246
1247 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1248 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1249 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1250 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1251 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1252
1253 done:
1254 if (view) msiobj_release(&view->hdr);
1255 if (row) msiobj_release(&row->hdr);
1256 return r;
1257 }
1258
1259 static UINT load_file(MSIRECORD *row, LPVOID param)
1260 {
1261 MSIPACKAGE* package = param;
1262 LPCWSTR component;
1263 MSIFILE *file;
1264
1265 /* fill in the data */
1266
1267 file = msi_alloc_zero( sizeof (MSIFILE) );
1268 if (!file)
1269 return ERROR_NOT_ENOUGH_MEMORY;
1270
1271 file->File = msi_dup_record_field( row, 1 );
1272
1273 component = MSI_RecordGetString( row, 2 );
1274 file->Component = get_loaded_component( package, component );
1275
1276 if (!file->Component)
1277 {
1278 WARN("Component not found: %s\n", debugstr_w(component));
1279 msi_free(file->File);
1280 msi_free(file);
1281 return ERROR_SUCCESS;
1282 }
1283
1284 file->FileName = msi_dup_record_field( row, 3 );
1285 reduce_to_longfilename( file->FileName );
1286
1287 file->ShortName = msi_dup_record_field( row, 3 );
1288 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1289
1290 file->FileSize = MSI_RecordGetInteger( row, 4 );
1291 file->Version = msi_dup_record_field( row, 5 );
1292 file->Language = msi_dup_record_field( row, 6 );
1293 file->Attributes = MSI_RecordGetInteger( row, 7 );
1294 file->Sequence = MSI_RecordGetInteger( row, 8 );
1295
1296 file->state = msifs_invalid;
1297
1298 /* if the compressed bits are not set in the file attributes,
1299 * then read the information from the package word count property
1300 */
1301 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1302 {
1303 file->IsCompressed = FALSE;
1304 }
1305 else if (file->Attributes &
1306 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1307 {
1308 file->IsCompressed = TRUE;
1309 }
1310 else if (file->Attributes & msidbFileAttributesNoncompressed)
1311 {
1312 file->IsCompressed = FALSE;
1313 }
1314 else
1315 {
1316 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1317 }
1318
1319 load_file_hash(package, file);
1320
1321 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1322
1323 list_add_tail( &package->files, &file->entry );
1324
1325 return ERROR_SUCCESS;
1326 }
1327
1328 static UINT load_all_files(MSIPACKAGE *package)
1329 {
1330 MSIQUERY * view;
1331 UINT rc;
1332 static const WCHAR Query[] =
1333 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1334 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1335 '`','S','e','q','u','e','n','c','e','`', 0};
1336
1337 if (!list_empty(&package->files))
1338 return ERROR_SUCCESS;
1339
1340 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1341 if (rc != ERROR_SUCCESS)
1342 return ERROR_SUCCESS;
1343
1344 rc = MSI_IterateRecords(view, NULL, load_file, package);
1345 msiobj_release(&view->hdr);
1346
1347 return ERROR_SUCCESS;
1348 }
1349
1350 static UINT load_folder( MSIRECORD *row, LPVOID param )
1351 {
1352 MSIPACKAGE *package = param;
1353 static WCHAR szEmpty[] = { 0 };
1354 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1355 MSIFOLDER *folder;
1356
1357 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1358 if (!folder)
1359 return ERROR_NOT_ENOUGH_MEMORY;
1360
1361 folder->Directory = msi_dup_record_field( row, 1 );
1362
1363 TRACE("%s\n", debugstr_w(folder->Directory));
1364
1365 p = msi_dup_record_field(row, 3);
1366
1367 /* split src and target dir */
1368 tgt_short = p;
1369 src_short = folder_split_path( p, ':' );
1370
1371 /* split the long and short paths */
1372 tgt_long = folder_split_path( tgt_short, '|' );
1373 src_long = folder_split_path( src_short, '|' );
1374
1375 /* check for no-op dirs */
1376 if (!lstrcmpW(szDot, tgt_short))
1377 tgt_short = szEmpty;
1378 if (!lstrcmpW(szDot, src_short))
1379 src_short = szEmpty;
1380
1381 if (!tgt_long)
1382 tgt_long = tgt_short;
1383
1384 if (!src_short) {
1385 src_short = tgt_short;
1386 src_long = tgt_long;
1387 }
1388
1389 if (!src_long)
1390 src_long = src_short;
1391
1392 /* FIXME: use the target short path too */
1393 folder->TargetDefault = strdupW(tgt_long);
1394 folder->SourceShortPath = strdupW(src_short);
1395 folder->SourceLongPath = strdupW(src_long);
1396 msi_free(p);
1397
1398 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1399 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1400 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1401
1402 folder->Parent = msi_dup_record_field( row, 2 );
1403
1404 folder->Property = msi_dup_property( package, folder->Directory );
1405
1406 list_add_tail( &package->folders, &folder->entry );
1407
1408 TRACE("returning %p\n", folder);
1409
1410 return ERROR_SUCCESS;
1411 }
1412
1413 static UINT load_all_folders( MSIPACKAGE *package )
1414 {
1415 static const WCHAR query[] = {
1416 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1417 '`','D','i','r','e','c','t','o','r','y','`',0 };
1418 MSIQUERY *view;
1419 UINT r;
1420
1421 if (!list_empty(&package->folders))
1422 return ERROR_SUCCESS;
1423
1424 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1425 if (r != ERROR_SUCCESS)
1426 return r;
1427
1428 r = MSI_IterateRecords(view, NULL, load_folder, package);
1429 msiobj_release(&view->hdr);
1430 return r;
1431 }
1432
1433 /*
1434 * I am not doing any of the costing functionality yet.
1435 * Mostly looking at doing the Component and Feature loading
1436 *
1437 * The native MSI does A LOT of modification to tables here. Mostly adding
1438 * a lot of temporary columns to the Feature and Component tables.
1439 *
1440 * note: Native msi also tracks the short filename. But I am only going to
1441 * track the long ones. Also looking at this directory table
1442 * it appears that the directory table does not get the parents
1443 * resolved base on property only based on their entries in the
1444 * directory table.
1445 */
1446 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1447 {
1448 static const WCHAR szCosting[] =
1449 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1450
1451 MSI_SetPropertyW(package, szCosting, szZero);
1452 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1453
1454 load_all_folders( package );
1455 load_all_components( package );
1456 load_all_features( package );
1457 load_all_files( package );
1458
1459 return ERROR_SUCCESS;
1460 }
1461
1462 static UINT execute_script(MSIPACKAGE *package, UINT script )
1463 {
1464 UINT i;
1465 UINT rc = ERROR_SUCCESS;
1466
1467 TRACE("Executing Script %i\n",script);
1468
1469 if (!package->script)
1470 {
1471 ERR("no script!\n");
1472 return ERROR_FUNCTION_FAILED;
1473 }
1474
1475 for (i = 0; i < package->script->ActionCount[script]; i++)
1476 {
1477 LPWSTR action;
1478 action = package->script->Actions[script][i];
1479 ui_actionstart(package, action);
1480 TRACE("Executing Action (%s)\n",debugstr_w(action));
1481 rc = ACTION_PerformAction(package, action, script, TRUE);
1482 if (rc != ERROR_SUCCESS)
1483 break;
1484 }
1485 msi_free_action_script(package, script);
1486 return rc;
1487 }
1488
1489 static UINT ACTION_FileCost(MSIPACKAGE *package)
1490 {
1491 return ERROR_SUCCESS;
1492 }
1493
1494 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1495 {
1496 MSICOMPONENT *comp;
1497 INSTALLSTATE state;
1498 UINT r;
1499
1500 state = MsiQueryProductStateW(package->ProductCode);
1501
1502 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1503 {
1504 if (!comp->ComponentId)
1505 continue;
1506
1507 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1508 comp->Installed = INSTALLSTATE_ABSENT;
1509 else
1510 {
1511 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1512 package->Context, comp->ComponentId,
1513 &comp->Installed);
1514 if (r != ERROR_SUCCESS)
1515 comp->Installed = INSTALLSTATE_ABSENT;
1516 }
1517 }
1518 }
1519
1520 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1521 {
1522 MSIFEATURE *feature;
1523 INSTALLSTATE state;
1524
1525 state = MsiQueryProductStateW(package->ProductCode);
1526
1527 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1528 {
1529 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1530 feature->Installed = INSTALLSTATE_ABSENT;
1531 else
1532 {
1533 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1534 feature->Feature);
1535 }
1536 }
1537 }
1538
1539 static BOOL process_state_property(MSIPACKAGE* package, int level,
1540 LPCWSTR property, INSTALLSTATE state)
1541 {
1542 LPWSTR override;
1543 MSIFEATURE *feature;
1544
1545 override = msi_dup_property( package, property );
1546 if (!override)
1547 return FALSE;
1548
1549 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1550 {
1551 if (lstrcmpW(property, szRemove) &&
1552 (feature->Level <= 0 || feature->Level > level))
1553 continue;
1554
1555 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1556
1557 if (strcmpiW(override, szAll)==0)
1558 msi_feature_set_state(package, feature, state);
1559 else
1560 {
1561 LPWSTR ptr = override;
1562 LPWSTR ptr2 = strchrW(override,',');
1563
1564 while (ptr)
1565 {
1566 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1567 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1568 {
1569 msi_feature_set_state(package, feature, state);
1570 break;
1571 }
1572 if (ptr2)
1573 {
1574 ptr=ptr2+1;
1575 ptr2 = strchrW(ptr,',');
1576 }
1577 else
1578 break;
1579 }
1580 }
1581 }
1582 msi_free(override);
1583
1584 return TRUE;
1585 }
1586
1587 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1588 {
1589 int level;
1590 static const WCHAR szlevel[] =
1591 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1592 static const WCHAR szAddLocal[] =
1593 {'A','D','D','L','O','C','A','L',0};
1594 static const WCHAR szAddSource[] =
1595 {'A','D','D','S','O','U','R','C','E',0};
1596 static const WCHAR szAdvertise[] =
1597 {'A','D','V','E','R','T','I','S','E',0};
1598 BOOL override = FALSE;
1599 MSICOMPONENT* component;
1600 MSIFEATURE *feature;
1601
1602
1603 /* I do not know if this is where it should happen.. but */
1604
1605 TRACE("Checking Install Level\n");
1606
1607 level = msi_get_property_int(package, szlevel, 1);
1608
1609 /* ok here is the _real_ rub
1610 * all these activation/deactivation things happen in order and things
1611 * later on the list override things earlier on the list.
1612 * 0) INSTALLLEVEL processing
1613 * 1) ADDLOCAL
1614 * 2) REMOVE
1615 * 3) ADDSOURCE
1616 * 4) ADDDEFAULT
1617 * 5) REINSTALL
1618 * 6) ADVERTISE
1619 * 7) COMPADDLOCAL
1620 * 8) COMPADDSOURCE
1621 * 9) FILEADDLOCAL
1622 * 10) FILEADDSOURCE
1623 * 11) FILEADDDEFAULT
1624 *
1625 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1626 * REMOVE are the big ones, since we don't handle administrative installs
1627 * yet anyway.
1628 */
1629 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1630 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1631 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1632 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1633 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1634
1635 if (!override)
1636 {
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1638 {
1639 BOOL feature_state = ((feature->Level > 0) &&
1640 (feature->Level <= level));
1641
1642 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1643 {
1644 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1645 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1646 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1647 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1648 else
1649 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1650 }
1651 }
1652
1653 /* disable child features of unselected parent features */
1654 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1655 {
1656 FeatureList *fl;
1657
1658 if (feature->Level > 0 && feature->Level <= level)
1659 continue;
1660
1661 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1662 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1663 }
1664 }
1665 else
1666 MSI_SetPropertyW(package, szPreselected, szOne);
1667
1668 /*
1669 * now we want to enable or disable components base on feature
1670 */
1671
1672 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1673 {
1674 ComponentList *cl;
1675
1676 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1677 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1678
1679 if (!feature->Level)
1680 continue;
1681
1682 /* features with components that have compressed files are made local */
1683 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1684 {
1685 if (cl->component->Enabled &&
1686 cl->component->ForceLocalState &&
1687 feature->Action == INSTALLSTATE_SOURCE)
1688 {
1689 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1690 break;
1691 }
1692 }
1693
1694 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1695 {
1696 component = cl->component;
1697
1698 if (!component->Enabled)
1699 continue;
1700
1701 switch (feature->Action)
1702 {
1703 case INSTALLSTATE_ABSENT:
1704 component->anyAbsent = 1;
1705 break;
1706 case INSTALLSTATE_ADVERTISED:
1707 component->hasAdvertiseFeature = 1;
1708 break;
1709 case INSTALLSTATE_SOURCE:
1710 component->hasSourceFeature = 1;
1711 break;
1712 case INSTALLSTATE_LOCAL:
1713 component->hasLocalFeature = 1;
1714 break;
1715 case INSTALLSTATE_DEFAULT:
1716 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1717 component->hasAdvertiseFeature = 1;
1718 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1719 component->hasSourceFeature = 1;
1720 else
1721 component->hasLocalFeature = 1;
1722 break;
1723 default:
1724 break;
1725 }
1726 }
1727 }
1728
1729 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1730 {
1731 /* if the component isn't enabled, leave it alone */
1732 if (!component->Enabled)
1733 continue;
1734
1735 /* check if it's local or source */
1736 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1737 (component->hasLocalFeature || component->hasSourceFeature))
1738 {
1739 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1740 !component->ForceLocalState)
1741 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1742 else
1743 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1744 continue;
1745 }
1746
1747 /* if any feature is local, the component must be local too */
1748 if (component->hasLocalFeature)
1749 {
1750 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1751 continue;
1752 }
1753
1754 if (component->hasSourceFeature)
1755 {
1756 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1757 continue;
1758 }
1759
1760 if (component->hasAdvertiseFeature)
1761 {
1762 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1763 continue;
1764 }
1765
1766 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1767 if (component->anyAbsent)
1768 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1769 }
1770
1771 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1772 {
1773 if (component->Action == INSTALLSTATE_DEFAULT)
1774 {
1775 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1776 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1777 }
1778
1779 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1780 debugstr_w(component->Component), component->Installed, component->Action);
1781 }
1782
1783
1784 return ERROR_SUCCESS;
1785 }
1786
1787 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1788 {
1789 MSIPACKAGE *package = param;
1790 LPCWSTR name;
1791 LPWSTR path;
1792 MSIFOLDER *f;
1793
1794 name = MSI_RecordGetString(row,1);
1795
1796 f = get_loaded_folder(package, name);
1797 if (!f) return ERROR_SUCCESS;
1798
1799 /* reset the ResolvedTarget */
1800 msi_free(f->ResolvedTarget);
1801 f->ResolvedTarget = NULL;
1802
1803 /* This helper function now does ALL the work */
1804 TRACE("Dir %s ...\n",debugstr_w(name));
1805 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1806 TRACE("resolves to %s\n",debugstr_w(path));
1807 msi_free(path);
1808
1809 return ERROR_SUCCESS;
1810 }
1811
1812 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1813 {
1814 MSIPACKAGE *package = param;
1815 LPCWSTR name;
1816 MSIFEATURE *feature;
1817
1818 name = MSI_RecordGetString( row, 1 );
1819
1820 feature = get_loaded_feature( package, name );
1821 if (!feature)
1822 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1823 else
1824 {
1825 LPCWSTR Condition;
1826 Condition = MSI_RecordGetString(row,3);
1827
1828 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1829 {
1830 int level = MSI_RecordGetInteger(row,2);
1831 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1832 feature->Level = level;
1833 }
1834 }
1835 return ERROR_SUCCESS;
1836 }
1837
1838 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1839 {
1840 static const WCHAR name_fmt[] =
1841 {'%','u','.','%','u','.','%','u','.','%','u',0};
1842 static const WCHAR name[] = {'\\',0};
1843 VS_FIXEDFILEINFO *lpVer;
1844 WCHAR filever[0x100];
1845 LPVOID version;
1846 DWORD versize;
1847 DWORD handle;
1848 UINT sz;
1849
1850 TRACE("%s\n", debugstr_w(filename));
1851
1852 versize = GetFileVersionInfoSizeW( filename, &handle );
1853 if (!versize)
1854 return NULL;
1855
1856 version = msi_alloc( versize );
1857 GetFileVersionInfoW( filename, 0, versize, version );
1858
1859 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1860 {
1861 msi_free( version );
1862 return NULL;
1863 }
1864
1865 sprintfW( filever, name_fmt,
1866 HIWORD(lpVer->dwFileVersionMS),
1867 LOWORD(lpVer->dwFileVersionMS),
1868 HIWORD(lpVer->dwFileVersionLS),
1869 LOWORD(lpVer->dwFileVersionLS));
1870
1871 msi_free( version );
1872
1873 return strdupW( filever );
1874 }
1875
1876 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1877 {
1878 LPWSTR file_version;
1879 MSIFILE *file;
1880
1881 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1882 {
1883 MSICOMPONENT* comp = file->Component;
1884 LPWSTR p;
1885
1886 if (!comp)
1887 continue;
1888
1889 if (file->IsCompressed)
1890 comp->ForceLocalState = TRUE;
1891
1892 /* calculate target */
1893 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1894
1895 msi_free(file->TargetPath);
1896
1897 TRACE("file %s is named %s\n",
1898 debugstr_w(file->File), debugstr_w(file->FileName));
1899
1900 file->TargetPath = build_directory_name(2, p, file->FileName);
1901
1902 msi_free(p);
1903
1904 TRACE("file %s resolves to %s\n",
1905 debugstr_w(file->File), debugstr_w(file->TargetPath));
1906
1907 /* don't check files of components that aren't installed */
1908 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1909 comp->Installed == INSTALLSTATE_ABSENT)
1910 {
1911 file->state = msifs_missing; /* assume files are missing */
1912 continue;
1913 }
1914
1915 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1916 {
1917 file->state = msifs_missing;
1918 comp->Cost += file->FileSize;
1919 continue;
1920 }
1921
1922 if (file->Version &&
1923 (file_version = msi_get_disk_file_version( file->TargetPath )))
1924 {
1925 TRACE("new %s old %s\n", debugstr_w(file->Version),
1926 debugstr_w(file_version));
1927 /* FIXME: seems like a bad way to compare version numbers */
1928 if (lstrcmpiW(file_version, file->Version)<0)
1929 {
1930 file->state = msifs_overwrite;
1931 comp->Cost += file->FileSize;
1932 }
1933 else
1934 file->state = msifs_present;
1935 msi_free( file_version );
1936 }
1937 else
1938 file->state = msifs_present;
1939 }
1940
1941 return ERROR_SUCCESS;
1942 }
1943
1944 /*
1945 * A lot is done in this function aside from just the costing.
1946 * The costing needs to be implemented at some point but for now I am going
1947 * to focus on the directory building
1948 *
1949 */
1950 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
1951 {
1952 static const WCHAR ExecSeqQuery[] =
1953 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1954 '`','D','i','r','e','c','t','o','r','y','`',0};
1955 static const WCHAR ConditionQuery[] =
1956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1957 '`','C','o','n','d','i','t','i','o','n','`',0};
1958 static const WCHAR szCosting[] =
1959 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1960 static const WCHAR szlevel[] =
1961 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1962 static const WCHAR szOutOfDiskSpace[] =
1963 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
1964 MSICOMPONENT *comp;
1965 UINT rc;
1966 MSIQUERY * view;
1967 LPWSTR level;
1968
1969 TRACE("Building Directory properties\n");
1970
1971 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1972 if (rc == ERROR_SUCCESS)
1973 {
1974 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
1975 package);
1976 msiobj_release(&view->hdr);
1977 }
1978
1979 /* read components states from the registry */
1980 ACTION_GetComponentInstallStates(package);
1981 ACTION_GetFeatureInstallStates(package);
1982
1983 TRACE("File calculations\n");
1984 msi_check_file_install_states( package );
1985
1986 TRACE("Evaluating Condition Table\n");
1987
1988 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
1989 if (rc == ERROR_SUCCESS)
1990 {
1991 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
1992 package);
1993 msiobj_release(&view->hdr);
1994 }
1995
1996 TRACE("Enabling or Disabling Components\n");
1997 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1998 {
1999 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2000 {
2001 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2002 comp->Enabled = FALSE;
2003 }
2004 else
2005 comp->Enabled = TRUE;
2006 }
2007
2008 MSI_SetPropertyW(package,szCosting,szOne);
2009 /* set default run level if not set */
2010 level = msi_dup_property( package, szlevel );
2011 if (!level)
2012 MSI_SetPropertyW(package,szlevel, szOne);
2013 msi_free(level);
2014
2015 /* FIXME: check volume disk space */
2016 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2017
2018 return MSI_SetFeatureStates(package);
2019 }
2020
2021 /* OK this value is "interpreted" and then formatted based on the
2022 first few characters */
2023 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2024 DWORD *size)
2025 {
2026 LPSTR data = NULL;
2027
2028 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2029 {
2030 if (value[1]=='x')
2031 {
2032 LPWSTR ptr;
2033 CHAR byte[5];
2034 LPWSTR deformated = NULL;
2035 int count;
2036
2037 deformat_string(package, &value[2], &deformated);
2038
2039 /* binary value type */
2040 ptr = deformated;
2041 *type = REG_BINARY;
2042 if (strlenW(ptr)%2)
2043 *size = (strlenW(ptr)/2)+1;
2044 else
2045 *size = strlenW(ptr)/2;
2046
2047 data = msi_alloc(*size);
2048
2049 byte[0] = '0';
2050 byte[1] = 'x';
2051 byte[4] = 0;
2052 count = 0;
2053 /* if uneven pad with a zero in front */
2054 if (strlenW(ptr)%2)
2055 {
2056 byte[2]= '0';
2057 byte[3]= *ptr;
2058 ptr++;
2059 data[count] = (BYTE)strtol(byte,NULL,0);
2060 count ++;
2061 TRACE("Uneven byte count\n");
2062 }
2063 while (*ptr)
2064 {
2065 byte[2]= *ptr;
2066 ptr++;
2067 byte[3]= *ptr;
2068 ptr++;
2069 data[count] = (BYTE)strtol(byte,NULL,0);
2070 count ++;
2071 }
2072 msi_free(deformated);
2073
2074 TRACE("Data %i bytes(%i)\n",*size,count);
2075 }
2076 else
2077 {
2078 LPWSTR deformated;
2079 LPWSTR p;
2080 DWORD d = 0;
2081 deformat_string(package, &value[1], &deformated);
2082
2083 *type=REG_DWORD;
2084 *size = sizeof(DWORD);
2085 data = msi_alloc(*size);
2086 p = deformated;
2087 if (*p == '-')
2088 p++;
2089 while (*p)
2090 {
2091 if ( (*p < '0') || (*p > '9') )
2092 break;
2093 d *= 10;
2094 d += (*p - '0');
2095 p++;
2096 }
2097 if (deformated[0] == '-')
2098 d = -d;
2099 *(LPDWORD)data = d;
2100 TRACE("DWORD %i\n",*(LPDWORD)data);
2101
2102 msi_free(deformated);
2103 }
2104 }
2105 else
2106 {
2107 static const WCHAR szMulti[] = {'[','~',']',0};
2108 LPCWSTR ptr;
2109 *type=REG_SZ;
2110
2111 if (value[0]=='#')
2112 {
2113 if (value[1]=='%')
2114 {
2115 ptr = &value[2];
2116 *type=REG_EXPAND_SZ;
2117 }
2118 else
2119 ptr = &value[1];
2120 }
2121 else
2122 ptr=value;
2123
2124 if (strstrW(value,szMulti))
2125 *type = REG_MULTI_SZ;
2126
2127 /* remove initial delimiter */
2128 if (!strncmpW(value, szMulti, 3))
2129 ptr = value + 3;
2130
2131 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2132
2133 /* add double NULL terminator */
2134 if (*type == REG_MULTI_SZ)
2135 {
2136 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2137 data = msi_realloc_zero(data, *size);
2138 }
2139 }
2140 return data;
2141 }
2142
2143 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2144 {
2145 MSIPACKAGE *package = param;
2146 static const WCHAR szHCR[] =
2147 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2148 'R','O','O','T','\\',0};
2149 static const WCHAR szHCU[] =
2150 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2151 'U','S','E','R','\\',0};
2152 static const WCHAR szHLM[] =
2153 {'H','K','E','Y','_','L','O','C','A','L','_',
2154 'M','A','C','H','I','N','E','\\',0};
2155 static const WCHAR szHU[] =
2156 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2157
2158 LPSTR value_data = NULL;
2159 HKEY root_key, hkey;
2160 DWORD type,size;
2161 LPWSTR deformated;
2162 LPCWSTR szRoot, component, name, key, value;
2163 MSICOMPONENT *comp;
2164 MSIRECORD * uirow;
2165 LPWSTR uikey;
2166 INT root;
2167 BOOL check_first = FALSE;
2168 UINT rc;
2169
2170 ui_progress(package,2,0,0,0);
2171
2172 value = NULL;
2173 key = NULL;
2174 uikey = NULL;
2175 name = NULL;
2176
2177 component = MSI_RecordGetString(row, 6);
2178 comp = get_loaded_component(package,component);
2179 if (!comp)
2180 return ERROR_SUCCESS;
2181
2182 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2183 {
2184 TRACE("Skipping write due to disabled component %s\n",
2185 debugstr_w(component));
2186
2187 comp->Action = comp->Installed;
2188
2189 return ERROR_SUCCESS;
2190 }
2191
2192 comp->Action = INSTALLSTATE_LOCAL;
2193
2194 name = MSI_RecordGetString(row, 4);
2195 if( MSI_RecordIsNull(row,5) && name )
2196 {
2197 /* null values can have special meanings */
2198 if (name[0]=='-' && name[1] == 0)
2199 return ERROR_SUCCESS;
2200 else if ((name[0]=='+' && name[1] == 0) ||
2201 (name[0] == '*' && name[1] == 0))
2202 name = NULL;
2203 check_first = TRUE;
2204 }
2205
2206 root = MSI_RecordGetInteger(row,2);
2207 key = MSI_RecordGetString(row, 3);
2208
2209 /* get the root key */
2210 switch (root)
2211 {
2212 case -1:
2213 {
2214 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2215 if (all_users && all_users[0] == '1')
2216 {
2217 root_key = HKEY_LOCAL_MACHINE;
2218 szRoot = szHLM;
2219 }
2220 else
2221 {
2222 root_key = HKEY_CURRENT_USER;
2223 szRoot = szHCU;
2224 }
2225 msi_free(all_users);
2226 }
2227 break;
2228 case 0: root_key = HKEY_CLASSES_ROOT;
2229 szRoot = szHCR;
2230 break;
2231 case 1: root_key = HKEY_CURRENT_USER;
2232 szRoot = szHCU;
2233 break;
2234 case 2: root_key = HKEY_LOCAL_MACHINE;
2235 szRoot = szHLM;
2236 break;
2237 case 3: root_key = HKEY_USERS;
2238 szRoot = szHU;
2239 break;
2240 default:
2241 ERR("Unknown root %i\n",root);
2242 root_key=NULL;
2243 szRoot = NULL;
2244 break;
2245 }
2246 if (!root_key)
2247 return ERROR_SUCCESS;
2248
2249 deformat_string(package, key , &deformated);
2250 size = strlenW(deformated) + strlenW(szRoot) + 1;
2251 uikey = msi_alloc(size*sizeof(WCHAR));
2252 strcpyW(uikey,szRoot);
2253 strcatW(uikey,deformated);
2254
2255 if (RegCreateKeyW( root_key, deformated, &hkey))
2256 {
2257 ERR("Could not create key %s\n",debugstr_w(deformated));
2258 msi_free(deformated);
2259 msi_free(uikey);
2260 return ERROR_SUCCESS;
2261 }
2262 msi_free(deformated);
2263
2264 value = MSI_RecordGetString(row,5);
2265 if (value)
2266 value_data = parse_value(package, value, &type, &size);
2267 else
2268 {
2269 value_data = (LPSTR)strdupW(szEmpty);
2270 size = sizeof(szEmpty);
2271 type = REG_SZ;
2272 }
2273
2274 deformat_string(package, name, &deformated);
2275
2276 if (!check_first)
2277 {
2278 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2279 debugstr_w(uikey));
2280 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2281 }
2282 else
2283 {
2284 DWORD sz = 0;
2285 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2286 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2287 {
2288 TRACE("value %s of %s checked already exists\n",
2289 debugstr_w(deformated), debugstr_w(uikey));
2290 }
2291 else
2292 {
2293 TRACE("Checked and setting value %s of %s\n",
2294 debugstr_w(deformated), debugstr_w(uikey));
2295 if (deformated || size)
2296 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2297 }
2298 }
2299 RegCloseKey(hkey);
2300
2301 uirow = MSI_CreateRecord(3);
2302 MSI_RecordSetStringW(uirow,2,deformated);
2303 MSI_RecordSetStringW(uirow,1,uikey);
2304
2305 if (type == REG_SZ)
2306 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2307 else
2308 MSI_RecordSetStringW(uirow,3,value);
2309
2310 ui_actiondata(package,szWriteRegistryValues,uirow);
2311 msiobj_release( &uirow->hdr );
2312
2313 msi_free(value_data);
2314 msi_free(deformated);
2315 msi_free(uikey);
2316
2317 return ERROR_SUCCESS;
2318 }
2319
2320 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2321 {
2322 UINT rc;
2323 MSIQUERY * view;
2324 static const WCHAR ExecSeqQuery[] =
2325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2326 '`','R','e','g','i','s','t','r','y','`',0 };
2327
2328 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2329 if (rc != ERROR_SUCCESS)
2330 return ERROR_SUCCESS;
2331
2332 /* increment progress bar each time action data is sent */
2333 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2334
2335 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2336
2337 msiobj_release(&view->hdr);
2338 return rc;
2339 }
2340
2341 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2342 {
2343 package->script->CurrentlyScripting = TRUE;
2344
2345 return ERROR_SUCCESS;
2346 }
2347
2348
2349 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2350 {
2351 MSICOMPONENT *comp;
2352 DWORD progress = 0;
2353 DWORD total = 0;
2354 static const WCHAR q1[]=
2355 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2356 '`','R','e','g','i','s','t','r','y','`',0};
2357 UINT rc;
2358 MSIQUERY * view;
2359 MSIFEATURE *feature;
2360 MSIFILE *file;
2361
2362 TRACE("InstallValidate\n");
2363
2364 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2365 if (rc == ERROR_SUCCESS)
2366 {
2367 MSI_IterateRecords( view, &progress, NULL, package );
2368 msiobj_release( &view->hdr );
2369 total += progress * REG_PROGRESS_VALUE;
2370 }
2371
2372 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2373 total += COMPONENT_PROGRESS_VALUE;
2374
2375 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2376 total += file->FileSize;
2377
2378 ui_progress(package,0,total,0,0);
2379
2380 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2381 {
2382 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2383 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2384 feature->ActionRequest);
2385 }
2386
2387 return ERROR_SUCCESS;
2388 }
2389
2390 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2391 {
2392 MSIPACKAGE* package = param;
2393 LPCWSTR cond = NULL;
2394 LPCWSTR message = NULL;
2395 UINT r;
2396
2397 static const WCHAR title[]=
2398 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2399
2400 cond = MSI_RecordGetString(row,1);
2401
2402 r = MSI_EvaluateConditionW(package,cond);
2403 if (r == MSICONDITION_FALSE)
2404 {
2405 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2406 {
2407 LPWSTR deformated;
2408 message = MSI_RecordGetString(row,2);
2409 deformat_string(package,message,&deformated);
2410 MessageBoxW(NULL,deformated,title,MB_OK);
2411 msi_free(deformated);
2412 }
2413
2414 return ERROR_INSTALL_FAILURE;
2415 }
2416
2417 return ERROR_SUCCESS;
2418 }
2419
2420 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2421 {
2422 UINT rc;
2423 MSIQUERY * view = NULL;
2424 static const WCHAR ExecSeqQuery[] =
2425 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2426 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2427
2428 TRACE("Checking launch conditions\n");
2429
2430 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2431 if (rc != ERROR_SUCCESS)
2432 return ERROR_SUCCESS;
2433
2434 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2435 msiobj_release(&view->hdr);
2436
2437 return rc;
2438 }
2439
2440 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2441 {
2442
2443 if (!cmp->KeyPath)
2444 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2445
2446 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2447 {
2448 MSIRECORD * row = 0;
2449 UINT root,len;
2450 LPWSTR deformated,buffer,deformated_name;
2451 LPCWSTR key,name;
2452 static const WCHAR ExecSeqQuery[] =
2453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2454 '`','R','e','g','i','s','t','r','y','`',' ',
2455 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2456 ' ','=',' ' ,'\'','%','s','\'',0 };
2457 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2458 static const WCHAR fmt2[]=
2459 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2460
2461 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2462 if (!row)
2463 return NULL;
2464
2465 root = MSI_RecordGetInteger(row,2);
2466 key = MSI_RecordGetString(row, 3);
2467 name = MSI_RecordGetString(row, 4);
2468 deformat_string(package, key , &deformated);
2469 deformat_string(package, name, &deformated_name);
2470
2471 len = strlenW(deformated) + 6;
2472 if (deformated_name)
2473 len+=strlenW(deformated_name);
2474
2475 buffer = msi_alloc( len *sizeof(WCHAR));
2476
2477 if (deformated_name)
2478 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2479 else
2480 sprintfW(buffer,fmt,root,deformated);
2481
2482 msi_free(deformated);
2483 msi_free(deformated_name);
2484 msiobj_release(&row->hdr);
2485
2486 return buffer;
2487 }
2488 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2489 {
2490 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2491 return NULL;
2492 }
2493 else
2494 {
2495 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2496
2497 if (file)
2498 return strdupW( file->TargetPath );
2499 }
2500 return NULL;
2501 }
2502
2503 static HKEY openSharedDLLsKey(void)
2504 {
2505 HKEY hkey=0;
2506 static const WCHAR path[] =
2507 {'S','o','f','t','w','a','r','e','\\',
2508 'M','i','c','r','o','s','o','f','t','\\',
2509 'W','i','n','d','o','w','s','\\',
2510 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2511 'S','h','a','r','e','d','D','L','L','s',0};
2512
2513 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2514 return hkey;
2515 }
2516
2517 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2518 {
2519 HKEY hkey;
2520 DWORD count=0;
2521 DWORD type;
2522 DWORD sz = sizeof(count);
2523 DWORD rc;
2524
2525 hkey = openSharedDLLsKey();
2526 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2527 if (rc != ERROR_SUCCESS)
2528 count = 0;
2529 RegCloseKey(hkey);
2530 return count;
2531 }
2532
2533 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2534 {
2535 HKEY hkey;
2536
2537 hkey = openSharedDLLsKey();
2538 if (count > 0)
2539 msi_reg_set_val_dword( hkey, path, count );
2540 else
2541 RegDeleteValueW(hkey,path);
2542 RegCloseKey(hkey);
2543 return count;
2544 }
2545
2546 /*
2547 * Return TRUE if the count should be written out and FALSE if not
2548 */
2549 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2550 {
2551 MSIFEATURE *feature;
2552 INT count = 0;
2553 BOOL write = FALSE;
2554
2555 /* only refcount DLLs */
2556 if (comp->KeyPath == NULL ||
2557 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2558 comp->Attributes & msidbComponentAttributesODBCDataSource)
2559 write = FALSE;
2560 else
2561 {
2562 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2563 write = (count > 0);
2564
2565 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2566 write = TRUE;
2567 }
2568
2569 /* increment counts */
2570 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2571 {
2572 ComponentList *cl;
2573
2574 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2575 continue;
2576
2577 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2578 {
2579 if ( cl->component == comp )
2580 count++;
2581 }
2582 }
2583
2584 /* decrement counts */
2585 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2586 {
2587 ComponentList *cl;
2588
2589 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2590 continue;
2591
2592 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2593 {
2594 if ( cl->component == comp )
2595 count--;
2596 }
2597 }
2598
2599 /* ref count all the files in the component */
2600 if (write)
2601 {
2602 MSIFILE *file;
2603
2604 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2605 {
2606 if (file->Component == comp)
2607 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2608 }
2609 }
2610
2611 /* add a count for permanent */
2612 if (comp->Attributes & msidbComponentAttributesPermanent)
2613 count ++;
2614
2615 comp->RefCount = count;
2616
2617 if (write)
2618 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2619 }
2620
2621 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2622 {
2623 WCHAR squished_pc[GUID_SIZE];
2624 WCHAR squished_cc[GUID_SIZE];
2625 UINT rc;
2626 MSICOMPONENT *comp;
2627 HKEY hkey;
2628
2629 TRACE("\n");
2630
2631 squash_guid(package->ProductCode,squished_pc);
2632 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2633
2634 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2635 {
2636 MSIRECORD * uirow;
2637
2638 ui_progress(package,2,0,0,0);
2639 if (!comp->ComponentId)
2640 continue;
2641
2642 squash_guid(comp->ComponentId,squished_cc);
2643
2644 msi_free(comp->FullKeypath);
2645 comp->FullKeypath = resolve_keypath( package, comp );
2646
2647 ACTION_RefCountComponent( package, comp );
2648
2649 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2650 debugstr_w(comp->Component),
2651 debugstr_w(squished_cc),
2652 debugstr_w(comp->FullKeypath),
2653 comp->RefCount);
2654
2655 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2656 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2657 {
2658 if (!comp->FullKeypath)
2659 continue;
2660
2661 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2662 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2663 &hkey, TRUE);
2664 else
2665 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2666 &hkey, TRUE);
2667
2668 if (rc != ERROR_SUCCESS)
2669 continue;
2670
2671 if (comp->Attributes & msidbComponentAttributesPermanent)
2672 {
2673 static const WCHAR szPermKey[] =
2674 { '0','0','0','0','0','0','0','0','0','0','0','0',
2675 '0','0','0','0','0','0','0','0','0','0','0','0',
2676 '0','0','0','0','0','0','0','0',0 };
2677
2678 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2679 }
2680
2681 if (comp->Action == INSTALLSTATE_LOCAL)
2682 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2683 else
2684 {
2685 MSIFILE *file;
2686 MSIRECORD *row;
2687 LPWSTR ptr, ptr2;
2688 WCHAR source[MAX_PATH];
2689 WCHAR base[MAX_PATH];
2690 LPWSTR sourcepath;
2691
2692 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2693 static const WCHAR query[] = {
2694 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2695 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2696 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2697 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2698 '`','D','i','s','k','I','d','`',0};
2699
2700 file = get_loaded_file(package, comp->KeyPath);
2701 if (!file)
2702 continue;
2703
2704 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2705 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2706 ptr2 = strrchrW(source, '\\') + 1;
2707 msiobj_release(&row->hdr);
2708
2709 lstrcpyW(base, package->PackagePath);
2710 ptr = strrchrW(base, '\\');
2711 *(ptr + 1) = '\0';
2712
2713 sourcepath = resolve_file_source(package, file);
2714 ptr = sourcepath + lstrlenW(base);
2715 lstrcpyW(ptr2, ptr);
2716 msi_free(sourcepath);
2717
2718 msi_reg_set_val_str(hkey, squished_pc, source);
2719 }
2720 RegCloseKey(hkey);
2721 }
2722 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2723 {
2724 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2725 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2726 else
2727 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2728 }
2729
2730 /* UI stuff */
2731 uirow = MSI_CreateRecord(3);
2732 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2733 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2734 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2735 ui_actiondata(package,szProcessComponents,uirow);
2736 msiobj_release( &uirow->hdr );
2737 }
2738
2739 return ERROR_SUCCESS;
2740 }
2741
2742 typedef struct {
2743 CLSID clsid;