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