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