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