ca8588f45296aaa9a888cbdc97eec7ff3b55ac95
[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, component;
890 LPWSTR full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
893 MSICOMPONENT *comp;
894
895 component = MSI_RecordGetString(row, 2);
896 comp = get_loaded_component(package, component);
897 if (!comp)
898 return ERROR_SUCCESS;
899
900 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
901 {
902 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
903 comp->Action = comp->Installed;
904 return ERROR_SUCCESS;
905 }
906 comp->Action = INSTALLSTATE_LOCAL;
907
908 dir = MSI_RecordGetString(row,1);
909 if (!dir)
910 {
911 ERR("Unable to get folder id\n");
912 return ERROR_SUCCESS;
913 }
914
915 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
916 if (!full_path)
917 {
918 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
919 return ERROR_SUCCESS;
920 }
921
922 TRACE("Folder is %s\n",debugstr_w(full_path));
923
924 /* UI stuff */
925 uirow = MSI_CreateRecord(1);
926 MSI_RecordSetStringW(uirow,1,full_path);
927 ui_actiondata(package,szCreateFolders,uirow);
928 msiobj_release( &uirow->hdr );
929
930 if (folder->State == 0)
931 create_full_pathW(full_path);
932
933 folder->State = 3;
934
935 msi_free(full_path);
936 return ERROR_SUCCESS;
937 }
938
939 /* FIXME: probably should merge this with the above function */
940 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
941 {
942 UINT rc = ERROR_SUCCESS;
943 MSIFOLDER *folder;
944 LPWSTR install_path;
945
946 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
947 if (!install_path)
948 return ERROR_FUNCTION_FAILED;
949
950 /* create the path */
951 if (folder->State == 0)
952 {
953 create_full_pathW(install_path);
954 folder->State = 2;
955 }
956 msi_free(install_path);
957
958 return rc;
959 }
960
961 UINT msi_create_component_directories( MSIPACKAGE *package )
962 {
963 MSICOMPONENT *comp;
964
965 /* create all the folders required by the components are going to install */
966 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
967 {
968 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
969 continue;
970 msi_create_directory( package, comp->Directory );
971 }
972
973 return ERROR_SUCCESS;
974 }
975
976 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
977 {
978 static const WCHAR ExecSeqQuery[] =
979 {'S','E','L','E','C','T',' ',
980 '`','D','i','r','e','c','t','o','r','y','_','`',
981 ' ','F','R','O','M',' ',
982 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
983 UINT rc;
984 MSIQUERY *view;
985
986 /* create all the empty folders specified in the CreateFolder table */
987 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
988 if (rc != ERROR_SUCCESS)
989 return ERROR_SUCCESS;
990
991 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
992 msiobj_release(&view->hdr);
993
994 return rc;
995 }
996
997 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
998 {
999 MSIPACKAGE *package = param;
1000 LPCWSTR dir, component;
1001 LPWSTR full_path;
1002 MSIRECORD *uirow;
1003 MSIFOLDER *folder;
1004 MSICOMPONENT *comp;
1005
1006 component = MSI_RecordGetString(row, 2);
1007 comp = get_loaded_component(package, component);
1008 if (!comp)
1009 return ERROR_SUCCESS;
1010
1011 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1012 {
1013 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1014 comp->Action = comp->Installed;
1015 return ERROR_SUCCESS;
1016 }
1017 comp->Action = INSTALLSTATE_ABSENT;
1018
1019 dir = MSI_RecordGetString( row, 1 );
1020 if (!dir)
1021 {
1022 ERR("Unable to get folder id\n");
1023 return ERROR_SUCCESS;
1024 }
1025
1026 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1027 if (!full_path)
1028 {
1029 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1030 return ERROR_SUCCESS;
1031 }
1032
1033 TRACE("folder is %s\n", debugstr_w(full_path));
1034
1035 uirow = MSI_CreateRecord( 1 );
1036 MSI_RecordSetStringW( uirow, 1, full_path );
1037 ui_actiondata( package, szRemoveFolders, uirow );
1038 msiobj_release( &uirow->hdr );
1039
1040 RemoveDirectoryW( full_path );
1041 folder->State = 0;
1042
1043 msi_free( full_path );
1044 return ERROR_SUCCESS;
1045 }
1046
1047 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1048 {
1049 static const WCHAR query[] =
1050 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1051 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1052
1053 MSIQUERY *view;
1054 UINT rc;
1055
1056 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1057 if (rc != ERROR_SUCCESS)
1058 return ERROR_SUCCESS;
1059
1060 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1061 msiobj_release( &view->hdr );
1062
1063 return rc;
1064 }
1065
1066 static UINT load_component( MSIRECORD *row, LPVOID param )
1067 {
1068 MSIPACKAGE *package = param;
1069 MSICOMPONENT *comp;
1070
1071 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1072 if (!comp)
1073 return ERROR_FUNCTION_FAILED;
1074
1075 list_add_tail( &package->components, &comp->entry );
1076
1077 /* fill in the data */
1078 comp->Component = msi_dup_record_field( row, 1 );
1079
1080 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1081
1082 comp->ComponentId = msi_dup_record_field( row, 2 );
1083 comp->Directory = msi_dup_record_field( row, 3 );
1084 comp->Attributes = MSI_RecordGetInteger(row,4);
1085 comp->Condition = msi_dup_record_field( row, 5 );
1086 comp->KeyPath = msi_dup_record_field( row, 6 );
1087
1088 comp->Installed = INSTALLSTATE_UNKNOWN;
1089 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1090
1091 return ERROR_SUCCESS;
1092 }
1093
1094 static UINT load_all_components( MSIPACKAGE *package )
1095 {
1096 static const WCHAR query[] = {
1097 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1098 '`','C','o','m','p','o','n','e','n','t','`',0 };
1099 MSIQUERY *view;
1100 UINT r;
1101
1102 if (!list_empty(&package->components))
1103 return ERROR_SUCCESS;
1104
1105 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1106 if (r != ERROR_SUCCESS)
1107 return r;
1108
1109 r = MSI_IterateRecords(view, NULL, load_component, package);
1110 msiobj_release(&view->hdr);
1111 return r;
1112 }
1113
1114 typedef struct {
1115 MSIPACKAGE *package;
1116 MSIFEATURE *feature;
1117 } _ilfs;
1118
1119 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1120 {
1121 ComponentList *cl;
1122
1123 cl = msi_alloc( sizeof (*cl) );
1124 if ( !cl )
1125 return ERROR_NOT_ENOUGH_MEMORY;
1126 cl->component = comp;
1127 list_add_tail( &feature->Components, &cl->entry );
1128
1129 return ERROR_SUCCESS;
1130 }
1131
1132 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1133 {
1134 FeatureList *fl;
1135
1136 fl = msi_alloc( sizeof(*fl) );
1137 if ( !fl )
1138 return ERROR_NOT_ENOUGH_MEMORY;
1139 fl->feature = child;
1140 list_add_tail( &parent->Children, &fl->entry );
1141
1142 return ERROR_SUCCESS;
1143 }
1144
1145 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1146 {
1147 _ilfs* ilfs = param;
1148 LPCWSTR component;
1149 MSICOMPONENT *comp;
1150
1151 component = MSI_RecordGetString(row,1);
1152
1153 /* check to see if the component is already loaded */
1154 comp = get_loaded_component( ilfs->package, component );
1155 if (!comp)
1156 {
1157 ERR("unknown component %s\n", debugstr_w(component));
1158 return ERROR_FUNCTION_FAILED;
1159 }
1160
1161 add_feature_component( ilfs->feature, comp );
1162 comp->Enabled = TRUE;
1163
1164 return ERROR_SUCCESS;
1165 }
1166
1167 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1168 {
1169 MSIFEATURE *feature;
1170
1171 if ( !name )
1172 return NULL;
1173
1174 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1175 {
1176 if ( !lstrcmpW( feature->Feature, name ) )
1177 return feature;
1178 }
1179
1180 return NULL;
1181 }
1182
1183 static UINT load_feature(MSIRECORD * row, LPVOID param)
1184 {
1185 MSIPACKAGE* package = param;
1186 MSIFEATURE* feature;
1187 static const WCHAR Query1[] =
1188 {'S','E','L','E','C','T',' ',
1189 '`','C','o','m','p','o','n','e','n','t','_','`',
1190 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1191 'C','o','m','p','o','n','e','n','t','s','`',' ',
1192 'W','H','E','R','E',' ',
1193 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1194 MSIQUERY * view;
1195 UINT rc;
1196 _ilfs ilfs;
1197
1198 /* fill in the data */
1199
1200 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1201 if (!feature)
1202 return ERROR_NOT_ENOUGH_MEMORY;
1203
1204 list_init( &feature->Children );
1205 list_init( &feature->Components );
1206
1207 feature->Feature = msi_dup_record_field( row, 1 );
1208
1209 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1210
1211 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1212 feature->Title = msi_dup_record_field( row, 3 );
1213 feature->Description = msi_dup_record_field( row, 4 );
1214
1215 if (!MSI_RecordIsNull(row,5))
1216 feature->Display = MSI_RecordGetInteger(row,5);
1217
1218 feature->Level= MSI_RecordGetInteger(row,6);
1219 feature->Directory = msi_dup_record_field( row, 7 );
1220 feature->Attributes = MSI_RecordGetInteger(row,8);
1221
1222 feature->Installed = INSTALLSTATE_UNKNOWN;
1223 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1224
1225 list_add_tail( &package->features, &feature->entry );
1226
1227 /* load feature components */
1228
1229 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1230 if (rc != ERROR_SUCCESS)
1231 return ERROR_SUCCESS;
1232
1233 ilfs.package = package;
1234 ilfs.feature = feature;
1235
1236 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1237 msiobj_release(&view->hdr);
1238
1239 return ERROR_SUCCESS;
1240 }
1241
1242 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1243 {
1244 MSIPACKAGE* package = param;
1245 MSIFEATURE *parent, *child;
1246
1247 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1248 if (!child)
1249 return ERROR_FUNCTION_FAILED;
1250
1251 if (!child->Feature_Parent)
1252 return ERROR_SUCCESS;
1253
1254 parent = find_feature_by_name( package, child->Feature_Parent );
1255 if (!parent)
1256 return ERROR_FUNCTION_FAILED;
1257
1258 add_feature_child( parent, child );
1259 return ERROR_SUCCESS;
1260 }
1261
1262 static UINT load_all_features( MSIPACKAGE *package )
1263 {
1264 static const WCHAR query[] = {
1265 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1266 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1267 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1268 MSIQUERY *view;
1269 UINT r;
1270
1271 if (!list_empty(&package->features))
1272 return ERROR_SUCCESS;
1273
1274 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1275 if (r != ERROR_SUCCESS)
1276 return r;
1277
1278 r = MSI_IterateRecords( view, NULL, load_feature, package );
1279 if (r != ERROR_SUCCESS)
1280 return r;
1281
1282 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1283 msiobj_release( &view->hdr );
1284
1285 return r;
1286 }
1287
1288 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1289 {
1290 if (!p)
1291 return p;
1292 p = strchrW(p, ch);
1293 if (!p)
1294 return p;
1295 *p = 0;
1296 return p+1;
1297 }
1298
1299 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1300 {
1301 static const WCHAR query[] = {
1302 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1303 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1304 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1305 MSIQUERY *view = NULL;
1306 MSIRECORD *row = NULL;
1307 UINT r;
1308
1309 TRACE("%s\n", debugstr_w(file->File));
1310
1311 r = MSI_OpenQuery(package->db, &view, query, file->File);
1312 if (r != ERROR_SUCCESS)
1313 goto done;
1314
1315 r = MSI_ViewExecute(view, NULL);
1316 if (r != ERROR_SUCCESS)
1317 goto done;
1318
1319 r = MSI_ViewFetch(view, &row);
1320 if (r != ERROR_SUCCESS)
1321 goto done;
1322
1323 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1324 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1325 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1326 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1327 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1328
1329 done:
1330 if (view) msiobj_release(&view->hdr);
1331 if (row) msiobj_release(&row->hdr);
1332 return r;
1333 }
1334
1335 static UINT load_file(MSIRECORD *row, LPVOID param)
1336 {
1337 MSIPACKAGE* package = param;
1338 LPCWSTR component;
1339 MSIFILE *file;
1340
1341 /* fill in the data */
1342
1343 file = msi_alloc_zero( sizeof (MSIFILE) );
1344 if (!file)
1345 return ERROR_NOT_ENOUGH_MEMORY;
1346
1347 file->File = msi_dup_record_field( row, 1 );
1348
1349 component = MSI_RecordGetString( row, 2 );
1350 file->Component = get_loaded_component( package, component );
1351
1352 if (!file->Component)
1353 {
1354 WARN("Component not found: %s\n", debugstr_w(component));
1355 msi_free(file->File);
1356 msi_free(file);
1357 return ERROR_SUCCESS;
1358 }
1359
1360 file->FileName = msi_dup_record_field( row, 3 );
1361 reduce_to_longfilename( file->FileName );
1362
1363 file->ShortName = msi_dup_record_field( row, 3 );
1364 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1365
1366 file->FileSize = MSI_RecordGetInteger( row, 4 );
1367 file->Version = msi_dup_record_field( row, 5 );
1368 file->Language = msi_dup_record_field( row, 6 );
1369 file->Attributes = MSI_RecordGetInteger( row, 7 );
1370 file->Sequence = MSI_RecordGetInteger( row, 8 );
1371
1372 file->state = msifs_invalid;
1373
1374 /* if the compressed bits are not set in the file attributes,
1375 * then read the information from the package word count property
1376 */
1377 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1378 {
1379 file->IsCompressed = FALSE;
1380 }
1381 else if (file->Attributes &
1382 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1383 {
1384 file->IsCompressed = TRUE;
1385 }
1386 else if (file->Attributes & msidbFileAttributesNoncompressed)
1387 {
1388 file->IsCompressed = FALSE;
1389 }
1390 else
1391 {
1392 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1393 }
1394
1395 load_file_hash(package, file);
1396
1397 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1398
1399 list_add_tail( &package->files, &file->entry );
1400
1401 return ERROR_SUCCESS;
1402 }
1403
1404 static UINT load_all_files(MSIPACKAGE *package)
1405 {
1406 MSIQUERY * view;
1407 UINT rc;
1408 static const WCHAR Query[] =
1409 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1410 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1411 '`','S','e','q','u','e','n','c','e','`', 0};
1412
1413 if (!list_empty(&package->files))
1414 return ERROR_SUCCESS;
1415
1416 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1417 if (rc != ERROR_SUCCESS)
1418 return ERROR_SUCCESS;
1419
1420 rc = MSI_IterateRecords(view, NULL, load_file, package);
1421 msiobj_release(&view->hdr);
1422
1423 return ERROR_SUCCESS;
1424 }
1425
1426 static UINT load_folder( MSIRECORD *row, LPVOID param )
1427 {
1428 MSIPACKAGE *package = param;
1429 static WCHAR szEmpty[] = { 0 };
1430 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1431 MSIFOLDER *folder;
1432
1433 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1434 if (!folder)
1435 return ERROR_NOT_ENOUGH_MEMORY;
1436
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438
1439 TRACE("%s\n", debugstr_w(folder->Directory));
1440
1441 p = msi_dup_record_field(row, 3);
1442
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1446
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1450
1451 /* check for no-op dirs */
1452 if (!lstrcmpW(szDot, tgt_short))
1453 tgt_short = szEmpty;
1454 if (!lstrcmpW(szDot, src_short))
1455 src_short = szEmpty;
1456
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1459
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1463 }
1464
1465 if (!src_long)
1466 src_long = src_short;
1467
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1473
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1477
1478 folder->Parent = msi_dup_record_field( row, 2 );
1479
1480 folder->Property = msi_dup_property( package, folder->Directory );
1481
1482 list_add_tail( &package->folders, &folder->entry );
1483
1484 TRACE("returning %p\n", folder);
1485
1486 return ERROR_SUCCESS;
1487 }
1488
1489 static UINT load_all_folders( MSIPACKAGE *package )
1490 {
1491 static const WCHAR query[] = {
1492 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1493 '`','D','i','r','e','c','t','o','r','y','`',0 };
1494 MSIQUERY *view;
1495 UINT r;
1496
1497 if (!list_empty(&package->folders))
1498 return ERROR_SUCCESS;
1499
1500 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1501 if (r != ERROR_SUCCESS)
1502 return r;
1503
1504 r = MSI_IterateRecords(view, NULL, load_folder, package);
1505 msiobj_release(&view->hdr);
1506 return r;
1507 }
1508
1509 /*
1510 * I am not doing any of the costing functionality yet.
1511 * Mostly looking at doing the Component and Feature loading
1512 *
1513 * The native MSI does A LOT of modification to tables here. Mostly adding
1514 * a lot of temporary columns to the Feature and Component tables.
1515 *
1516 * note: Native msi also tracks the short filename. But I am only going to
1517 * track the long ones. Also looking at this directory table
1518 * it appears that the directory table does not get the parents
1519 * resolved base on property only based on their entries in the
1520 * directory table.
1521 */
1522 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1523 {
1524 static const WCHAR szCosting[] =
1525 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1526
1527 MSI_SetPropertyW(package, szCosting, szZero);
1528 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1529
1530 load_all_folders( package );
1531 load_all_components( package );
1532 load_all_features( package );
1533 load_all_files( package );
1534
1535 return ERROR_SUCCESS;
1536 }
1537
1538 static UINT execute_script(MSIPACKAGE *package, UINT script )
1539 {
1540 UINT i;
1541 UINT rc = ERROR_SUCCESS;
1542
1543 TRACE("Executing Script %i\n",script);
1544
1545 if (!package->script)
1546 {
1547 ERR("no script!\n");
1548 return ERROR_FUNCTION_FAILED;
1549 }
1550
1551 for (i = 0; i < package->script->ActionCount[script]; i++)
1552 {
1553 LPWSTR action;
1554 action = package->script->Actions[script][i];
1555 ui_actionstart(package, action);
1556 TRACE("Executing Action (%s)\n",debugstr_w(action));
1557 rc = ACTION_PerformAction(package, action, script, TRUE);
1558 if (rc != ERROR_SUCCESS)
1559 break;
1560 }
1561 msi_free_action_script(package, script);
1562 return rc;
1563 }
1564
1565 static UINT ACTION_FileCost(MSIPACKAGE *package)
1566 {
1567 return ERROR_SUCCESS;
1568 }
1569
1570 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1571 {
1572 MSICOMPONENT *comp;
1573 INSTALLSTATE state;
1574 UINT r;
1575
1576 state = MsiQueryProductStateW(package->ProductCode);
1577
1578 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1579 {
1580 if (!comp->ComponentId)
1581 continue;
1582
1583 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1584 comp->Installed = INSTALLSTATE_ABSENT;
1585 else
1586 {
1587 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1588 package->Context, comp->ComponentId,
1589 &comp->Installed);
1590 if (r != ERROR_SUCCESS)
1591 comp->Installed = INSTALLSTATE_ABSENT;
1592 }
1593 }
1594 }
1595
1596 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1597 {
1598 MSIFEATURE *feature;
1599 INSTALLSTATE state;
1600
1601 state = MsiQueryProductStateW(package->ProductCode);
1602
1603 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1604 {
1605 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1606 feature->Installed = INSTALLSTATE_ABSENT;
1607 else
1608 {
1609 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1610 feature->Feature);
1611 }
1612 }
1613 }
1614
1615 static BOOL process_state_property(MSIPACKAGE* package, int level,
1616 LPCWSTR property, INSTALLSTATE state)
1617 {
1618 LPWSTR override;
1619 MSIFEATURE *feature;
1620
1621 override = msi_dup_property( package, property );
1622 if (!override)
1623 return FALSE;
1624
1625 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1626 {
1627 if (lstrcmpW(property, szRemove) &&
1628 (feature->Level <= 0 || feature->Level > level))
1629 continue;
1630
1631 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1632
1633 if (strcmpiW(override, szAll)==0)
1634 msi_feature_set_state(package, feature, state);
1635 else
1636 {
1637 LPWSTR ptr = override;
1638 LPWSTR ptr2 = strchrW(override,',');
1639
1640 while (ptr)
1641 {
1642 int len = ptr2 - ptr;
1643
1644 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1645 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1646 {
1647 msi_feature_set_state(package, feature, state);
1648 break;
1649 }
1650 if (ptr2)
1651 {
1652 ptr=ptr2+1;
1653 ptr2 = strchrW(ptr,',');
1654 }
1655 else
1656 break;
1657 }
1658 }
1659 }
1660 msi_free(override);
1661
1662 return TRUE;
1663 }
1664
1665 static BOOL process_overrides( MSIPACKAGE *package, int level )
1666 {
1667 static const WCHAR szAddLocal[] =
1668 {'A','D','D','L','O','C','A','L',0};
1669 static const WCHAR szAddSource[] =
1670 {'A','D','D','S','O','U','R','C','E',0};
1671 static const WCHAR szAdvertise[] =
1672 {'A','D','V','E','R','T','I','S','E',0};
1673 BOOL ret = FALSE;
1674
1675 /* all these activation/deactivation things happen in order and things
1676 * later on the list override things earlier on the list.
1677 *
1678 * 0 INSTALLLEVEL processing
1679 * 1 ADDLOCAL
1680 * 2 REMOVE
1681 * 3 ADDSOURCE
1682 * 4 ADDDEFAULT
1683 * 5 REINSTALL
1684 * 6 ADVERTISE
1685 * 7 COMPADDLOCAL
1686 * 8 COMPADDSOURCE
1687 * 9 FILEADDLOCAL
1688 * 10 FILEADDSOURCE
1689 * 11 FILEADDDEFAULT
1690 */
1691 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1692 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1693 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1694 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1695 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1696
1697 if (ret)
1698 MSI_SetPropertyW( package, szPreselected, szOne );
1699
1700 return ret;
1701 }
1702
1703 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1704 {
1705 int level;
1706 static const WCHAR szlevel[] =
1707 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1708 MSICOMPONENT* component;
1709 MSIFEATURE *feature;
1710
1711 TRACE("Checking Install Level\n");
1712
1713 level = msi_get_property_int(package, szlevel, 1);
1714
1715 if (!msi_get_property_int( package, szPreselected, 0 ))
1716 {
1717 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1718 {
1719 BOOL feature_state = ((feature->Level > 0) &&
1720 (feature->Level <= level));
1721
1722 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1723 {
1724 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1725 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1726 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1727 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1728 else
1729 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1730 }
1731 }
1732
1733 /* disable child features of unselected parent features */
1734 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1735 {
1736 FeatureList *fl;
1737
1738 if (feature->Level > 0 && feature->Level <= level)
1739 continue;
1740
1741 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1742 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1743 }
1744 }
1745
1746 /*
1747 * now we want to enable or disable components base on feature
1748 */
1749
1750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1751 {
1752 ComponentList *cl;
1753
1754 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1755 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1756
1757 if (!feature->Level)
1758 continue;
1759
1760 /* features with components that have compressed files are made local */
1761 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1762 {
1763 if (cl->component->Enabled &&
1764 cl->component->ForceLocalState &&
1765 feature->Action == INSTALLSTATE_SOURCE)
1766 {
1767 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1768 break;
1769 }
1770 }
1771
1772 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1773 {
1774 component = cl->component;
1775
1776 if (!component->Enabled)
1777 continue;
1778
1779 switch (feature->Action)
1780 {
1781 case INSTALLSTATE_ABSENT:
1782 component->anyAbsent = 1;
1783 break;
1784 case INSTALLSTATE_ADVERTISED:
1785 component->hasAdvertiseFeature = 1;
1786 break;
1787 case INSTALLSTATE_SOURCE:
1788 component->hasSourceFeature = 1;
1789 break;
1790 case INSTALLSTATE_LOCAL:
1791 component->hasLocalFeature = 1;
1792 break;
1793 case INSTALLSTATE_DEFAULT:
1794 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1795 component->hasAdvertiseFeature = 1;
1796 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1797 component->hasSourceFeature = 1;
1798 else
1799 component->hasLocalFeature = 1;
1800 break;
1801 default:
1802 break;
1803 }
1804 }
1805 }
1806
1807 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1808 {
1809 /* if the component isn't enabled, leave it alone */
1810 if (!component->Enabled)
1811 continue;
1812
1813 /* check if it's local or source */
1814 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1815 (component->hasLocalFeature || component->hasSourceFeature))
1816 {
1817 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1818 !component->ForceLocalState)
1819 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1820 else
1821 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1822 continue;
1823 }
1824
1825 /* if any feature is local, the component must be local too */
1826 if (component->hasLocalFeature)
1827 {
1828 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1829 continue;
1830 }
1831
1832 if (component->hasSourceFeature)
1833 {
1834 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1835 continue;
1836 }
1837
1838 if (component->hasAdvertiseFeature)
1839 {
1840 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1841 continue;
1842 }
1843
1844 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1845 if (component->anyAbsent)
1846 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1847 }
1848
1849 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1850 {
1851 if (component->Action == INSTALLSTATE_DEFAULT)
1852 {
1853 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1854 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1855 }
1856
1857 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1858 debugstr_w(component->Component), component->Installed, component->Action);
1859 }
1860
1861
1862 return ERROR_SUCCESS;
1863 }
1864
1865 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1866 {
1867 MSIPACKAGE *package = param;
1868 LPCWSTR name;
1869 LPWSTR path;
1870 MSIFOLDER *f;
1871
1872 name = MSI_RecordGetString(row,1);
1873
1874 f = get_loaded_folder(package, name);
1875 if (!f) return ERROR_SUCCESS;
1876
1877 /* reset the ResolvedTarget */
1878 msi_free(f->ResolvedTarget);
1879 f->ResolvedTarget = NULL;
1880
1881 /* This helper function now does ALL the work */
1882 TRACE("Dir %s ...\n",debugstr_w(name));
1883 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1884 TRACE("resolves to %s\n",debugstr_w(path));
1885 msi_free(path);
1886
1887 return ERROR_SUCCESS;
1888 }
1889
1890 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1891 {
1892 MSIPACKAGE *package = param;
1893 LPCWSTR name;
1894 MSIFEATURE *feature;
1895
1896 name = MSI_RecordGetString( row, 1 );
1897
1898 feature = get_loaded_feature( package, name );
1899 if (!feature)
1900 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1901 else
1902 {
1903 LPCWSTR Condition;
1904 Condition = MSI_RecordGetString(row,3);
1905
1906 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1907 {
1908 int level = MSI_RecordGetInteger(row,2);
1909 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1910 feature->Level = level;
1911 }
1912 }
1913 return ERROR_SUCCESS;
1914 }
1915
1916 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1917 {
1918 static const WCHAR name_fmt[] =
1919 {'%','u','.','%','u','.','%','u','.','%','u',0};
1920 static const WCHAR name[] = {'\\',0};
1921 VS_FIXEDFILEINFO *lpVer;
1922 WCHAR filever[0x100];
1923 LPVOID version;
1924 DWORD versize;
1925 DWORD handle;
1926 UINT sz;
1927
1928 TRACE("%s\n", debugstr_w(filename));
1929
1930 versize = GetFileVersionInfoSizeW( filename, &handle );
1931 if (!versize)
1932 return NULL;
1933
1934 version = msi_alloc( versize );
1935 GetFileVersionInfoW( filename, 0, versize, version );
1936
1937 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1938 {
1939 msi_free( version );
1940 return NULL;
1941 }
1942
1943 sprintfW( filever, name_fmt,
1944 HIWORD(lpVer->dwFileVersionMS),
1945 LOWORD(lpVer->dwFileVersionMS),
1946 HIWORD(lpVer->dwFileVersionLS),
1947 LOWORD(lpVer->dwFileVersionLS));
1948
1949 msi_free( version );
1950
1951 return strdupW( filever );
1952 }
1953
1954 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1955 {
1956 LPWSTR file_version;
1957 MSIFILE *file;
1958
1959 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1960 {
1961 MSICOMPONENT* comp = file->Component;
1962 LPWSTR p;
1963
1964 if (!comp)
1965 continue;
1966
1967 if (file->IsCompressed)
1968 comp->ForceLocalState = TRUE;
1969
1970 /* calculate target */
1971 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1972
1973 msi_free(file->TargetPath);
1974
1975 TRACE("file %s is named %s\n",
1976 debugstr_w(file->File), debugstr_w(file->FileName));
1977
1978 file->TargetPath = build_directory_name(2, p, file->FileName);
1979
1980 msi_free(p);
1981
1982 TRACE("file %s resolves to %s\n",
1983 debugstr_w(file->File), debugstr_w(file->TargetPath));
1984
1985 /* don't check files of components that aren't installed */
1986 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1987 comp->Installed == INSTALLSTATE_ABSENT)
1988 {
1989 file->state = msifs_missing; /* assume files are missing */
1990 continue;
1991 }
1992
1993 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1994 {
1995 file->state = msifs_missing;
1996 comp->Cost += file->FileSize;
1997 continue;
1998 }
1999
2000 if (file->Version &&
2001 (file_version = msi_get_disk_file_version( file->TargetPath )))
2002 {
2003 TRACE("new %s old %s\n", debugstr_w(file->Version),
2004 debugstr_w(file_version));
2005 /* FIXME: seems like a bad way to compare version numbers */
2006 if (lstrcmpiW(file_version, file->Version)<0)
2007 {
2008 file->state = msifs_overwrite;
2009 comp->Cost += file->FileSize;
2010 }
2011 else
2012 file->state = msifs_present;
2013 msi_free( file_version );
2014 }
2015 else
2016 file->state = msifs_present;
2017 }
2018
2019 return ERROR_SUCCESS;
2020 }
2021
2022 /*
2023 * A lot is done in this function aside from just the costing.
2024 * The costing needs to be implemented at some point but for now I am going
2025 * to focus on the directory building
2026 *
2027 */
2028 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2029 {
2030 static const WCHAR ExecSeqQuery[] =
2031 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2032 '`','D','i','r','e','c','t','o','r','y','`',0};
2033 static const WCHAR ConditionQuery[] =
2034 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2035 '`','C','o','n','d','i','t','i','o','n','`',0};
2036 static const WCHAR szCosting[] =
2037 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2038 static const WCHAR szlevel[] =
2039 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2040 static const WCHAR szOutOfDiskSpace[] =
2041 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2042 MSICOMPONENT *comp;
2043 UINT rc = ERROR_SUCCESS;
2044 MSIQUERY * view;
2045 LPWSTR level;
2046
2047 TRACE("Building Directory properties\n");
2048
2049 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2050 if (rc == ERROR_SUCCESS)
2051 {
2052 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2053 package);
2054 msiobj_release(&view->hdr);
2055 }
2056
2057 /* read components states from the registry */
2058 ACTION_GetComponentInstallStates(package);
2059 ACTION_GetFeatureInstallStates(package);
2060
2061 TRACE("File calculations\n");
2062 msi_check_file_install_states( package );
2063
2064 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2065 {
2066 TRACE("Evaluating Condition Table\n");
2067
2068 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2069 if (rc == ERROR_SUCCESS)
2070 {
2071 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2072 msiobj_release( &view->hdr );
2073 }
2074
2075 TRACE("Enabling or Disabling Components\n");
2076 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2077 {
2078 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2079 {
2080 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2081 comp->Enabled = FALSE;
2082 }
2083 else
2084 comp->Enabled = TRUE;
2085 }
2086 }
2087
2088 MSI_SetPropertyW(package,szCosting,szOne);
2089 /* set default run level if not set */
2090 level = msi_dup_property( package, szlevel );
2091 if (!level)
2092 MSI_SetPropertyW(package,szlevel, szOne);
2093 msi_free(level);
2094
2095 /* FIXME: check volume disk space */
2096 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2097
2098 return MSI_SetFeatureStates(package);
2099 }
2100
2101 /* OK this value is "interpreted" and then formatted based on the
2102 first few characters */
2103 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2104 DWORD *size)
2105 {
2106 LPSTR data = NULL;
2107
2108 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2109 {
2110 if (value[1]=='x')
2111 {
2112 LPWSTR ptr;
2113 CHAR byte[5];
2114 LPWSTR deformated = NULL;
2115 int count;
2116
2117 deformat_string(package, &value[2], &deformated);
2118
2119 /* binary value type */
2120 ptr = deformated;
2121 *type = REG_BINARY;
2122 if (strlenW(ptr)%2)
2123 *size = (strlenW(ptr)/2)+1;
2124 else
2125 *size = strlenW(ptr)/2;
2126
2127 data = msi_alloc(*size);
2128
2129 byte[0] = '0';
2130 byte[1] = 'x';
2131 byte[4] = 0;
2132 count = 0;
2133 /* if uneven pad with a zero in front */
2134 if (strlenW(ptr)%2)
2135 {
2136 byte[2]= '0';
2137 byte[3]= *ptr;
2138 ptr++;
2139 data[count] = (BYTE)strtol(byte,NULL,0);
2140 count ++;
2141 TRACE("Uneven byte count\n");
2142 }
2143 while (*ptr)
2144 {
2145 byte[2]= *ptr;
2146 ptr++;
2147 byte[3]= *ptr;
2148 ptr++;
2149 data[count] = (BYTE)strtol(byte,NULL,0);
2150 count ++;
2151 }
2152 msi_free(deformated);
2153
2154 TRACE("Data %i bytes(%i)\n",*size,count);
2155 }
2156 else
2157 {
2158 LPWSTR deformated;
2159 LPWSTR p;
2160 DWORD d = 0;
2161 deformat_string(package, &value[1], &deformated);
2162
2163 *type=REG_DWORD;
2164 *size = sizeof(DWORD);
2165 data = msi_alloc(*size);
2166 p = deformated;
2167 if (*p == '-')
2168 p++;
2169 while (*p)
2170 {
2171 if ( (*p < '0') || (*p > '9') )
2172 break;
2173 d *= 10;
2174 d += (*p - '0');
2175 p++;
2176 }
2177 if (deformated[0] == '-')
2178 d = -d;
2179 *(LPDWORD)data = d;
2180 TRACE("DWORD %i\n",*(LPDWORD)data);
2181
2182 msi_free(deformated);
2183 }
2184 }
2185 else
2186 {
2187 static const WCHAR szMulti[] = {'[','~',']',0};
2188 LPCWSTR ptr;
2189 *type=REG_SZ;
2190
2191 if (value[0]=='#')
2192 {
2193 if (value[1]=='%')
2194 {
2195 ptr = &value[2];
2196 *type=REG_EXPAND_SZ;
2197 }
2198 else
2199 ptr = &value[1];
2200 }
2201 else
2202 ptr=value;
2203
2204 if (strstrW(value,szMulti))
2205 *type = REG_MULTI_SZ;
2206
2207 /* remove initial delimiter */
2208 if (!strncmpW(value, szMulti, 3))
2209 ptr = value + 3;
2210
2211 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2212
2213 /* add double NULL terminator */
2214 if (*type == REG_MULTI_SZ)
2215 {
2216 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2217 data = msi_realloc_zero(data, *size);
2218 }
2219 }
2220 return data;
2221 }
2222
2223 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2224 {
2225 MSIPACKAGE *package = param;
2226 static const WCHAR szHCR[] =
2227 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2228 'R','O','O','T','\\',0};
2229 static const WCHAR szHCU[] =
2230 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2231 'U','S','E','R','\\',0};
2232 static const WCHAR szHLM[] =
2233 {'H','K','E','Y','_','L','O','C','A','L','_',
2234 'M','A','C','H','I','N','E','\\',0};
2235 static const WCHAR szHU[] =
2236 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2237
2238 LPSTR value_data = NULL;
2239 HKEY root_key, hkey;
2240 DWORD type,size;
2241 LPWSTR deformated;
2242 LPCWSTR szRoot, component, name, key, value;
2243 MSICOMPONENT *comp;
2244 MSIRECORD * uirow;
2245 LPWSTR uikey;
2246 INT root;
2247 BOOL check_first = FALSE;
2248 UINT rc;
2249
2250 ui_progress(package,2,0,0,0);
2251
2252 value = NULL;
2253 key = NULL;
2254 uikey = NULL;
2255 name = NULL;
2256
2257 component = MSI_RecordGetString(row, 6);
2258 comp = get_loaded_component(package,component);
2259 if (!comp)
2260 return ERROR_SUCCESS;
2261
2262 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2263 {
2264 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2265 comp->Action = comp->Installed;
2266 return ERROR_SUCCESS;
2267 }
2268 comp->Action = INSTALLSTATE_LOCAL;
2269
2270 name = MSI_RecordGetString(row, 4);
2271 if( MSI_RecordIsNull(row,5) && name )
2272 {
2273 /* null values can have special meanings */
2274 if (name[0]=='-' && name[1] == 0)
2275 return ERROR_SUCCESS;
2276 else if ((name[0]=='+' && name[1] == 0) ||
2277 (name[0] == '*' && name[1] == 0))
2278 name = NULL;
2279 check_first = TRUE;
2280 }
2281
2282 root = MSI_RecordGetInteger(row,2);
2283 key = MSI_RecordGetString(row, 3);
2284
2285 /* get the root key */
2286 switch (root)
2287 {
2288 case -1:
2289 {
2290 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2291 if (all_users && all_users[0] == '1')
2292 {
2293 root_key = HKEY_LOCAL_MACHINE;
2294 szRoot = szHLM;
2295 }
2296 else
2297 {
2298 root_key = HKEY_CURRENT_USER;
2299 szRoot = szHCU;
2300 }
2301 msi_free(all_users);
2302 }
2303 break;
2304 case 0: root_key = HKEY_CLASSES_ROOT;
2305 szRoot = szHCR;
2306 break;
2307 case 1: root_key = HKEY_CURRENT_USER;
2308 szRoot = szHCU;
2309 break;
2310 case 2: root_key = HKEY_LOCAL_MACHINE;
2311 szRoot = szHLM;
2312 break;
2313 case 3: root_key = HKEY_USERS;
2314 szRoot = szHU;
2315 break;
2316 default:
2317 ERR("Unknown root %i\n",root);
2318 root_key=NULL;
2319 szRoot = NULL;
2320 break;
2321 }
2322 if (!root_key)
2323 return ERROR_SUCCESS;
2324
2325 deformat_string(package, key , &deformated);
2326 size = strlenW(deformated) + strlenW(szRoot) + 1;
2327 uikey = msi_alloc(size*sizeof(WCHAR));
2328 strcpyW(uikey,szRoot);
2329 strcatW(uikey,deformated);
2330
2331 if (RegCreateKeyW( root_key, deformated, &hkey))
2332 {
2333 ERR("Could not create key %s\n",debugstr_w(deformated));
2334 msi_free(deformated);
2335 msi_free(uikey);
2336 return ERROR_SUCCESS;
2337 }
2338 msi_free(deformated);
2339
2340 value = MSI_RecordGetString(row,5);
2341 if (value)
2342 value_data = parse_value(package, value, &type, &size);
2343 else
2344 {
2345 value_data = (LPSTR)strdupW(szEmpty);
2346 size = sizeof(szEmpty);
2347 type = REG_SZ;
2348 }
2349
2350 deformat_string(package, name, &deformated);
2351
2352 if (!check_first)
2353 {
2354 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2355 debugstr_w(uikey));
2356 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2357 }
2358 else
2359 {
2360 DWORD sz = 0;
2361 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2362 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2363 {
2364 TRACE("value %s of %s checked already exists\n",
2365 debugstr_w(deformated), debugstr_w(uikey));
2366 }
2367 else
2368 {
2369 TRACE("Checked and setting value %s of %s\n",
2370 debugstr_w(deformated), debugstr_w(uikey));
2371 if (deformated || size)
2372 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2373 }
2374 }
2375 RegCloseKey(hkey);
2376
2377 uirow = MSI_CreateRecord(3);
2378 MSI_RecordSetStringW(uirow,2,deformated);
2379 MSI_RecordSetStringW(uirow,1,uikey);
2380
2381 if (type == REG_SZ)
2382 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2383 else
2384 MSI_RecordSetStringW(uirow,3,value);
2385
2386 ui_actiondata(package,szWriteRegistryValues,uirow);
2387 msiobj_release( &uirow->hdr );
2388
2389 msi_free(value_data);
2390 msi_free(deformated);
2391 msi_free(uikey);
2392
2393 return ERROR_SUCCESS;
2394 }
2395
2396 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2397 {
2398 UINT rc;
2399 MSIQUERY * view;
2400 static const WCHAR ExecSeqQuery[] =
2401 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2402 '`','R','e','g','i','s','t','r','y','`',0 };
2403
2404 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2405 if (rc != ERROR_SUCCESS)
2406 return ERROR_SUCCESS;
2407
2408 /* increment progress bar each time action data is sent */
2409 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2410
2411 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2412
2413 msiobj_release(&view->hdr);
2414 return rc;
2415 }
2416
2417 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2418 {
2419 package->script->CurrentlyScripting = TRUE;
2420
2421 return ERROR_SUCCESS;
2422 }
2423
2424
2425 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2426 {
2427 MSICOMPONENT *comp;
2428 DWORD progress = 0;
2429 DWORD total = 0;
2430 static const WCHAR q1[]=
2431 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2432 '`','R','e','g','i','s','t','r','y','`',0};
2433 UINT rc;
2434 MSIQUERY * view;
2435 MSIFEATURE *feature;
2436 MSIFILE *file;
2437
2438 TRACE("InstallValidate\n");
2439
2440 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2441 if (rc == ERROR_SUCCESS)
2442 {
2443 MSI_IterateRecords( view, &progress, NULL, package );
2444 msiobj_release( &view->hdr );
2445 total += progress * REG_PROGRESS_VALUE;
2446 }
2447
2448 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2449 total += COMPONENT_PROGRESS_VALUE;
2450
2451 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2452 total += file->FileSize;
2453
2454 ui_progress(package,0,total,0,0);
2455
2456 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2457 {
2458 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2459 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2460 feature->ActionRequest);
2461 }
2462
2463 return ERROR_SUCCESS;
2464 }
2465
2466 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2467 {
2468 MSIPACKAGE* package = param;
2469 LPCWSTR cond = NULL;
2470 LPCWSTR message = NULL;
2471 UINT r;
2472
2473 static const WCHAR title[]=
2474 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2475
2476 cond = MSI_RecordGetString(row,1);
2477
2478 r = MSI_EvaluateConditionW(package,cond);
2479 if (r == MSICONDITION_FALSE)
2480 {
2481 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2482 {
2483 LPWSTR deformated;
2484 message = MSI_RecordGetString(row,2);
2485 deformat_string(package,message,&deformated);
2486 MessageBoxW(NULL,deformated,title,MB_OK);
2487 msi_free(deformated);
2488 }
2489
2490 return ERROR_INSTALL_FAILURE;
2491 }
2492
2493 return ERROR_SUCCESS;
2494 }
2495
2496 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2497 {
2498 UINT rc;
2499 MSIQUERY * view = NULL;
2500 static const WCHAR ExecSeqQuery[] =
2501 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2502 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2503
2504 TRACE("Checking launch conditions\n");
2505
2506 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2507 if (rc != ERROR_SUCCESS)
2508 return ERROR_SUCCESS;
2509
2510 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2511 msiobj_release(&view->hdr);
2512
2513 return rc;
2514 }
2515
2516 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2517 {
2518
2519 if (!cmp->KeyPath)
2520 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2521
2522 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2523 {
2524 MSIRECORD * row = 0;
2525 UINT root,len;
2526 LPWSTR deformated,buffer,deformated_name;
2527 LPCWSTR key,name;
2528 static const WCHAR ExecSeqQuery[] =
2529 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2530 '`','R','e','g','i','s','t','r','y','`',' ',
2531 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2532 ' ','=',' ' ,'\'','%','s','\'',0 };
2533 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2534 static const WCHAR fmt2[]=
2535 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2536
2537 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2538 if (!row)
2539 return NULL;
2540
2541 root = MSI_RecordGetInteger(row,2);
2542 key = MSI_RecordGetString(row, 3);
2543 name = MSI_RecordGetString(row, 4);
2544 deformat_string(package, key , &deformated);
2545 deformat_string(package, name, &deformated_name);
2546
2547 len = strlenW(deformated) + 6;
2548 if (deformated_name)
2549 len+=strlenW(deformated_name);
2550
2551 buffer = msi_alloc( len *sizeof(WCHAR));
2552
2553 if (deformated_name)
2554 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2555 else
2556 sprintfW(buffer,fmt,root,deformated);
2557
2558 msi_free(deformated);
2559 msi_free(deformated_name);
2560 msiobj_release(&row->hdr);
2561
2562 return buffer;
2563 }
2564 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2565 {
2566 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2567 return NULL;
2568 }
2569 else
2570 {
2571 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2572
2573 if (file)
2574 return strdupW( file->TargetPath );
2575 }
2576 return NULL;
2577 }
2578
2579 static HKEY openSharedDLLsKey(void)
2580 {
2581 HKEY hkey=0;
2582 static const WCHAR path[] =
2583 {'S','o','f','t','w','a','r','e','\\',
2584 'M','i','c','r','o','s','o','f','t','\\',
2585 'W','i','n','d','o','w','s','\\',
2586 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2587 'S','h','a','r','e','d','D','L','L','s',0};
2588
2589 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2590 return hkey;
2591 }
2592
2593 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2594 {
2595 HKEY hkey;
2596 DWORD count=0;
2597 DWORD type;
2598 DWORD sz = sizeof(count);
2599 DWORD rc;
2600
2601 hkey = openSharedDLLsKey();
2602 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2603 if (rc != ERROR_SUCCESS)
2604 count = 0;
2605 RegCloseKey(hkey);
2606 return count;
2607 }
2608
2609 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2610 {
2611 HKEY hkey;
2612
2613 hkey = openSharedDLLsKey();
2614 if (count > 0)
2615 msi_reg_set_val_dword( hkey, path, count );
2616 else
2617 RegDeleteValueW(hkey,path);
2618 RegCloseKey(hkey);
2619 return count;
2620 }
2621
2622 /*
2623 * Return TRUE if the count should be written out and FALSE if not
2624 */
2625 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2626 {
2627 MSIFEATURE *feature;
2628 INT count = 0;
2629 BOOL write = FALSE;
2630
2631 /* only refcount DLLs */
2632 if (comp->KeyPath == NULL ||
2633 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2634 comp->Attributes & msidbComponentAttributesODBCDataSource)
2635 write = FALSE;
2636 else
2637 {
2638 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2639 write = (count > 0);
2640
2641 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2642 write = TRUE;
2643 }
2644
2645 /* increment counts */
2646 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2647 {
2648 ComponentList *cl;
2649
2650 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
2651 continue;
2652
2653 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2654 {
2655 if ( cl->component == comp )
2656 count++;
2657 }
2658 }
2659
2660 /* decrement counts */
2661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2662 {
2663 ComponentList *cl;
2664
2665 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
2666 continue;
2667
2668 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2669 {
2670 if ( cl->component == comp )
2671 count--;
2672 }
2673 }
2674
2675 /* ref count all the files in the component */
2676 if (write)
2677 {
2678 MSIFILE *file;
2679
2680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2681 {
2682 if (file->Component == comp)
2683 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2684 }
2685 }
2686
2687 /* add a count for permanent */
2688 if (comp->Attributes & msidbComponentAttributesPermanent)
2689 count ++;
2690
2691 comp->RefCount = count;
2692
2693 if (write)
2694 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2695 }
2696
2697 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2698 {
2699 WCHAR squished_pc[GUID_SIZE];
2700 WCHAR squished_cc[GUID_SIZE];
2701 UINT rc;
2702 MSICOMPONENT *comp;
2703 HKEY hkey;
2704
2705 TRACE("\n");
2706
2707 squash_guid(package->ProductCode,squished_pc);
2708 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2709
2710 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2711 {
2712 MSIRECORD * uirow;
2713
2714 ui_progress(package,2,0,0,0);
2715 if (!comp->ComponentId)
2716 continue;
2717
2718 squash_guid(comp->ComponentId,squished_cc);
2719
2720 msi_free(comp->FullKeypath);
2721 comp->FullKeypath = resolve_keypath( package, comp );
2722
2723 ACTION_RefCountComponent( package, comp );
2724
2725 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2726 debugstr_w(comp->Component),
2727 debugstr_w(squished_cc),
2728 debugstr_w(comp->FullKeypath),
2729 comp->RefCount);
2730
2731 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
2732 comp->ActionRequest == INSTALLSTATE_SOURCE)
2733 {
2734 if (!comp->FullKeypath)
2735 continue;
2736
2737 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2738 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2739 &hkey, TRUE);
2740 else
2741 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2742 &hkey, TRUE);
2743
2744 if (rc != ERROR_SUCCESS)
2745 continue;
2746
2747 if (comp->Attributes & msidbComponentAttributesPermanent)
2748 {
2749 static const WCHAR szPermKey[] =
2750 { '0','0','0','0','0','0','0','0','0','0','0','0',
2751 '0','0','0','0','0','0','0','0','0','0','0','0',
2752 '0','0','0','0','0','0','0','0',0 };
2753
2754 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2755 }
2756
2757 if (comp->Action == INSTALLSTATE_LOCAL)
2758 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2759 else
2760 {
2761 MSIFILE *file;
2762 MSIRECORD *row;
2763 LPWSTR ptr, ptr2;
2764 WCHAR source[MAX_PATH];
2765 WCHAR base[MAX_PATH];
2766 LPWSTR sourcepath;
2767
2768 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2769 static const WCHAR query[] = {
2770 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2771 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2772 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2773 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2774 '`','D','i','s','k','I','d','`',0};
2775
2776 file = get_loaded_file(package, comp->KeyPath);
2777 if (!file)
2778 continue;
2779
2780 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2781 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2782 ptr2 = strrchrW(source, '\\') + 1;
2783 msiobj_release(&row->hdr);
2784
2785 lstrcpyW(base, package->PackagePath);
2786 ptr = strrchrW(base, '\\');
2787 *(ptr + 1) = '\0';
2788
2789 sourcepath = resolve_file_source(package, file);
2790 ptr = sourcepath + lstrlenW(base);
2791 lstrcpyW(ptr2, ptr);
2792 msi_free(sourcepath);
2793
2794 msi_reg_set_val_str(hkey, squished_pc, source);
2795 }
2796 RegCloseKey(hkey);
2797 }
2798 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
2799 {
2800 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2801 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2802 else
2803 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2804 }
2805
2806 /* UI stuff */
2807 uirow = MSI_CreateRecord(3);
2808 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2809 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2810 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2811 ui_actiondata(package,szProcessComponents,uirow);
2812 msiobj_release( &uirow->hdr );
2813 }
2814
2815 return ERROR_SUCCESS;
2816 }
2817
2818 typedef struct {
2819 CLSID clsid;
2820 LPWSTR source;
2821
2822 LPWSTR path;
2823 ITypeLib *ptLib;
2824 } typelib_struct;
2825
2826 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2827 LPWSTR lpszName, LONG_PTR lParam)
2828 {
2829 TLIBATTR *attr;
2830 typelib_struct *tl_struct = (typelib_struct*) lParam;
2831 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2832 int sz;
2833 HRESULT res;
2834
2835 if (!IS_INTRESOURCE(lpszName))
2836 {
2837 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2838 return TRUE;
2839 }
2840
2841 sz = strlenW(tl_struct->source)+4;
2842 sz *= sizeof(WCHAR);
2843
2844 if ((INT_PTR)lpszName == 1)
2845 tl_struct->path = strdupW(tl_struct->source);
2846 else
2847 {
2848 tl_struct->path = msi_alloc(sz);
2849 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2850 }
2851
2852 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2853 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2854 if (FAILED(res))
2855 {
2856 msi_free(tl_struct->path);
2857 tl_struct->path = NULL;
2858
2859 return TRUE;
2860 }
2861
2862 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2863 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2864 {
2865 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2866 return FALSE;
2867 }
2868
2869 msi_free(tl_struct->path);
2870 tl_struct->path = NULL;
2871
2872 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2873 ITypeLib_Release(tl_struct->ptLib);
2874
2875 return TRUE;
2876 }
2877
2878 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2879 {
2880 MSIPACKAGE* package = param;
2881 LPCWSTR component;
2882 MSICOMPONENT *comp;
2883 MSIFILE *file;
2884 typelib_struct tl_struct;
2885 ITypeLib *tlib;
2886 HMODULE module;
2887 HRESULT hr;
2888
2889 component = MSI_RecordGetString(row,3);
2890 comp = get_loaded_component(package,component);
2891 if (!comp)
2892 return ERROR_SUCCESS;
2893
2894 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2895 {
2896 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2897 comp->Action = comp->Installed;
2898 return ERROR_SUCCESS;
2899 }
2900 comp->Action = INSTALLSTATE_LOCAL;
2901
2902 file = get_loaded_file( package, comp->KeyPath );
2903 if (!file)
2904 return ERROR_SUCCESS;
2905
2906 ui_actiondata( package, szRegisterTypeLibraries, row );
2907
2908 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2909 if (module)
2910 {
2911 LPCWSTR guid;
2912 guid = MSI_RecordGetString(row,1);
2913 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2914 tl_struct.source = strdupW( file->TargetPath );
2915 tl_struct.path = NULL;
2916
2917 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2918 (LONG_PTR)&tl_struct);
2919
2920 if (tl_struct.path)
2921 {
2922 LPWSTR help = NULL;
2923 LPCWSTR helpid;
2924 HRESULT res;
2925
2926 helpid = MSI_RecordGetString(row,6);
2927
2928 if (helpid)
2929 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2930 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2931 msi_free(help);
2932
2933 if (FAILED(res))
2934 ERR("Failed to register type library %s\n",
2935 debugstr_w(tl_struct.path));
2936 else
2937 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2938
2939 ITypeLib_Release(tl_struct.ptLib);
2940 msi_free(tl_struct.path);
2941 }
2942 else
2943 ERR("Failed to load type library %s\n",
2944 debugstr_w(tl_struct.source));
2945
2946 FreeLibrary(module);
2947 msi_free(tl_struct.source);
2948 }
2949 else
2950 {
2951 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2952 if (FAILED(hr))
2953 {
2954 ERR("Failed to load type library: %08x\n", hr);
2955 return ERROR_INSTALL_FAILURE;
2956 }
2957
2958 ITypeLib_Release(tlib);
2959 }
2960
2961 return ERROR_SUCCESS;
2962 }
2963
2964 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2965 {
2966 /*
2967 * OK this is a bit confusing.. I am given a _Component key and I believe
2968 * that the file that is being registered as a type library is the "key file
2969 * of that component" which I interpret to mean "The file in the KeyPath of
2970 * that component".
2971 */
2972 UINT rc;
2973 MSIQUERY * view;
2974 static const WCHAR Query[] =
2975 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','T','y','p','e','L','i','b','`',0};
2977
2978 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2979 if (rc != ERROR_SUCCESS)
2980 return ERROR_SUCCESS;
2981
2982 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2983 msiobj_release(&view->hdr);
2984 return rc;
2985 }
2986
2987 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
2988 {
2989 MSIPACKAGE *package = param;
2990 LPCWSTR component, guid;
2991 MSICOMPONENT *comp;
2992 GUID libid;
2993 UINT version;
2994 LCID language;
2995 SYSKIND syskind;
2996 HRESULT hr;
2997
2998 component = MSI_RecordGetString( row, 3 );
2999 comp = get_loaded_component( package, component );
3000 if (!comp)
3001 return ERROR_SUCCESS;
3002
3003 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3004 {
3005 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3006 comp->Action = comp->Installed;
3007 return ERROR_SUCCESS;
3008 }
3009 comp->Action = INSTALLSTATE_ABSENT;
3010
3011 ui_actiondata( package, szUnregisterTypeLibraries, row );
3012
3013 guid = MSI_RecordGetString( row, 1 );
3014 CLSIDFromString( (LPWSTR)guid, &libid );
3015 version = MSI_RecordGetInteger( row, 4 );
3016 language = MSI_RecordGetInteger( row, 2 );
3017
3018 #ifdef _WIN64
3019 syskind = SYS_WIN64;
3020 #else
3021 syskind = SYS_WIN32;
3022 #endif
3023
3024 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3025 if (FAILED(hr))
3026 {
3027 WARN("Failed to unregister typelib: %08x\n", hr);
3028 }
3029
3030 return ERROR_SUCCESS;
3031 }
3032
3033 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3034 {
3035 UINT rc;
3036 MSIQUERY *view;
3037 static const WCHAR query[] =
3038 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3039 '`','T','y','p','e','L','i','b','`',0};
3040
3041 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3042 if (rc != ERROR_SUCCESS)
3043 return ERROR_SUCCESS;
3044
3045 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3046 msiobj_release( &view->hdr );
3047 return rc;
3048 }
3049
3050 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3051 {
3052 static const WCHAR szlnk[] = {'.','l','n','k',0};
3053 LPCWSTR directory, extension;
3054 LPWSTR link_folder, link_file, filename;
3055
3056 directory = MSI_RecordGetString( row, 2 );
3057 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3058
3059 /* may be needed because of a bug somewhere else */
3060 create_full_pathW( link_folder );
3061
3062 filename = msi_dup_record_field( row, 3 );
3063 reduce_to_longfilename( filename );
3064
3065 extension = strchrW( filename, '.' );
3066 if (!extension || strcmpiW( extension, szlnk ))
3067 {
3068 int len = strlenW( filename );
3069 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3070 memcpy( filename + len, szlnk, sizeof(szlnk) );
3071 }
3072 link_file = build_directory_name( 2, link_folder, filename );
3073 msi_free( link_folder );
3074 msi_free( filename );
3075
3076 return link_file;
3077 }
3078
3079 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3080 {
3081 MSIPACKAGE *package = param;
3082 LPWSTR link_file, deformated, path;
3083 LPCWSTR component, target;
3084 MSICOMPONENT *comp;
3085 IShellLinkW *sl = NULL;
3086 IPersistFile *pf = NULL;
3087 HRESULT res;
3088
3089 component = MSI_RecordGetString(row, 4);
3090 comp = get_loaded_component(package, component);
3091 if (!comp)
3092 return ERROR_SUCCESS;
3093
3094 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3095 {
3096 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3097 comp->Action = comp->Installed;
3098 return ERROR_SUCCESS;
3099 }
3100 comp->Action = INSTALLSTATE_LOCAL;
3101
3102 ui_actiondata(package,szCreateShortcuts,row);
3103
3104 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3105 &IID_IShellLinkW, (LPVOID *) &sl );
3106
3107 if (FAILED( res ))
3108 {
3109 ERR("CLSID_ShellLink not available\n");
3110 goto err;
3111 }
3112
3113 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3114 if (FAILED( res ))
3115 {
3116 ERR("QueryInterface(IID_IPersistFile) failed\n");
3117 goto err;
3118 }
3119
3120 target = MSI_RecordGetString(row, 5);
3121 if (strchrW(target, '['))
3122 {
3123 deformat_string(package, target, &deformated);
3124 IShellLinkW_SetPath(sl,deformated);
3125 msi_free(deformated);
3126 }
3127 else
3128 {
3129 FIXME("poorly handled shortcut format, advertised shortcut\n");
3130 IShellLinkW_SetPath(sl,comp->FullKeypath);
3131 }
3132
3133 if (!MSI_RecordIsNull(row,6))
3134 {
3135 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3136 deformat_string(package, arguments, &deformated);
3137 IShellLinkW_SetArguments(sl,deformated);
3138 msi_free(deformated);
3139 }
3140
3141 if (!MSI_RecordIsNull(row,7))
3142 {
3143 LPCWSTR description = MSI_RecordGetString(row, 7);
3144 IShellLinkW_SetDescription(sl, description);
3145 }
3146
3147 if (!MSI_RecordIsNull(row,8))
3148 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3149
3150 if (!MSI_RecordIsNull(row,9))
3151 {
3152 INT index