d94d842cae1e3bc120f5bb23e635640389a485a8
[reactos.git] / reactos / dll / win32 / msi / action.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
42
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47
48 /*
49 * consts and values used
50 */
51 static const WCHAR c_colon[] = {'C',':','\\',0};
52
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings[] =
140 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
141 static const WCHAR szRemoveExistingProducts[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
187
188 /********************************************************
189 * helper functions
190 ********************************************************/
191
192 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
193 {
194 static const WCHAR Query_t[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
199 MSIRECORD * row;
200
201 row = MSI_QueryGetRecord( package->db, Query_t, action );
202 if (!row)
203 return;
204 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
205 msiobj_release(&row->hdr);
206 }
207
208 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
209 UINT rc)
210 {
211 MSIRECORD * row;
212 static const WCHAR template_s[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
214 '%','s', '.',0};
215 static const WCHAR template_e[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
218 '%','i','.',0};
219 static const WCHAR format[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
221 WCHAR message[1024];
222 WCHAR timet[0x100];
223
224 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
225 if (start)
226 sprintfW(message,template_s,timet,action);
227 else
228 sprintfW(message,template_e,timet,action,rc);
229
230 row = MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row,1,message);
232
233 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
234 msiobj_release(&row->hdr);
235 }
236
237 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
238 BOOL preserve_case )
239 {
240 LPCWSTR ptr,ptr2;
241 BOOL quote;
242 DWORD len;
243 LPWSTR prop = NULL, val = NULL;
244
245 if (!szCommandLine)
246 return ERROR_SUCCESS;
247
248 ptr = szCommandLine;
249
250 while (*ptr)
251 {
252 if (*ptr==' ')
253 {
254 ptr++;
255 continue;
256 }
257
258 TRACE("Looking at %s\n",debugstr_w(ptr));
259
260 ptr2 = strchrW(ptr,'=');
261 if (!ptr2)
262 {
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
264 break;
265 }
266
267 quote = FALSE;
268
269 len = ptr2-ptr;
270 prop = msi_alloc((len+1)*sizeof(WCHAR));
271 memcpy(prop,ptr,len*sizeof(WCHAR));
272 prop[len]=0;
273
274 if (!preserve_case)
275 struprW(prop);
276
277 ptr2++;
278
279 len = 0;
280 ptr = ptr2;
281 while (*ptr && (quote || (!quote && *ptr!=' ')))
282 {
283 if (*ptr == '"')
284 quote = !quote;
285 ptr++;
286 len++;
287 }
288
289 if (*ptr2=='"')
290 {
291 ptr2++;
292 len -= 2;
293 }
294 val = msi_alloc((len+1)*sizeof(WCHAR));
295 memcpy(val,ptr2,len*sizeof(WCHAR));
296 val[len] = 0;
297
298 if (lstrlenW(prop) > 0)
299 {
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop), debugstr_w(val));
302 MSI_SetPropertyW(package,prop,val);
303 }
304 msi_free(val);
305 msi_free(prop);
306 }
307
308 return ERROR_SUCCESS;
309 }
310
311
312 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
313 {
314 LPCWSTR pc;
315 LPWSTR p, *ret = NULL;
316 UINT count = 0;
317
318 if (!str)
319 return ret;
320
321 /* count the number of substrings */
322 for ( pc = str, count = 0; pc; count++ )
323 {
324 pc = strchrW( pc, sep );
325 if (pc)
326 pc++;
327 }
328
329 /* allocate space for an array of substring pointers and the substrings */
330 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
331 (lstrlenW(str)+1) * sizeof(WCHAR) );
332 if (!ret)
333 return ret;
334
335 /* copy the string and set the pointers */
336 p = (LPWSTR) &ret[count+1];
337 lstrcpyW( p, str );
338 for( count = 0; (ret[count] = p); count++ )
339 {
340 p = strchrW( p, sep );
341 if (p)
342 *p++ = 0;
343 }
344
345 return ret;
346 }
347
348 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
349 {
350 static const WCHAR szSystemLanguageID[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
352
353 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
354 UINT ret = ERROR_FUNCTION_FAILED;
355
356 prod_code = msi_dup_property( package, szProductCode );
357 patch_product = msi_get_suminfo_product( patch );
358
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
360
361 if ( strstrW( patch_product, prod_code ) )
362 {
363 MSISUMMARYINFO *si;
364 const WCHAR *p;
365
366 si = MSI_GetSummaryInformationW( patch, 0 );
367 if (!si)
368 {
369 ERR("no summary information!\n");
370 goto end;
371 }
372
373 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
374 if (!template)
375 {
376 ERR("no template property!\n");
377 msiobj_release( &si->hdr );
378 goto end;
379 }
380
381 if (!template[0])
382 {
383 ret = ERROR_SUCCESS;
384 msiobj_release( &si->hdr );
385 goto end;
386 }
387
388 langid = msi_dup_property( package, szSystemLanguageID );
389 if (!langid)
390 {
391 msiobj_release( &si->hdr );
392 goto end;
393 }
394
395 p = strchrW( template, ';' );
396 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
397 {
398 TRACE("applicable transform\n");
399 ret = ERROR_SUCCESS;
400 }
401
402 /* FIXME: check platform */
403
404 msiobj_release( &si->hdr );
405 }
406
407 end:
408 msi_free( patch_product );
409 msi_free( prod_code );
410 msi_free( template );
411 msi_free( langid );
412
413 return ret;
414 }
415
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
418 {
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
421 HRESULT r;
422
423 TRACE("%p %s\n", package, debugstr_w(name) );
424
425 if (*name++ != ':')
426 {
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
429 }
430
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 if (SUCCEEDED(r))
433 {
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
437 else
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
440 }
441 else
442 ERR("failed to open substorage %s\n", debugstr_w(name));
443
444 return ERROR_SUCCESS;
445 }
446
447 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
448 {
449 LPWSTR guid_list, *guids, product_code;
450 UINT i, ret = ERROR_FUNCTION_FAILED;
451
452 product_code = msi_dup_property( package, szProductCode );
453 if (!product_code)
454 {
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS;
458 }
459
460 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
461 guids = msi_split_string( guid_list, ';' );
462 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
463 {
464 if (!lstrcmpW( guids[i], product_code ))
465 ret = ERROR_SUCCESS;
466 }
467 msi_free( guids );
468 msi_free( guid_list );
469 msi_free( product_code );
470
471 return ret;
472 }
473
474 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
475 {
476 MSIQUERY *view;
477 MSIRECORD *rec = NULL;
478 LPWSTR patch;
479 LPCWSTR prop;
480 UINT r;
481
482 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
487
488 r = MSI_DatabaseOpenViewW(package->db, query, &view);
489 if (r != ERROR_SUCCESS)
490 return r;
491
492 r = MSI_ViewExecute(view, 0);
493 if (r != ERROR_SUCCESS)
494 goto done;
495
496 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
497 {
498 prop = MSI_RecordGetString(rec, 1);
499 patch = msi_dup_property(package, szPatch);
500 MSI_SetPropertyW(package, prop, patch);
501 msi_free(patch);
502 }
503
504 done:
505 if (rec) msiobj_release(&rec->hdr);
506 msiobj_release(&view->hdr);
507
508 return r;
509 }
510
511 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
512 {
513 MSISUMMARYINFO *si;
514 LPWSTR str, *substorage;
515 UINT i, r = ERROR_SUCCESS;
516
517 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
518 if (!si)
519 return ERROR_FUNCTION_FAILED;
520
521 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
522 {
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS;
525 }
526
527 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
528 if (!package->patch)
529 return ERROR_OUTOFMEMORY;
530
531 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
532 if (!package->patch->patchcode)
533 return ERROR_OUTOFMEMORY;
534
535 /* enumerate the substorage */
536 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 package->patch->transforms = str;
538
539 substorage = msi_split_string( str, ';' );
540 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
541 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
542
543 msi_free( substorage );
544 msiobj_release( &si->hdr );
545
546 msi_set_media_source_prop(package);
547
548 return r;
549 }
550
551 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
552 {
553 MSIDATABASE *patch_db = NULL;
554 UINT r;
555
556 TRACE("%p %s\n", package, debugstr_w( file ) );
557
558 /* FIXME:
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
562 */
563 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
564 if ( r != ERROR_SUCCESS )
565 {
566 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
567 return r;
568 }
569
570 msi_parse_patch_summary( package, patch_db );
571
572 /*
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
575 */
576 append_storage_to_db( package->db, patch_db->storage );
577
578 msiobj_release( &patch_db->hdr );
579
580 return ERROR_SUCCESS;
581 }
582
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT msi_apply_patches( MSIPACKAGE *package )
585 {
586 LPWSTR patch_list, *patches;
587 UINT i, r = ERROR_SUCCESS;
588
589 patch_list = msi_dup_property( package, szPatch );
590
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
592
593 patches = msi_split_string( patch_list, ';' );
594 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
595 r = msi_apply_patch_package( package, patches[i] );
596
597 msi_free( patches );
598 msi_free( patch_list );
599
600 return r;
601 }
602
603 static UINT msi_apply_transforms( MSIPACKAGE *package )
604 {
605 static const WCHAR szTransforms[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list, *xforms;
608 UINT i, r = ERROR_SUCCESS;
609
610 xform_list = msi_dup_property( package, szTransforms );
611 xforms = msi_split_string( xform_list, ';' );
612
613 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
614 {
615 if (xforms[i][0] == ':')
616 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
617 else
618 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
619 }
620
621 msi_free( xforms );
622 msi_free( xform_list );
623
624 return r;
625 }
626
627 static BOOL ui_sequence_exists( MSIPACKAGE *package )
628 {
629 MSIQUERY *view;
630 UINT rc;
631
632 static const WCHAR ExecSeqQuery [] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
640
641 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642 if (rc == ERROR_SUCCESS)
643 {
644 msiobj_release(&view->hdr);
645 return TRUE;
646 }
647
648 return FALSE;
649 }
650
651 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
652 {
653 LPWSTR p, db;
654 LPWSTR source, check;
655 DWORD len;
656
657 static const WCHAR szOriginalDatabase[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
659
660 db = msi_dup_property( package, szOriginalDatabase );
661 if (!db)
662 return ERROR_OUTOFMEMORY;
663
664 p = strrchrW( db, '\\' );
665 if (!p)
666 {
667 p = strrchrW( db, '/' );
668 if (!p)
669 {
670 msi_free(db);
671 return ERROR_SUCCESS;
672 }
673 }
674
675 len = p - db + 2;
676 source = msi_alloc( len * sizeof(WCHAR) );
677 lstrcpynW( source, db, len );
678
679 check = msi_dup_property( package, cszSourceDir );
680 if (!check || replace)
681 MSI_SetPropertyW( package, cszSourceDir, source );
682
683 msi_free( check );
684
685 check = msi_dup_property( package, cszSOURCEDIR );
686 if (!check || replace)
687 MSI_SetPropertyW( package, cszSOURCEDIR, source );
688
689 msi_free( check );
690 msi_free( source );
691 msi_free( db );
692
693 return ERROR_SUCCESS;
694 }
695
696 static BOOL needs_ui_sequence(MSIPACKAGE *package)
697 {
698 INT level = msi_get_property_int(package, szUILevel, 0);
699 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
700 }
701
702 static UINT msi_set_context(MSIPACKAGE *package)
703 {
704 WCHAR val[10];
705 DWORD sz = 10;
706 DWORD num;
707 UINT r;
708
709 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
710
711 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
712 if (r == ERROR_SUCCESS)
713 {
714 num = atolW(val);
715 if (num == 1 || num == 2)
716 package->Context = MSIINSTALLCONTEXT_MACHINE;
717 }
718
719 return ERROR_SUCCESS;
720 }
721
722 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
723 {
724 UINT rc;
725 LPCWSTR cond, action;
726 MSIPACKAGE *package = param;
727
728 action = MSI_RecordGetString(row,1);
729 if (!action)
730 {
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED;
733 }
734
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
737
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
740 {
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
742 return ERROR_SUCCESS;
743 }
744
745 if (needs_ui_sequence(package))
746 rc = ACTION_PerformUIAction(package, action, -1);
747 else
748 rc = ACTION_PerformAction(package, action, -1, FALSE);
749
750 msi_dialog_check_messages( NULL );
751
752 if (package->CurrentInstallState != ERROR_SUCCESS)
753 rc = package->CurrentInstallState;
754
755 if (rc == ERROR_FUNCTION_NOT_CALLED)
756 rc = ERROR_SUCCESS;
757
758 if (rc != ERROR_SUCCESS)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
760
761 return rc;
762 }
763
764 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
765 {
766 MSIQUERY * view;
767 UINT r;
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','%','s','`',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
775
776 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
777
778 r = MSI_OpenQuery( package->db, &view, query, szTable );
779 if (r == ERROR_SUCCESS)
780 {
781 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
782 msiobj_release(&view->hdr);
783 }
784
785 return r;
786 }
787
788 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
789 {
790 MSIQUERY * view;
791 UINT rc;
792 static const WCHAR ExecSeqQuery[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
806 INT seq = 0;
807
808 if (package->script->ExecuteSequenceRun)
809 {
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS;
812 }
813
814 package->script->ExecuteSequenceRun = TRUE;
815
816 /* get the sequence number */
817 if (UIran)
818 {
819 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
820 if( !row )
821 return ERROR_FUNCTION_FAILED;
822 seq = MSI_RecordGetInteger(row,1);
823 msiobj_release(&row->hdr);
824 }
825
826 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
827 if (rc == ERROR_SUCCESS)
828 {
829 TRACE("Running the actions\n");
830
831 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
832 msiobj_release(&view->hdr);
833 }
834
835 return rc;
836 }
837
838 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
839 {
840 MSIQUERY * view;
841 UINT rc;
842 static const WCHAR ExecSeqQuery [] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
850
851 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
852 if (rc == ERROR_SUCCESS)
853 {
854 TRACE("Running the actions\n");
855
856 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
857 msiobj_release(&view->hdr);
858 }
859
860 return rc;
861 }
862
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
867 UINT* rc, UINT script, BOOL force )
868 {
869 BOOL ret=FALSE;
870 UINT arc;
871
872 arc = ACTION_CustomAction(package, action, script, force);
873
874 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
875 {
876 *rc = arc;
877 ret = TRUE;
878 }
879 return ret;
880 }
881
882 /*
883 * Actual Action Handlers
884 */
885
886 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
887 {
888 MSIPACKAGE *package = param;
889 LPCWSTR dir;
890 LPWSTR full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
893
894 dir = MSI_RecordGetString(row,1);
895 if (!dir)
896 {
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS;
899 }
900
901 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
902 if (!full_path)
903 {
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
905 return ERROR_SUCCESS;
906 }
907
908 TRACE("Folder is %s\n",debugstr_w(full_path));
909
910 /* UI stuff */
911 uirow = MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow,1,full_path);
913 ui_actiondata(package,szCreateFolders,uirow);
914 msiobj_release( &uirow->hdr );
915
916 if (folder->State == 0)
917 create_full_pathW(full_path);
918
919 folder->State = 3;
920
921 msi_free(full_path);
922 return ERROR_SUCCESS;
923 }
924
925 /* FIXME: probably should merge this with the above function */
926 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
927 {
928 UINT rc = ERROR_SUCCESS;
929 MSIFOLDER *folder;
930 LPWSTR install_path;
931
932 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
933 if (!install_path)
934 return ERROR_FUNCTION_FAILED;
935
936 /* create the path */
937 if (folder->State == 0)
938 {
939 create_full_pathW(install_path);
940 folder->State = 2;
941 }
942 msi_free(install_path);
943
944 return rc;
945 }
946
947 UINT msi_create_component_directories( MSIPACKAGE *package )
948 {
949 MSICOMPONENT *comp;
950
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
953 {
954 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
955 continue;
956 msi_create_directory( package, comp->Directory );
957 }
958
959 return ERROR_SUCCESS;
960 }
961
962 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
963 {
964 static const WCHAR ExecSeqQuery[] =
965 {'S','E','L','E','C','T',' ',
966 '`','D','i','r','e','c','t','o','r','y','_','`',
967 ' ','F','R','O','M',' ',
968 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
969 UINT rc;
970 MSIQUERY *view;
971
972 /* create all the empty folders specified in the CreateFolder table */
973 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
974 if (rc != ERROR_SUCCESS)
975 return ERROR_SUCCESS;
976
977 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
978 msiobj_release(&view->hdr);
979
980 return rc;
981 }
982
983 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
984 {
985 MSIPACKAGE *package = param;
986 LPCWSTR dir;
987 LPWSTR full_path;
988 MSIRECORD *uirow;
989 MSIFOLDER *folder;
990
991 dir = MSI_RecordGetString( row, 1 );
992 if (!dir)
993 {
994 ERR("Unable to get folder id\n");
995 return ERROR_SUCCESS;
996 }
997
998 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
999 if (!full_path)
1000 {
1001 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1002 return ERROR_SUCCESS;
1003 }
1004
1005 TRACE("folder is %s\n", debugstr_w(full_path));
1006
1007 uirow = MSI_CreateRecord( 1 );
1008 MSI_RecordSetStringW( uirow, 1, full_path );
1009 ui_actiondata( package, szRemoveFolders, uirow );
1010 msiobj_release( &uirow->hdr );
1011
1012 RemoveDirectoryW( full_path );
1013 folder->State = 0;
1014
1015 msi_free( full_path );
1016 return ERROR_SUCCESS;
1017 }
1018
1019 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1020 {
1021 static const WCHAR query[] =
1022 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1023 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1024
1025 MSIQUERY *view;
1026 UINT rc;
1027
1028 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1029 if (rc != ERROR_SUCCESS)
1030 return ERROR_SUCCESS;
1031
1032 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1033 msiobj_release( &view->hdr );
1034
1035 return rc;
1036 }
1037
1038 static UINT load_component( MSIRECORD *row, LPVOID param )
1039 {
1040 MSIPACKAGE *package = param;
1041 MSICOMPONENT *comp;
1042
1043 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1044 if (!comp)
1045 return ERROR_FUNCTION_FAILED;
1046
1047 list_add_tail( &package->components, &comp->entry );
1048
1049 /* fill in the data */
1050 comp->Component = msi_dup_record_field( row, 1 );
1051
1052 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1053
1054 comp->ComponentId = msi_dup_record_field( row, 2 );
1055 comp->Directory = msi_dup_record_field( row, 3 );
1056 comp->Attributes = MSI_RecordGetInteger(row,4);
1057 comp->Condition = msi_dup_record_field( row, 5 );
1058 comp->KeyPath = msi_dup_record_field( row, 6 );
1059
1060 comp->Installed = INSTALLSTATE_UNKNOWN;
1061 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1062
1063 return ERROR_SUCCESS;
1064 }
1065
1066 static UINT load_all_components( MSIPACKAGE *package )
1067 {
1068 static const WCHAR query[] = {
1069 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1070 '`','C','o','m','p','o','n','e','n','t','`',0 };
1071 MSIQUERY *view;
1072 UINT r;
1073
1074 if (!list_empty(&package->components))
1075 return ERROR_SUCCESS;
1076
1077 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1078 if (r != ERROR_SUCCESS)
1079 return r;
1080
1081 r = MSI_IterateRecords(view, NULL, load_component, package);
1082 msiobj_release(&view->hdr);
1083 return r;
1084 }
1085
1086 typedef struct {
1087 MSIPACKAGE *package;
1088 MSIFEATURE *feature;
1089 } _ilfs;
1090
1091 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1092 {
1093 ComponentList *cl;
1094
1095 cl = msi_alloc( sizeof (*cl) );
1096 if ( !cl )
1097 return ERROR_NOT_ENOUGH_MEMORY;
1098 cl->component = comp;
1099 list_add_tail( &feature->Components, &cl->entry );
1100
1101 return ERROR_SUCCESS;
1102 }
1103
1104 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1105 {
1106 FeatureList *fl;
1107
1108 fl = msi_alloc( sizeof(*fl) );
1109 if ( !fl )
1110 return ERROR_NOT_ENOUGH_MEMORY;
1111 fl->feature = child;
1112 list_add_tail( &parent->Children, &fl->entry );
1113
1114 return ERROR_SUCCESS;
1115 }
1116
1117 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1118 {
1119 _ilfs* ilfs = param;
1120 LPCWSTR component;
1121 MSICOMPONENT *comp;
1122
1123 component = MSI_RecordGetString(row,1);
1124
1125 /* check to see if the component is already loaded */
1126 comp = get_loaded_component( ilfs->package, component );
1127 if (!comp)
1128 {
1129 ERR("unknown component %s\n", debugstr_w(component));
1130 return ERROR_FUNCTION_FAILED;
1131 }
1132
1133 add_feature_component( ilfs->feature, comp );
1134 comp->Enabled = TRUE;
1135
1136 return ERROR_SUCCESS;
1137 }
1138
1139 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1140 {
1141 MSIFEATURE *feature;
1142
1143 if ( !name )
1144 return NULL;
1145
1146 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1147 {
1148 if ( !lstrcmpW( feature->Feature, name ) )
1149 return feature;
1150 }
1151
1152 return NULL;
1153 }
1154
1155 static UINT load_feature(MSIRECORD * row, LPVOID param)
1156 {
1157 MSIPACKAGE* package = param;
1158 MSIFEATURE* feature;
1159 static const WCHAR Query1[] =
1160 {'S','E','L','E','C','T',' ',
1161 '`','C','o','m','p','o','n','e','n','t','_','`',
1162 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1163 'C','o','m','p','o','n','e','n','t','s','`',' ',
1164 'W','H','E','R','E',' ',
1165 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1166 MSIQUERY * view;
1167 UINT rc;
1168 _ilfs ilfs;
1169
1170 /* fill in the data */
1171
1172 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1173 if (!feature)
1174 return ERROR_NOT_ENOUGH_MEMORY;
1175
1176 list_init( &feature->Children );
1177 list_init( &feature->Components );
1178
1179 feature->Feature = msi_dup_record_field( row, 1 );
1180
1181 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1182
1183 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1184 feature->Title = msi_dup_record_field( row, 3 );
1185 feature->Description = msi_dup_record_field( row, 4 );
1186
1187 if (!MSI_RecordIsNull(row,5))
1188 feature->Display = MSI_RecordGetInteger(row,5);
1189
1190 feature->Level= MSI_RecordGetInteger(row,6);
1191 feature->Directory = msi_dup_record_field( row, 7 );
1192 feature->Attributes = MSI_RecordGetInteger(row,8);
1193
1194 feature->Installed = INSTALLSTATE_UNKNOWN;
1195 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1196
1197 list_add_tail( &package->features, &feature->entry );
1198
1199 /* load feature components */
1200
1201 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1202 if (rc != ERROR_SUCCESS)
1203 return ERROR_SUCCESS;
1204
1205 ilfs.package = package;
1206 ilfs.feature = feature;
1207
1208 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1209 msiobj_release(&view->hdr);
1210
1211 return ERROR_SUCCESS;
1212 }
1213
1214 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1215 {
1216 MSIPACKAGE* package = param;
1217 MSIFEATURE *parent, *child;
1218
1219 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1220 if (!child)
1221 return ERROR_FUNCTION_FAILED;
1222
1223 if (!child->Feature_Parent)
1224 return ERROR_SUCCESS;
1225
1226 parent = find_feature_by_name( package, child->Feature_Parent );
1227 if (!parent)
1228 return ERROR_FUNCTION_FAILED;
1229
1230 add_feature_child( parent, child );
1231 return ERROR_SUCCESS;
1232 }
1233
1234 static UINT load_all_features( MSIPACKAGE *package )
1235 {
1236 static const WCHAR query[] = {
1237 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1238 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1239 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1240 MSIQUERY *view;
1241 UINT r;
1242
1243 if (!list_empty(&package->features))
1244 return ERROR_SUCCESS;
1245
1246 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1247 if (r != ERROR_SUCCESS)
1248 return r;
1249
1250 r = MSI_IterateRecords( view, NULL, load_feature, package );
1251 if (r != ERROR_SUCCESS)
1252 return r;
1253
1254 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1255 msiobj_release( &view->hdr );
1256
1257 return r;
1258 }
1259
1260 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1261 {
1262 if (!p)
1263 return p;
1264 p = strchrW(p, ch);
1265 if (!p)
1266 return p;
1267 *p = 0;
1268 return p+1;
1269 }
1270
1271 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1272 {
1273 static const WCHAR query[] = {
1274 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1275 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1276 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1277 MSIQUERY *view = NULL;
1278 MSIRECORD *row = NULL;
1279 UINT r;
1280
1281 TRACE("%s\n", debugstr_w(file->File));
1282
1283 r = MSI_OpenQuery(package->db, &view, query, file->File);
1284 if (r != ERROR_SUCCESS)
1285 goto done;
1286
1287 r = MSI_ViewExecute(view, NULL);
1288 if (r != ERROR_SUCCESS)
1289 goto done;
1290
1291 r = MSI_ViewFetch(view, &row);
1292 if (r != ERROR_SUCCESS)
1293 goto done;
1294
1295 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1296 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1297 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1298 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1299 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1300
1301 done:
1302 if (view) msiobj_release(&view->hdr);
1303 if (row) msiobj_release(&row->hdr);
1304 return r;
1305 }
1306
1307 static UINT load_file(MSIRECORD *row, LPVOID param)
1308 {
1309 MSIPACKAGE* package = param;
1310 LPCWSTR component;
1311 MSIFILE *file;
1312
1313 /* fill in the data */
1314
1315 file = msi_alloc_zero( sizeof (MSIFILE) );
1316 if (!file)
1317 return ERROR_NOT_ENOUGH_MEMORY;
1318
1319 file->File = msi_dup_record_field( row, 1 );
1320
1321 component = MSI_RecordGetString( row, 2 );
1322 file->Component = get_loaded_component( package, component );
1323
1324 if (!file->Component)
1325 {
1326 WARN("Component not found: %s\n", debugstr_w(component));
1327 msi_free(file->File);
1328 msi_free(file);
1329 return ERROR_SUCCESS;
1330 }
1331
1332 file->FileName = msi_dup_record_field( row, 3 );
1333 reduce_to_longfilename( file->FileName );
1334
1335 file->ShortName = msi_dup_record_field( row, 3 );
1336 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1337
1338 file->FileSize = MSI_RecordGetInteger( row, 4 );
1339 file->Version = msi_dup_record_field( row, 5 );
1340 file->Language = msi_dup_record_field( row, 6 );
1341 file->Attributes = MSI_RecordGetInteger( row, 7 );
1342 file->Sequence = MSI_RecordGetInteger( row, 8 );
1343
1344 file->state = msifs_invalid;
1345
1346 /* if the compressed bits are not set in the file attributes,
1347 * then read the information from the package word count property
1348 */
1349 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1350 {
1351 file->IsCompressed = FALSE;
1352 }
1353 else if (file->Attributes &
1354 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1355 {
1356 file->IsCompressed = TRUE;
1357 }
1358 else if (file->Attributes & msidbFileAttributesNoncompressed)
1359 {
1360 file->IsCompressed = FALSE;
1361 }
1362 else
1363 {
1364 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1365 }
1366
1367 load_file_hash(package, file);
1368
1369 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1370
1371 list_add_tail( &package->files, &file->entry );
1372
1373 return ERROR_SUCCESS;
1374 }
1375
1376 static UINT load_all_files(MSIPACKAGE *package)
1377 {
1378 MSIQUERY * view;
1379 UINT rc;
1380 static const WCHAR Query[] =
1381 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1382 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1383 '`','S','e','q','u','e','n','c','e','`', 0};
1384
1385 if (!list_empty(&package->files))
1386 return ERROR_SUCCESS;
1387
1388 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1389 if (rc != ERROR_SUCCESS)
1390 return ERROR_SUCCESS;
1391
1392 rc = MSI_IterateRecords(view, NULL, load_file, package);
1393 msiobj_release(&view->hdr);
1394
1395 return ERROR_SUCCESS;
1396 }
1397
1398 static UINT load_folder( MSIRECORD *row, LPVOID param )
1399 {
1400 MSIPACKAGE *package = param;
1401 static WCHAR szEmpty[] = { 0 };
1402 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1403 MSIFOLDER *folder;
1404
1405 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1406 if (!folder)
1407 return ERROR_NOT_ENOUGH_MEMORY;
1408
1409 folder->Directory = msi_dup_record_field( row, 1 );
1410
1411 TRACE("%s\n", debugstr_w(folder->Directory));
1412
1413 p = msi_dup_record_field(row, 3);
1414
1415 /* split src and target dir */
1416 tgt_short = p;
1417 src_short = folder_split_path( p, ':' );
1418
1419 /* split the long and short paths */
1420 tgt_long = folder_split_path( tgt_short, '|' );
1421 src_long = folder_split_path( src_short, '|' );
1422
1423 /* check for no-op dirs */
1424 if (!lstrcmpW(szDot, tgt_short))
1425 tgt_short = szEmpty;
1426 if (!lstrcmpW(szDot, src_short))
1427 src_short = szEmpty;
1428
1429 if (!tgt_long)
1430 tgt_long = tgt_short;
1431
1432 if (!src_short) {
1433 src_short = tgt_short;
1434 src_long = tgt_long;
1435 }
1436
1437 if (!src_long)
1438 src_long = src_short;
1439
1440 /* FIXME: use the target short path too */
1441 folder->TargetDefault = strdupW(tgt_long);
1442 folder->SourceShortPath = strdupW(src_short);
1443 folder->SourceLongPath = strdupW(src_long);
1444 msi_free(p);
1445
1446 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1447 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1448 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1449
1450 folder->Parent = msi_dup_record_field( row, 2 );
1451
1452 folder->Property = msi_dup_property( package, folder->Directory );
1453
1454 list_add_tail( &package->folders, &folder->entry );
1455
1456 TRACE("returning %p\n", folder);
1457
1458 return ERROR_SUCCESS;
1459 }
1460
1461 static UINT load_all_folders( MSIPACKAGE *package )
1462 {
1463 static const WCHAR query[] = {
1464 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1465 '`','D','i','r','e','c','t','o','r','y','`',0 };
1466 MSIQUERY *view;
1467 UINT r;
1468
1469 if (!list_empty(&package->folders))
1470 return ERROR_SUCCESS;
1471
1472 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1473 if (r != ERROR_SUCCESS)
1474 return r;
1475
1476 r = MSI_IterateRecords(view, NULL, load_folder, package);
1477 msiobj_release(&view->hdr);
1478 return r;
1479 }
1480
1481 /*
1482 * I am not doing any of the costing functionality yet.
1483 * Mostly looking at doing the Component and Feature loading
1484 *
1485 * The native MSI does A LOT of modification to tables here. Mostly adding
1486 * a lot of temporary columns to the Feature and Component tables.
1487 *
1488 * note: Native msi also tracks the short filename. But I am only going to
1489 * track the long ones. Also looking at this directory table
1490 * it appears that the directory table does not get the parents
1491 * resolved base on property only based on their entries in the
1492 * directory table.
1493 */
1494 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1495 {
1496 static const WCHAR szCosting[] =
1497 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1498
1499 MSI_SetPropertyW(package, szCosting, szZero);
1500 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1501
1502 load_all_folders( package );
1503 load_all_components( package );
1504 load_all_features( package );
1505 load_all_files( package );
1506
1507 return ERROR_SUCCESS;
1508 }
1509
1510 static UINT execute_script(MSIPACKAGE *package, UINT script )
1511 {
1512 UINT i;
1513 UINT rc = ERROR_SUCCESS;
1514
1515 TRACE("Executing Script %i\n",script);
1516
1517 if (!package->script)
1518 {
1519 ERR("no script!\n");
1520 return ERROR_FUNCTION_FAILED;
1521 }
1522
1523 for (i = 0; i < package->script->ActionCount[script]; i++)
1524 {
1525 LPWSTR action;
1526 action = package->script->Actions[script][i];
1527 ui_actionstart(package, action);
1528 TRACE("Executing Action (%s)\n",debugstr_w(action));
1529 rc = ACTION_PerformAction(package, action, script, TRUE);
1530 if (rc != ERROR_SUCCESS)
1531 break;
1532 }
1533 msi_free_action_script(package, script);
1534 return rc;
1535 }
1536
1537 static UINT ACTION_FileCost(MSIPACKAGE *package)
1538 {
1539 return ERROR_SUCCESS;
1540 }
1541
1542 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1543 {
1544 MSICOMPONENT *comp;
1545 INSTALLSTATE state;
1546 UINT r;
1547
1548 state = MsiQueryProductStateW(package->ProductCode);
1549
1550 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1551 {
1552 if (!comp->ComponentId)
1553 continue;
1554
1555 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1556 comp->Installed = INSTALLSTATE_ABSENT;
1557 else
1558 {
1559 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1560 package->Context, comp->ComponentId,
1561 &comp->Installed);
1562 if (r != ERROR_SUCCESS)
1563 comp->Installed = INSTALLSTATE_ABSENT;
1564 }
1565 }
1566 }
1567
1568 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1569 {
1570 MSIFEATURE *feature;
1571 INSTALLSTATE state;
1572
1573 state = MsiQueryProductStateW(package->ProductCode);
1574
1575 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1576 {
1577 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1578 feature->Installed = INSTALLSTATE_ABSENT;
1579 else
1580 {
1581 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1582 feature->Feature);
1583 }
1584 }
1585 }
1586
1587 static BOOL process_state_property(MSIPACKAGE* package, int level,
1588 LPCWSTR property, INSTALLSTATE state)
1589 {
1590 LPWSTR override;
1591 MSIFEATURE *feature;
1592
1593 override = msi_dup_property( package, property );
1594 if (!override)
1595 return FALSE;
1596
1597 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1598 {
1599 if (lstrcmpW(property, szRemove) &&
1600 (feature->Level <= 0 || feature->Level > level))
1601 continue;
1602
1603 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1604
1605 if (strcmpiW(override, szAll)==0)
1606 msi_feature_set_state(package, feature, state);
1607 else
1608 {
1609 LPWSTR ptr = override;
1610 LPWSTR ptr2 = strchrW(override,',');
1611
1612 while (ptr)
1613 {
1614 int len = ptr2 - ptr;
1615
1616 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1617 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1618 {
1619 msi_feature_set_state(package, feature, state);
1620 break;
1621 }
1622 if (ptr2)
1623 {
1624 ptr=ptr2+1;
1625 ptr2 = strchrW(ptr,',');
1626 }
1627 else
1628 break;
1629 }
1630 }
1631 }
1632 msi_free(override);
1633
1634 return TRUE;
1635 }
1636
1637 static BOOL process_overrides( MSIPACKAGE *package, int level )
1638 {
1639 static const WCHAR szAddLocal[] =
1640 {'A','D','D','L','O','C','A','L',0};
1641 static const WCHAR szAddSource[] =
1642 {'A','D','D','S','O','U','R','C','E',0};
1643 static const WCHAR szAdvertise[] =
1644 {'A','D','V','E','R','T','I','S','E',0};
1645 BOOL ret = FALSE;
1646
1647 /* all these activation/deactivation things happen in order and things
1648 * later on the list override things earlier on the list.
1649 *
1650 * 0 INSTALLLEVEL processing
1651 * 1 ADDLOCAL
1652 * 2 REMOVE
1653 * 3 ADDSOURCE
1654 * 4 ADDDEFAULT
1655 * 5 REINSTALL
1656 * 6 ADVERTISE
1657 * 7 COMPADDLOCAL
1658 * 8 COMPADDSOURCE
1659 * 9 FILEADDLOCAL
1660 * 10 FILEADDSOURCE
1661 * 11 FILEADDDEFAULT
1662 */
1663 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1664 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1665 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1666 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1667 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1668
1669 if (ret)
1670 MSI_SetPropertyW( package, szPreselected, szOne );
1671
1672 return ret;
1673 }
1674
1675 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1676 {
1677 int level;
1678 static const WCHAR szlevel[] =
1679 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1680 MSICOMPONENT* component;
1681 MSIFEATURE *feature;
1682
1683 TRACE("Checking Install Level\n");
1684
1685 level = msi_get_property_int(package, szlevel, 1);
1686
1687 if (!msi_get_property_int( package, szPreselected, 0 ))
1688 {
1689 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1690 {
1691 BOOL feature_state = ((feature->Level > 0) &&
1692 (feature->Level <= level));
1693
1694 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1695 {
1696 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1697 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1698 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1699 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1700 else
1701 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1702 }
1703 }
1704
1705 /* disable child features of unselected parent features */
1706 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1707 {
1708 FeatureList *fl;
1709
1710 if (feature->Level > 0 && feature->Level <= level)
1711 continue;
1712
1713 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1714 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1715 }
1716 }
1717
1718 /*
1719 * now we want to enable or disable components base on feature
1720 */
1721
1722 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1723 {
1724 ComponentList *cl;
1725
1726 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1727 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1728
1729 if (!feature->Level)
1730 continue;
1731
1732 /* features with components that have compressed files are made local */
1733 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1734 {
1735 if (cl->component->Enabled &&
1736 cl->component->ForceLocalState &&
1737 feature->Action == INSTALLSTATE_SOURCE)
1738 {
1739 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1740 break;
1741 }
1742 }
1743
1744 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1745 {
1746 component = cl->component;
1747
1748 if (!component->Enabled)
1749 continue;
1750
1751 switch (feature->Action)
1752 {
1753 case INSTALLSTATE_ABSENT:
1754 component->anyAbsent = 1;
1755 break;
1756 case INSTALLSTATE_ADVERTISED:
1757 component->hasAdvertiseFeature = 1;
1758 break;
1759 case INSTALLSTATE_SOURCE:
1760 component->hasSourceFeature = 1;
1761 break;
1762 case INSTALLSTATE_LOCAL:
1763 component->hasLocalFeature = 1;
1764 break;
1765 case INSTALLSTATE_DEFAULT:
1766 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1767 component->hasAdvertiseFeature = 1;
1768 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 component->hasSourceFeature = 1;
1770 else
1771 component->hasLocalFeature = 1;
1772 break;
1773 default:
1774 break;
1775 }
1776 }
1777 }
1778
1779 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1780 {
1781 /* if the component isn't enabled, leave it alone */
1782 if (!component->Enabled)
1783 continue;
1784
1785 /* check if it's local or source */
1786 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1787 (component->hasLocalFeature || component->hasSourceFeature))
1788 {
1789 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1790 !component->ForceLocalState)
1791 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1792 else
1793 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1794 continue;
1795 }
1796
1797 /* if any feature is local, the component must be local too */
1798 if (component->hasLocalFeature)
1799 {
1800 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1801 continue;
1802 }
1803
1804 if (component->hasSourceFeature)
1805 {
1806 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1807 continue;
1808 }
1809
1810 if (component->hasAdvertiseFeature)
1811 {
1812 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1813 continue;
1814 }
1815
1816 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1817 if (component->anyAbsent)
1818 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1819 }
1820
1821 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1822 {
1823 if (component->Action == INSTALLSTATE_DEFAULT)
1824 {
1825 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1826 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1827 }
1828
1829 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1830 debugstr_w(component->Component), component->Installed, component->Action);
1831 }
1832
1833
1834 return ERROR_SUCCESS;
1835 }
1836
1837 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1838 {
1839 MSIPACKAGE *package = param;
1840 LPCWSTR name;
1841 LPWSTR path;
1842 MSIFOLDER *f;
1843
1844 name = MSI_RecordGetString(row,1);
1845
1846 f = get_loaded_folder(package, name);
1847 if (!f) return ERROR_SUCCESS;
1848
1849 /* reset the ResolvedTarget */
1850 msi_free(f->ResolvedTarget);
1851 f->ResolvedTarget = NULL;
1852
1853 /* This helper function now does ALL the work */
1854 TRACE("Dir %s ...\n",debugstr_w(name));
1855 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1856 TRACE("resolves to %s\n",debugstr_w(path));
1857 msi_free(path);
1858
1859 return ERROR_SUCCESS;
1860 }
1861
1862 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1863 {
1864 MSIPACKAGE *package = param;
1865 LPCWSTR name;
1866 MSIFEATURE *feature;
1867
1868 name = MSI_RecordGetString( row, 1 );
1869
1870 feature = get_loaded_feature( package, name );
1871 if (!feature)
1872 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1873 else
1874 {
1875 LPCWSTR Condition;
1876 Condition = MSI_RecordGetString(row,3);
1877
1878 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1879 {
1880 int level = MSI_RecordGetInteger(row,2);
1881 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1882 feature->Level = level;
1883 }
1884 }
1885 return ERROR_SUCCESS;
1886 }
1887
1888 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1889 {
1890 static const WCHAR name_fmt[] =
1891 {'%','u','.','%','u','.','%','u','.','%','u',0};
1892 static const WCHAR name[] = {'\\',0};
1893 VS_FIXEDFILEINFO *lpVer;
1894 WCHAR filever[0x100];
1895 LPVOID version;
1896 DWORD versize;
1897 DWORD handle;
1898 UINT sz;
1899
1900 TRACE("%s\n", debugstr_w(filename));
1901
1902 versize = GetFileVersionInfoSizeW( filename, &handle );
1903 if (!versize)
1904 return NULL;
1905
1906 version = msi_alloc( versize );
1907 GetFileVersionInfoW( filename, 0, versize, version );
1908
1909 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1910 {
1911 msi_free( version );
1912 return NULL;
1913 }
1914
1915 sprintfW( filever, name_fmt,
1916 HIWORD(lpVer->dwFileVersionMS),
1917 LOWORD(lpVer->dwFileVersionMS),
1918 HIWORD(lpVer->dwFileVersionLS),
1919 LOWORD(lpVer->dwFileVersionLS));
1920
1921 msi_free( version );
1922
1923 return strdupW( filever );
1924 }
1925
1926 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1927 {
1928 LPWSTR file_version;
1929 MSIFILE *file;
1930
1931 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1932 {
1933 MSICOMPONENT* comp = file->Component;
1934 LPWSTR p;
1935
1936 if (!comp)
1937 continue;
1938
1939 if (file->IsCompressed)
1940 comp->ForceLocalState = TRUE;
1941
1942 /* calculate target */
1943 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1944
1945 msi_free(file->TargetPath);
1946
1947 TRACE("file %s is named %s\n",
1948 debugstr_w(file->File), debugstr_w(file->FileName));
1949
1950 file->TargetPath = build_directory_name(2, p, file->FileName);
1951
1952 msi_free(p);
1953
1954 TRACE("file %s resolves to %s\n",
1955 debugstr_w(file->File), debugstr_w(file->TargetPath));
1956
1957 /* don't check files of components that aren't installed */
1958 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1959 comp->Installed == INSTALLSTATE_ABSENT)
1960 {
1961 file->state = msifs_missing; /* assume files are missing */
1962 continue;
1963 }
1964
1965 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1966 {
1967 file->state = msifs_missing;
1968 comp->Cost += file->FileSize;
1969 continue;
1970 }
1971
1972 if (file->Version &&
1973 (file_version = msi_get_disk_file_version( file->TargetPath )))
1974 {
1975 TRACE("new %s old %s\n", debugstr_w(file->Version),
1976 debugstr_w(file_version));
1977 /* FIXME: seems like a bad way to compare version numbers */
1978 if (lstrcmpiW(file_version, file->Version)<0)
1979 {
1980 file->state = msifs_overwrite;
1981 comp->Cost += file->FileSize;
1982 }
1983 else
1984 file->state = msifs_present;
1985 msi_free( file_version );
1986 }
1987 else
1988 file->state = msifs_present;
1989 }
1990
1991 return ERROR_SUCCESS;
1992 }
1993
1994 /*
1995 * A lot is done in this function aside from just the costing.
1996 * The costing needs to be implemented at some point but for now I am going
1997 * to focus on the directory building
1998 *
1999 */
2000 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2001 {
2002 static const WCHAR ExecSeqQuery[] =
2003 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2004 '`','D','i','r','e','c','t','o','r','y','`',0};
2005 static const WCHAR ConditionQuery[] =
2006 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2007 '`','C','o','n','d','i','t','i','o','n','`',0};
2008 static const WCHAR szCosting[] =
2009 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2010 static const WCHAR szlevel[] =
2011 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2012 static const WCHAR szOutOfDiskSpace[] =
2013 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2014 MSICOMPONENT *comp;
2015 UINT rc = ERROR_SUCCESS;
2016 MSIQUERY * view;
2017 LPWSTR level;
2018
2019 TRACE("Building Directory properties\n");
2020
2021 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2022 if (rc == ERROR_SUCCESS)
2023 {
2024 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2025 package);
2026 msiobj_release(&view->hdr);
2027 }
2028
2029 /* read components states from the registry */
2030 ACTION_GetComponentInstallStates(package);
2031 ACTION_GetFeatureInstallStates(package);
2032
2033 TRACE("File calculations\n");
2034 msi_check_file_install_states( package );
2035
2036 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2037 {
2038 TRACE("Evaluating Condition Table\n");
2039
2040 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2041 if (rc == ERROR_SUCCESS)
2042 {
2043 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2044 msiobj_release( &view->hdr );
2045 }
2046
2047 TRACE("Enabling or Disabling Components\n");
2048 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2049 {
2050 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2051 {
2052 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2053 comp->Enabled = FALSE;
2054 }
2055 else
2056 comp->Enabled = TRUE;
2057 }
2058 }
2059
2060 MSI_SetPropertyW(package,szCosting,szOne);
2061 /* set default run level if not set */
2062 level = msi_dup_property( package, szlevel );
2063 if (!level)
2064 MSI_SetPropertyW(package,szlevel, szOne);
2065 msi_free(level);
2066
2067 /* FIXME: check volume disk space */
2068 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2069
2070 return MSI_SetFeatureStates(package);
2071 }
2072
2073 /* OK this value is "interpreted" and then formatted based on the
2074 first few characters */
2075 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2076 DWORD *size)
2077 {
2078 LPSTR data = NULL;
2079
2080 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2081 {
2082 if (value[1]=='x')
2083 {
2084 LPWSTR ptr;
2085 CHAR byte[5];
2086 LPWSTR deformated = NULL;
2087 int count;
2088
2089 deformat_string(package, &value[2], &deformated);
2090
2091 /* binary value type */
2092 ptr = deformated;
2093 *type = REG_BINARY;
2094 if (strlenW(ptr)%2)
2095 *size = (strlenW(ptr)/2)+1;
2096 else
2097 *size = strlenW(ptr)/2;
2098
2099 data = msi_alloc(*size);
2100
2101 byte[0] = '0';
2102 byte[1] = 'x';
2103 byte[4] = 0;
2104 count = 0;
2105 /* if uneven pad with a zero in front */
2106 if (strlenW(ptr)%2)
2107 {
2108 byte[2]= '0';
2109 byte[3]= *ptr;
2110 ptr++;
2111 data[count] = (BYTE)strtol(byte,NULL,0);
2112 count ++;
2113 TRACE("Uneven byte count\n");
2114 }
2115 while (*ptr)
2116 {
2117 byte[2]= *ptr;
2118 ptr++;
2119 byte[3]= *ptr;
2120 ptr++;
2121 data[count] = (BYTE)strtol(byte,NULL,0);
2122 count ++;
2123 }
2124 msi_free(deformated);
2125
2126 TRACE("Data %i bytes(%i)\n",*size,count);
2127 }
2128 else
2129 {
2130 LPWSTR deformated;
2131 LPWSTR p;
2132 DWORD d = 0;
2133 deformat_string(package, &value[1], &deformated);
2134
2135 *type=REG_DWORD;
2136 *size = sizeof(DWORD);
2137 data = msi_alloc(*size);
2138 p = deformated;
2139 if (*p == '-')
2140 p++;
2141 while (*p)
2142 {
2143 if ( (*p < '0') || (*p > '9') )
2144 break;
2145 d *= 10;
2146 d += (*p - '0');
2147 p++;
2148 }
2149 if (deformated[0] == '-')
2150 d = -d;
2151 *(LPDWORD)data = d;
2152 TRACE("DWORD %i\n",*(LPDWORD)data);
2153
2154 msi_free(deformated);
2155 }
2156 }
2157 else
2158 {
2159 static const WCHAR szMulti[] = {'[','~',']',0};
2160 LPCWSTR ptr;
2161 *type=REG_SZ;
2162
2163 if (value[0]=='#')
2164 {
2165 if (value[1]=='%')
2166 {
2167 ptr = &value[2];
2168 *type=REG_EXPAND_SZ;
2169 }
2170 else
2171 ptr = &value[1];
2172 }
2173 else
2174 ptr=value;
2175
2176 if (strstrW(value,szMulti))
2177 *type = REG_MULTI_SZ;
2178
2179 /* remove initial delimiter */
2180 if (!strncmpW(value, szMulti, 3))
2181 ptr = value + 3;
2182
2183 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2184
2185 /* add double NULL terminator */
2186 if (*type == REG_MULTI_SZ)
2187 {
2188 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2189 data = msi_realloc_zero(data, *size);
2190 }
2191 }
2192 return data;
2193 }
2194
2195 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2196 {
2197 MSIPACKAGE *package = param;
2198 static const WCHAR szHCR[] =
2199 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2200 'R','O','O','T','\\',0};
2201 static const WCHAR szHCU[] =
2202 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2203 'U','S','E','R','\\',0};
2204 static const WCHAR szHLM[] =
2205 {'H','K','E','Y','_','L','O','C','A','L','_',
2206 'M','A','C','H','I','N','E','\\',0};
2207 static const WCHAR szHU[] =
2208 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2209
2210 LPSTR value_data = NULL;
2211 HKEY root_key, hkey;
2212 DWORD type,size;
2213 LPWSTR deformated;
2214 LPCWSTR szRoot, component, name, key, value;
2215 MSICOMPONENT *comp;
2216 MSIRECORD * uirow;
2217 LPWSTR uikey;
2218 INT root;
2219 BOOL check_first = FALSE;
2220 UINT rc;
2221
2222 ui_progress(package,2,0,0,0);
2223
2224 value = NULL;
2225 key = NULL;
2226 uikey = NULL;
2227 name = NULL;
2228
2229 component = MSI_RecordGetString(row, 6);
2230 comp = get_loaded_component(package,component);
2231 if (!comp)
2232 return ERROR_SUCCESS;
2233
2234 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2235 {
2236 TRACE("Skipping write due to disabled component %s\n",
2237 debugstr_w(component));
2238
2239 comp->Action = comp->Installed;
2240
2241 return ERROR_SUCCESS;
2242 }
2243
2244 comp->Action = INSTALLSTATE_LOCAL;
2245
2246 name = MSI_RecordGetString(row, 4);
2247 if( MSI_RecordIsNull(row,5) && name )
2248 {
2249 /* null values can have special meanings */
2250 if (name[0]=='-' && name[1] == 0)
2251 return ERROR_SUCCESS;
2252 else if ((name[0]=='+' && name[1] == 0) ||
2253 (name[0] == '*' && name[1] == 0))
2254 name = NULL;
2255 check_first = TRUE;
2256 }
2257
2258 root = MSI_RecordGetInteger(row,2);
2259 key = MSI_RecordGetString(row, 3);
2260
2261 /* get the root key */
2262 switch (root)
2263 {
2264 case -1:
2265 {
2266 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2267 if (all_users && all_users[0] == '1')
2268 {
2269 root_key = HKEY_LOCAL_MACHINE;
2270 szRoot = szHLM;
2271 }
2272 else
2273 {
2274 root_key = HKEY_CURRENT_USER;
2275 szRoot = szHCU;
2276 }
2277 msi_free(all_users);
2278 }
2279 break;
2280 case 0: root_key = HKEY_CLASSES_ROOT;
2281 szRoot = szHCR;
2282 break;
2283 case 1: root_key = HKEY_CURRENT_USER;
2284 szRoot = szHCU;
2285 break;
2286 case 2: root_key = HKEY_LOCAL_MACHINE;
2287 szRoot = szHLM;
2288 break;
2289 case 3: root_key = HKEY_USERS;
2290 szRoot = szHU;
2291 break;
2292 default:
2293 ERR("Unknown root %i\n",root);
2294 root_key=NULL;
2295 szRoot = NULL;
2296 break;
2297 }
2298 if (!root_key)
2299 return ERROR_SUCCESS;
2300
2301 deformat_string(package, key , &deformated);
2302 size = strlenW(deformated) + strlenW(szRoot) + 1;
2303 uikey = msi_alloc(size*sizeof(WCHAR));
2304 strcpyW(uikey,szRoot);
2305 strcatW(uikey,deformated);
2306
2307 if (RegCreateKeyW( root_key, deformated, &hkey))
2308 {
2309 ERR("Could not create key %s\n",debugstr_w(deformated));
2310 msi_free(deformated);
2311 msi_free(uikey);
2312 return ERROR_SUCCESS;
2313 }
2314 msi_free(deformated);
2315
2316 value = MSI_RecordGetString(row,5);
2317 if (value)
2318 value_data = parse_value(package, value, &type, &size);
2319 else
2320 {
2321 value_data = (LPSTR)strdupW(szEmpty);
2322 size = sizeof(szEmpty);
2323 type = REG_SZ;
2324 }
2325
2326 deformat_string(package, name, &deformated);
2327
2328 if (!check_first)
2329 {
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2331 debugstr_w(uikey));
2332 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2333 }
2334 else
2335 {
2336 DWORD sz = 0;
2337 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2338 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2339 {
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated), debugstr_w(uikey));
2342 }
2343 else
2344 {
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated), debugstr_w(uikey));
2347 if (deformated || size)
2348 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2349 }
2350 }
2351 RegCloseKey(hkey);
2352
2353 uirow = MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow,2,deformated);
2355 MSI_RecordSetStringW(uirow,1,uikey);
2356
2357 if (type == REG_SZ)
2358 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2359 else
2360 MSI_RecordSetStringW(uirow,3,value);
2361
2362 ui_actiondata(package,szWriteRegistryValues,uirow);
2363 msiobj_release( &uirow->hdr );
2364
2365 msi_free(value_data);
2366 msi_free(deformated);
2367 msi_free(uikey);
2368
2369 return ERROR_SUCCESS;
2370 }
2371
2372 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2373 {
2374 UINT rc;
2375 MSIQUERY * view;
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','`',0 };
2379
2380 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2381 if (rc != ERROR_SUCCESS)
2382 return ERROR_SUCCESS;
2383
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2386
2387 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2388
2389 msiobj_release(&view->hdr);
2390 return rc;
2391 }
2392
2393 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2394 {
2395 package->script->CurrentlyScripting = TRUE;
2396
2397 return ERROR_SUCCESS;
2398 }
2399
2400
2401 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2402 {
2403 MSICOMPONENT *comp;
2404 DWORD progress = 0;
2405 DWORD total = 0;
2406 static const WCHAR q1[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2409 UINT rc;
2410 MSIQUERY * view;
2411 MSIFEATURE *feature;
2412 MSIFILE *file;
2413
2414 TRACE("InstallValidate\n");
2415
2416 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2417 if (rc == ERROR_SUCCESS)
2418 {
2419 MSI_IterateRecords( view, &progress, NULL, package );
2420 msiobj_release( &view->hdr );
2421 total += progress * REG_PROGRESS_VALUE;
2422 }
2423
2424 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2425 total += COMPONENT_PROGRESS_VALUE;
2426
2427 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2428 total += file->FileSize;
2429
2430 ui_progress(package,0,total,0,0);
2431
2432 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2433 {
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2436 feature->ActionRequest);
2437 }
2438
2439 return ERROR_SUCCESS;
2440 }
2441
2442 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2443 {
2444 MSIPACKAGE* package = param;
2445 LPCWSTR cond = NULL;
2446 LPCWSTR message = NULL;
2447 UINT r;
2448
2449 static const WCHAR title[]=
2450 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2451
2452 cond = MSI_RecordGetString(row,1);
2453
2454 r = MSI_EvaluateConditionW(package,cond);
2455 if (r == MSICONDITION_FALSE)
2456 {
2457 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2458 {
2459 LPWSTR deformated;
2460 message = MSI_RecordGetString(row,2);
2461 deformat_string(package,message,&deformated);
2462 MessageBoxW(NULL,deformated,title,MB_OK);
2463 msi_free(deformated);
2464 }
2465
2466 return ERROR_INSTALL_FAILURE;
2467 }
2468
2469 return ERROR_SUCCESS;
2470 }
2471
2472 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2473 {
2474 UINT rc;
2475 MSIQUERY * view = NULL;
2476 static const WCHAR ExecSeqQuery[] =
2477 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2478 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2479
2480 TRACE("Checking launch conditions\n");
2481
2482 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2483 if (rc != ERROR_SUCCESS)
2484 return ERROR_SUCCESS;
2485
2486 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2487 msiobj_release(&view->hdr);
2488
2489 return rc;
2490 }
2491
2492 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2493 {
2494
2495 if (!cmp->KeyPath)
2496 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2497
2498 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2499 {
2500 MSIRECORD * row = 0;
2501 UINT root,len;
2502 LPWSTR deformated,buffer,deformated_name;
2503 LPCWSTR key,name;
2504 static const WCHAR ExecSeqQuery[] =
2505 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2506 '`','R','e','g','i','s','t','r','y','`',' ',
2507 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2508 ' ','=',' ' ,'\'','%','s','\'',0 };
2509 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2510 static const WCHAR fmt2[]=
2511 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2512
2513 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2514 if (!row)
2515 return NULL;
2516
2517 root = MSI_RecordGetInteger(row,2);
2518 key = MSI_RecordGetString(row, 3);
2519 name = MSI_RecordGetString(row, 4);
2520 deformat_string(package, key , &deformated);
2521 deformat_string(package, name, &deformated_name);
2522
2523 len = strlenW(deformated) + 6;
2524 if (deformated_name)
2525 len+=strlenW(deformated_name);
2526
2527 buffer = msi_alloc( len *sizeof(WCHAR));
2528
2529 if (deformated_name)
2530 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2531 else
2532 sprintfW(buffer,fmt,root,deformated);
2533
2534 msi_free(deformated);
2535 msi_free(deformated_name);
2536 msiobj_release(&row->hdr);
2537
2538 return buffer;
2539 }
2540 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2541 {
2542 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2543 return NULL;
2544 }
2545 else
2546 {
2547 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2548
2549 if (file)
2550 return strdupW( file->TargetPath );
2551 }
2552 return NULL;
2553 }
2554
2555 static HKEY openSharedDLLsKey(void)
2556 {
2557 HKEY hkey=0;
2558 static const WCHAR path[] =
2559 {'S','o','f','t','w','a','r','e','\\',
2560 'M','i','c','r','o','s','o','f','t','\\',
2561 'W','i','n','d','o','w','s','\\',
2562 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2563 'S','h','a','r','e','d','D','L','L','s',0};
2564
2565 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2566 return hkey;
2567 }
2568
2569 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2570 {
2571 HKEY hkey;
2572 DWORD count=0;
2573 DWORD type;
2574 DWORD sz = sizeof(count);
2575 DWORD rc;
2576
2577 hkey = openSharedDLLsKey();
2578 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2579 if (rc != ERROR_SUCCESS)
2580 count = 0;
2581 RegCloseKey(hkey);
2582 return count;
2583 }
2584
2585 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2586 {
2587 HKEY hkey;
2588
2589 hkey = openSharedDLLsKey();
2590 if (count > 0)
2591 msi_reg_set_val_dword( hkey, path, count );
2592 else
2593 RegDeleteValueW(hkey,path);
2594 RegCloseKey(hkey);
2595 return count;
2596 }
2597
2598 /*
2599 * Return TRUE if the count should be written out and FALSE if not
2600 */
2601 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2602 {
2603 MSIFEATURE *feature;
2604 INT count = 0;
2605 BOOL write = FALSE;
2606
2607 /* only refcount DLLs */
2608 if (comp->KeyPath == NULL ||
2609 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2610 comp->Attributes & msidbComponentAttributesODBCDataSource)
2611 write = FALSE;
2612 else
2613 {
2614 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2615 write = (count > 0);
2616
2617 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2618 write = TRUE;
2619 }
2620
2621 /* increment counts */
2622 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2623 {
2624 ComponentList *cl;
2625
2626 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2627 continue;
2628
2629 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2630 {
2631 if ( cl->component == comp )
2632 count++;
2633 }
2634 }
2635
2636 /* decrement counts */
2637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2638 {
2639 ComponentList *cl;
2640
2641 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2642 continue;
2643
2644 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2645 {
2646 if ( cl->component == comp )
2647 count--;
2648 }
2649 }
2650
2651 /* ref count all the files in the component */
2652 if (write)
2653 {
2654 MSIFILE *file;
2655
2656 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2657 {
2658 if (file->Component == comp)
2659 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2660 }
2661 }
2662
2663 /* add a count for permanent */
2664 if (comp->Attributes & msidbComponentAttributesPermanent)
2665 count ++;
2666
2667 comp->RefCount = count;
2668
2669 if (write)
2670 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2671 }
2672
2673 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2674 {
2675 WCHAR squished_pc[GUID_SIZE];
2676 WCHAR squished_cc[GUID_SIZE];
2677 UINT rc;
2678 MSICOMPONENT *comp;
2679 HKEY hkey;
2680
2681 TRACE("\n");
2682
2683 squash_guid(package->ProductCode,squished_pc);
2684 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2685
2686 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2687 {
2688 MSIRECORD * uirow;
2689
2690 ui_progress(package,2,0,0,0);
2691 if (!comp->ComponentId)
2692 continue;
2693
2694 squash_guid(comp->ComponentId,squished_cc);
2695
2696 msi_free(comp->FullKeypath);
2697 comp->FullKeypath = resolve_keypath( package, comp );
2698
2699 ACTION_RefCountComponent( package, comp );
2700
2701 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2702 debugstr_w(comp->Component),
2703 debugstr_w(squished_cc),
2704 debugstr_w(comp->FullKeypath),
2705 comp->RefCount);
2706
2707 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2708 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2709 {
2710 if (!comp->FullKeypath)
2711 continue;
2712
2713 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2714 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2715 &hkey, TRUE);
2716 else
2717 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2718 &hkey, TRUE);
2719
2720 if (rc != ERROR_SUCCESS)
2721 continue;
2722
2723 if (comp->Attributes & msidbComponentAttributesPermanent)
2724 {
2725 static const WCHAR szPermKey[] =
2726 { '0','0','0','0','0','0','0','0','0','0','0','0',
2727 '0','0','0','0','0','0','0','0','0','0','0','0',
2728 '0','0','0','0','0','0','0','0',0 };
2729
2730 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2731 }
2732
2733 if (comp->Action == INSTALLSTATE_LOCAL)
2734 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2735 else
2736 {
2737 MSIFILE *file;
2738 MSIRECORD *row;
2739 LPWSTR ptr, ptr2;
2740 WCHAR source[MAX_PATH];
2741 WCHAR base[MAX_PATH];
2742 LPWSTR sourcepath;
2743
2744 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2745 static const WCHAR query[] = {
2746 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2747 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2748 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2749 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2750 '`','D','i','s','k','I','d','`',0};
2751
2752 file = get_loaded_file(package, comp->KeyPath);
2753 if (!file)
2754 continue;
2755
2756 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2757 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2758 ptr2 = strrchrW(source, '\\') + 1;
2759 msiobj_release(&row->hdr);
2760
2761 lstrcpyW(base, package->PackagePath);
2762 ptr = strrchrW(base, '\\');
2763 *(ptr + 1) = '\0';
2764
2765 sourcepath = resolve_file_source(package, file);
2766 ptr = sourcepath + lstrlenW(base);
2767 lstrcpyW(ptr2, ptr);
2768 msi_free(sourcepath);
2769
2770 msi_reg_set_val_str(hkey, squished_pc, source);
2771 }
2772 RegCloseKey(hkey);
2773 }
2774 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2775 {
2776 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2777 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2778 else
2779 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2780 }
2781
2782 /* UI stuff */
2783 uirow = MSI_CreateRecord(3);
2784 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2785 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2786 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2787 ui_actiondata(package,szProcessComponents,uirow);
2788 msiobj_release( &uirow->hdr );
2789 }
2790
2791 return ERROR_SUCCESS;
2792 }
2793
2794 typedef struct {
2795 CLSID clsid;
2796 LPWSTR source;
2797
2798 LPWSTR path;
2799 ITypeLib *ptLib;
2800 } typelib_struct;
2801
2802 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2803 LPWSTR lpszName, LONG_PTR lParam)
2804 {
2805 TLIBATTR *attr;
2806 typelib_struct *tl_struct = (typelib_struct*) lParam;
2807 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2808 int sz;
2809 HRESULT res;
2810
2811 if (!IS_INTRESOURCE(lpszName))
2812 {
2813 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2814 return TRUE;
2815 }
2816
2817 sz = strlenW(tl_struct->source)+4;
2818 sz *= sizeof(WCHAR);
2819
2820 if ((INT_PTR)lpszName == 1)
2821 tl_struct->path = strdupW(tl_struct->source);
2822 else
2823 {
2824 tl_struct->path = msi_alloc(sz);
2825 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2826 }
2827
2828 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2829 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2830 if (FAILED(res))
2831 {
2832 msi_free(tl_struct->path);
2833 tl_struct->path = NULL;
2834
2835 return TRUE;
2836 }
2837
2838 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2839 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2840 {
2841 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2842 return FALSE;
2843 }
2844
2845 msi_free(tl_struct->path);
2846 tl_struct->path = NULL;
2847
2848 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2849 ITypeLib_Release(tl_struct->ptLib);
2850
2851 return TRUE;
2852 }
2853
2854 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2855 {
2856 MSIPACKAGE* package = param;
2857 LPCWSTR component;
2858 MSICOMPONENT *comp;
2859 MSIFILE *file;
2860 typelib_struct tl_struct;
2861 ITypeLib *tlib;
2862 HMODULE module;
2863 HRESULT hr;
2864
2865 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2866
2867 component = MSI_RecordGetString(row,3);
2868 comp = get_loaded_component(package,component);
2869 if (!comp)
2870 return ERROR_SUCCESS;
2871
2872 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2873 {
2874 TRACE("Skipping typelib reg due to disabled component\n");
2875
2876 comp->Action = comp->Installed;
2877
2878 return ERROR_SUCCESS;
2879 }
2880
2881 comp->Action = INSTALLSTATE_LOCAL;
2882
2883 file = get_loaded_file( package, comp->KeyPath );
2884 if (!file)
2885 return ERROR_SUCCESS;
2886
2887 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2888 if (module)
2889 {
2890 LPCWSTR guid;
2891 guid = MSI_RecordGetString(row,1);
2892 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2893 tl_struct.source = strdupW( file->TargetPath );
2894 tl_struct.path = NULL;
2895
2896 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2897 (LONG_PTR)&tl_struct);
2898
2899 if (tl_struct.path)
2900 {
2901 LPWSTR help = NULL;
2902 LPCWSTR helpid;
2903 HRESULT res;
2904
2905 helpid = MSI_RecordGetString(row,6);
2906
2907 if (helpid)
2908 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2909 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2910 msi_free(help);
2911
2912 if (FAILED(res))
2913 ERR("Failed to register type library %s\n",
2914 debugstr_w(tl_struct.path));
2915 else
2916 {
2917 ui_actiondata(package,szRegisterTypeLibraries,row);
2918
2919 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2920 }
2921
2922 ITypeLib_Release(tl_struct.ptLib);
2923 msi_free(tl_struct.path);
2924 }
2925 else
2926 ERR("Failed to load type library %s\n",
2927 debugstr_w(tl_struct.source));
2928
2929 FreeLibrary(module);
2930 msi_free(tl_struct.source);
2931 }
2932 else
2933 {
2934 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2935 if (FAILED(hr))
2936 {
2937 ERR("Failed to load type library: %08x\n", hr);
2938 return ERROR_FUNCTION_FAILED;
2939 }
2940
2941 ITypeLib_Release(tlib);
2942 }
2943
2944 return ERROR_SUCCESS;
2945 }
2946
2947 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2948 {
2949 /*
2950 * OK this is a bit confusing.. I am given a _Component key and I believe
2951 * that the file that is being registered as a type library is the "key file
2952 * of that component" which I interpret to mean "The file in the KeyPath of
2953 * that component".
2954 */
2955 UINT rc;
2956 MSIQUERY * view;
2957 static const WCHAR Query[] =
2958 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2959 '`','T','y','p','e','L','i','b','`',0};
2960
2961 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2962 if (rc != ERROR_SUCCESS)
2963 return ERROR_SUCCESS;
2964
2965 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2966 msiobj_release(&view->hdr);
2967 return rc;
2968 }
2969
2970 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2971 {
2972 MSIPACKAGE *package = param;
2973 LPWSTR target_file, target_folder, filename;
2974 LPCWSTR buffer, extension;
2975 MSICOMPONENT *comp;
2976 static const WCHAR szlnk[]={'.','l','n','k',0};
2977 IShellLinkW *sl = NULL;
2978 IPersistFile *pf = NULL;
2979 HRESULT res;
2980
2981 buffer = MSI_RecordGetString(row,4);
2982 comp = get_loaded_component(package,buffer);
2983 if (!comp)
2984 return ERROR_SUCCESS;
2985
2986 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2987 {
2988 TRACE("Skipping shortcut creation due to disabled component\n");
2989
2990 comp->Action = comp->Installed;
2991
2992 return ERROR_SUCCESS;
2993 }
2994
2995 comp->Action = INSTALLSTATE_LOCAL;
2996
2997 ui_actiondata(package,szCreateShortcuts,row);
2998
2999 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3000 &IID_IShellLinkW, (LPVOID *) &sl );
3001
3002 if (FAILED( res ))
3003 {
3004 ERR("CLSID_ShellLink not available\n");
3005 goto err;
3006 }
3007
3008 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3009 if (FAILED( res ))
3010 {
3011 ERR("QueryInterface(IID_IPersistFile) failed\n");
3012 goto err;
3013 }
3014
3015 buffer = MSI_RecordGetString(row,2);
3016 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3017
3018 /* may be needed because of a bug somewhere else */
3019 create_full_pathW(target_folder);
3020
3021 filename = msi_dup_record_field( row, 3 );
3022 reduce_to_longfilename(filename);
3023
3024 extension = strchrW(filename,'.');
3025 if (!extension || strcmpiW(extension,szlnk))
3026 {
3027 int len = strlenW(filename);
3028 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3029 memcpy(filename + len, szlnk, sizeof(szlnk));
3030 }
3031 target_file = build_directory_name(2, target_folder, filename);
3032 msi_free(target_folder);
3033 msi_free(filename);
3034
3035 buffer = MSI_RecordGetString(row,5);
3036 if (strchrW(buffer,'['))
3037 {
3038 LPWSTR deformated;
3039 deformat_string(package,buffer,&deformated);
3040 IShellLinkW_SetPath(sl,deformated);
3041 msi_free(deformated);
3042 }
3043 else
3044 {
3045 FIXME("poorly handled shortcut format, advertised shortcut\n");
3046 IShellLinkW_SetPath(sl,comp->FullKeypath);
3047 }
3048
3049 if (!MSI_RecordIsNull(row,6))
3050 {
3051 LPWSTR deformated;
3052 buffer = MSI_RecordGetString(row,6);
3053 deformat_string(package,buffer,&deformated);
3054 IShellLinkW_SetArguments(sl,deformated);
3055 msi_free(deformated);
3056 }
3057
3058 if (!MSI_RecordIsNull(row,7))
3059 {
3060 buffer = MSI_RecordGetString(row,7);
3061 IShellLinkW_SetDescription(sl,buffer);
3062 }
3063
3064 if (!MSI_RecordIsNull(row,8))
3065 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3066
3067 if (!MSI_RecordIsNull(row,9))
3068 {
3069 LPWSTR Path;
3070 INT index;
3071
3072 buffer = MSI_RecordGetString(row,9);
3073
3074 Path = build_icon_path(package,buffer);
3075 index = MSI_RecordGetInteger(row,10);
3076
3077 /* no value means 0 */
3078 if (index == MSI_NULL_INTEGER)
3079 index = 0;
3080
3081 IShellLinkW_SetIconLocation(sl,Path,index);
3082 msi_free(Path);
3083 }
3084
3085 if (!MSI_RecordIsNull(row,11))
3086 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3087
3088 if (!MSI_RecordIsNull(row,12))
3089 {
3090 LPWSTR Path;
3091 buffer = MSI_RecordGetString(row,12);
3092 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3093 if (Path)
3094 IShellLinkW_SetWorkingDirectory(sl,Path);
3095 msi_free(Path);
3096 }
3097
3098 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3099 IPersistFile_Save(pf,target_file,FALSE);
3100
3101 msi_free(target_file);
3102
3103 err:
3104 if (pf)
3105 IPersistFile_Release( pf );
3106 if (sl)
3107 IShellLinkW_Release( sl );
3108
3109 return ERROR_SUCCESS;
3110 }
3111
3112 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3113 {
3114 UINT rc;
3115 HRESULT res;
3116 MSIQUERY * view;
3117 static const WCHAR Query[] =
3118 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3119 '`','S','h','o','r','t','c','u','t','`',0};
3120
3121 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3122 if (rc != ERROR_SUCCESS)
3123 return ERROR_SUCCESS;
3124
3125 res = CoInitialize( NULL );
3126
3127 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3128 msiobj_release(&view->hdr);
3129
3130 if (SUCCEEDED(res))
3131 CoUninitialize();
3132
3133 return rc;
3134 }
3135
3136 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3137 {
3138 MSIPACKAGE* package = param;
3139 HANDLE the_file;
3140 LPWSTR FilePath;
3141 LPCWSTR FileName;
3142 CHAR buffer[1024];
3143 DWORD sz;
3144 UINT rc;
3145 MSIRECORD *uirow;
3146
3147 FileName = MSI_RecordGetString(row,1);
3148 if (!FileName)
3149 {
3150 ERR("Unable to get FileName\n");
3151 return ERROR_SUCCESS;
3152 }
3153
3154 FilePath = build_icon_path(package,FileName);
3155
3156 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3157
3158 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3159 FILE_ATTRIBUTE_NORMAL, NULL);
3160
3161 if (the_file == INVALID_HANDLE_VALUE)
3162 {
3163 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3164 msi_free(FilePath);
3165 return ERROR_SUCCESS;
3166 }
3167
3168 do
3169 {
3170 DWORD write;
3171 sz = 1024;
3172 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3173 if (rc != ERROR_SUCCESS)
3174 {
3175 ERR("Failed to get stream\n");
3176 CloseHandle(the_file);
3177 DeleteFileW(FilePath);
3178 break;
3179 }
3180 WriteFile(the_file,buffer,sz,&write,NULL);
3181 } while (sz == 1024);
3182
3183 msi_free(FilePath);
3184
3185 CloseHandle(the_file);
3186
3187 uirow = MSI_CreateRecord(1);
3188 MSI_RecordSetStringW(uirow,1,FileName);
3189 ui_actiondata(package,szPublishProduct,uirow);
3190 msiobj_release( &uirow->hdr );
3191
3192 return ERROR_SUCCESS;
3193 }
3194
3195 static UINT msi_publish_icons(MSIPACKAGE *package)
3196 {
3197 UINT r;
3198 MSIQUERY *view;
3199
3200 static const WCHAR query[]= {
3201 'S','E','L','E','C','T',' ','*',' ',
3202 'F','R','O','M',' ','`','I','c','o','n','`',0};
3203
3204 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3205 if (r == ERROR_SUCCESS)
3206 {
3207 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3208 msiobj_release(&view->hdr);
3209 }
3210
3211 return ERROR_SUCCESS;
3212 }
3213
3214 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3215 {
3216 UINT r;
3217 HKEY source;
3218 LPWSTR buffer;
3219 MSIMEDIADISK *disk;
3220 MSISOURCELISTINFO *info;
3221
3222 r = RegCreateKeyW(hkey, szSourceList, &source);
3223 if (r != ERROR_SUCCESS)
3224 return r;
3225
3226 RegCloseKey(source);
3227
3228 buffer = strrchrW(package->PackagePath, '\\') + 1;
3229 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3230 package->Context, MSICODE_PRODUCT,
3231 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3232 if (r != ERROR_SUCCESS)
3233 return r;
3234
3235 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3236 package->Context, MSICODE_PRODUCT,
3237 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3238 if (r != ERROR_SUCCESS)
3239 return r;
3240
3241 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3242 package->Context, MSICODE_PRODUCT,
3243 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3244 if (r != ERROR_SUCCESS)
3245 return r;
3246
3247 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3248 {
3249 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3250 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3251 info->options, info->value);
3252 else
3253 MsiSourceListSetInfoW(package->ProductCode, NULL,
3254 info->context, info->options,
3255 info->property, info->value);
3256 }
3257
3258 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3259 {
3260 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3261 disk->context, disk->options,
3262 disk->disk_id, disk->volume_label, disk->disk_prompt);
3263 }
3264
3265 return ERROR_SUCCESS;
3266 }
3267
3268 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3269 {
3270 MSIHANDLE hdb, suminfo;
3271 WCHAR guids[MAX_PATH];
3272 WCHAR packcode[SQUISH_GUID_SIZE];
3273 LPWSTR buffer;
3274 LPWSTR ptr;
3275 DWORD langid;
3276 DWORD size;
3277 UINT r;
3278
3279 static const WCHAR szProductLanguage[] =
3280 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3281 static const WCHAR szARPProductIcon[] =
3282 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3283 static const WCHAR szProductVersion[] =
3284 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3285 static const WCHAR szAssignment[] =
3286 {'A','s','s','i','g','n','m','e','n','t',0};
3287 static const WCHAR szAdvertiseFlags[] =
3288 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3289 static const WCHAR szClients[] =
3290 {'C','l','i','e','n','t','s',0};
3291 static const WCHAR szColon[] = {':',0};
3292
3293 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3294 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3295 msi_free(buffer);
3296
3297 langid = msi_get_property_int(package, szProductLanguage, 0);
3298 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3299
3300 /* FIXME */
3301 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3302
3303 buffer = msi_dup_property(package, szARPProductIcon);
3304 if (buffer)
3305 {
3306 LPWSTR path = build_icon_path(package,buffer);
3307 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3308 msi_free(path);
3309 msi_free(buffer);
3310 }
3311
3312 buffer = msi_dup_property(package, szProductVersion);
3313 if (buffer)
3314 {
3315 DWORD verdword = msi_version_str_to_dword(buffer);
3316 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3317 msi_free(buffer);
3318 }
3319
3320 msi_reg_set_val_dword(hkey, szAssignment, 0);
3321 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3322 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3323 msi_reg_set_val_str(hkey, szClients, szColon);
3324
3325 hdb = alloc_msihandle(&package->db->hdr);
3326 if (!hdb)
3327 return ERROR_NOT_ENOUGH_MEMORY;
3328
3329 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3330 MsiCloseHandle(hdb);
3331 if (r != ERROR_SUCCESS)
3332 goto done;
3333
3334 size = MAX_PATH;
3335 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3336 NULL, guids, &size);
3337 if (r != ERROR_SUCCESS)
3338 goto done;
3339
3340 ptr = strchrW(guids, ';');
3341 if (ptr) *ptr = 0;
3342 squash_guid(guids, packcode);
3343 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3344
3345 done:
3346 MsiCloseHandle(suminfo);
3347 return ERROR_SUCCESS;
3348 }
3349
3350 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3351 {
3352 UINT r;
3353 HKEY hkey;
3354 LPWSTR upgrade;
3355 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3356
3357 static const WCHAR szUpgradeCode[] =
3358 {'U','p','g','r','a','d','e','C','o','d','e',0};
3359
3360 upgrade = msi_dup_property(package, szUpgradeCode);
3361 if (!upgrade)
3362 return ERROR_SUCCESS;
3363
3364 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3365 {
3366 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3367 if (r != ERROR_SUCCESS)
3368 goto done;
3369 }
3370 else
3371 {
3372 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3373 if (r != ERROR_SUCCESS)
3374 goto done;
3375 }
3376
3377 squash_guid(package->ProductCode, squashed_pc);
3378 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3379
3380 RegCloseKey(hkey);
3381
3382 done:
3383 msi_free(upgrade);
3384 return r;
3385 }
3386
3387 static BOOL msi_check_publish(MSIPACKAGE *package)
3388 {
3389 MSIFEATURE *feature;
3390
3391 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3392 {
3393 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3394 return TRUE;
3395 }
3396
3397 return FALSE;
3398 }
3399
3400 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3401 {
3402 MSIFEATURE *feature;
3403
3404 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3405 {
3406 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3407 return FALSE;
3408 }
3409
3410 return TRUE;
3411 }
3412
3413 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3414 {
3415 WCHAR patch_squashed[GUID_SIZE];
3416 HKEY patches;
3417 LONG res;
3418 UINT r = ERROR_FUNCTION_FAILED;
3419
3420 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3421 &patches, NULL);
3422 if (res != ERROR_SUCCESS)
3423 return ERROR_FUNCTION_FAILED;
3424
3425 squash_guid(package->patch->patchcode, patch_squashed);
3426
3427 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3428 (const BYTE *)patch_squashed,
3429 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3430 if (res != ERROR_SUCCESS)
3431 goto done;
3432
3433 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3434 (const BYTE *)package->patch->transforms,
3435 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3436 if (res == ERROR_SUCCESS)
3437 r = ERROR_SUCCESS;
3438
3439 done:
3440 RegCloseKey(patches);
3441 return r;
3442 }
3443
3444 /*
3445 * 99% of the work done here is only done for
3446 * advertised installs. However this is where the
3447 * Icon table is processed and written out
3448 * so that is what I am going to do here.
3449 */
3450 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3451 {
3452 UINT rc;
3453 HKEY hukey=0;
3454 HKEY hudkey=0;
3455
3456 /* FIXME: also need to publish if the product is in advertise mode */
3457 if (!msi_check_publish(package))
3458 return ERROR_SUCCESS;
3459
3460 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3461 &hukey, TRUE);
3462 if (rc != ERROR_SUCCESS)
3463 goto end;
3464
3465 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3466 NULL, &hudkey, TRUE);
3467 if (rc != ERROR_SUCCESS)
3468 goto end;
3469
3470 rc = msi_publish_upgrade_code(package);
3471 if (rc != ERROR_SUCCESS)
3472 goto end;
3473
3474 if (package->patch)
3475 {
3476 rc = msi_publish_patch(package, hukey, hudkey);
3477 if (rc != ERROR_SUCCESS)
3478 goto end;
3479 }
3480
3481 rc = msi_publish_product_properties(package, hukey);
3482 if (rc != ERROR_SUCCESS)
3483 goto end;
3484
3485 rc = msi_publish_sourcelist(package, hukey);
3486 if (rc != ERROR_SUCCESS)
3487 goto end;
3488
3489 rc = msi_publish_icons(package);
3490
3491 end:
3492 RegCloseKey(hukey);
3493 RegCloseKey(hudkey);
3494
3495 return rc;
3496 }
3497
3498 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3499 {
3500 MSIPACKAGE *package = param;
3501 LPCWSTR component, section, key, value, identifier, dirproperty;
3502 LPWSTR deformated_section, deformated_key, deformated_value;
3503 LPWSTR folder, filename, fullname = NULL;
3504 LPCWSTR filenameptr;
3505 MSIRECORD * uirow;
3506 INT action;
3507 MSICOMPONENT *comp;
3508 static const WCHAR szWindowsFolder[] =
3509 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3510
3511 component = MSI_RecordGetString(row, 8);
3512 comp = get_loaded_component(package,component);
3513
3514 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3515 {
3516 TRACE("Skipping ini file due to disabled component %s\n",
3517 debugstr_w(component));
3518
3519 comp->Action = comp->Installed;
3520
3521 return ERROR_SUCCESS;
3522 }
3523
3524 comp->Action = INSTALLSTATE_LOCAL;
3525
3526 identifier = MSI_RecordGetString(row,1);
3527 dirproperty = MSI_RecordGetString(row,3);
3528 section = MSI_RecordGetString(row,4);
3529 key = MSI_RecordGetString(row,5);
3530 value = MSI_RecordGetString(row,6);
3531 action = MSI_RecordGetInteger(row,7);
3532
3533 deformat_string(package,section,&deformated_section);
3534 deformat_string(package,key,&deformated_key);
3535 deformat_string(package,value,&deformated_value);
3536
3537 filename = msi_dup_record_field(row, 2);
3538 if (filename && (filenameptr = strchrW(filename, '|')))
3539 filenameptr++;
3540 else
3541 filenameptr = filename;
3542
3543 if (dirproperty)
3544 {
3545 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3546 if (!folder)
3547 folder = msi_dup_property( package, dirproperty );
3548 }
3549 else
3550 folder = msi_dup_property( package, szWindowsFolder );
3551
3552 if (!folder)
3553 {
3554 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3555 goto cleanup;
3556 }
3557
3558 fullname = build_directory_name(2, folder, filenameptr);
3559
3560 if (action == 0)
3561 {
3562 TRACE("Adding value %s to section %s in %s\n",
3563 debugstr_w(deformated_key), debugstr_w(deformated_section),
3564 debugstr_w(fullname));
3565 WritePrivateProfileStringW(deformated_section, deformated_key,
3566 deformated_value, fullname);
3567 }
3568 else if (action == 1)
3569 {
3570 WCHAR returned[10];
3571 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3572 returned, 10, fullname);
3573 if (returned[0] == 0)
3574 {
3575 TRACE("Adding value %s to section %s in %s\n",
3576 debugstr_w(deformated_key), debugstr_w(deformated_section),
3577 debugstr_w(fullname));
3578
3579 WritePrivateProfileStringW(deformated_section, deformated_key,
3580 deformated_value, fullname);
3581 }
3582 }
3583 else if (action == 3)
3584 FIXME("Append to existing section not yet implemented\n");
3585
3586 uirow = MSI_CreateRecord(4);
3587 MSI_RecordSetStringW(uirow,1,identifier);
3588 MSI_RecordSetStringW(uirow,2,deformated_section);
3589 MSI_RecordSetStringW(uirow,3,deformated_key);
3590 MSI_RecordSetStringW(uirow,4,deformated_value);
3591 ui_actiondata(package,szWriteIniValues,uirow);
3592 msiobj_release( &uirow->hdr );
3593
3594 cleanup:
3595 msi_free(filename);
3596 msi_free(fullname);
3597 msi_free(folder);
3598 msi_free(deformated_key);
3599 msi_free(deformated_value);
3600 msi_free(deformated_section);
3601 return ERROR_SUCCESS;
3602 }
3603
3604 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3605 {
3606 UINT rc;
3607 MSIQUERY * view;
3608 static const WCHAR ExecSeqQuery[] =
3609 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3610 '`','I','n','i','F','i','l','e','`',0};
3611
3612 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3613 if (rc != ERROR_SUCCESS)
3614 {
3615 TRACE("no IniFile table\n");
3616 return ERROR_SUCCESS;
3617 }
3618
3619 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3620 msiobj_release(&view->hdr);
3621 return rc;
3622 }
3623
3624 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3625 {
3626 MSIPACKAGE *package = param;
3627 LPCWSTR filename;
3628 LPWSTR FullName;
3629 MSIFILE *file;
3630 DWORD len;
3631 static const WCHAR ExeStr[] =
3632 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3633 static const WCHAR close[] = {'\"',0};
3634 STARTUPINFOW si;
3635 PROCESS_INFORMATION info;
3636 BOOL brc;
3637 MSIRECORD *uirow;
3638 LPWSTR uipath, p;
3639
3640 memset(&si,0,sizeof(STARTUPINFOW));
3641
3642 filename = MSI_RecordGetString(row,1);
3643 file = get_loaded_file( package, filename );
3644
3645 if (!file)
3646 {
3647 ERR("Unable to find file id %s\n",debugstr_w(filename));
3648 return ERROR_SUCCESS;
3649 }
3650
3651 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3652
3653 FullName = msi_alloc(len*sizeof(WCHAR));
3654 strcpyW(FullName,ExeStr);
3655 strcatW( FullName, file->TargetPath );
3656 strcatW(FullName,close);
3657
3658 TRACE("Registering %s\n",debugstr_w(FullName));
3659 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3660 &si, &info);
3661
3662 if (brc)
3663 {
3664 CloseHandle(info.hThread);
3665 msi_dialog_check_messages(info.hProcess);
3666 CloseHandle(info.hProcess);
3667 }
3668
3669 msi_free(FullName);
3670
3671 /* the UI chunk */
3672 uirow = MSI_CreateRecord( 2 );
3673 uipath = strdupW( file->TargetPath );
3674 p = strrchrW(uipath,'\\');
3675 if (p)
3676 p[0]=0;
3677 MSI_RecordSetStringW( uirow, 1, &p[1] );
3678 MSI_RecordSetStringW( uirow, 2, uipath);
3679 ui_actiondata( package, szSelfRegModules, uirow);
3680 msiobj_release( &uirow->hdr );
3681 msi_free( uipath );
3682 /* FIXME: call ui_progress? */
3683
3684 return ERROR_SUCCESS;
3685 }
3686
3687 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3688 {
3689 UINT rc;
3690 MSIQUERY * view;
3691 static const WCHAR ExecSeqQuery[] =
3692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3693 '`','S','e','l','f','R','e','g','`',0};
3694
3695 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3696 if (rc != ERROR_SUCCESS)
3697 {
3698 TRACE("no SelfReg table\n");
3699 return ERROR_SUCCESS;
3700 }
3701
3702 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3703 msiobj_release(&view->hdr);
3704
3705 return ERROR_SUCCESS;
3706 }
3707
3708 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3709 {
3710 static const WCHAR regsvr32[] =
3711 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3712 static const WCHAR close[] = {'\"',0};
3713 MSIPACKAGE *package = param;
3714 LPCWSTR filename;
3715 LPWSTR cmdline;
3716 MSIFILE *file;
3717 DWORD len;
3718 STARTUPINFOW si;
3719 PROCESS_INFORMATION pi;
3720 BOOL ret;
3721 MSIRECORD *uirow;
3722 LPWSTR uipath, p;
3723
3724 memset( &si, 0, sizeof(STARTUPINFOW) );
3725
3726 filename = MSI_RecordGetString( row, 1 );
3727 file = get_loaded_file( package, filename );
3728
3729 if (!file)
3730 {
3731 ERR("Unable to find file id %s\n", debugstr_w(filename));
3732 return ERROR_SUCCESS;
3733 }
3734
3735 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3736
3737 cmdline = msi_alloc( len * sizeof(WCHAR) );
3738 strcpyW( cmdline, regsvr32 );
3739 strcatW( cmdline, file->TargetPath );
3740 strcatW( cmdline, close );
3741
3742 TRACE("Unregistering %s\n", debugstr_w(cmdline));
3743
3744 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3745 if (ret)
3746 {
3747 CloseHandle( pi.hThread );
3748 msi_dialog_check_messages( pi.hProcess );
3749 CloseHandle( pi.hProcess );
3750 }
3751
3752 msi_free( cmdline );
3753
3754 uirow = MSI_CreateRecord( 2 );
3755 uipath = strdupW( file->TargetPath );
3756 if ((p = strrchrW( uipath, '\\' )))
3757 {
3758 *p = 0;
3759 MSI_RecordSetStringW( uirow, 1, ++p );
3760 }
3761 MSI_RecordSetStringW( uirow, 2, uipath );
3762 ui_actiondata( package, szSelfUnregModules, uirow );
3763 msiobj_release( &uirow->hdr );
3764 msi_free( uipath );
3765 /* FIXME call ui_progress? */
3766
3767 return ERROR_SUCCESS;
3768 }
3769
3770 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3771 {
3772 UINT rc;
3773 MSIQUERY *view;
3774 static const WCHAR query[] =
3775 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3776 '`','S','e','l','f','R','e','g','`',0};
3777
3778 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3779 if (rc != ERROR_SUCCESS)
3780 {
3781 TRACE("no SelfReg table\n");
3782 return ERROR_SUCCESS;
3783 }
3784
3785 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3786 msiobj_release( &view->hdr );
3787
3788 return ERROR_SUCCESS;
3789 }
3790
3791 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3792 {
3793 MSIFEATURE *feature;
3794 UINT rc;
3795 HKEY hkey;
3796 HKEY userdata = NULL;
3797
3798 if (!msi_check_publish(package))
3799 return ERROR_SUCCESS;
3800
3801 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3802 &hkey, TRUE);
3803 if (rc != ERROR_SUCCESS)
3804 goto end;
3805
3806 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3807 &userdata, TRUE);
3808 if (rc != ERROR_SUCCESS)
3809 goto end;
3810
3811 /* here the guids are base 85 encoded */
3812 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3813 {
3814 ComponentList *cl;
3815 LPWSTR data = NULL;
3816 GUID clsid;
3817 INT size;
3818 BOOL absent = FALSE;
3819 MSIRECORD *uirow;
3820
3821 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3822 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3823 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3824 absent = TRUE;
3825
3826 size = 1;
3827 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3828 {
3829 size += 21;
3830 }
3831 if (feature->Feature_Parent)
3832 size += strlenW( feature->Feature_Parent )+2;
3833
3834 data = msi_alloc(size * sizeof(WCHAR));
3835
3836 data[0] = 0;
3837 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3838 {
3839 MSICOMPONENT* component = cl->component;
3840 WCHAR buf[21];
3841
3842 buf[0] = 0;
3843 if (component->ComponentId)
3844 {
3845 TRACE("From %s\n",debugstr_w(component->ComponentId));
3846 CLSIDFromString(component->ComponentId, &clsid);
3847 encode_base85_guid(&clsid,buf);
3848 TRACE("to %s\n",debugstr_w(buf));
3849 strcatW(data,buf);
3850 }
3851 }
3852
3853 if (feature->Feature_Parent)
3854 {
3855 static const WCHAR sep[] = {'\2',0};
3856 strcatW(data,sep);
3857 strcatW(data,feature->Feature_Parent);
3858 }
3859
3860 msi_reg_set_val_str( userdata, feature->Feature, data );
3861 msi_free(data);
3862
3863 size = 0;
3864 if (feature->Feature_Parent)
3865 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3866 if (!absent)
3867 {
3868 size += sizeof(WCHAR);
3869 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3870 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3871 }
3872 else
3873 {
3874 size += 2*sizeof(WCHAR);
3875 data = msi_alloc(size);
3876 data[0] = 0x6;
3877 data[1] = 0;
3878 if (feature->Feature_Parent)
3879 strcpyW( &data[1], feature->Feature_Parent );
3880 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3881 (LPBYTE)data,size);
3882 msi_free(data);
3883 }
3884
3885 /* the UI chunk */
3886 uirow = MSI_CreateRecord( 1 );
3887 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3888 ui_actiondata( package, szPublishFeatures, uirow);
3889 msiobj_release( &uirow->hdr );
3890 /* FIXME: call ui_progress? */
3891 }
3892
3893 end:
3894 RegCloseKey(hkey);
3895 RegCloseKey(userdata);
3896 return rc;
3897 }
3898
3899 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3900 {
3901 UINT r;
3902 HKEY hkey;
3903
3904 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3905
3906 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3907 &hkey, FALSE);
3908 if (r == ERROR_SUCCESS)
3909 {
3910 RegDeleteValueW(hkey, feature->Feature);
3911 RegCloseKey(hkey);
3912 }
3913
3914 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3915 &hkey, FALSE);
3916 if (r == ERROR_SUCCESS)
3917 {
3918 RegDeleteValueW(hkey, feature->Feature);
3919 RegCloseKey(hkey);
3920 }
3921
3922 return ERROR_SUCCESS;
3923 }
3924
3925 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3926 {
3927 MSIFEATURE *feature;
3928
3929 if (!msi_check_unpublish(package))
3930 return ERROR_SUCCESS;
3931
3932 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3933 {
3934 msi_unpublish_feature(package, feature);
3935 }
3936
3937 return ERROR_SUCCESS;
3938 }
3939
3940 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
3941 {
3942 LPWSTR prop, val, key;
3943 SYSTEMTIME systime;
3944 DWORD size, langid;
3945 WCHAR date[9];
3946 LPWSTR buffer;
3947
3948 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3949 static const WCHAR szWindowsInstaller[] =
3950 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3951 static const WCHAR modpath_fmt[] =
3952 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3953 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3954 static const WCHAR szModifyPath[] =
3955 {'M','o','d','i','f','y','P','a','t','h',0};
3956 static const WCHAR szUninstallString[] =
3957 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3958 static const WCHAR szEstimatedSize[] =
3959 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3960 static const WCHAR szProductLanguage[] =
3961 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3962 static const WCHAR szProductVersion[] =
3963 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3964 static const WCHAR szProductName[] =
3965 {'P','r','o','d','u','c','t','N','a','m','e',0};
3966 static const WCHAR szDisplayName[] =
3967 {'D','i','s','p','l','a','y','N','a','m','e',0};
3968 static const WCHAR szDisplayVersion[] =
3969 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3970 static const WCHAR szManufacturer[] =
3971 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3972
3973 static const LPCSTR propval[] = {
3974 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3975 "ARPCONTACT", "Contact",
3976 "ARPCOMMENTS", "Comments",
3977 "ProductName", "DisplayName",
3978 "ProductVersion", "DisplayVersion",
3979 "ARPHELPLINK", "HelpLink",
3980 "ARPHELPTELEPHONE", "HelpTelephone",
3981 "ARPINSTALLLOCATION", "InstallLocation",
3982 "SourceDir", "InstallSource",
3983 "Manufacturer", "Publisher",
3984 "ARPREADME", "Readme",
3985 "ARPSIZE", "Size",
3986 "ARPURLINFOABOUT", "URLInfoAbout",
3987 "ARPURLUPDATEINFO", "URLUpdateInfo",
3988 NULL,
3989 };
3990 const LPCSTR *p = propval;
3991
3992 while (*p)
3993 {
3994 prop = strdupAtoW(*p++);
3995 key = strdupAtoW(*p++);
3996 val = msi_dup_property(package, prop);
3997 msi_reg_set_val_str(hkey, key, val);
3998 msi_free(val);
3999 msi_free(key);
4000 msi_free(prop);
4001 }
4002
4003 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4004
4005 size = deformat_string(package, modpath_fmt, &buffer);
4006 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4007 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4008 msi_free(buffer);
4009
4010 /* FIXME: Write real Estimated Size when we have it */
4011 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4012
4013 buffer = msi_dup_property(package, szProductName);
4014 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4015 msi_free(buffer);
4016
4017 buffer = msi_dup_property(package, cszSourceDir);
4018 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4019 msi_free(buffer);
4020
4021 buffer = msi_dup_property(package, szManufacturer);
4022 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4023 msi_free(buffer);
4024
4025 GetLocalTime(&systime);
4026 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4027 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4028
4029 langid = msi_get_property_int(package, szProductLanguage, 0);
4030 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4031
4032 buffer = msi_dup_property(package, szProductVersion);
4033 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4034 if (buffer)
4035 {
4036 DWORD verdword = msi_version_str_to_dword(buffer);
4037
4038 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4039 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4040 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4041 msi_free(buffer);
4042 }
4043
4044 return ERROR_SUCCESS;
4045 }
4046
4047 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4048 {
4049 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4050 LPWSTR upgrade_code;
4051 HKEY hkey, props;
4052 HKEY upgrade;
4053 UINT rc;
4054
4055 static const WCHAR szUpgradeCode[] = {
4056 'U','p','g','r','a','d','e','C','o','d','e',0};
4057
4058 /* FIXME: also need to publish if the product is in advertise mode */
4059 if (!msi_check_publish(package))
4060 return ERROR_SUCCESS;
4061
4062 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4063 if (rc != ERROR_SUCCESS)
4064 return rc;
4065
4066 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4067 NULL, &props, TRUE);
4068 if (rc != ERROR_SUCCESS)
4069 goto done;
4070
4071 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4072 msi_free( package->db->localfile );
4073 package->db->localfile = NULL;
4074
4075 rc = msi_publish_install_properties(package, hkey);
4076 if (rc != ERROR_SUCCESS)
4077 goto done;
4078
4079 rc = msi_publish_install_properties(package, props);
4080 if (rc != ERROR_SUCCESS)
4081 goto done;
4082
4083 upgrade_code = msi_dup_property(package, szUpgradeCode);
4084 if (upgrade_code)
4085 {
4086 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4087 squash_guid(package->ProductCode, squashed_pc);
4088 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4089 RegCloseKey(upgrade);
4090 msi_free(upgrade_code);
4091 }
4092
4093 done:
4094 RegCloseKey(hkey);
4095
4096 return ERROR_SUCCESS;
4097 }
4098
4099 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4100 {
4101 return execute_script(package,INSTALL_SCRIPT);
4102 }
4103
4104 static UINT msi_unpublish_product(MSIPACKAGE *package)
4105 {
4106 LPWSTR upgrade;
4107 LPWSTR remove = NULL;
4108 LPWSTR *features = NULL;
4109 BOOL full_uninstall = TRUE;
4110 MSIFEATURE *feature;
4111
4112 static const WCHAR szUpgradeCode[] =
4113 {'U','p','g','r','a','d','e','C','o','d','e',0};
4114
4115 remove = msi_dup_property(package, szRemove);
4116 if (!remove)
4117 return ERROR_SUCCESS;
4118
4119 features = msi_split_string(remove, ',');
4120 if (!features)
4121 {
4122 msi_free(remove);
4123 ERR("REMOVE feature list is empty!\n");
4124 return ERROR_FUNCTION_FAILED;
4125 }
4126
4127 if (!lstrcmpW(features[0], szAll))
4128 full_uninstall = TRUE;
4129 else
4130 {
4131 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4132 {
4133 if (feature->Action != INSTALLSTATE_ABSENT)
4134 full_uninstall = FALSE;
4135 }
4136 }
4137
4138 if (!full_uninstall)
4139 goto done;
4140
4141 MSIREG_DeleteProductKey(package->ProductCode);
4142 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4143 MSIREG_DeleteUninstallKey(package->ProductCode);
4144
4145 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4146 {
4147 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4148 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4149 }
4150 else
4151 {
4152 MSIREG_DeleteUserProductKey(package->ProductCode);
4153 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4154 }
4155
4156 upgrade = msi_dup_property(package, szUpgradeCode);
4157 if (upgrade)
4158 {
4159 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4160 msi_free(upgrade);
4161 }
4162
4163 done:
4164 msi_free(remove);
4165 msi_free(features);
4166 return ERROR_SUCCESS;
4167 }
4168
4169 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4170 {
4171 UINT rc;
4172
4173 rc = msi_unpublish_product(package);
4174 if (rc != ERROR_SUCCESS)
4175 return rc;
4176
4177 /* turn off scheduling */
4178 package->script->CurrentlyScripting= FALSE;
4179
4180 /* first do the same as an InstallExecute */
4181 rc = ACTION_InstallExecute(package);
4182 if (rc != ERROR_SUCCESS)
4183 return rc;
4184
4185 /* then handle Commit Actions */
4186 rc = execute_script(package,COMMIT_SCRIPT);
4187
4188 return rc;
4189 }
4190
4191 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4192 {
4193 static const WCHAR RunOnce[] = {
4194 'S','o','f','t','w','a','r','e','\\',
4195 'M','i','c','r','o','s','o','f','t','\\',
4196 'W','i','n','d','o','w','s','\\',
4197 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4198 'R','u','n','O','n','c','e',0};
4199 static const WCHAR InstallRunOnce[] = {
4200 'S','o','f','t','w','a','r','e','\\',
4201 'M','i','c','r','o','s','o','f','t','\\',
4202 'W','i','n','d','o','w','s','\\',
4203 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4204 'I','n','s','t','a','l','l','e','r','\\',
4205 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4206
4207 static const WCHAR msiexec_fmt[] = {
4208 '%','s',
4209 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4210 '\"','%','s','\"',0};
4211 static const WCHAR install_fmt[] = {
4212 '/','I',' ','\"','%','s','\"',' ',
4213 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4214 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4215 WCHAR buffer[256], sysdir[MAX_PATH];
4216 HKEY hkey;
4217 WCHAR squished_pc[100];
4218
4219 squash_guid(package->ProductCode,squished_pc);
4220
4221 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4222 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4223 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4224 squished_pc);
4225
4226 msi_reg_set_val_str( hkey, squished_pc, buffer );
4227 RegCloseKey(hkey);
4228
4229 TRACE("Reboot command %s\n",debugstr_w(buffer));
4230
4231 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4232 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4233
4234 msi_reg_set_val_str( hkey, squished_pc, buffer );
4235 RegCloseKey(hkey);
4236
4237 return ERROR_INSTALL_SUSPEND;
4238 }
4239
4240 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4241 {
4242 DWORD attrib;
4243 UINT rc;
4244
4245 /*
4246 * We are currently doing what should be done here in the top level Install
4247 * however for Administrative and uninstalls this step will be needed
4248 */
4249 if (!package->PackagePath)
4250 return ERROR_SUCCESS;
4251
4252 msi_set_sourcedir_props(package, TRUE);
4253
4254 attrib = GetFileAttributesW(package->db->path);
4255 if (attrib == INVALID_FILE_ATTRIBUTES)
4256 {
4257 LPWSTR prompt;
4258 LPWSTR msg;
4259 DWORD size = 0;
4260
4261 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4262 package->Context, MSICODE_PRODUCT,
4263 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4264 if (rc == ERROR_MORE_DATA)
4265 {
4266 prompt = msi_alloc(size * sizeof(WCHAR));
4267 MsiSourceListGetInfoW(package->ProductCode, NULL,
4268 package->Context, MSICODE_PRODUCT,
4269 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4270 }
4271 else
4272 prompt = strdupW(package->db->path);
4273
4274 msg = generate_error_string(package,1302,1,prompt);
4275 while(attrib == INVALID_FILE_ATTRIBUTES)
4276 {
4277 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4278 if (rc == IDCANCEL)
4279 {
4280 rc = ERROR_INSTALL_USEREXIT;
4281 break;
4282 }
4283 attrib = GetFileAttributesW(package->db->path);
4284 }
4285 msi_free(prompt);
4286 rc = ERROR_SUCCESS;
4287 }
4288 else
4289 return ERROR_SUCCESS;
4290
4291 return rc;
4292 }
4293
4294 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4295 {
4296 HKEY hkey=0;
4297 LPWSTR buffer;
4298 LPWSTR productid;
4299 UINT rc,i;
4300
4301 static const WCHAR szPropKeys[][80] =
4302 {
4303 {'P','r','o','d','u','c','t','I','D',0},
4304 {'U','S','E','R','N','A','M','E',0},
4305 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4306 {0},
4307 };
4308
4309 static const WCHAR szRegKeys[][80] =
4310 {
4311 {'P','r','o','d','u','c','t','I','D',0},
4312 {'R','e','g','O','w','n','e','r',0},
4313 {'R','e','g','C','o','m','p','a','n','y',0},
4314 {0},
4315 };
4316
4317 if (msi_check_unpublish(package))
4318 {
4319 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4320 return ERROR_SUCCESS;
4321 }
4322
4323 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4324 if (!productid)
4325 return ERROR_SUCCESS;
4326
4327 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4328 NULL, &hkey, TRUE);
4329 if (rc != ERROR_SUCCESS)
4330 goto end;
4331
4332 for( i = 0; szPropKeys[i][0]; i++ )
4333 {
4334 buffer = msi_dup_property( package, szPropKeys[i] );
4335 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4336 msi_free( buffer );
4337 }
4338
4339 end:
4340 msi_free(productid);
4341 RegCloseKey(hkey);
4342
4343 /* FIXME: call ui_actiondata */
4344
4345 return rc;
4346 }
4347
4348
4349 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4350 {
4351 UINT rc;
4352
4353 package->script->InWhatSequence |= SEQUENCE_EXEC;
4354 rc = ACTION_ProcessExecSequence(package,FALSE);
4355 return rc;
4356 }
4357
4358
4359 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4360 {
4361 MSIPACKAGE *package = param;
4362 LPCWSTR compgroupid=NULL;
4363 LPCWSTR feature=NULL;
4364 LPCWSTR text = NULL;
4365 LPCWSTR qualifier = NULL;
4366 LPCWSTR component = NULL;
4367 LPWSTR advertise = NULL;
4368 LPWSTR output = NULL;
4369 HKEY hkey;
4370 UINT rc = ERROR_SUCCESS;
4371 MSICOMPONENT *comp;
4372 DWORD sz = 0;
4373 MSIRECORD *uirow;
4374
4375 component = MSI_RecordGetString(rec,3);
4376 comp = get_loaded_component(package,component);
4377
4378 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4379 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4380 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4381 {
4382 TRACE("Skipping: Component %s not scheduled for install\n",
4383 debugstr_w(component));
4384
4385 return ERROR_SUCCESS;
4386 }
4387
4388 compgroupid = MSI_RecordGetString(rec,1);
4389 qualifier = MSI_RecordGetString(rec,2);
4390
4391 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4392 if (rc != ERROR_SUCCESS)
4393 goto end;
4394
4395 text = MSI_RecordGetString(rec,4);
4396 feature = MSI_RecordGetString(rec,5);
4397
4398 advertise = create_component_advertise_string(package, comp, feature);
4399
4400 sz = strlenW(advertise);
4401
4402 if (text)
4403 sz += lstrlenW(text);
4404
4405 sz+=3;
4406 sz *= sizeof(WCHAR);
4407
4408 output = msi_alloc_zero(sz);
4409 strcpyW(output,advertise);
4410 msi_free(advertise);
4411
4412 if (text)
4413 strcatW(output,text);
4414
4415 msi_reg_set_val_multi_str( hkey, qualifier, output );
4416
4417 end:
4418 RegCloseKey(hkey);
4419 msi_free(output);
4420
4421 /* the UI chunk */
4422 uirow = MSI_CreateRecord( 2 );
4423 MSI_RecordSetStringW( uirow, 1, compgroupid );
4424 MSI_RecordSetStringW( uirow, 2, qualifier);
4425 ui_actiondata( package, szPublishComponents, uirow);
4426 msiobj_release( &uirow->hdr );
4427 /* FIXME: call ui_progress? */
4428
4429 return rc;
4430 }
4431
4432 /*
4433 * At present I am ignorning the advertised components part of this and only
4434 * focusing on the qualified component sets
4435 */
4436 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4437 {
4438 UINT rc;
4439 MSIQUERY * view;
4440 static const WCHAR ExecSeqQuery[] =
4441 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4442 '`','P','u','b','l','i','s','h',
4443 'C','o','m','p','o','n','e','n','t','`',0};
4444
4445 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4446 if (rc != ERROR_SUCCESS)
4447 return ERROR_SUCCESS;
4448
4449 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4450 msiobj_release(&view->hdr);
4451
4452 return rc;
4453 }
4454
4455 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4456 {
4457 MSIPACKAGE *package = param;
4458 MSIRECORD *row;
4459 MSIFILE *file;
4460 SC_HANDLE hscm, service = NULL;
4461 LPCWSTR comp, depends, pass;
4462 LPWSTR name = NULL, disp = NULL;
4463 LPCWSTR load_order, serv_name, key;
4464 DWORD serv_type, start_type;
4465 DWORD err_control;
4466
4467 static const WCHAR query[] =
4468 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4469 '`','C','o','m','p','o','n','e','n','t','`',' ',
4470 'W','H','E','R','E',' ',
4471 '`','C','o','m','p','o','n','e','n','t','`',' ',
4472 '=','\'','%','s','\'',0};
4473
4474 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4475 if (!hscm)
4476 {
4477 ERR("Failed to open the SC Manager!\n");
4478 goto done;
4479 }
4480
4481 start_type = MSI_RecordGetInteger(rec, 5);
4482 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4483 goto done;
4484
4485 depends = MSI_RecordGetString(rec, 8);
4486 if (depends && *depends)
4487 FIXME("Dependency list unhandled!\n");
4488
4489 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4490 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4491 serv_type = MSI_RecordGetInteger(rec, 4);
4492 err_control = MSI_RecordGetInteger(rec, 6);
4493 load_order = MSI_RecordGetString(rec, 7);
4494 serv_name = MSI_RecordGetString(rec, 9);
4495 pass = MSI_RecordGetString(rec, 10);
4496 comp = MSI_RecordGetString(rec, 12);
4497
4498 /* fetch the service path */
4499 row = MSI_QueryGetRecord(package->db, query, comp);
4500 if (!row)
4501 {
4502 ERR("Control query failed!\n");
4503 goto done;
4504 }
4505
4506 key = MSI_RecordGetString(row, 6);
4507
4508 file = get_loaded_file(package, key);
4509 msiobj_release(&row->hdr);
4510 if (!file)
4511 {
4512 ERR("Failed to load the service file\n");
4513 goto done;
4514 }
4515
4516 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4517 start_type, err_control, file->TargetPath,
4518 load_order, NULL, NULL, serv_name, pass);
4519 if (!service)
4520 {
4521 if (GetLastError() != ERROR_SERVICE_EXISTS)
4522 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4523 }
4524
4525 done:
4526 CloseServiceHandle(service);
4527 CloseServiceHandle(hscm);
4528 msi_free(name);
4529 msi_free(disp);
4530
4531 return ERROR_SUCCESS;
4532 }
4533
4534 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4535 {
4536 UINT rc;
4537 MSIQUERY * view;
4538 static const WCHAR ExecSeqQuery[] =
4539 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4540 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4541
4542 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4543 if (rc != ERROR_SUCCESS)
4544 return ERROR_SUCCESS;
4545
4546 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4547 msiobj_release(&view->hdr);
4548
4549 return rc;
4550 }
4551
4552 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4553 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4554 {
4555 LPCWSTR *vector, *temp_vector;
4556 LPWSTR p, q;
4557 DWORD sep_len;
4558
4559 static const WCHAR separator[] = {'[','~',']',0};
4560
4561 *numargs = 0;
4562 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4563
4564 if (!args)
4565 return NULL;
4566
4567 vector = msi_alloc(sizeof(LPWSTR));
4568 if (!vector)
4569 return NULL;
4570
4571 p = args;
4572 do
4573 {
4574 (*numargs)++;
4575 vector[*numargs - 1] = p;
4576
4577 if ((q = strstrW(p, separator)))
4578 {
4579 *q = '\0';
4580
4581 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4582 if (!temp_vector)
4583 {
4584 msi_free(vector);
4585 return NULL;
4586 }
4587 vector = temp_vector;
4588
4589 p = q + sep_len;
4590 }
4591 } while (q);
4592
4593 return vector;
4594 }
4595
4596 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4597 {
4598 MSIPACKAGE *package = param;
4599 MSICOMPONENT *comp;
4600 SC_HANDLE scm, service = NULL;
4601 LPCWSTR *vector = NULL;
4602 LPWSTR name, args;
4603 DWORD event, numargs;
4604 UINT r = ERROR_FUNCTION_FAILED;
4605
4606 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4607 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4608 return ERROR_SUCCESS;
4609
4610 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4611 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4612 event = MSI_RecordGetInteger(rec, 3);
4613
4614 if (!(event & msidbServiceControlEventStart))
4615 return ERROR_SUCCESS;
4616
4617 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4618 if (!scm)
4619 {
4620 ERR("Failed to open the service control manager\n");
4621 goto done;
4622 }
4623
4624 service = OpenServiceW(scm, name, SERVICE_START);
4625 if (!service)
4626 {
4627 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4628 goto done;
4629 }
4630
4631 vector = msi_service_args_to_vector(args, &numargs);
4632
4633 if (!StartServiceW(service, numargs, vector) &&
4634 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4635 {
4636 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4637 goto done;
4638 }
4639
4640 r = ERROR_SUCCESS;
4641
4642 done:
4643 CloseServiceHandle(service);
4644 CloseServiceHandle(scm);
4645
4646 msi_free(name);
4647 msi_free(args);
4648 msi_free(vector);
4649 return r;
4650 }
4651
4652 static UINT ACTION_StartServices( MSIPACKAGE *package )
4653 {
4654 UINT rc;
4655 MSIQUERY *view;
4656
4657 static const WCHAR query[] = {
4658 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4659 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4660
4661 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4662 if (rc != ERROR_SUCCESS)
4663 return ERROR_SUCCESS;
4664
4665 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4666 msiobj_release(&view->hdr);
4667
4668 return rc;
4669 }
4670
4671 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4672 {
4673 DWORD i, needed, count;
4674 ENUM_SERVICE_STATUSW *dependencies;
4675 SERVICE_STATUS ss;
4676 SC_HANDLE depserv;
4677
4678 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4679 0, &needed, &count))
4680 return TRUE;
4681
4682 if (GetLastError() != ERROR_MORE_DATA)
4683 return FALSE;
4684
4685 dependencies = msi_alloc(needed);
4686 if (!dependencies)
4687 return FALSE;
4688
4689 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4690 needed, &needed, &count))
4691 goto error;
4692
4693 for (i = 0; i < count; i++)
4694 {
4695 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4696 SERVICE_STOP | SERVICE_QUERY_STATUS);
4697 if (!depserv)
4698 goto error;
4699
4700 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4701 goto error;
4702 }
4703
4704 return TRUE;
4705
4706 error:
4707 msi_free(dependencies);
4708 return FALSE;
4709 }
4710
4711 static UINT stop_service( LPCWSTR name )
4712 {
4713 SC_HANDLE scm = NULL, service = NULL;
4714 SERVICE_STATUS status;
4715 SERVICE_STATUS_PROCESS ssp;
4716 DWORD needed;
4717
4718 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4719 if (!scm)
4720 {
4721 WARN("Failed to open the SCM: %d\n", GetLastError());
4722 goto done;
4723 }
4724
4725 service = OpenServiceW(scm, name,
4726 SERVICE_STOP |
4727 SERVICE_QUERY_STATUS |
4728 SERVICE_ENUMERATE_DEPENDENTS);
4729 if (!service)
4730 {
4731 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4732 goto done;
4733 }
4734
4735 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4736 sizeof(SERVICE_STATUS_PROCESS), &needed))
4737 {
4738 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4739 goto done;
4740 }
4741
4742 if (ssp.dwCurrentState == SERVICE_STOPPED)
4743 goto done;
4744
4745 stop_service_dependents(scm, service);
4746
4747 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4748 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4749
4750 done:
4751 CloseServiceHandle(service);
4752 CloseServiceHandle(scm);
4753
4754 return ERROR_SUCCESS;
4755 }
4756
4757 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4758 {
4759 MSIPACKAGE *package = param;
4760 MSICOMPONENT *comp;
4761 LPWSTR name;
4762 DWORD event;
4763
4764 event = MSI_RecordGetInteger( rec, 3 );
4765 if (!(event & msidbServiceControlEventStop))
4766 return ERROR_SUCCESS;
4767
4768 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4769 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4770 return ERROR_SUCCESS;
4771
4772 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4773 stop_service( name );
4774 msi_free( name );
4775
4776 return ERROR_SUCCESS;
4777 }
4778
4779 static UINT ACTION_StopServices( MSIPACKAGE *package )
4780 {
4781 UINT rc;
4782 MSIQUERY *view;
4783
4784 static const WCHAR query[] = {
4785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4786 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4787
4788 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4789 if (rc != ERROR_SUCCESS)
4790 return ERROR_SUCCESS;
4791
4792 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4793 msiobj_release(&view->hdr);
4794
4795 return rc;
4796 }
4797
4798 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
4799 {
4800 MSIPACKAGE *package = param;
4801 MSICOMPONENT *comp;
4802 LPWSTR name = NULL;
4803 DWORD event;
4804 SC_HANDLE scm = NULL, service = NULL;
4805
4806 event = MSI_RecordGetInteger( rec, 3 );
4807 if (!(event & msidbServiceControlEventDelete))
4808 return ERROR_SUCCESS;
4809
4810 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
4811 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4812 return ERROR_SUCCESS;
4813
4814 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
4815 stop_service( name );
4816
4817 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
4818 if (!scm)
4819 {
4820 WARN("Failed to open the SCM: %d\n", GetLastError());
4821 goto done;
4822 }
4823
4824 service = OpenServiceW( scm, name, DELETE );
4825 if (!service)
4826 {
4827 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
4828 goto done;
4829 }
4830
4831 if (!DeleteService( service ))
4832 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
4833
4834 done:
4835 CloseServiceHandle( service );
4836 CloseServiceHandle( scm );
4837 msi_free( name );
4838
4839 return ERROR_SUCCESS;
4840 }
4841
4842 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4843 {
4844 UINT rc;
4845 MSIQUERY *view;
4846
4847 static const WCHAR query[] = {
4848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4849 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4850
4851 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4852 if (rc != ERROR_SUCCESS)
4853 return ERROR_SUCCESS;
4854
4855 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
4856 msiobj_release( &view->hdr );
4857
4858 return rc;
4859 }
4860
4861 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4862 {
4863 MSIFILE *file;
4864
4865 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4866 {
4867 if (!lstrcmpW(file->File, filename))
4868 return file;
4869 }
4870
4871 return NULL;
4872 }
4873
4874 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4875 {
4876 MSIPACKAGE *package = param;
4877 LPWSTR driver, driver_path, ptr;
4878 WCHAR outpath[MAX_PATH];
4879 MSIFILE *driver_file, *setup_file;
4880 LPCWSTR desc;
4881 DWORD len, usage;
4882 UINT r = ERROR_SUCCESS;
4883
4884 static const WCHAR driver_fmt[] = {
4885 'D','r','i','v','e','r','=','%','s',0};
4886 static const WCHAR setup_fmt[] = {
4887 'S','e','t','u','p','=','%','s',0};
4888 static const WCHAR usage_fmt[] = {
4889 'F','i','l','e','U','s','a','g','e','=','1',0};
4890
4891 desc = MSI_RecordGetString(rec, 3);
4892
4893 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4894 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4895
4896 if (!driver_file || !setup_file)
4897 {
4898 ERR("ODBC Driver entry not found!\n");
4899 return ERROR_FUNCTION_FAILED;
4900 }
4901
4902 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4903 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4904 lstrlenW(usage_fmt) + 1;
4905 driver = msi_alloc(len * sizeof(WCHAR));
4906 if (!driver)
4907 return ERROR_OUTOFMEMORY;
4908
4909 ptr = driver;
4910 lstrcpyW(ptr, desc);
4911 ptr += lstrlenW(ptr) + 1;
4912
4913 sprintfW(ptr, driver_fmt, driver_file->FileName);
4914 ptr += lstrlenW(ptr) + 1;
4915
4916 sprintfW(ptr, setup_fmt, setup_file->FileName);
4917 ptr += lstrlenW(ptr) + 1;
4918
4919 lstrcpyW(ptr, usage_fmt);
4920 ptr += lstrlenW(ptr) + 1;
4921 *ptr = '\0';
4922
4923 driver_path = strdupW(driver_file->TargetPath);
4924 ptr = strrchrW(driver_path, '\\');
4925 if (ptr) *ptr = '\0';
4926
4927 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4928 NULL, ODBC_INSTALL_COMPLETE, &usage))
4929 {
4930 ERR("Failed to install SQL driver!\n");
4931 r = ERROR_FUNCTION_FAILED;
4932 }
4933
4934 msi_free(driver);
4935 msi_free(driver_path);
4936
4937 return r;
4938 }
4939
4940 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4941 {
4942 MSIPACKAGE *package = param;
4943 LPWSTR translator, translator_path, ptr;
4944 WCHAR outpath[MAX_PATH];
4945 MSIFILE *translator_file, *setup_file;
4946 LPCWSTR desc;
4947 DWORD len, usage;
4948 UINT r = ERROR_SUCCESS;
4949
4950 static const WCHAR translator_fmt[] = {
4951 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4952 static const WCHAR setup_fmt[] = {
4953 'S','e','t','u','p','=','%','s',0};
4954
4955 desc = MSI_RecordGetString(rec, 3);
4956
4957 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4958 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4959
4960 if (!translator_file || !setup_file)
4961 {
4962 ERR("ODBC Translator entry not found!\n");
4963 return ERROR_FUNCTION_FAILED;
4964 }
4965
4966 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4967 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4968 translator = msi_alloc(len * sizeof(WCHAR));
4969 if (!translator)
4970 return ERROR_OUTOFMEMORY;
4971
4972 ptr = translator;
4973 lstrcpyW(ptr, desc);
4974 ptr += lstrlenW(ptr) + 1;
4975
4976 sprintfW(ptr, translator_fmt, translator_file->FileName);
4977 ptr += lstrlenW(ptr) + 1;
4978
4979 sprintfW(ptr, setup_fmt, setup_file->FileName);
4980 ptr += lstrlenW(ptr) + 1;
4981 *ptr = '\0';
4982
4983 translator_path = strdupW(translator_file->TargetPath);
4984 ptr = strrchrW(translator_path, '\\');
4985 if (ptr) *ptr = '\0';
4986
4987 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4988 NULL, ODBC_INSTALL_COMPLETE, &usage))
4989 {
4990 ERR("Failed to install SQL translator!\n");
4991 r = ERROR_FUNCTION_FAILED;
4992 }
4993
4994 msi_free(translator);
4995 msi_free(translator_path);
4996
4997 return r;
4998 }
4999
5000 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5001 {
5002 LPWSTR attrs;
5003 LPCWSTR desc, driver;
5004 WORD request = ODBC_ADD_SYS_DSN;
5005 INT registration;
5006 DWORD len;
5007 UINT r = ERROR_SUCCESS;
5008
5009 static const WCHAR attrs_fmt[] = {
5010 'D','S','N','=','%','s',0 };
5011
5012 desc = MSI_RecordGetString(rec, 3);
5013 driver = MSI_RecordGetString(rec, 4);
5014 registration = MSI_RecordGetInteger(rec, 5);
5015
5016 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5017 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5018
5019 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5020 attrs = msi_alloc(len * sizeof(WCHAR));
5021 if (!attrs)
5022 return ERROR_OUTOFMEMORY;
5023
5024 sprintfW(attrs, attrs_fmt, desc);
5025 attrs[len - 1] = '\0';
5026
5027 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5028 {
5029 ERR("Failed to install SQL data source!\n");
5030 r = ERROR_FUNCTION_FAILED;
5031 }
5032
5033 msi_free(attrs);
5034
5035 return r;
5036 }
5037
5038 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5039 {
5040 UINT rc;
5041 MSIQUERY *view;
5042
5043 static const WCHAR driver_query[] = {
5044 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5045 'O','D','B','C','D','r','i','v','e','r',0 };
5046
5047 static const WCHAR translator_query[] = {
5048 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5049 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5050
5051 static const WCHAR source_query[] = {
5052 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5053 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5054
5055 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5056 if (rc != ERROR_SUCCESS)
5057 return ERROR_SUCCESS;
5058
5059 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5060 msiobj_release(&view->hdr);
5061
5062 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5063 if (rc != ERROR_SUCCESS)
5064 return ERROR_SUCCESS;
5065
5066 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5067 msiobj_release(&view->hdr);
5068
5069 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5070 if (rc != ERROR_SUCCESS)
5071 return ERROR_SUCCESS;
5072
5073 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5074 msiobj_release(&view->hdr);
5075
5076 return rc;
5077 }
5078
5079 #define ENV_ACT_SETALWAYS 0x1
5080 #define ENV_ACT_SETABSENT 0x2
5081 #define ENV_ACT_REMOVE 0x4
5082 #define ENV_ACT_REMOVEMATCH 0x8
5083
5084 #define ENV_MOD_MACHINE 0x20000000
5085 #define ENV_MOD_APPEND 0x40000000
5086 #define ENV_MOD_PREFIX 0x80000000
5087 #define ENV_MOD_MASK 0xC0000000
5088
5089 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5090
5091 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5092 {
5093 LPCWSTR cptr = *name;
5094
5095 static const WCHAR prefix[] = {'[','~',']',0};
5096 static const int prefix_len = 3;
5097
5098 *flags = 0;
5099 while (*cptr)
5100 {
5101 if (*cptr == '=')
5102 *flags |= ENV_ACT_SETALWAYS;
5103 else if (*cptr == '+')
5104 *flags |= ENV_ACT_SETABSENT;
5105 else if (*cptr == '-')
5106 *flags |= ENV_ACT_REMOVE;
5107 else if (*cptr == '!')
5108 *flags |= ENV_ACT_REMOVEMATCH;
5109 else if (*cptr == '*')
5110 *flags |= ENV_MOD_MACHINE;
5111 else
5112 break;
5113
5114 cptr++;
5115 (*name)++;
5116 }
5117
5118 if (!*cptr)
5119 {
5120 ERR("Missing environment variable\n");
5121 return ERROR_FUNCTION_FAILED;
5122 }
5123
5124 if (*value)
5125 {
5126 LPCWSTR ptr = *value;
5127 if (!strncmpW(ptr, prefix, prefix_len))
5128 {
5129 if (ptr[prefix_len] == szSemiColon[0])
5130 {
5131 *flags |= ENV_MOD_APPEND;
5132 *value += lstrlenW(prefix);
5133 }
5134 else
5135 {
5136 *value = NULL;
5137 }
5138 }
5139 else if (lstrlenW(*value) >= prefix_len)
5140 {
5141 ptr += lstrlenW(ptr) - prefix_len;
5142 if (!lstrcmpW(ptr, prefix))
5143 {
5144 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5145 {
5146 *flags |= ENV_MOD_PREFIX;
5147 /* the "[~]" will be removed by deformat_string */;
5148 }
5149 else
5150 {
5151 *value = NULL;
5152 }
5153 }
5154 }
5155 }
5156
5157 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5158 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5159 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5160 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5161 {
5162 ERR("Invalid flags: %08x\n", *flags);
5163 return ERROR_FUNCTION_FAILED;
5164 }
5165
5166 if (!*flags)
5167 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5168
5169 return ERROR_SUCCESS;
5170 }
5171
5172 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5173 {
5174 MSIPACKAGE *package = param;
5175 LPCWSTR name, value;
5176 LPWSTR data = NULL, newval = NULL;
5177 LPWSTR deformatted = NULL, ptr;
5178 DWORD flags, type, size;
5179 LONG res;
5180 HKEY env = NULL, root;
5181 LPCWSTR environment;
5182
5183 static const WCHAR user_env[] =
5184 {'E','n','v','i','r','o','n','m','e','n','t',0};
5185 static const WCHAR machine_env[] =
5186 {'S','y','s','t','e','m','\\',
5187 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5188 'C','o','n','t','r','o','l','\\',
5189 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5190 'E','n','v','i','r','o','n','m','e','n','t',0};
5191
5192 name = MSI_RecordGetString(rec, 2);
5193 value = MSI_RecordGetString(rec, 3);
5194
5195 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5196
5197 res = env_set_flags(&name, &value, &flags);
5198 if (res != ERROR_SUCCESS || !value)
5199 goto done;
5200
5201 if (value && !deformat_string(package, value, &deformatted))
5202 {
5203 res = ERROR_OUTOFMEMORY;
5204 goto done;
5205 }
5206
5207 value = deformatted;
5208
5209 if (flags & ENV_MOD_MACHINE)
5210 {
5211 environment = machine_env;
5212 root = HKEY_LOCAL_MACHINE;
5213 }
5214 else
5215 {
5216 environment = user_env;
5217 root = HKEY_CURRENT_USER;
5218 }
5219
5220 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5221 KEY_ALL_ACCESS, NULL, &env, NULL);
5222 if (res != ERROR_SUCCESS)
5223 goto done;
5224
5225 if (flags & ENV_ACT_REMOVE)
5226 FIXME("Not removing environment variable on uninstall!\n");
5227
5228 size = 0;
5229 type = REG_SZ;
5230 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5231 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5232 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5233 goto done;
5234
5235 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5236 {
5237 /* Nothing to do. */
5238 if (!value)
5239 {
5240 res = ERROR_SUCCESS;
5241 goto done;
5242 }
5243
5244 /* If we are appending but the string was empty, strip ; */
5245 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5246
5247 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5248 newval = strdupW(value);
5249 if (!newval)
5250 {
5251 res = ERROR_OUTOFMEMORY;
5252 goto done;
5253 }
5254 }
5255 else
5256 {
5257 /* Contrary to MSDN, +-variable to [~];path works */
5258 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5259 {
5260 res = ERROR_SUCCESS;
5261 goto done;
5262 }
5263
5264 data = msi_alloc(size);
5265 if (!data)
5266 {
5267 RegCloseKey(env);
5268 return ERROR_OUTOFMEMORY;
5269 }
5270
5271 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5272 if (res != ERROR_SUCCESS)
5273 goto done;
5274
5275 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5276 {
5277 res = RegDeleteKeyW(env, name);
5278 goto done;
5279 }
5280
5281 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5282 if (flags & ENV_MOD_MASK)
5283 {
5284 DWORD mod_size;
5285 int multiplier = 0;
5286 if (flags & ENV_MOD_APPEND) multiplier++;
5287 if (flags & ENV_MOD_PREFIX) multiplier++;
5288 mod_size = lstrlenW(value) * multiplier;
5289 size += mod_size * sizeof(WCHAR);
5290 }
5291
5292 newval = msi_alloc(size);
5293 ptr = newval;
5294 if (!newval)
5295 {
5296 res = ERROR_OUTOFMEMORY;
5297 goto done;
5298 }
5299
5300 if (flags & ENV_MOD_PREFIX)
5301 {
5302 lstrcpyW(newval, value);
5303 ptr = newval + lstrlenW(value);
5304 }
5305
5306 lstrcpyW(ptr, data);
5307
5308 if (flags & ENV_MOD_APPEND)
5309 {
5310 lstrcatW(newval, value);
5311 }
5312 }
5313 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5314 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5315
5316 done:
5317 if (env) RegCloseKey(env);
5318 msi_free(deformatted);
5319 msi_free(data);
5320 msi_free(newval);
5321 return res;
5322 }
5323
5324 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5325 {
5326 UINT rc;
5327 MSIQUERY * view;
5328 static const WCHAR ExecSeqQuery[] =
5329 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5330 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5331 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5332 if (rc != ERROR_SUCCESS)
5333 return ERROR_SUCCESS;
5334
5335 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5336 msiobj_release(&view->hdr);
5337
5338 return rc;
5339 }
5340
5341 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5342
5343 typedef struct
5344 {
5345 struct list entry;
5346 LPWSTR sourcename;
5347 LPWSTR destname;
5348 LPWSTR source;
5349 LPWSTR dest;
5350 } FILE_LIST;
5351
5352 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5353 {
5354 BOOL ret;
5355
5356 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5357 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5358 {
5359 WARN("Source or dest is directory, not moving\n");
5360 return FALSE;
5361 }
5362
5363 if (options == msidbMoveFileOptionsMove)
5364 {
5365 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5366 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5367 if (!ret)
5368 {
5369 WARN("MoveFile failed: %d\n", GetLastError());
5370 return FALSE;
5371 }
5372 }
5373 else
5374 {
5375 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5376 ret = CopyFileW(source, dest, FALSE);
5377 if (!ret)
5378 {
5379 WARN("CopyFile failed: %d\n", GetLastError());
5380 return FALSE;
5381 }
5382 }
5383
5384 return TRUE;
5385 }
5386
5387 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5388 {
5389 LPWSTR path, ptr;
5390 DWORD dirlen, pathlen;
5391
5392 ptr = strrchrW(wildcard, '\\');
5393 dirlen = ptr - wildcard + 1;
5394
5395 pathlen = dirlen + lstrlenW(filename) + 1;
5396 path = msi_alloc(pathlen * sizeof(WCHAR));
5397
5398 lstrcpynW(path, wildcard, dirlen + 1);
5399 lstrcatW(path, filename);
5400
5401 return path;
5402 }
5403
5404 static void free_file_entry(FILE_LIST *file)
5405 {
5406 msi_free(file->source);
5407 msi_free(file->dest);
5408 msi_free(file);
5409 }
5410
5411 static void free_list(FILE_LIST *list)
5412 {
5413 while (!list_empty(&list->entry))
5414 {
5415 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5416
5417 list_remove(&file->entry);
5418 free_file_entry(file);
5419 }
5420 }
5421
5422 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5423 {
5424 FILE_LIST *new, *file;
5425 LPWSTR ptr, filename;
5426 DWORD size;
5427
5428 new = msi_alloc_zero(sizeof(FILE_LIST));
5429 if (!new)
5430 return FALSE;
5431
5432 new->source = strdupW(source);
5433 ptr = strrchrW(dest, '\\') + 1;
5434 filename = strrchrW(new->source, '\\') + 1;
5435
5436 new->sourcename = filename;
5437
5438 if (*ptr)
5439 new->destname = ptr;
5440 else
5441 new->destname = new->sourcename;
5442
5443 size = (ptr - dest) + lstrlenW(filename) + 1;
5444 new->dest = msi_alloc(size * sizeof(WCHAR));
5445 if (!new->dest)
5446 {
5447 free_file_entry(new);
5448 return FALSE;
5449 }
5450
5451 lstrcpynW(new->dest, dest, ptr - dest + 1);
5452 lstrcatW(new->dest, filename);
5453
5454 if (list_empty(&files->entry))
5455 {
5456 list_add_head(&files->entry, &new->entry);
5457 return TRUE;
5458 }
5459
5460 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5461 {
5462 if (lstrcmpW(source, file->source) < 0)
5463 {
5464 list_add_before(&file->entry, &new->entry);
5465 return TRUE;
5466 }
5467 }
5468
5469 list_add_after(&file->entry, &new->entry);
5470 return TRUE;
5471 }
5472
5473 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5474 {
5475 WIN32_FIND_DATAW wfd;
5476 HANDLE hfile;
5477 LPWSTR path;
5478 BOOL res;
5479 FILE_LIST files, *file;
5480 DWORD size;
5481
5482 hfile = FindFirstFileW(source, &wfd);
5483 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5484
5485 list_init(&files.entry);
5486
5487 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5488 {
5489 if (is_dot_dir(wfd.cFileName)) continue;
5490
5491 path = wildcard_to_file(source, wfd.cFileName);
5492 if (!path)
5493 {
5494 res = FALSE;
5495 goto done;
5496 }
5497
5498 add_wildcard(&files, path, dest);
5499 msi_free(path);
5500 }
5501
5502 /* no files match the wildcard */
5503 if (list_empty(&files.entry))
5504 goto done;
5505
5506 /* only the first wildcard match gets renamed to dest */
5507 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5508 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5509 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5510 if (!file->dest)
5511 {
5512 res = FALSE;
5513 goto done;
5514 }
5515
5516 /* file->dest may be shorter after the reallocation, so add a NULL
5517 * terminator. This is needed for the call to strrchrW, as there will no
5518 * longer be a NULL terminator within the bounds of the allocation in this case.
5519 */
5520 file->dest[size - 1] = '\0';
5521 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5522
5523 while (!list_empty(&files.entry))
5524 {
5525 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5526
5527 msi_move_file(file->source, file->dest, options);
5528
5529 list_remove(&file->entry);
5530 free_file_entry(file);
5531 }
5532
5533 res = TRUE;
5534
5535 done:
5536 free_list(&files);
5537 FindClose(hfile);
5538 return res;
5539 }
5540
5541 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5542 {
5543 MSIPACKAGE *package = param;
5544 MSICOMPONENT *comp;
5545 LPCWSTR sourcename;
5546 LPWSTR destname = NULL;
5547 LPWSTR sourcedir = NULL, destdir = NULL;
5548 LPWSTR source = NULL, dest = NULL;
5549 int options;
5550 DWORD size;
5551 BOOL ret, wildcards;
5552
5553 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5554 if (!comp || !comp->Enabled ||
5555 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5556 {
5557 TRACE("Component not set for install, not moving file\n");
5558 return ERROR_SUCCESS;
5559 }
5560
5561 sourcename = MSI_RecordGetString(rec, 3);
5562 options = MSI_RecordGetInteger(rec, 7);
5563
5564 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5565 if (!sourcedir)
5566 goto done;
5567
5568 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5569 if (!destdir)
5570 goto done;
5571
5572 if (!sourcename)
5573 {
5574 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5575 goto done;
5576
5577 source = strdupW(sourcedir);
5578 if (!source)
5579 goto done;
5580 }
5581 else
5582 {
5583 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5584 source = msi_alloc(size * sizeof(WCHAR));
5585 if (!source)
5586 goto done;
5587
5588 lstrcpyW(source, sourcedir);
5589 if (source[lstrlenW(source) - 1] != '\\')
5590 lstrcatW(source, szBackSlash);
5591 lstrcatW(source, sourcename);
5592 }
5593
5594 wildcards = strchrW(source, '*') || strchrW(source, '?');
5595
5596 if (MSI_RecordIsNull(rec, 4))
5597 {
5598 if (!wildcards)
5599 {
5600 destname = strdupW(sourcename);
5601 if (!destname)
5602 goto done;
5603 }
5604 }
5605 else
5606 {
5607 destname = strdupW(MSI_RecordGetString(rec, 4));
5608 if (destname)
5609 reduce_to_longfilename(destname);
5610 }
5611
5612 size = 0;
5613 if (destname)
5614 size = lstrlenW(destname);
5615
5616 size += lstrlenW(destdir) + 2;
5617 dest = msi_alloc(size * sizeof(WCHAR));
5618 if (!dest)
5619 goto done;
5620
5621 lstrcpyW(dest, destdir);
5622 if (dest[lstrlenW(dest) - 1] != '\\')
5623 lstrcatW(dest, szBackSlash);
5624
5625 if (destname)
5626 lstrcatW(dest, destname);
5627
5628 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5629 {
5630 ret = CreateDirectoryW(destdir, NULL);
5631 if (!ret)
5632 {
5633 WARN("CreateDirectory failed: %d\n", GetLastError());
5634 return ERROR_SUCCESS;
5635 }
5636 }
5637
5638 if (!wildcards)
5639 msi_move_file(source, dest, options);
5640 else
5641 move_files_wildcard(source, dest, options);
5642
5643 done:
5644 msi_free(sourcedir);
5645 msi_free(destdir);
5646 msi_free(destname);
5647 msi_free(source);
5648 msi_free(dest);
5649
5650 return ERROR_SUCCESS;
5651 }
5652
5653 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5654 {
5655 UINT rc;
5656 MSIQUERY *view;
5657
5658 static const WCHAR ExecSeqQuery[] =
5659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5660 '`','M','o','v','e','F','i','l','e','`',0};
5661
5662 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5663 if (rc != ERROR_SUCCESS)
5664 return ERROR_SUCCESS;
5665
5666 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5667 msiobj_release(&view->hdr);
5668
5669 return rc;
5670 }
5671
5672 typedef struct tagMSIASSEMBLY
5673 {
5674 struct list entry;
5675 MSICOMPONENT *component;
5676 MSIFEATURE *feature;
5677 MSIFILE *file;
5678 LPWSTR manifest;
5679 LPWSTR application;
5680 DWORD attributes;
5681 BOOL installed;
5682 } MSIASSEMBLY;
5683
5684 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5685 DWORD dwReserved);
5686 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5687 LPVOID pvReserved, HMODULE *phModDll);
5688
5689 static BOOL init_functionpointers(void)
5690 {
5691 HRESULT hr;
5692 HMODULE hfusion;
5693 HMODULE hmscoree;
5694
5695 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5696
5697 hmscoree = LoadLibraryA("mscoree.dll");
5698 if (!hmscoree)
5699 {
5700 WARN("mscoree.dll not available\n");
5701 return FALSE;
5702 }
5703
5704 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5705 if (!pLoadLibraryShim)
5706 {
5707 WARN("LoadLibraryShim not available\n");
5708 FreeLibrary(hmscoree);
5709 return FALSE;
5710 }
5711
5712 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5713 if (FAILED(hr))
5714 {
5715 WARN("fusion.dll not available\n");
5716 FreeLibrary(hmscoree);
5717 return FALSE;
5718 }
5719
5720 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5721
5722 FreeLibrary(hmscoree);
5723 return TRUE;
5724 }
5725
5726 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5727 LPWSTR path)
5728 {
5729 IAssemblyCache *cache;
5730 HRESULT hr;
5731 UINT r = ERROR_FUNCTION_FAILED;
5732
5733 TRACE("installing assembly: %s\n", debugstr_w(path));
5734
5735 if (assembly->feature)
5736 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5737
5738 if (assembly->manifest)
5739 FIXME("Manifest unhandled\n");
5740
5741 if (assembly->application)
5742 {
5743 FIXME("Assembly should be privately installed\n");
5744 return ERROR_SUCCESS;
5745 }
5746
5747 if (assembly->attributes == msidbAssemblyAttributesWin32)
5748 {
5749 FIXME("Win32 assemblies not handled\n");
5750 return ERROR_SUCCESS;
5751 }
5752
5753 hr = pCreateAssemblyCache(&cache, 0);
5754 if (FAILED(hr))
5755 goto done;
5756
5757 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5758 if (FAILED(hr))
5759 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5760
5761 r = ERROR_SUCCESS;
5762
5763 done:
5764 IAssemblyCache_Release(cache);
5765 return r;
5766 }
5767
5768 typedef struct tagASSEMBLY_LIST
5769 {
5770 MSIPACKAGE *package;
5771 IAssemblyCache *cache;
5772 struct list *assemblies;
5773 } ASSEMBLY_LIST;
5774
5775 typedef struct tagASSEMBLY_NAME
5776 {
5777 LPWSTR name;
5778 LPWSTR version;
5779 LPWSTR culture;
5780 LPWSTR pubkeytoken;
5781 } ASSEMBLY_NAME;
5782
5783 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5784 {
5785 ASSEMBLY_NAME *asmname = param;
5786 LPCWSTR name = MSI_RecordGetString(rec, 2);
5787 LPWSTR val = msi_dup_record_field(rec, 3);
5788
5789 static const WCHAR Name[] = {'N','a','m','e',0};
5790 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5791 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5792 static const WCHAR PublicKeyToken[] = {
5793 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5794
5795 if (!strcmpiW(name, Name))
5796 asmname->name = val;
5797 else if (!strcmpiW(name, Version))
5798 asmname->version = val;
5799 else if (!strcmpiW(name, Culture))
5800 asmname->culture = val;
5801 else if (!strcmpiW(name, PublicKeyToken))
5802 asmname->pubkeytoken = val;
5803 else
5804 msi_free(val);
5805
5806 return ERROR_SUCCESS;
5807 }
5808
5809 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5810 {
5811 if (!*str)
5812 {
5813 *size = lstrlenW(append) + 1;
5814 *str = msi_alloc((*size) * sizeof(WCHAR));
5815 lstrcpyW(*str, append);
5816 return;
5817 }
5818
5819 (*size) += lstrlenW(append);
5820 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5821 lstrcatW(*str, append);
5822 }
5823
5824 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5825 MSICOMPONENT *comp)
5826 {
5827 ASSEMBLY_INFO asminfo;
5828 ASSEMBLY_NAME name;
5829 MSIQUERY *view;
5830 LPWSTR disp;
5831 DWORD size;
5832 BOOL found;
5833 UINT r;
5834
5835 static const WCHAR separator[] = {',',' ',0};
5836 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5837 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5838 static const WCHAR PublicKeyToken[] = {
5839 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5840 static const WCHAR query[] = {
5841 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5842 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5843 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5844 '=','\'','%','s','\'',0};
5845
5846 disp = NULL;
5847 found = FALSE;
5848 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5849 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5850
5851 r = MSI_OpenQuery(db, &view, query, comp->Component);
5852 if (r != ERROR_SUCCESS)
5853 return ERROR_SUCCESS;
5854
5855 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5856 msiobj_release(&view->hdr);
5857
5858 if (!name.name)
5859 {
5860 ERR("No assembly name specified!\n");
5861 goto done;
5862 }
5863
5864 append_str(&disp, &size, name.name);
5865
5866 if (name.version)
5867 {
5868 append_str(&disp, &size, separator);
5869 append_str(&disp, &size, Version);
5870 append_str(&disp, &size, name.version);
5871 }
5872
5873 if (name.culture)
5874 {
5875 append_str(&disp, &size, separator);
5876 append_str(&disp, &size, Culture);
5877 append_str(&disp, &size, name.culture);
5878 }
5879
5880 if (name.pubkeytoken)
5881 {
5882 append_str(&disp, &size, separator);
5883 append_str(&disp, &size, PublicKeyToken);
5884 append_str(&disp, &size, name.pubkeytoken);
5885 }
5886
5887 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5888 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5889 disp, &asminfo);
5890 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5891
5892 done:
5893 msi_free(disp);
5894 msi_free(name.name);
5895 msi_free(name.version);
5896 msi_free(name.culture);
5897 msi_free(name.pubkeytoken);
5898
5899 return found;
5900 }
5901
5902 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5903 {
5904 ASSEMBLY_LIST *list = param;
5905 MSIASSEMBLY *assembly;
5906
5907 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5908 if (!assembly)
5909 return ERROR_OUTOFMEMORY;
5910
5911 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5912
5913 if (!assembly->component || !assembly->component->Enabled ||
5914 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5915 {
5916 TRACE("Component not set for install, not publishing assembly\n");
5917 msi_free(assembly);
5918 return ERROR_SUCCESS;
5919 }
5920
5921 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5922 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5923
5924 if (!assembly->file)
5925 {
5926 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5927 return ERROR_FUNCTION_FAILED;
5928 }
5929
5930 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5931 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5932 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5933
5934 if (assembly->application)
5935 {
5936 WCHAR version[24];
5937 DWORD size = sizeof(version)/sizeof(WCHAR);
5938
5939 /* FIXME: we should probably check the manifest file here */
5940
5941 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
5942 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
5943 {
5944 assembly->installed = TRUE;
5945 }
5946 }
5947 else
5948 assembly->installed = check_assembly_installed(list->package->db,
5949 list->cache,
5950 assembly->component);
5951
5952 list_add_head(list->assemblies, &assembly->entry);
5953 return ERROR_SUCCESS;
5954 }
5955
5956 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5957 {
5958 IAssemblyCache *cache = NULL;
5959 ASSEMBLY_LIST list;
5960 MSIQUERY *view;
5961 HRESULT hr;
5962 UINT r;
5963
5964 static const WCHAR query[] =
5965 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5966 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5967
5968 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5969 if (r != ERROR_SUCCESS)
5970 return ERROR_SUCCESS;
5971
5972 hr = pCreateAssemblyCache(&cache, 0);
5973 if (FAILED(hr))
5974 return ERROR_FUNCTION_FAILED;
5975
5976 list.package = package;
5977 list.cache = cache;
5978 list.assemblies = assemblies;
5979
5980 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5981 msiobj_release(&view->hdr);
5982
5983 IAssemblyCache_Release(cache);
5984
5985 return r;
5986 }
5987
5988 static void free_assemblies(struct list *assemblies)
5989 {
5990 struct list *item, *cursor;
5991
5992 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5993 {
5994 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5995
5996 list_remove(&assembly->entry);
5997 msi_free(assembly->application);
5998 msi_free(assembly->manifest);
5999 msi_free(assembly);
6000 }
6001 }
6002
6003 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6004 {
6005 MSIASSEMBLY *assembly;
6006
6007 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6008 {
6009 if (!lstrcmpW(assembly->file->File, file))
6010 {
6011 *out = assembly;
6012 return TRUE;
6013 }
6014 }
6015
6016 return FALSE;
6017 }
6018
6019 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6020 LPWSTR *path, DWORD *attrs, PVOID user)
6021 {
6022 MSIASSEMBLY *assembly;
6023 WCHAR temppath[MAX_PATH];
6024 struct list *assemblies = user;
6025 UINT r;
6026
6027 if (!find_assembly(assemblies, file, &assembly))
6028 return FALSE;
6029
6030 GetTempPathW(MAX_PATH, temppath);
6031 PathAddBackslashW(temppath);
6032 lstrcatW(temppath, assembly->file->FileName);
6033
6034 if (action == MSICABEXTRACT_BEGINEXTRACT)
6035 {
6036 if (assembly->installed)
6037 return FALSE;
6038
6039 *path = strdupW(temppath);
6040 *attrs = assembly->file->Attributes;
6041 }
6042 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6043 {
6044 assembly->installed = TRUE;
6045
6046 r = install_assembly(package, assembly, temppath);
6047 if (r != ERROR_SUCCESS)
6048 ERR("Failed to install assembly\n");
6049 }
6050
6051 return TRUE;
6052 }
6053
6054 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6055 {
6056 UINT r;
6057 struct list assemblies = LIST_INIT(assemblies);
6058 MSIASSEMBLY *assembly;
6059 MSIMEDIAINFO *mi;
6060
6061 if (!init_functionpointers() || !pCreateAssemblyCache)
6062 return ERROR_FUNCTION_FAILED;
6063
6064 r = load_assemblies(package, &assemblies);
6065 if (r != ERROR_SUCCESS)
6066 goto done;
6067
6068 if (list_empty(&assemblies))
6069 goto done;
6070
6071 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6072 if (!mi)
6073 {
6074 r = ERROR_OUTOFMEMORY;
6075 goto done;
6076 }
6077
6078 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6079 {
6080 if (assembly->installed && !mi->is_continuous)
6081 continue;
6082
6083 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6084 (assembly->file->IsCompressed && !mi->is_extracted))
6085 {
6086 MSICABDATA data;
6087
6088 r = ready_media(package, assembly->file, mi);
6089 if (r != ERROR_SUCCESS)
6090 {
6091 ERR("Failed to ready media\n");
6092 break;
6093 }
6094
6095 data.mi = mi;
6096 data.package = package;
6097 data.cb = installassembly_cb;
6098 data.user = &assemblies;
6099
6100 if (assembly->file->IsCompressed &&
6101 !msi_cabextract(package, mi, &data))
6102 {
6103 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6104 r = ERROR_FUNCTION_FAILED;
6105 break;
6106 }
6107 }
6108
6109 if (!assembly->file->IsCompressed)
6110 {
6111 LPWSTR source = resolve_file_source(package, assembly->file);
6112
6113 r = install_assembly(package, assembly, source);
6114 if (r != ERROR_SUCCESS)
6115 ERR("Failed to install assembly\n");
6116
6117 msi_free(source);
6118 }
6119
6120 /* FIXME: write Installer assembly reg values */
6121 }
6122
6123 done:
6124 free_assemblies(&assemblies);
6125 return r;
6126 }
6127
6128 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6129 {
6130 TRACE("\n");
6131 package->need_reboot = 1;
6132 return ERROR_SUCCESS;
6133 }
6134
6135 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6136 LPCSTR action, LPCWSTR table )
6137 {
6138 static const WCHAR query[] = {
6139 'S','E','L','E','C','T',' ','*',' ',
6140 'F','R','O','M',' ','`','%','s','`',0 };
6141 MSIQUERY *view = NULL;
6142 DWORD count = 0;
6143 UINT r;
6144
6145 r = MSI_OpenQuery( package->db, &view, query, table );
6146 if (r == ERROR_SUCCESS)
6147 {
6148 r = MSI_IterateRecords(view, &count, NULL, package);
6149 msiobj_release(&view->hdr);
6150 }
6151
6152 if (count)
6153 FIXME("%s -> %u ignored %s table values\n",
6154 action, count, debugstr_w(table));
6155
6156 return ERROR_SUCCESS;
6157 }
6158
6159 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6160 {
6161 TRACE("%p\n", package);
6162 return ERROR_SUCCESS;
6163 }
6164
6165 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6166 {
6167 static const WCHAR table[] =
6168 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6169 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6170 }
6171
6172 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6173 {
6174 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6175 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6176 }
6177
6178 static UINT ACTION_BindImage( MSIPACKAGE *package )
6179 {
6180 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6181 return msi_unimplemented_action_stub( package, "BindImage", table );
6182 }
6183
6184 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6185 {
6186 static const WCHAR table[] = {
6187 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6188 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6189 }
6190
6191 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6192 {
6193 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6194 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6195 }
6196
6197 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6198 {
6199 static const WCHAR table[] = {
6200 'P','r','o','d','u','c','t','I','D',0 };
6201 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6202 }
6203
6204 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6205 {
6206 static const WCHAR table[] = {
6207 'E','n','v','i','r','o','n','m','e','n','t',0 };
6208 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6209 }
6210
6211 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6212 {
6213 static const WCHAR table[] = {
6214 'M','s','i','A','s','s','e','m','b','l','y',0 };
6215 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6216 }
6217
6218 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6219 {
6220 static const WCHAR table[] = { 'F','o','n','t',0 };
6221 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6222 }
6223
6224 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6225 {
6226 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6227 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6228 }
6229
6230 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6231 {
6232 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6233 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6234 }
6235
6236 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6237 {
6238 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6239 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6240 }
6241
6242 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6243 {
6244 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6245 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6246 }
6247
6248 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6249 {
6250 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6251 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6252 }
6253
6254 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6255 {
6256 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6257 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6258 }
6259
6260 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6261 {
6262 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6263 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6264 }
6265
6266 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6267 {
6268 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6269 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6270 }
6271
6272 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6273 {
6274 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6275 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6276 }
6277
6278 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6279 {
6280 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6281 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6282 }
6283
6284 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6285 {
6286 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6287 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6288 }
6289
6290 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6291 {
6292 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6293 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6294 }
6295
6296 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6297 {
6298 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6299 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6300 }
6301
6302 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6303 {
6304 static const WCHAR table[] = { 'M','I','M','E',0 };
6305 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6306 }
6307
6308 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6309 {
6310 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6311 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6312 }
6313
6314 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6315 {
6316 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6317 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6318 }
6319
6320 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6321
6322 static const struct
6323 {
6324 const WCHAR *action;
6325 UINT (*handler)(MSIPACKAGE *);
6326 }
6327 StandardActions[] =
6328 {
6329 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6330 { szAppSearch, ACTION_AppSearch },
6331 { szBindImage, ACTION_BindImage },
6332 { szCCPSearch, ACTION_CCPSearch },
6333 { szCostFinalize, ACTION_CostFinalize },
6334 { szCostInitialize, ACTION_CostInitialize },
6335 { szCreateFolders, ACTION_CreateFolders },
6336 { szCreateShortcuts, ACTION_CreateShortcuts },
6337 { szDeleteServices, ACTION_DeleteServices },
6338 { szDisableRollback, NULL },
6339 { szDuplicateFiles, ACTION_DuplicateFiles },
6340 { szExecuteAction, ACTION_ExecuteAction },
6341 { szFileCost, ACTION_FileCost },
6342 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6343 { szForceReboot, ACTION_ForceReboot },
6344 { szInstallAdminPackage, NULL },
6345 { szInstallExecute, ACTION_InstallExecute },
6346 { szInstallExecuteAgain, ACTION_InstallExecute },
6347 { szInstallFiles, ACTION_InstallFiles},
6348 { szInstallFinalize, ACTION_InstallFinalize },
6349 { szInstallInitialize, ACTION_InstallInitialize },
6350 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6351 { szInstallValidate, ACTION_InstallValidate },
6352 { szIsolateComponents, ACTION_IsolateComponents },
6353 { szLaunchConditions, ACTION_LaunchConditions },
6354 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6355 { szMoveFiles, ACTION_MoveFiles },
6356 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6357 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6358 { szInstallODBC, ACTION_InstallODBC },
6359 { szInstallServices, ACTION_InstallServices },
6360 { szPatchFiles, ACTION_PatchFiles },
6361 { szProcessComponents, ACTION_ProcessComponents },
6362 { szPublishComponents, ACTION_PublishComponents },
6363 { szPublishFeatures, ACTION_PublishFeatures },
6364 { szPublishProduct, ACTION_PublishProduct },
6365 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6366 { szRegisterComPlus, ACTION_RegisterComPlus},
6367 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6368 { szRegisterFonts, ACTION_RegisterFonts },
6369 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6370 { szRegisterProduct, ACTION_RegisterProduct },
6371 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6372 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6373 { szRegisterUser, ACTION_RegisterUser },
6374 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6375 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6376 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6377 { szRemoveFiles, ACTION_RemoveFiles },
6378 { szRemoveFolders, ACTION_RemoveFolders },
6379 { szRemoveIniValues, ACTION_RemoveIniValues },
6380 { szRemoveODBC, ACTION_RemoveODBC },
6381 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6382 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6383 { szResolveSource, ACTION_ResolveSource },
6384 { szRMCCPSearch, ACTION_RMCCPSearch },
6385 { szScheduleReboot, ACTION_ScheduleReboot },
6386 { szSelfRegModules, ACTION_SelfRegModules },
6387 { szSelfUnregModules, ACTION_SelfUnregModules },
6388 { szSetODBCFolders, ACTION_SetODBCFolders },
6389 { szStartServices, ACTION_StartServices },
6390 { szStopServices, ACTION_StopServices },
6391 { szUnpublishComponents, ACTION_UnpublishComponents },
6392 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6393 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6394 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6395 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6396 { szUnregisterFonts, ACTION_UnregisterFonts },
6397 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6398 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6399 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6400 { szValidateProductID, ACTION_ValidateProductID },
6401 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6402 { szWriteIniValues, ACTION_WriteIniValues },
6403 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6404 { NULL, NULL },
6405 };
6406
6407 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6408 UINT* rc, BOOL force )
6409 {
6410 BOOL ret = FALSE;
6411 BOOL run = force;
6412 int i;
6413
6414 if (!run && !package->script->CurrentlyScripting)
6415 run = TRUE;
6416
6417 if (!run)
6418 {
6419 if (strcmpW(action,szInstallFinalize) == 0 ||
6420 strcmpW(action,szInstallExecute) == 0 ||
6421 strcmpW(action,szInstallExecuteAgain) == 0)
6422 run = TRUE;
6423 }
6424
6425 i = 0;
6426 while (StandardActions[i].action != NULL)
6427 {
6428 if (strcmpW(StandardActions[i].action, action)==0)
6429 {
6430 if (!run)
6431 {
6432 ui_actioninfo(package, action, TRUE, 0);
6433 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6434 ui_actioninfo(package, action, FALSE, *rc);
6435 }
6436 else
6437 {
6438 ui_actionstart(package, action);
6439 if (StandardActions[i].handler)
6440 {
6441 *rc = StandardActions[i].handler(package);
6442 }
6443 else
6444 {
6445 FIXME("unhandled standard action %s\n",debugstr_w(action));
6446 *rc = ERROR_SUCCESS;
6447 }
6448 }
6449 ret = TRUE;
6450 break;
6451 }
6452 i++;
6453 }
6454 return ret;
6455 }
6456
6457 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6458 {
6459 UINT rc = ERROR_SUCCESS;
6460 BOOL handled;
6461
6462 TRACE("Performing action (%s)\n", debugstr_w(action));
6463
6464 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6465
6466 if (!handled)
6467 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6468
6469 if (!handled)
6470 {
6471 WARN("unhandled msi action %s\n", debugstr_w(action));
6472 rc = ERROR_FUNCTION_NOT_CALLED;
6473 }
6474
6475 return rc;
6476 }
6477
6478 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6479 {
6480 UINT rc = ERROR_SUCCESS;
6481 BOOL handled = FALSE;
6482
6483 TRACE("Performing action (%s)\n", debugstr_w(action));
6484
6485 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6486
6487 if (!handled)
6488 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6489
6490 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6491 handled = TRUE;
6492
6493 if (!handled)
6494 {
6495 WARN("unhandled msi action %s\n", debugstr_w(action));
6496 rc = ERROR_FUNCTION_NOT_CALLED;
6497 }
6498
6499 return rc;
6500 }
6501
6502 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6503 {
6504 UINT rc = ERROR_SUCCESS;
6505 MSIRECORD *row;
6506
6507 static const WCHAR ExecSeqQuery[] =
6508 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6509 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6510 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6511 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6512 static const WCHAR UISeqQuery[] =
6513 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6514 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6515 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6516 ' ', '=',' ','%','i',0};
6517
6518 if (needs_ui_sequence(package))
6519 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6520 else
6521 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6522
6523 if (row)
6524 {
6525 LPCWSTR action, cond;
6526
6527 TRACE("Running the actions\n");
6528
6529 /* check conditions */
6530 cond = MSI_RecordGetString(row, 2);
6531
6532 /* this is a hack to skip errors in the condition code */
6533 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6534 {
6535 msiobj_release(&row->hdr);
6536 return ERROR_SUCCESS;
6537 }
6538
6539 action = MSI_RecordGetString(row, 1);
6540 if (!action)
6541 {
6542 ERR("failed to fetch action\n");
6543 msiobj_release(&row->hdr);
6544 return ERROR_FUNCTION_FAILED;
6545 }
6546
6547 if (needs_ui_sequence(package))
6548 rc = ACTION_PerformUIAction(package, action, -1);
6549 else
6550 rc = ACTION_PerformAction(package, action, -1, FALSE);
6551
6552 msiobj_release(&row->hdr);
6553 }
6554
6555 return rc;
6556 }
6557
6558 /****************************************************
6559 * TOP level entry points
6560 *****************************************************/
6561
6562 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6563 LPCWSTR szCommandLine )
6564 {
6565 UINT rc;
6566 BOOL ui_exists;
6567
6568 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6569 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6570
6571 MSI_SetPropertyW(package, szAction, szInstall);
6572
6573 package->script->InWhatSequence = SEQUENCE_INSTALL;
6574
6575 if (szPackagePath)
6576 {
6577 LPWSTR p, dir;
6578 LPCWSTR file;
6579
6580 dir = strdupW(szPackagePath);
6581 p = strrchrW(dir, '\\');
6582 if (p)
6583 {
6584 *(++p) = 0;
6585 file = szPackagePath + (p - dir);
6586 }
6587 else
6588 {
6589 msi_free(dir);
6590 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6591 GetCurrentDirectoryW(MAX_PATH, dir);
6592 lstrcatW(dir, szBackSlash);
6593 file = szPackagePath;
6594 }
6595
6596 msi_free( package->PackagePath );
6597 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6598 if (!package->PackagePath)
6599 {
6600 msi_free(dir);
6601 return ERROR_OUTOFMEMORY;
6602 }
6603
6604 lstrcpyW(package->PackagePath, dir);
6605 lstrcatW(package->PackagePath, file);
6606 msi_free(dir);
6607
6608 msi_set_sourcedir_props(package, FALSE);
6609 }
6610
6611 msi_parse_command_line( package, szCommandLine, FALSE );
6612
6613 msi_apply_transforms( package );
6614 msi_apply_patches( package );
6615
6616 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6617 {
6618 TRACE("setting reinstall property\n");
6619 MSI_SetPropertyW( package, szReinstall, szAll );
6620 }
6621
6622 /* properties may have been added by a transform */
6623 msi_clone_properties( package );
6624 msi_set_context( package );
6625
6626 if (needs_ui_sequence( package))
6627 {
6628 package->script->InWhatSequence |= SEQUENCE_UI;
6629 rc = ACTION_ProcessUISequence(package);
6630 ui_exists = ui_sequence_exists(package);
6631 if (rc == ERROR_SUCCESS || !ui_exists)
6632 {
6633 package->script->InWhatSequence |= SEQUENCE_EXEC;
6634 rc = ACTION_ProcessExecSequence(package, ui_exists);
6635 }
6636 }
6637 else
6638 rc = ACTION_ProcessExecSequence(package, FALSE);
6639
6640 package->script->CurrentlyScripting = FALSE;
6641
6642 /* process the ending type action */
6643 if (rc == ERROR_SUCCESS)
6644 ACTION_PerformActionSequence(package, -1);
6645 else if (rc == ERROR_INSTALL_USEREXIT)
6646 ACTION_PerformActionSequence(package, -2);
6647 else if (rc == ERROR_INSTALL_SUSPEND)
6648 ACTION_PerformActionSequence(package, -4);
6649 else /* failed */
6650 ACTION_PerformActionSequence(package, -3);
6651
6652 /* finish up running custom actions */
6653 ACTION_FinishCustomActions(package);
6654
6655 if (rc == ERROR_SUCCESS && package->need_reboot)
6656 return ERROR_SUCCESS_REBOOT_REQUIRED;
6657
6658 return rc;
6659 }