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 '>',' ','',' ','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 '>',' ','',' ','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 '>',' ','',' ','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] = '';
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]= '';
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 < '') || (*p > '9') )
2092 break;
2093 d *= 10;
2094 d += (*p - '');
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[]={'%','','2','i',':','\\','%','s','\\',0};
2458 static const WCHAR fmt2[]=
2459 {'%','','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 { '','','','','','','','','','','','',
2675 '','','','','','','','','','','','',
2676 '','','','','','','','',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[] = {'%','','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','%','','2','i','%','','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