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