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