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