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