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