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