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