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