msi: Implement the RemoveFolders standard action.
[wine/multimedia.git] / dlls / msi / action.c
blob39db2ca03fdcc0d89c8e5a5d02c0af7698ca68cf
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * consts and values used
51 static const WCHAR c_colon[] = {'C',':','\\',0};
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings[] =
140 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
141 static const WCHAR szRemoveExistingProducts[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
188 /********************************************************
189 * helper functions
190 ********************************************************/
192 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
194 static const WCHAR Query_t[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
199 MSIRECORD * row;
201 row = MSI_QueryGetRecord( package->db, Query_t, action );
202 if (!row)
203 return;
204 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
205 msiobj_release(&row->hdr);
208 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
209 UINT rc)
211 MSIRECORD * row;
212 static const WCHAR template_s[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
214 '%','s', '.',0};
215 static const WCHAR template_e[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
218 '%','i','.',0};
219 static const WCHAR format[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
221 WCHAR message[1024];
222 WCHAR timet[0x100];
224 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
225 if (start)
226 sprintfW(message,template_s,timet,action);
227 else
228 sprintfW(message,template_e,timet,action,rc);
230 row = MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row,1,message);
233 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
234 msiobj_release(&row->hdr);
237 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
238 BOOL preserve_case )
240 LPCWSTR ptr,ptr2;
241 BOOL quote;
242 DWORD len;
243 LPWSTR prop = NULL, val = NULL;
245 if (!szCommandLine)
246 return ERROR_SUCCESS;
248 ptr = szCommandLine;
250 while (*ptr)
252 if (*ptr==' ')
254 ptr++;
255 continue;
258 TRACE("Looking at %s\n",debugstr_w(ptr));
260 ptr2 = strchrW(ptr,'=');
261 if (!ptr2)
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
264 break;
267 quote = FALSE;
269 len = ptr2-ptr;
270 prop = msi_alloc((len+1)*sizeof(WCHAR));
271 memcpy(prop,ptr,len*sizeof(WCHAR));
272 prop[len]=0;
274 if (!preserve_case)
275 struprW(prop);
277 ptr2++;
279 len = 0;
280 ptr = ptr2;
281 while (*ptr && (quote || (!quote && *ptr!=' ')))
283 if (*ptr == '"')
284 quote = !quote;
285 ptr++;
286 len++;
289 if (*ptr2=='"')
291 ptr2++;
292 len -= 2;
294 val = msi_alloc((len+1)*sizeof(WCHAR));
295 memcpy(val,ptr2,len*sizeof(WCHAR));
296 val[len] = 0;
298 if (lstrlenW(prop) > 0)
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop), debugstr_w(val));
302 MSI_SetPropertyW(package,prop,val);
304 msi_free(val);
305 msi_free(prop);
308 return ERROR_SUCCESS;
312 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
314 LPCWSTR pc;
315 LPWSTR p, *ret = NULL;
316 UINT count = 0;
318 if (!str)
319 return ret;
321 /* count the number of substrings */
322 for ( pc = str, count = 0; pc; count++ )
324 pc = strchrW( pc, sep );
325 if (pc)
326 pc++;
329 /* allocate space for an array of substring pointers and the substrings */
330 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
331 (lstrlenW(str)+1) * sizeof(WCHAR) );
332 if (!ret)
333 return ret;
335 /* copy the string and set the pointers */
336 p = (LPWSTR) &ret[count+1];
337 lstrcpyW( p, str );
338 for( count = 0; (ret[count] = p); count++ )
340 p = strchrW( p, sep );
341 if (p)
342 *p++ = 0;
345 return ret;
348 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
350 static const WCHAR szSystemLanguageID[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
353 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
354 UINT ret = ERROR_FUNCTION_FAILED;
356 prod_code = msi_dup_property( package, szProductCode );
357 patch_product = msi_get_suminfo_product( patch );
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
361 if ( strstrW( patch_product, prod_code ) )
363 MSISUMMARYINFO *si;
364 const WCHAR *p;
366 si = MSI_GetSummaryInformationW( patch, 0 );
367 if (!si)
369 ERR("no summary information!\n");
370 goto end;
373 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
374 if (!template)
376 ERR("no template property!\n");
377 msiobj_release( &si->hdr );
378 goto end;
381 if (!template[0])
383 ret = ERROR_SUCCESS;
384 msiobj_release( &si->hdr );
385 goto end;
388 langid = msi_dup_property( package, szSystemLanguageID );
389 if (!langid)
391 msiobj_release( &si->hdr );
392 goto end;
395 p = strchrW( template, ';' );
396 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
398 TRACE("applicable transform\n");
399 ret = ERROR_SUCCESS;
402 /* FIXME: check platform */
404 msiobj_release( &si->hdr );
407 end:
408 msi_free( patch_product );
409 msi_free( prod_code );
410 msi_free( template );
411 msi_free( langid );
413 return ret;
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
421 HRESULT r;
423 TRACE("%p %s\n", package, debugstr_w(name) );
425 if (*name++ != ':')
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 if (SUCCEEDED(r))
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
437 else
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
441 else
442 ERR("failed to open substorage %s\n", debugstr_w(name));
444 return ERROR_SUCCESS;
447 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
449 LPWSTR guid_list, *guids, product_code;
450 UINT i, ret = ERROR_FUNCTION_FAILED;
452 product_code = msi_dup_property( package, szProductCode );
453 if (!product_code)
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS;
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++ )
464 if (!lstrcmpW( guids[i], product_code ))
465 ret = ERROR_SUCCESS;
467 msi_free( guids );
468 msi_free( guid_list );
469 msi_free( product_code );
471 return ret;
474 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
476 MSIQUERY *view;
477 MSIRECORD *rec = NULL;
478 LPWSTR patch;
479 LPCWSTR prop;
480 UINT r;
482 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
488 r = MSI_DatabaseOpenViewW(package->db, query, &view);
489 if (r != ERROR_SUCCESS)
490 return r;
492 r = MSI_ViewExecute(view, 0);
493 if (r != ERROR_SUCCESS)
494 goto done;
496 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
498 prop = MSI_RecordGetString(rec, 1);
499 patch = msi_dup_property(package, szPatch);
500 MSI_SetPropertyW(package, prop, patch);
501 msi_free(patch);
504 done:
505 if (rec) msiobj_release(&rec->hdr);
506 msiobj_release(&view->hdr);
508 return r;
511 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
513 MSISUMMARYINFO *si;
514 LPWSTR str, *substorage;
515 UINT i, r = ERROR_SUCCESS;
517 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
518 if (!si)
519 return ERROR_FUNCTION_FAILED;
521 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS;
527 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
528 if (!package->patch)
529 return ERROR_OUTOFMEMORY;
531 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
532 if (!package->patch->patchcode)
533 return ERROR_OUTOFMEMORY;
535 /* enumerate the substorage */
536 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 package->patch->transforms = str;
539 substorage = msi_split_string( str, ';' );
540 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
541 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
543 msi_free( substorage );
544 msiobj_release( &si->hdr );
546 msi_set_media_source_prop(package);
548 return r;
551 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
553 MSIDATABASE *patch_db = NULL;
554 UINT r;
556 TRACE("%p %s\n", package, debugstr_w( file ) );
558 /* FIXME:
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
563 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
564 if ( r != ERROR_SUCCESS )
566 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
567 return r;
570 msi_parse_patch_summary( package, patch_db );
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
576 append_storage_to_db( package->db, patch_db->storage );
578 msiobj_release( &patch_db->hdr );
580 return ERROR_SUCCESS;
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT msi_apply_patches( MSIPACKAGE *package )
586 LPWSTR patch_list, *patches;
587 UINT i, r = ERROR_SUCCESS;
589 patch_list = msi_dup_property( package, szPatch );
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
593 patches = msi_split_string( patch_list, ';' );
594 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
595 r = msi_apply_patch_package( package, patches[i] );
597 msi_free( patches );
598 msi_free( patch_list );
600 return r;
603 static UINT msi_apply_transforms( MSIPACKAGE *package )
605 static const WCHAR szTransforms[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list, *xforms;
608 UINT i, r = ERROR_SUCCESS;
610 xform_list = msi_dup_property( package, szTransforms );
611 xforms = msi_split_string( xform_list, ';' );
613 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
615 if (xforms[i][0] == ':')
616 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
617 else
618 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
621 msi_free( xforms );
622 msi_free( xform_list );
624 return r;
627 static BOOL ui_sequence_exists( MSIPACKAGE *package )
629 MSIQUERY *view;
630 UINT rc;
632 static const WCHAR ExecSeqQuery [] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
641 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642 if (rc == ERROR_SUCCESS)
644 msiobj_release(&view->hdr);
645 return TRUE;
648 return FALSE;
651 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
653 LPWSTR p, db;
654 LPWSTR source, check;
655 DWORD len;
657 static const WCHAR szOriginalDatabase[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
660 db = msi_dup_property( package, szOriginalDatabase );
661 if (!db)
662 return ERROR_OUTOFMEMORY;
664 p = strrchrW( db, '\\' );
665 if (!p)
667 p = strrchrW( db, '/' );
668 if (!p)
670 msi_free(db);
671 return ERROR_SUCCESS;
675 len = p - db + 2;
676 source = msi_alloc( len * sizeof(WCHAR) );
677 lstrcpynW( source, db, len );
679 check = msi_dup_property( package, cszSourceDir );
680 if (!check || replace)
681 MSI_SetPropertyW( package, cszSourceDir, source );
683 msi_free( check );
685 check = msi_dup_property( package, cszSOURCEDIR );
686 if (!check || replace)
687 MSI_SetPropertyW( package, cszSOURCEDIR, source );
689 msi_free( check );
690 msi_free( source );
691 msi_free( db );
693 return ERROR_SUCCESS;
696 static BOOL needs_ui_sequence(MSIPACKAGE *package)
698 INT level = msi_get_property_int(package, szUILevel, 0);
699 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
702 static UINT msi_set_context(MSIPACKAGE *package)
704 WCHAR val[10];
705 DWORD sz = 10;
706 DWORD num;
707 UINT r;
709 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
711 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
712 if (r == ERROR_SUCCESS)
714 num = atolW(val);
715 if (num == 1 || num == 2)
716 package->Context = MSIINSTALLCONTEXT_MACHINE;
719 return ERROR_SUCCESS;
722 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
724 UINT rc;
725 LPCWSTR cond, action;
726 MSIPACKAGE *package = param;
728 action = MSI_RecordGetString(row,1);
729 if (!action)
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED;
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
742 return ERROR_SUCCESS;
745 if (needs_ui_sequence(package))
746 rc = ACTION_PerformUIAction(package, action, -1);
747 else
748 rc = ACTION_PerformAction(package, action, -1, FALSE);
750 msi_dialog_check_messages( NULL );
752 if (package->CurrentInstallState != ERROR_SUCCESS)
753 rc = package->CurrentInstallState;
755 if (rc == ERROR_FUNCTION_NOT_CALLED)
756 rc = ERROR_SUCCESS;
758 if (rc != ERROR_SUCCESS)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
761 return rc;
764 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
766 MSIQUERY * view;
767 UINT r;
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','%','s','`',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
776 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
778 r = MSI_OpenQuery( package->db, &view, query, szTable );
779 if (r == ERROR_SUCCESS)
781 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
782 msiobj_release(&view->hdr);
785 return r;
788 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
790 MSIQUERY * view;
791 UINT rc;
792 static const WCHAR ExecSeqQuery[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
806 INT seq = 0;
808 if (package->script->ExecuteSequenceRun)
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS;
814 package->script->ExecuteSequenceRun = TRUE;
816 /* get the sequence number */
817 if (UIran)
819 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
820 if( !row )
821 return ERROR_FUNCTION_FAILED;
822 seq = MSI_RecordGetInteger(row,1);
823 msiobj_release(&row->hdr);
826 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
827 if (rc == ERROR_SUCCESS)
829 TRACE("Running the actions\n");
831 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
832 msiobj_release(&view->hdr);
835 return rc;
838 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
840 MSIQUERY * view;
841 UINT rc;
842 static const WCHAR ExecSeqQuery [] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
851 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
852 if (rc == ERROR_SUCCESS)
854 TRACE("Running the actions\n");
856 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
857 msiobj_release(&view->hdr);
860 return rc;
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
867 UINT* rc, UINT script, BOOL force )
869 BOOL ret=FALSE;
870 UINT arc;
872 arc = ACTION_CustomAction(package, action, script, force);
874 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
876 *rc = arc;
877 ret = TRUE;
879 return ret;
883 * Actual Action Handlers
886 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
888 MSIPACKAGE *package = param;
889 LPCWSTR dir;
890 LPWSTR full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
894 dir = MSI_RecordGetString(row,1);
895 if (!dir)
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS;
901 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
902 if (!full_path)
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
905 return ERROR_SUCCESS;
908 TRACE("Folder is %s\n",debugstr_w(full_path));
910 /* UI stuff */
911 uirow = MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow,1,full_path);
913 ui_actiondata(package,szCreateFolders,uirow);
914 msiobj_release( &uirow->hdr );
916 if (folder->State == 0)
917 create_full_pathW(full_path);
919 folder->State = 3;
921 msi_free(full_path);
922 return ERROR_SUCCESS;
925 /* FIXME: probably should merge this with the above function */
926 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
928 UINT rc = ERROR_SUCCESS;
929 MSIFOLDER *folder;
930 LPWSTR install_path;
932 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
933 if (!install_path)
934 return ERROR_FUNCTION_FAILED;
936 /* create the path */
937 if (folder->State == 0)
939 create_full_pathW(install_path);
940 folder->State = 2;
942 msi_free(install_path);
944 return rc;
947 UINT msi_create_component_directories( MSIPACKAGE *package )
949 MSICOMPONENT *comp;
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
954 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
955 continue;
956 msi_create_directory( package, comp->Directory );
959 return ERROR_SUCCESS;
962 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
964 static const WCHAR ExecSeqQuery[] =
965 {'S','E','L','E','C','T',' ',
966 '`','D','i','r','e','c','t','o','r','y','_','`',
967 ' ','F','R','O','M',' ',
968 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
969 UINT rc;
970 MSIQUERY *view;
972 /* create all the empty folders specified in the CreateFolder table */
973 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
974 if (rc != ERROR_SUCCESS)
975 return ERROR_SUCCESS;
977 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
978 msiobj_release(&view->hdr);
980 return rc;
983 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
985 MSIPACKAGE *package = param;
986 LPCWSTR dir;
987 LPWSTR full_path;
988 MSIRECORD *uirow;
989 MSIFOLDER *folder;
991 dir = MSI_RecordGetString( row, 1 );
992 if (!dir)
994 ERR("Unable to get folder id\n");
995 return ERROR_SUCCESS;
998 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
999 if (!full_path)
1001 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1002 return ERROR_SUCCESS;
1005 TRACE("folder is %s\n", debugstr_w(full_path));
1007 uirow = MSI_CreateRecord( 1 );
1008 MSI_RecordSetStringW( uirow, 1, full_path );
1009 ui_actiondata( package, szRemoveFolders, uirow );
1010 msiobj_release( &uirow->hdr );
1012 RemoveDirectoryW( full_path );
1013 folder->State = 0;
1015 msi_free( full_path );
1016 return ERROR_SUCCESS;
1019 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1021 static const WCHAR query[] =
1022 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1023 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1025 MSIQUERY *view;
1026 UINT rc;
1028 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1029 if (rc != ERROR_SUCCESS)
1030 return ERROR_SUCCESS;
1032 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1033 msiobj_release( &view->hdr );
1035 return rc;
1038 static UINT load_component( MSIRECORD *row, LPVOID param )
1040 MSIPACKAGE *package = param;
1041 MSICOMPONENT *comp;
1043 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1044 if (!comp)
1045 return ERROR_FUNCTION_FAILED;
1047 list_add_tail( &package->components, &comp->entry );
1049 /* fill in the data */
1050 comp->Component = msi_dup_record_field( row, 1 );
1052 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1054 comp->ComponentId = msi_dup_record_field( row, 2 );
1055 comp->Directory = msi_dup_record_field( row, 3 );
1056 comp->Attributes = MSI_RecordGetInteger(row,4);
1057 comp->Condition = msi_dup_record_field( row, 5 );
1058 comp->KeyPath = msi_dup_record_field( row, 6 );
1060 comp->Installed = INSTALLSTATE_UNKNOWN;
1061 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1063 return ERROR_SUCCESS;
1066 static UINT load_all_components( MSIPACKAGE *package )
1068 static const WCHAR query[] = {
1069 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1070 '`','C','o','m','p','o','n','e','n','t','`',0 };
1071 MSIQUERY *view;
1072 UINT r;
1074 if (!list_empty(&package->components))
1075 return ERROR_SUCCESS;
1077 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1078 if (r != ERROR_SUCCESS)
1079 return r;
1081 r = MSI_IterateRecords(view, NULL, load_component, package);
1082 msiobj_release(&view->hdr);
1083 return r;
1086 typedef struct {
1087 MSIPACKAGE *package;
1088 MSIFEATURE *feature;
1089 } _ilfs;
1091 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1093 ComponentList *cl;
1095 cl = msi_alloc( sizeof (*cl) );
1096 if ( !cl )
1097 return ERROR_NOT_ENOUGH_MEMORY;
1098 cl->component = comp;
1099 list_add_tail( &feature->Components, &cl->entry );
1101 return ERROR_SUCCESS;
1104 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1106 FeatureList *fl;
1108 fl = msi_alloc( sizeof(*fl) );
1109 if ( !fl )
1110 return ERROR_NOT_ENOUGH_MEMORY;
1111 fl->feature = child;
1112 list_add_tail( &parent->Children, &fl->entry );
1114 return ERROR_SUCCESS;
1117 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1119 _ilfs* ilfs = param;
1120 LPCWSTR component;
1121 MSICOMPONENT *comp;
1123 component = MSI_RecordGetString(row,1);
1125 /* check to see if the component is already loaded */
1126 comp = get_loaded_component( ilfs->package, component );
1127 if (!comp)
1129 ERR("unknown component %s\n", debugstr_w(component));
1130 return ERROR_FUNCTION_FAILED;
1133 add_feature_component( ilfs->feature, comp );
1134 comp->Enabled = TRUE;
1136 return ERROR_SUCCESS;
1139 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1141 MSIFEATURE *feature;
1143 if ( !name )
1144 return NULL;
1146 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1148 if ( !lstrcmpW( feature->Feature, name ) )
1149 return feature;
1152 return NULL;
1155 static UINT load_feature(MSIRECORD * row, LPVOID param)
1157 MSIPACKAGE* package = param;
1158 MSIFEATURE* feature;
1159 static const WCHAR Query1[] =
1160 {'S','E','L','E','C','T',' ',
1161 '`','C','o','m','p','o','n','e','n','t','_','`',
1162 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1163 'C','o','m','p','o','n','e','n','t','s','`',' ',
1164 'W','H','E','R','E',' ',
1165 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1166 MSIQUERY * view;
1167 UINT rc;
1168 _ilfs ilfs;
1170 /* fill in the data */
1172 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1173 if (!feature)
1174 return ERROR_NOT_ENOUGH_MEMORY;
1176 list_init( &feature->Children );
1177 list_init( &feature->Components );
1179 feature->Feature = msi_dup_record_field( row, 1 );
1181 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1183 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1184 feature->Title = msi_dup_record_field( row, 3 );
1185 feature->Description = msi_dup_record_field( row, 4 );
1187 if (!MSI_RecordIsNull(row,5))
1188 feature->Display = MSI_RecordGetInteger(row,5);
1190 feature->Level= MSI_RecordGetInteger(row,6);
1191 feature->Directory = msi_dup_record_field( row, 7 );
1192 feature->Attributes = MSI_RecordGetInteger(row,8);
1194 feature->Installed = INSTALLSTATE_UNKNOWN;
1195 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1197 list_add_tail( &package->features, &feature->entry );
1199 /* load feature components */
1201 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1202 if (rc != ERROR_SUCCESS)
1203 return ERROR_SUCCESS;
1205 ilfs.package = package;
1206 ilfs.feature = feature;
1208 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1209 msiobj_release(&view->hdr);
1211 return ERROR_SUCCESS;
1214 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1216 MSIPACKAGE* package = param;
1217 MSIFEATURE *parent, *child;
1219 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1220 if (!child)
1221 return ERROR_FUNCTION_FAILED;
1223 if (!child->Feature_Parent)
1224 return ERROR_SUCCESS;
1226 parent = find_feature_by_name( package, child->Feature_Parent );
1227 if (!parent)
1228 return ERROR_FUNCTION_FAILED;
1230 add_feature_child( parent, child );
1231 return ERROR_SUCCESS;
1234 static UINT load_all_features( MSIPACKAGE *package )
1236 static const WCHAR query[] = {
1237 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1238 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1239 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1240 MSIQUERY *view;
1241 UINT r;
1243 if (!list_empty(&package->features))
1244 return ERROR_SUCCESS;
1246 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1247 if (r != ERROR_SUCCESS)
1248 return r;
1250 r = MSI_IterateRecords( view, NULL, load_feature, package );
1251 if (r != ERROR_SUCCESS)
1252 return r;
1254 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1255 msiobj_release( &view->hdr );
1257 return r;
1260 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1262 if (!p)
1263 return p;
1264 p = strchrW(p, ch);
1265 if (!p)
1266 return p;
1267 *p = 0;
1268 return p+1;
1271 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1273 static const WCHAR query[] = {
1274 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1275 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1276 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1277 MSIQUERY *view = NULL;
1278 MSIRECORD *row = NULL;
1279 UINT r;
1281 TRACE("%s\n", debugstr_w(file->File));
1283 r = MSI_OpenQuery(package->db, &view, query, file->File);
1284 if (r != ERROR_SUCCESS)
1285 goto done;
1287 r = MSI_ViewExecute(view, NULL);
1288 if (r != ERROR_SUCCESS)
1289 goto done;
1291 r = MSI_ViewFetch(view, &row);
1292 if (r != ERROR_SUCCESS)
1293 goto done;
1295 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1296 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1297 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1298 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1299 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1301 done:
1302 if (view) msiobj_release(&view->hdr);
1303 if (row) msiobj_release(&row->hdr);
1304 return r;
1307 static UINT load_file(MSIRECORD *row, LPVOID param)
1309 MSIPACKAGE* package = param;
1310 LPCWSTR component;
1311 MSIFILE *file;
1313 /* fill in the data */
1315 file = msi_alloc_zero( sizeof (MSIFILE) );
1316 if (!file)
1317 return ERROR_NOT_ENOUGH_MEMORY;
1319 file->File = msi_dup_record_field( row, 1 );
1321 component = MSI_RecordGetString( row, 2 );
1322 file->Component = get_loaded_component( package, component );
1324 if (!file->Component)
1326 WARN("Component not found: %s\n", debugstr_w(component));
1327 msi_free(file->File);
1328 msi_free(file);
1329 return ERROR_SUCCESS;
1332 file->FileName = msi_dup_record_field( row, 3 );
1333 reduce_to_longfilename( file->FileName );
1335 file->ShortName = msi_dup_record_field( row, 3 );
1336 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1338 file->FileSize = MSI_RecordGetInteger( row, 4 );
1339 file->Version = msi_dup_record_field( row, 5 );
1340 file->Language = msi_dup_record_field( row, 6 );
1341 file->Attributes = MSI_RecordGetInteger( row, 7 );
1342 file->Sequence = MSI_RecordGetInteger( row, 8 );
1344 file->state = msifs_invalid;
1346 /* if the compressed bits are not set in the file attributes,
1347 * then read the information from the package word count property
1349 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1351 file->IsCompressed = FALSE;
1353 else if (file->Attributes &
1354 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1356 file->IsCompressed = TRUE;
1358 else if (file->Attributes & msidbFileAttributesNoncompressed)
1360 file->IsCompressed = FALSE;
1362 else
1364 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1367 load_file_hash(package, file);
1369 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1371 list_add_tail( &package->files, &file->entry );
1373 return ERROR_SUCCESS;
1376 static UINT load_all_files(MSIPACKAGE *package)
1378 MSIQUERY * view;
1379 UINT rc;
1380 static const WCHAR Query[] =
1381 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1382 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1383 '`','S','e','q','u','e','n','c','e','`', 0};
1385 if (!list_empty(&package->files))
1386 return ERROR_SUCCESS;
1388 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1389 if (rc != ERROR_SUCCESS)
1390 return ERROR_SUCCESS;
1392 rc = MSI_IterateRecords(view, NULL, load_file, package);
1393 msiobj_release(&view->hdr);
1395 return ERROR_SUCCESS;
1398 static UINT load_folder( MSIRECORD *row, LPVOID param )
1400 MSIPACKAGE *package = param;
1401 static WCHAR szEmpty[] = { 0 };
1402 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1403 MSIFOLDER *folder;
1405 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1406 if (!folder)
1407 return ERROR_NOT_ENOUGH_MEMORY;
1409 folder->Directory = msi_dup_record_field( row, 1 );
1411 TRACE("%s\n", debugstr_w(folder->Directory));
1413 p = msi_dup_record_field(row, 3);
1415 /* split src and target dir */
1416 tgt_short = p;
1417 src_short = folder_split_path( p, ':' );
1419 /* split the long and short paths */
1420 tgt_long = folder_split_path( tgt_short, '|' );
1421 src_long = folder_split_path( src_short, '|' );
1423 /* check for no-op dirs */
1424 if (!lstrcmpW(szDot, tgt_short))
1425 tgt_short = szEmpty;
1426 if (!lstrcmpW(szDot, src_short))
1427 src_short = szEmpty;
1429 if (!tgt_long)
1430 tgt_long = tgt_short;
1432 if (!src_short) {
1433 src_short = tgt_short;
1434 src_long = tgt_long;
1437 if (!src_long)
1438 src_long = src_short;
1440 /* FIXME: use the target short path too */
1441 folder->TargetDefault = strdupW(tgt_long);
1442 folder->SourceShortPath = strdupW(src_short);
1443 folder->SourceLongPath = strdupW(src_long);
1444 msi_free(p);
1446 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1447 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1448 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1450 folder->Parent = msi_dup_record_field( row, 2 );
1452 folder->Property = msi_dup_property( package, folder->Directory );
1454 list_add_tail( &package->folders, &folder->entry );
1456 TRACE("returning %p\n", folder);
1458 return ERROR_SUCCESS;
1461 static UINT load_all_folders( MSIPACKAGE *package )
1463 static const WCHAR query[] = {
1464 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1465 '`','D','i','r','e','c','t','o','r','y','`',0 };
1466 MSIQUERY *view;
1467 UINT r;
1469 if (!list_empty(&package->folders))
1470 return ERROR_SUCCESS;
1472 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1473 if (r != ERROR_SUCCESS)
1474 return r;
1476 r = MSI_IterateRecords(view, NULL, load_folder, package);
1477 msiobj_release(&view->hdr);
1478 return r;
1482 * I am not doing any of the costing functionality yet.
1483 * Mostly looking at doing the Component and Feature loading
1485 * The native MSI does A LOT of modification to tables here. Mostly adding
1486 * a lot of temporary columns to the Feature and Component tables.
1488 * note: Native msi also tracks the short filename. But I am only going to
1489 * track the long ones. Also looking at this directory table
1490 * it appears that the directory table does not get the parents
1491 * resolved base on property only based on their entries in the
1492 * directory table.
1494 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1496 static const WCHAR szCosting[] =
1497 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1499 MSI_SetPropertyW(package, szCosting, szZero);
1500 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1502 load_all_folders( package );
1503 load_all_components( package );
1504 load_all_features( package );
1505 load_all_files( package );
1507 return ERROR_SUCCESS;
1510 static UINT execute_script(MSIPACKAGE *package, UINT script )
1512 UINT i;
1513 UINT rc = ERROR_SUCCESS;
1515 TRACE("Executing Script %i\n",script);
1517 if (!package->script)
1519 ERR("no script!\n");
1520 return ERROR_FUNCTION_FAILED;
1523 for (i = 0; i < package->script->ActionCount[script]; i++)
1525 LPWSTR action;
1526 action = package->script->Actions[script][i];
1527 ui_actionstart(package, action);
1528 TRACE("Executing Action (%s)\n",debugstr_w(action));
1529 rc = ACTION_PerformAction(package, action, script, TRUE);
1530 if (rc != ERROR_SUCCESS)
1531 break;
1533 msi_free_action_script(package, script);
1534 return rc;
1537 static UINT ACTION_FileCost(MSIPACKAGE *package)
1539 return ERROR_SUCCESS;
1542 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1544 MSICOMPONENT *comp;
1545 INSTALLSTATE state;
1546 UINT r;
1548 state = MsiQueryProductStateW(package->ProductCode);
1550 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1552 if (!comp->ComponentId)
1553 continue;
1555 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1556 comp->Installed = INSTALLSTATE_ABSENT;
1557 else
1559 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1560 package->Context, comp->ComponentId,
1561 &comp->Installed);
1562 if (r != ERROR_SUCCESS)
1563 comp->Installed = INSTALLSTATE_ABSENT;
1568 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1570 MSIFEATURE *feature;
1571 INSTALLSTATE state;
1573 state = MsiQueryProductStateW(package->ProductCode);
1575 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1577 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1578 feature->Installed = INSTALLSTATE_ABSENT;
1579 else
1581 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1582 feature->Feature);
1587 static BOOL process_state_property(MSIPACKAGE* package, int level,
1588 LPCWSTR property, INSTALLSTATE state)
1590 LPWSTR override;
1591 MSIFEATURE *feature;
1593 override = msi_dup_property( package, property );
1594 if (!override)
1595 return FALSE;
1597 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1599 if (lstrcmpW(property, szRemove) &&
1600 (feature->Level <= 0 || feature->Level > level))
1601 continue;
1603 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1605 if (strcmpiW(override, szAll)==0)
1606 msi_feature_set_state(package, feature, state);
1607 else
1609 LPWSTR ptr = override;
1610 LPWSTR ptr2 = strchrW(override,',');
1612 while (ptr)
1614 int len = ptr2 - ptr;
1616 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1617 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1619 msi_feature_set_state(package, feature, state);
1620 break;
1622 if (ptr2)
1624 ptr=ptr2+1;
1625 ptr2 = strchrW(ptr,',');
1627 else
1628 break;
1632 msi_free(override);
1634 return TRUE;
1637 static BOOL process_overrides( MSIPACKAGE *package, int level )
1639 static const WCHAR szAddLocal[] =
1640 {'A','D','D','L','O','C','A','L',0};
1641 static const WCHAR szAddSource[] =
1642 {'A','D','D','S','O','U','R','C','E',0};
1643 static const WCHAR szAdvertise[] =
1644 {'A','D','V','E','R','T','I','S','E',0};
1645 BOOL ret = FALSE;
1647 /* all these activation/deactivation things happen in order and things
1648 * later on the list override things earlier on the list.
1650 * 0 INSTALLLEVEL processing
1651 * 1 ADDLOCAL
1652 * 2 REMOVE
1653 * 3 ADDSOURCE
1654 * 4 ADDDEFAULT
1655 * 5 REINSTALL
1656 * 6 ADVERTISE
1657 * 7 COMPADDLOCAL
1658 * 8 COMPADDSOURCE
1659 * 9 FILEADDLOCAL
1660 * 10 FILEADDSOURCE
1661 * 11 FILEADDDEFAULT
1663 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1664 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1665 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1666 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1667 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1669 if (ret)
1670 MSI_SetPropertyW( package, szPreselected, szOne );
1672 return ret;
1675 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1677 int level;
1678 static const WCHAR szlevel[] =
1679 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1680 MSICOMPONENT* component;
1681 MSIFEATURE *feature;
1683 TRACE("Checking Install Level\n");
1685 level = msi_get_property_int(package, szlevel, 1);
1687 if (!msi_get_property_int( package, szPreselected, 0 ))
1689 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1691 BOOL feature_state = ((feature->Level > 0) &&
1692 (feature->Level <= level));
1694 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1696 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1697 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1698 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1699 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1700 else
1701 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1705 /* disable child features of unselected parent features */
1706 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1708 FeatureList *fl;
1710 if (feature->Level > 0 && feature->Level <= level)
1711 continue;
1713 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1714 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1719 * now we want to enable or disable components base on feature
1722 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1724 ComponentList *cl;
1726 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1727 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1729 if (!feature->Level)
1730 continue;
1732 /* features with components that have compressed files are made local */
1733 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1735 if (cl->component->Enabled &&
1736 cl->component->ForceLocalState &&
1737 feature->Action == INSTALLSTATE_SOURCE)
1739 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1740 break;
1744 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1746 component = cl->component;
1748 if (!component->Enabled)
1749 continue;
1751 switch (feature->Action)
1753 case INSTALLSTATE_ABSENT:
1754 component->anyAbsent = 1;
1755 break;
1756 case INSTALLSTATE_ADVERTISED:
1757 component->hasAdvertiseFeature = 1;
1758 break;
1759 case INSTALLSTATE_SOURCE:
1760 component->hasSourceFeature = 1;
1761 break;
1762 case INSTALLSTATE_LOCAL:
1763 component->hasLocalFeature = 1;
1764 break;
1765 case INSTALLSTATE_DEFAULT:
1766 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1767 component->hasAdvertiseFeature = 1;
1768 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 component->hasSourceFeature = 1;
1770 else
1771 component->hasLocalFeature = 1;
1772 break;
1773 default:
1774 break;
1779 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1781 /* if the component isn't enabled, leave it alone */
1782 if (!component->Enabled)
1783 continue;
1785 /* check if it's local or source */
1786 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1787 (component->hasLocalFeature || component->hasSourceFeature))
1789 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1790 !component->ForceLocalState)
1791 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1792 else
1793 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1794 continue;
1797 /* if any feature is local, the component must be local too */
1798 if (component->hasLocalFeature)
1800 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1801 continue;
1804 if (component->hasSourceFeature)
1806 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1807 continue;
1810 if (component->hasAdvertiseFeature)
1812 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1813 continue;
1816 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1817 if (component->anyAbsent)
1818 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1821 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1823 if (component->Action == INSTALLSTATE_DEFAULT)
1825 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1826 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1829 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1830 debugstr_w(component->Component), component->Installed, component->Action);
1834 return ERROR_SUCCESS;
1837 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1839 MSIPACKAGE *package = param;
1840 LPCWSTR name;
1841 LPWSTR path;
1842 MSIFOLDER *f;
1844 name = MSI_RecordGetString(row,1);
1846 f = get_loaded_folder(package, name);
1847 if (!f) return ERROR_SUCCESS;
1849 /* reset the ResolvedTarget */
1850 msi_free(f->ResolvedTarget);
1851 f->ResolvedTarget = NULL;
1853 /* This helper function now does ALL the work */
1854 TRACE("Dir %s ...\n",debugstr_w(name));
1855 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1856 TRACE("resolves to %s\n",debugstr_w(path));
1857 msi_free(path);
1859 return ERROR_SUCCESS;
1862 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1864 MSIPACKAGE *package = param;
1865 LPCWSTR name;
1866 MSIFEATURE *feature;
1868 name = MSI_RecordGetString( row, 1 );
1870 feature = get_loaded_feature( package, name );
1871 if (!feature)
1872 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1873 else
1875 LPCWSTR Condition;
1876 Condition = MSI_RecordGetString(row,3);
1878 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1880 int level = MSI_RecordGetInteger(row,2);
1881 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1882 feature->Level = level;
1885 return ERROR_SUCCESS;
1888 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1890 static const WCHAR name_fmt[] =
1891 {'%','u','.','%','u','.','%','u','.','%','u',0};
1892 static const WCHAR name[] = {'\\',0};
1893 VS_FIXEDFILEINFO *lpVer;
1894 WCHAR filever[0x100];
1895 LPVOID version;
1896 DWORD versize;
1897 DWORD handle;
1898 UINT sz;
1900 TRACE("%s\n", debugstr_w(filename));
1902 versize = GetFileVersionInfoSizeW( filename, &handle );
1903 if (!versize)
1904 return NULL;
1906 version = msi_alloc( versize );
1907 GetFileVersionInfoW( filename, 0, versize, version );
1909 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1911 msi_free( version );
1912 return NULL;
1915 sprintfW( filever, name_fmt,
1916 HIWORD(lpVer->dwFileVersionMS),
1917 LOWORD(lpVer->dwFileVersionMS),
1918 HIWORD(lpVer->dwFileVersionLS),
1919 LOWORD(lpVer->dwFileVersionLS));
1921 msi_free( version );
1923 return strdupW( filever );
1926 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1928 LPWSTR file_version;
1929 MSIFILE *file;
1931 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1933 MSICOMPONENT* comp = file->Component;
1934 LPWSTR p;
1936 if (!comp)
1937 continue;
1939 if (file->IsCompressed)
1940 comp->ForceLocalState = TRUE;
1942 /* calculate target */
1943 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1945 msi_free(file->TargetPath);
1947 TRACE("file %s is named %s\n",
1948 debugstr_w(file->File), debugstr_w(file->FileName));
1950 file->TargetPath = build_directory_name(2, p, file->FileName);
1952 msi_free(p);
1954 TRACE("file %s resolves to %s\n",
1955 debugstr_w(file->File), debugstr_w(file->TargetPath));
1957 /* don't check files of components that aren't installed */
1958 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1959 comp->Installed == INSTALLSTATE_ABSENT)
1961 file->state = msifs_missing; /* assume files are missing */
1962 continue;
1965 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1967 file->state = msifs_missing;
1968 comp->Cost += file->FileSize;
1969 continue;
1972 if (file->Version &&
1973 (file_version = msi_get_disk_file_version( file->TargetPath )))
1975 TRACE("new %s old %s\n", debugstr_w(file->Version),
1976 debugstr_w(file_version));
1977 /* FIXME: seems like a bad way to compare version numbers */
1978 if (lstrcmpiW(file_version, file->Version)<0)
1980 file->state = msifs_overwrite;
1981 comp->Cost += file->FileSize;
1983 else
1984 file->state = msifs_present;
1985 msi_free( file_version );
1987 else
1988 file->state = msifs_present;
1991 return ERROR_SUCCESS;
1995 * A lot is done in this function aside from just the costing.
1996 * The costing needs to be implemented at some point but for now I am going
1997 * to focus on the directory building
2000 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2002 static const WCHAR ExecSeqQuery[] =
2003 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2004 '`','D','i','r','e','c','t','o','r','y','`',0};
2005 static const WCHAR ConditionQuery[] =
2006 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2007 '`','C','o','n','d','i','t','i','o','n','`',0};
2008 static const WCHAR szCosting[] =
2009 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2010 static const WCHAR szlevel[] =
2011 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2012 static const WCHAR szOutOfDiskSpace[] =
2013 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2014 MSICOMPONENT *comp;
2015 UINT rc = ERROR_SUCCESS;
2016 MSIQUERY * view;
2017 LPWSTR level;
2019 TRACE("Building Directory properties\n");
2021 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2022 if (rc == ERROR_SUCCESS)
2024 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2025 package);
2026 msiobj_release(&view->hdr);
2029 /* read components states from the registry */
2030 ACTION_GetComponentInstallStates(package);
2031 ACTION_GetFeatureInstallStates(package);
2033 TRACE("File calculations\n");
2034 msi_check_file_install_states( package );
2036 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2038 TRACE("Evaluating Condition Table\n");
2040 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2041 if (rc == ERROR_SUCCESS)
2043 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2044 msiobj_release( &view->hdr );
2047 TRACE("Enabling or Disabling Components\n");
2048 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2050 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2052 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2053 comp->Enabled = FALSE;
2055 else
2056 comp->Enabled = TRUE;
2060 MSI_SetPropertyW(package,szCosting,szOne);
2061 /* set default run level if not set */
2062 level = msi_dup_property( package, szlevel );
2063 if (!level)
2064 MSI_SetPropertyW(package,szlevel, szOne);
2065 msi_free(level);
2067 /* FIXME: check volume disk space */
2068 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2070 return MSI_SetFeatureStates(package);
2073 /* OK this value is "interpreted" and then formatted based on the
2074 first few characters */
2075 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2076 DWORD *size)
2078 LPSTR data = NULL;
2080 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2082 if (value[1]=='x')
2084 LPWSTR ptr;
2085 CHAR byte[5];
2086 LPWSTR deformated = NULL;
2087 int count;
2089 deformat_string(package, &value[2], &deformated);
2091 /* binary value type */
2092 ptr = deformated;
2093 *type = REG_BINARY;
2094 if (strlenW(ptr)%2)
2095 *size = (strlenW(ptr)/2)+1;
2096 else
2097 *size = strlenW(ptr)/2;
2099 data = msi_alloc(*size);
2101 byte[0] = '0';
2102 byte[1] = 'x';
2103 byte[4] = 0;
2104 count = 0;
2105 /* if uneven pad with a zero in front */
2106 if (strlenW(ptr)%2)
2108 byte[2]= '0';
2109 byte[3]= *ptr;
2110 ptr++;
2111 data[count] = (BYTE)strtol(byte,NULL,0);
2112 count ++;
2113 TRACE("Uneven byte count\n");
2115 while (*ptr)
2117 byte[2]= *ptr;
2118 ptr++;
2119 byte[3]= *ptr;
2120 ptr++;
2121 data[count] = (BYTE)strtol(byte,NULL,0);
2122 count ++;
2124 msi_free(deformated);
2126 TRACE("Data %i bytes(%i)\n",*size,count);
2128 else
2130 LPWSTR deformated;
2131 LPWSTR p;
2132 DWORD d = 0;
2133 deformat_string(package, &value[1], &deformated);
2135 *type=REG_DWORD;
2136 *size = sizeof(DWORD);
2137 data = msi_alloc(*size);
2138 p = deformated;
2139 if (*p == '-')
2140 p++;
2141 while (*p)
2143 if ( (*p < '0') || (*p > '9') )
2144 break;
2145 d *= 10;
2146 d += (*p - '0');
2147 p++;
2149 if (deformated[0] == '-')
2150 d = -d;
2151 *(LPDWORD)data = d;
2152 TRACE("DWORD %i\n",*(LPDWORD)data);
2154 msi_free(deformated);
2157 else
2159 static const WCHAR szMulti[] = {'[','~',']',0};
2160 LPCWSTR ptr;
2161 *type=REG_SZ;
2163 if (value[0]=='#')
2165 if (value[1]=='%')
2167 ptr = &value[2];
2168 *type=REG_EXPAND_SZ;
2170 else
2171 ptr = &value[1];
2173 else
2174 ptr=value;
2176 if (strstrW(value,szMulti))
2177 *type = REG_MULTI_SZ;
2179 /* remove initial delimiter */
2180 if (!strncmpW(value, szMulti, 3))
2181 ptr = value + 3;
2183 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2185 /* add double NULL terminator */
2186 if (*type == REG_MULTI_SZ)
2188 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2189 data = msi_realloc_zero(data, *size);
2192 return data;
2195 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2197 MSIPACKAGE *package = param;
2198 static const WCHAR szHCR[] =
2199 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2200 'R','O','O','T','\\',0};
2201 static const WCHAR szHCU[] =
2202 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2203 'U','S','E','R','\\',0};
2204 static const WCHAR szHLM[] =
2205 {'H','K','E','Y','_','L','O','C','A','L','_',
2206 'M','A','C','H','I','N','E','\\',0};
2207 static const WCHAR szHU[] =
2208 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2210 LPSTR value_data = NULL;
2211 HKEY root_key, hkey;
2212 DWORD type,size;
2213 LPWSTR deformated;
2214 LPCWSTR szRoot, component, name, key, value;
2215 MSICOMPONENT *comp;
2216 MSIRECORD * uirow;
2217 LPWSTR uikey;
2218 INT root;
2219 BOOL check_first = FALSE;
2220 UINT rc;
2222 ui_progress(package,2,0,0,0);
2224 value = NULL;
2225 key = NULL;
2226 uikey = NULL;
2227 name = NULL;
2229 component = MSI_RecordGetString(row, 6);
2230 comp = get_loaded_component(package,component);
2231 if (!comp)
2232 return ERROR_SUCCESS;
2234 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2236 TRACE("Skipping write due to disabled component %s\n",
2237 debugstr_w(component));
2239 comp->Action = comp->Installed;
2241 return ERROR_SUCCESS;
2244 comp->Action = INSTALLSTATE_LOCAL;
2246 name = MSI_RecordGetString(row, 4);
2247 if( MSI_RecordIsNull(row,5) && name )
2249 /* null values can have special meanings */
2250 if (name[0]=='-' && name[1] == 0)
2251 return ERROR_SUCCESS;
2252 else if ((name[0]=='+' && name[1] == 0) ||
2253 (name[0] == '*' && name[1] == 0))
2254 name = NULL;
2255 check_first = TRUE;
2258 root = MSI_RecordGetInteger(row,2);
2259 key = MSI_RecordGetString(row, 3);
2261 /* get the root key */
2262 switch (root)
2264 case -1:
2266 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2267 if (all_users && all_users[0] == '1')
2269 root_key = HKEY_LOCAL_MACHINE;
2270 szRoot = szHLM;
2272 else
2274 root_key = HKEY_CURRENT_USER;
2275 szRoot = szHCU;
2277 msi_free(all_users);
2279 break;
2280 case 0: root_key = HKEY_CLASSES_ROOT;
2281 szRoot = szHCR;
2282 break;
2283 case 1: root_key = HKEY_CURRENT_USER;
2284 szRoot = szHCU;
2285 break;
2286 case 2: root_key = HKEY_LOCAL_MACHINE;
2287 szRoot = szHLM;
2288 break;
2289 case 3: root_key = HKEY_USERS;
2290 szRoot = szHU;
2291 break;
2292 default:
2293 ERR("Unknown root %i\n",root);
2294 root_key=NULL;
2295 szRoot = NULL;
2296 break;
2298 if (!root_key)
2299 return ERROR_SUCCESS;
2301 deformat_string(package, key , &deformated);
2302 size = strlenW(deformated) + strlenW(szRoot) + 1;
2303 uikey = msi_alloc(size*sizeof(WCHAR));
2304 strcpyW(uikey,szRoot);
2305 strcatW(uikey,deformated);
2307 if (RegCreateKeyW( root_key, deformated, &hkey))
2309 ERR("Could not create key %s\n",debugstr_w(deformated));
2310 msi_free(deformated);
2311 msi_free(uikey);
2312 return ERROR_SUCCESS;
2314 msi_free(deformated);
2316 value = MSI_RecordGetString(row,5);
2317 if (value)
2318 value_data = parse_value(package, value, &type, &size);
2319 else
2321 value_data = (LPSTR)strdupW(szEmpty);
2322 size = sizeof(szEmpty);
2323 type = REG_SZ;
2326 deformat_string(package, name, &deformated);
2328 if (!check_first)
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2331 debugstr_w(uikey));
2332 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2334 else
2336 DWORD sz = 0;
2337 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2338 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated), debugstr_w(uikey));
2343 else
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated), debugstr_w(uikey));
2347 if (deformated || size)
2348 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2351 RegCloseKey(hkey);
2353 uirow = MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow,2,deformated);
2355 MSI_RecordSetStringW(uirow,1,uikey);
2357 if (type == REG_SZ)
2358 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2359 else
2360 MSI_RecordSetStringW(uirow,3,value);
2362 ui_actiondata(package,szWriteRegistryValues,uirow);
2363 msiobj_release( &uirow->hdr );
2365 msi_free(value_data);
2366 msi_free(deformated);
2367 msi_free(uikey);
2369 return ERROR_SUCCESS;
2372 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2374 UINT rc;
2375 MSIQUERY * view;
2376 static const WCHAR ExecSeqQuery[] =
2377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2378 '`','R','e','g','i','s','t','r','y','`',0 };
2380 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2381 if (rc != ERROR_SUCCESS)
2382 return ERROR_SUCCESS;
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2387 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2389 msiobj_release(&view->hdr);
2390 return rc;
2393 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2395 package->script->CurrentlyScripting = TRUE;
2397 return ERROR_SUCCESS;
2401 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2403 MSICOMPONENT *comp;
2404 DWORD progress = 0;
2405 DWORD total = 0;
2406 static const WCHAR q1[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2409 UINT rc;
2410 MSIQUERY * view;
2411 MSIFEATURE *feature;
2412 MSIFILE *file;
2414 TRACE("InstallValidate\n");
2416 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2417 if (rc == ERROR_SUCCESS)
2419 MSI_IterateRecords( view, &progress, NULL, package );
2420 msiobj_release( &view->hdr );
2421 total += progress * REG_PROGRESS_VALUE;
2424 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2425 total += COMPONENT_PROGRESS_VALUE;
2427 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2428 total += file->FileSize;
2430 ui_progress(package,0,total,0,0);
2432 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2436 feature->ActionRequest);
2439 return ERROR_SUCCESS;
2442 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2444 MSIPACKAGE* package = param;
2445 LPCWSTR cond = NULL;
2446 LPCWSTR message = NULL;
2447 UINT r;
2449 static const WCHAR title[]=
2450 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2452 cond = MSI_RecordGetString(row,1);
2454 r = MSI_EvaluateConditionW(package,cond);
2455 if (r == MSICONDITION_FALSE)
2457 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2459 LPWSTR deformated;
2460 message = MSI_RecordGetString(row,2);
2461 deformat_string(package,message,&deformated);
2462 MessageBoxW(NULL,deformated,title,MB_OK);
2463 msi_free(deformated);
2466 return ERROR_INSTALL_FAILURE;
2469 return ERROR_SUCCESS;
2472 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2474 UINT rc;
2475 MSIQUERY * view = NULL;
2476 static const WCHAR ExecSeqQuery[] =
2477 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2478 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2480 TRACE("Checking launch conditions\n");
2482 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2483 if (rc != ERROR_SUCCESS)
2484 return ERROR_SUCCESS;
2486 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2487 msiobj_release(&view->hdr);
2489 return rc;
2492 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2495 if (!cmp->KeyPath)
2496 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2498 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2500 MSIRECORD * row = 0;
2501 UINT root,len;
2502 LPWSTR deformated,buffer,deformated_name;
2503 LPCWSTR key,name;
2504 static const WCHAR ExecSeqQuery[] =
2505 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2506 '`','R','e','g','i','s','t','r','y','`',' ',
2507 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2508 ' ','=',' ' ,'\'','%','s','\'',0 };
2509 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2510 static const WCHAR fmt2[]=
2511 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2513 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2514 if (!row)
2515 return NULL;
2517 root = MSI_RecordGetInteger(row,2);
2518 key = MSI_RecordGetString(row, 3);
2519 name = MSI_RecordGetString(row, 4);
2520 deformat_string(package, key , &deformated);
2521 deformat_string(package, name, &deformated_name);
2523 len = strlenW(deformated) + 6;
2524 if (deformated_name)
2525 len+=strlenW(deformated_name);
2527 buffer = msi_alloc( len *sizeof(WCHAR));
2529 if (deformated_name)
2530 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2531 else
2532 sprintfW(buffer,fmt,root,deformated);
2534 msi_free(deformated);
2535 msi_free(deformated_name);
2536 msiobj_release(&row->hdr);
2538 return buffer;
2540 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2542 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2543 return NULL;
2545 else
2547 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2549 if (file)
2550 return strdupW( file->TargetPath );
2552 return NULL;
2555 static HKEY openSharedDLLsKey(void)
2557 HKEY hkey=0;
2558 static const WCHAR path[] =
2559 {'S','o','f','t','w','a','r','e','\\',
2560 'M','i','c','r','o','s','o','f','t','\\',
2561 'W','i','n','d','o','w','s','\\',
2562 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2563 'S','h','a','r','e','d','D','L','L','s',0};
2565 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2566 return hkey;
2569 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2571 HKEY hkey;
2572 DWORD count=0;
2573 DWORD type;
2574 DWORD sz = sizeof(count);
2575 DWORD rc;
2577 hkey = openSharedDLLsKey();
2578 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2579 if (rc != ERROR_SUCCESS)
2580 count = 0;
2581 RegCloseKey(hkey);
2582 return count;
2585 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2587 HKEY hkey;
2589 hkey = openSharedDLLsKey();
2590 if (count > 0)
2591 msi_reg_set_val_dword( hkey, path, count );
2592 else
2593 RegDeleteValueW(hkey,path);
2594 RegCloseKey(hkey);
2595 return count;
2599 * Return TRUE if the count should be written out and FALSE if not
2601 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2603 MSIFEATURE *feature;
2604 INT count = 0;
2605 BOOL write = FALSE;
2607 /* only refcount DLLs */
2608 if (comp->KeyPath == NULL ||
2609 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2610 comp->Attributes & msidbComponentAttributesODBCDataSource)
2611 write = FALSE;
2612 else
2614 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2615 write = (count > 0);
2617 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2618 write = TRUE;
2621 /* increment counts */
2622 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2624 ComponentList *cl;
2626 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2627 continue;
2629 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2631 if ( cl->component == comp )
2632 count++;
2636 /* decrement counts */
2637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2639 ComponentList *cl;
2641 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2642 continue;
2644 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2646 if ( cl->component == comp )
2647 count--;
2651 /* ref count all the files in the component */
2652 if (write)
2654 MSIFILE *file;
2656 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2658 if (file->Component == comp)
2659 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2663 /* add a count for permanent */
2664 if (comp->Attributes & msidbComponentAttributesPermanent)
2665 count ++;
2667 comp->RefCount = count;
2669 if (write)
2670 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2673 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2675 WCHAR squished_pc[GUID_SIZE];
2676 WCHAR squished_cc[GUID_SIZE];
2677 UINT rc;
2678 MSICOMPONENT *comp;
2679 HKEY hkey;
2681 TRACE("\n");
2683 squash_guid(package->ProductCode,squished_pc);
2684 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2686 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2688 MSIRECORD * uirow;
2690 ui_progress(package,2,0,0,0);
2691 if (!comp->ComponentId)
2692 continue;
2694 squash_guid(comp->ComponentId,squished_cc);
2696 msi_free(comp->FullKeypath);
2697 comp->FullKeypath = resolve_keypath( package, comp );
2699 ACTION_RefCountComponent( package, comp );
2701 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2702 debugstr_w(comp->Component),
2703 debugstr_w(squished_cc),
2704 debugstr_w(comp->FullKeypath),
2705 comp->RefCount);
2707 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2708 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2710 if (!comp->FullKeypath)
2711 continue;
2713 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2714 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2715 &hkey, TRUE);
2716 else
2717 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2718 &hkey, TRUE);
2720 if (rc != ERROR_SUCCESS)
2721 continue;
2723 if (comp->Attributes & msidbComponentAttributesPermanent)
2725 static const WCHAR szPermKey[] =
2726 { '0','0','0','0','0','0','0','0','0','0','0','0',
2727 '0','0','0','0','0','0','0','0','0','0','0','0',
2728 '0','0','0','0','0','0','0','0',0 };
2730 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2733 if (comp->Action == INSTALLSTATE_LOCAL)
2734 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2735 else
2737 MSIFILE *file;
2738 MSIRECORD *row;
2739 LPWSTR ptr, ptr2;
2740 WCHAR source[MAX_PATH];
2741 WCHAR base[MAX_PATH];
2742 LPWSTR sourcepath;
2744 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2745 static const WCHAR query[] = {
2746 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2747 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2748 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2749 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2750 '`','D','i','s','k','I','d','`',0};
2752 file = get_loaded_file(package, comp->KeyPath);
2753 if (!file)
2754 continue;
2756 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2757 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2758 ptr2 = strrchrW(source, '\\') + 1;
2759 msiobj_release(&row->hdr);
2761 lstrcpyW(base, package->PackagePath);
2762 ptr = strrchrW(base, '\\');
2763 *(ptr + 1) = '\0';
2765 sourcepath = resolve_file_source(package, file);
2766 ptr = sourcepath + lstrlenW(base);
2767 lstrcpyW(ptr2, ptr);
2768 msi_free(sourcepath);
2770 msi_reg_set_val_str(hkey, squished_pc, source);
2772 RegCloseKey(hkey);
2774 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2776 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2777 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2778 else
2779 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2782 /* UI stuff */
2783 uirow = MSI_CreateRecord(3);
2784 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2785 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2786 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2787 ui_actiondata(package,szProcessComponents,uirow);
2788 msiobj_release( &uirow->hdr );
2791 return ERROR_SUCCESS;
2794 typedef struct {
2795 CLSID clsid;
2796 LPWSTR source;
2798 LPWSTR path;
2799 ITypeLib *ptLib;
2800 } typelib_struct;
2802 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2803 LPWSTR lpszName, LONG_PTR lParam)
2805 TLIBATTR *attr;
2806 typelib_struct *tl_struct = (typelib_struct*) lParam;
2807 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2808 int sz;
2809 HRESULT res;
2811 if (!IS_INTRESOURCE(lpszName))
2813 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2814 return TRUE;
2817 sz = strlenW(tl_struct->source)+4;
2818 sz *= sizeof(WCHAR);
2820 if ((INT_PTR)lpszName == 1)
2821 tl_struct->path = strdupW(tl_struct->source);
2822 else
2824 tl_struct->path = msi_alloc(sz);
2825 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2828 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2829 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2830 if (FAILED(res))
2832 msi_free(tl_struct->path);
2833 tl_struct->path = NULL;
2835 return TRUE;
2838 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2839 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2841 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2842 return FALSE;
2845 msi_free(tl_struct->path);
2846 tl_struct->path = NULL;
2848 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2849 ITypeLib_Release(tl_struct->ptLib);
2851 return TRUE;
2854 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2856 MSIPACKAGE* package = param;
2857 LPCWSTR component;
2858 MSICOMPONENT *comp;
2859 MSIFILE *file;
2860 typelib_struct tl_struct;
2861 ITypeLib *tlib;
2862 HMODULE module;
2863 HRESULT hr;
2865 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2867 component = MSI_RecordGetString(row,3);
2868 comp = get_loaded_component(package,component);
2869 if (!comp)
2870 return ERROR_SUCCESS;
2872 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2874 TRACE("Skipping typelib reg due to disabled component\n");
2876 comp->Action = comp->Installed;
2878 return ERROR_SUCCESS;
2881 comp->Action = INSTALLSTATE_LOCAL;
2883 file = get_loaded_file( package, comp->KeyPath );
2884 if (!file)
2885 return ERROR_SUCCESS;
2887 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2888 if (module)
2890 LPCWSTR guid;
2891 guid = MSI_RecordGetString(row,1);
2892 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2893 tl_struct.source = strdupW( file->TargetPath );
2894 tl_struct.path = NULL;
2896 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2897 (LONG_PTR)&tl_struct);
2899 if (tl_struct.path)
2901 LPWSTR help = NULL;
2902 LPCWSTR helpid;
2903 HRESULT res;
2905 helpid = MSI_RecordGetString(row,6);
2907 if (helpid)
2908 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2909 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2910 msi_free(help);
2912 if (FAILED(res))
2913 ERR("Failed to register type library %s\n",
2914 debugstr_w(tl_struct.path));
2915 else
2917 ui_actiondata(package,szRegisterTypeLibraries,row);
2919 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2922 ITypeLib_Release(tl_struct.ptLib);
2923 msi_free(tl_struct.path);
2925 else
2926 ERR("Failed to load type library %s\n",
2927 debugstr_w(tl_struct.source));
2929 FreeLibrary(module);
2930 msi_free(tl_struct.source);
2932 else
2934 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2935 if (FAILED(hr))
2937 ERR("Failed to load type library: %08x\n", hr);
2938 return ERROR_FUNCTION_FAILED;
2941 ITypeLib_Release(tlib);
2944 return ERROR_SUCCESS;
2947 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2950 * OK this is a bit confusing.. I am given a _Component key and I believe
2951 * that the file that is being registered as a type library is the "key file
2952 * of that component" which I interpret to mean "The file in the KeyPath of
2953 * that component".
2955 UINT rc;
2956 MSIQUERY * view;
2957 static const WCHAR Query[] =
2958 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2959 '`','T','y','p','e','L','i','b','`',0};
2961 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2962 if (rc != ERROR_SUCCESS)
2963 return ERROR_SUCCESS;
2965 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2966 msiobj_release(&view->hdr);
2967 return rc;
2970 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
2972 MSIPACKAGE *package = param;
2973 LPWSTR target_file, target_folder, filename;
2974 LPCWSTR buffer, extension;
2975 MSICOMPONENT *comp;
2976 static const WCHAR szlnk[]={'.','l','n','k',0};
2977 IShellLinkW *sl = NULL;
2978 IPersistFile *pf = NULL;
2979 HRESULT res;
2981 buffer = MSI_RecordGetString(row,4);
2982 comp = get_loaded_component(package,buffer);
2983 if (!comp)
2984 return ERROR_SUCCESS;
2986 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
2988 TRACE("Skipping shortcut creation due to disabled component\n");
2990 comp->Action = comp->Installed;
2992 return ERROR_SUCCESS;
2995 comp->Action = INSTALLSTATE_LOCAL;
2997 ui_actiondata(package,szCreateShortcuts,row);
2999 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3000 &IID_IShellLinkW, (LPVOID *) &sl );
3002 if (FAILED( res ))
3004 ERR("CLSID_ShellLink not available\n");
3005 goto err;
3008 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3009 if (FAILED( res ))
3011 ERR("QueryInterface(IID_IPersistFile) failed\n");
3012 goto err;
3015 buffer = MSI_RecordGetString(row,2);
3016 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3018 /* may be needed because of a bug somewhere else */
3019 create_full_pathW(target_folder);
3021 filename = msi_dup_record_field( row, 3 );
3022 reduce_to_longfilename(filename);
3024 extension = strchrW(filename,'.');
3025 if (!extension || strcmpiW(extension,szlnk))
3027 int len = strlenW(filename);
3028 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3029 memcpy(filename + len, szlnk, sizeof(szlnk));
3031 target_file = build_directory_name(2, target_folder, filename);
3032 msi_free(target_folder);
3033 msi_free(filename);
3035 buffer = MSI_RecordGetString(row,5);
3036 if (strchrW(buffer,'['))
3038 LPWSTR deformated;
3039 deformat_string(package,buffer,&deformated);
3040 IShellLinkW_SetPath(sl,deformated);
3041 msi_free(deformated);
3043 else
3045 FIXME("poorly handled shortcut format, advertised shortcut\n");
3046 IShellLinkW_SetPath(sl,comp->FullKeypath);
3049 if (!MSI_RecordIsNull(row,6))
3051 LPWSTR deformated;
3052 buffer = MSI_RecordGetString(row,6);
3053 deformat_string(package,buffer,&deformated);
3054 IShellLinkW_SetArguments(sl,deformated);
3055 msi_free(deformated);
3058 if (!MSI_RecordIsNull(row,7))
3060 buffer = MSI_RecordGetString(row,7);
3061 IShellLinkW_SetDescription(sl,buffer);
3064 if (!MSI_RecordIsNull(row,8))
3065 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3067 if (!MSI_RecordIsNull(row,9))
3069 LPWSTR Path;
3070 INT index;
3072 buffer = MSI_RecordGetString(row,9);
3074 Path = build_icon_path(package,buffer);
3075 index = MSI_RecordGetInteger(row,10);
3077 /* no value means 0 */
3078 if (index == MSI_NULL_INTEGER)
3079 index = 0;
3081 IShellLinkW_SetIconLocation(sl,Path,index);
3082 msi_free(Path);
3085 if (!MSI_RecordIsNull(row,11))
3086 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3088 if (!MSI_RecordIsNull(row,12))
3090 LPWSTR Path;
3091 buffer = MSI_RecordGetString(row,12);
3092 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3093 if (Path)
3094 IShellLinkW_SetWorkingDirectory(sl,Path);
3095 msi_free(Path);
3098 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3099 IPersistFile_Save(pf,target_file,FALSE);
3101 msi_free(target_file);
3103 err:
3104 if (pf)
3105 IPersistFile_Release( pf );
3106 if (sl)
3107 IShellLinkW_Release( sl );
3109 return ERROR_SUCCESS;
3112 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3114 UINT rc;
3115 HRESULT res;
3116 MSIQUERY * view;
3117 static const WCHAR Query[] =
3118 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3119 '`','S','h','o','r','t','c','u','t','`',0};
3121 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3122 if (rc != ERROR_SUCCESS)
3123 return ERROR_SUCCESS;
3125 res = CoInitialize( NULL );
3127 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3128 msiobj_release(&view->hdr);
3130 if (SUCCEEDED(res))
3131 CoUninitialize();
3133 return rc;
3136 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3138 MSIPACKAGE* package = param;
3139 HANDLE the_file;
3140 LPWSTR FilePath;
3141 LPCWSTR FileName;
3142 CHAR buffer[1024];
3143 DWORD sz;
3144 UINT rc;
3145 MSIRECORD *uirow;
3147 FileName = MSI_RecordGetString(row,1);
3148 if (!FileName)
3150 ERR("Unable to get FileName\n");
3151 return ERROR_SUCCESS;
3154 FilePath = build_icon_path(package,FileName);
3156 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3158 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3159 FILE_ATTRIBUTE_NORMAL, NULL);
3161 if (the_file == INVALID_HANDLE_VALUE)
3163 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3164 msi_free(FilePath);
3165 return ERROR_SUCCESS;
3170 DWORD write;
3171 sz = 1024;
3172 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3173 if (rc != ERROR_SUCCESS)
3175 ERR("Failed to get stream\n");
3176 CloseHandle(the_file);
3177 DeleteFileW(FilePath);
3178 break;
3180 WriteFile(the_file,buffer,sz,&write,NULL);
3181 } while (sz == 1024);
3183 msi_free(FilePath);
3185 CloseHandle(the_file);
3187 uirow = MSI_CreateRecord(1);
3188 MSI_RecordSetStringW(uirow,1,FileName);
3189 ui_actiondata(package,szPublishProduct,uirow);
3190 msiobj_release( &uirow->hdr );
3192 return ERROR_SUCCESS;
3195 static UINT msi_publish_icons(MSIPACKAGE *package)
3197 UINT r;
3198 MSIQUERY *view;
3200 static const WCHAR query[]= {
3201 'S','E','L','E','C','T',' ','*',' ',
3202 'F','R','O','M',' ','`','I','c','o','n','`',0};
3204 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3205 if (r == ERROR_SUCCESS)
3207 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3208 msiobj_release(&view->hdr);
3211 return ERROR_SUCCESS;
3214 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3216 UINT r;
3217 HKEY source;
3218 LPWSTR buffer;
3219 MSIMEDIADISK *disk;
3220 MSISOURCELISTINFO *info;
3222 r = RegCreateKeyW(hkey, szSourceList, &source);
3223 if (r != ERROR_SUCCESS)
3224 return r;
3226 RegCloseKey(source);
3228 buffer = strrchrW(package->PackagePath, '\\') + 1;
3229 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3230 package->Context, MSICODE_PRODUCT,
3231 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3232 if (r != ERROR_SUCCESS)
3233 return r;
3235 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3236 package->Context, MSICODE_PRODUCT,
3237 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3238 if (r != ERROR_SUCCESS)
3239 return r;
3241 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3242 package->Context, MSICODE_PRODUCT,
3243 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3244 if (r != ERROR_SUCCESS)
3245 return r;
3247 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3249 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3250 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3251 info->options, info->value);
3252 else
3253 MsiSourceListSetInfoW(package->ProductCode, NULL,
3254 info->context, info->options,
3255 info->property, info->value);
3258 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3260 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3261 disk->context, disk->options,
3262 disk->disk_id, disk->volume_label, disk->disk_prompt);
3265 return ERROR_SUCCESS;
3268 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3270 MSIHANDLE hdb, suminfo;
3271 WCHAR guids[MAX_PATH];
3272 WCHAR packcode[SQUISH_GUID_SIZE];
3273 LPWSTR buffer;
3274 LPWSTR ptr;
3275 DWORD langid;
3276 DWORD size;
3277 UINT r;
3279 static const WCHAR szProductLanguage[] =
3280 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3281 static const WCHAR szARPProductIcon[] =
3282 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3283 static const WCHAR szProductVersion[] =
3284 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3285 static const WCHAR szAssignment[] =
3286 {'A','s','s','i','g','n','m','e','n','t',0};
3287 static const WCHAR szAdvertiseFlags[] =
3288 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3289 static const WCHAR szClients[] =
3290 {'C','l','i','e','n','t','s',0};
3291 static const WCHAR szColon[] = {':',0};
3293 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3294 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3295 msi_free(buffer);
3297 langid = msi_get_property_int(package, szProductLanguage, 0);
3298 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3300 /* FIXME */
3301 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3303 buffer = msi_dup_property(package, szARPProductIcon);
3304 if (buffer)
3306 LPWSTR path = build_icon_path(package,buffer);
3307 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3308 msi_free(path);
3309 msi_free(buffer);
3312 buffer = msi_dup_property(package, szProductVersion);
3313 if (buffer)
3315 DWORD verdword = msi_version_str_to_dword(buffer);
3316 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3317 msi_free(buffer);
3320 msi_reg_set_val_dword(hkey, szAssignment, 0);
3321 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3322 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3323 msi_reg_set_val_str(hkey, szClients, szColon);
3325 hdb = alloc_msihandle(&package->db->hdr);
3326 if (!hdb)
3327 return ERROR_NOT_ENOUGH_MEMORY;
3329 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3330 MsiCloseHandle(hdb);
3331 if (r != ERROR_SUCCESS)
3332 goto done;
3334 size = MAX_PATH;
3335 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3336 NULL, guids, &size);
3337 if (r != ERROR_SUCCESS)
3338 goto done;
3340 ptr = strchrW(guids, ';');
3341 if (ptr) *ptr = 0;
3342 squash_guid(guids, packcode);
3343 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3345 done:
3346 MsiCloseHandle(suminfo);
3347 return ERROR_SUCCESS;
3350 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3352 UINT r;
3353 HKEY hkey;
3354 LPWSTR upgrade;
3355 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3357 static const WCHAR szUpgradeCode[] =
3358 {'U','p','g','r','a','d','e','C','o','d','e',0};
3360 upgrade = msi_dup_property(package, szUpgradeCode);
3361 if (!upgrade)
3362 return ERROR_SUCCESS;
3364 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3366 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3367 if (r != ERROR_SUCCESS)
3368 goto done;
3370 else
3372 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3373 if (r != ERROR_SUCCESS)
3374 goto done;
3377 squash_guid(package->ProductCode, squashed_pc);
3378 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3380 RegCloseKey(hkey);
3382 done:
3383 msi_free(upgrade);
3384 return r;
3387 static BOOL msi_check_publish(MSIPACKAGE *package)
3389 MSIFEATURE *feature;
3391 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3393 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3394 return TRUE;
3397 return FALSE;
3400 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3402 MSIFEATURE *feature;
3404 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3406 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3407 return FALSE;
3410 return TRUE;
3413 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3415 WCHAR patch_squashed[GUID_SIZE];
3416 HKEY patches;
3417 LONG res;
3418 UINT r = ERROR_FUNCTION_FAILED;
3420 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3421 &patches, NULL);
3422 if (res != ERROR_SUCCESS)
3423 return ERROR_FUNCTION_FAILED;
3425 squash_guid(package->patch->patchcode, patch_squashed);
3427 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3428 (const BYTE *)patch_squashed,
3429 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3430 if (res != ERROR_SUCCESS)
3431 goto done;
3433 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3434 (const BYTE *)package->patch->transforms,
3435 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3436 if (res == ERROR_SUCCESS)
3437 r = ERROR_SUCCESS;
3439 done:
3440 RegCloseKey(patches);
3441 return r;
3445 * 99% of the work done here is only done for
3446 * advertised installs. However this is where the
3447 * Icon table is processed and written out
3448 * so that is what I am going to do here.
3450 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3452 UINT rc;
3453 HKEY hukey=0;
3454 HKEY hudkey=0;
3456 /* FIXME: also need to publish if the product is in advertise mode */
3457 if (!msi_check_publish(package))
3458 return ERROR_SUCCESS;
3460 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3461 &hukey, TRUE);
3462 if (rc != ERROR_SUCCESS)
3463 goto end;
3465 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3466 NULL, &hudkey, TRUE);
3467 if (rc != ERROR_SUCCESS)
3468 goto end;
3470 rc = msi_publish_upgrade_code(package);
3471 if (rc != ERROR_SUCCESS)
3472 goto end;
3474 if (package->patch)
3476 rc = msi_publish_patch(package, hukey, hudkey);
3477 if (rc != ERROR_SUCCESS)
3478 goto end;
3481 rc = msi_publish_product_properties(package, hukey);
3482 if (rc != ERROR_SUCCESS)
3483 goto end;
3485 rc = msi_publish_sourcelist(package, hukey);
3486 if (rc != ERROR_SUCCESS)
3487 goto end;
3489 rc = msi_publish_icons(package);
3491 end:
3492 RegCloseKey(hukey);
3493 RegCloseKey(hudkey);
3495 return rc;
3498 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3500 MSIPACKAGE *package = param;
3501 LPCWSTR component, section, key, value, identifier, dirproperty;
3502 LPWSTR deformated_section, deformated_key, deformated_value;
3503 LPWSTR folder, filename, fullname = NULL;
3504 LPCWSTR filenameptr;
3505 MSIRECORD * uirow;
3506 INT action;
3507 MSICOMPONENT *comp;
3508 static const WCHAR szWindowsFolder[] =
3509 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3511 component = MSI_RecordGetString(row, 8);
3512 comp = get_loaded_component(package,component);
3514 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3516 TRACE("Skipping ini file due to disabled component %s\n",
3517 debugstr_w(component));
3519 comp->Action = comp->Installed;
3521 return ERROR_SUCCESS;
3524 comp->Action = INSTALLSTATE_LOCAL;
3526 identifier = MSI_RecordGetString(row,1);
3527 dirproperty = MSI_RecordGetString(row,3);
3528 section = MSI_RecordGetString(row,4);
3529 key = MSI_RecordGetString(row,5);
3530 value = MSI_RecordGetString(row,6);
3531 action = MSI_RecordGetInteger(row,7);
3533 deformat_string(package,section,&deformated_section);
3534 deformat_string(package,key,&deformated_key);
3535 deformat_string(package,value,&deformated_value);
3537 filename = msi_dup_record_field(row, 2);
3538 if (filename && (filenameptr = strchrW(filename, '|')))
3539 filenameptr++;
3540 else
3541 filenameptr = filename;
3543 if (dirproperty)
3545 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3546 if (!folder)
3547 folder = msi_dup_property( package, dirproperty );
3549 else
3550 folder = msi_dup_property( package, szWindowsFolder );
3552 if (!folder)
3554 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3555 goto cleanup;
3558 fullname = build_directory_name(2, folder, filenameptr);
3560 if (action == 0)
3562 TRACE("Adding value %s to section %s in %s\n",
3563 debugstr_w(deformated_key), debugstr_w(deformated_section),
3564 debugstr_w(fullname));
3565 WritePrivateProfileStringW(deformated_section, deformated_key,
3566 deformated_value, fullname);
3568 else if (action == 1)
3570 WCHAR returned[10];
3571 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3572 returned, 10, fullname);
3573 if (returned[0] == 0)
3575 TRACE("Adding value %s to section %s in %s\n",
3576 debugstr_w(deformated_key), debugstr_w(deformated_section),
3577 debugstr_w(fullname));
3579 WritePrivateProfileStringW(deformated_section, deformated_key,
3580 deformated_value, fullname);
3583 else if (action == 3)
3584 FIXME("Append to existing section not yet implemented\n");
3586 uirow = MSI_CreateRecord(4);
3587 MSI_RecordSetStringW(uirow,1,identifier);
3588 MSI_RecordSetStringW(uirow,2,deformated_section);
3589 MSI_RecordSetStringW(uirow,3,deformated_key);
3590 MSI_RecordSetStringW(uirow,4,deformated_value);
3591 ui_actiondata(package,szWriteIniValues,uirow);
3592 msiobj_release( &uirow->hdr );
3594 cleanup:
3595 msi_free(filename);
3596 msi_free(fullname);
3597 msi_free(folder);
3598 msi_free(deformated_key);
3599 msi_free(deformated_value);
3600 msi_free(deformated_section);
3601 return ERROR_SUCCESS;
3604 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3606 UINT rc;
3607 MSIQUERY * view;
3608 static const WCHAR ExecSeqQuery[] =
3609 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3610 '`','I','n','i','F','i','l','e','`',0};
3612 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3613 if (rc != ERROR_SUCCESS)
3615 TRACE("no IniFile table\n");
3616 return ERROR_SUCCESS;
3619 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3620 msiobj_release(&view->hdr);
3621 return rc;
3624 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3626 MSIPACKAGE *package = param;
3627 LPCWSTR filename;
3628 LPWSTR FullName;
3629 MSIFILE *file;
3630 DWORD len;
3631 static const WCHAR ExeStr[] =
3632 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3633 static const WCHAR close[] = {'\"',0};
3634 STARTUPINFOW si;
3635 PROCESS_INFORMATION info;
3636 BOOL brc;
3637 MSIRECORD *uirow;
3638 LPWSTR uipath, p;
3640 memset(&si,0,sizeof(STARTUPINFOW));
3642 filename = MSI_RecordGetString(row,1);
3643 file = get_loaded_file( package, filename );
3645 if (!file)
3647 ERR("Unable to find file id %s\n",debugstr_w(filename));
3648 return ERROR_SUCCESS;
3651 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3653 FullName = msi_alloc(len*sizeof(WCHAR));
3654 strcpyW(FullName,ExeStr);
3655 strcatW( FullName, file->TargetPath );
3656 strcatW(FullName,close);
3658 TRACE("Registering %s\n",debugstr_w(FullName));
3659 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3660 &si, &info);
3662 if (brc)
3664 CloseHandle(info.hThread);
3665 msi_dialog_check_messages(info.hProcess);
3666 CloseHandle(info.hProcess);
3669 msi_free(FullName);
3671 /* the UI chunk */
3672 uirow = MSI_CreateRecord( 2 );
3673 uipath = strdupW( file->TargetPath );
3674 p = strrchrW(uipath,'\\');
3675 if (p)
3676 p[0]=0;
3677 MSI_RecordSetStringW( uirow, 1, &p[1] );
3678 MSI_RecordSetStringW( uirow, 2, uipath);
3679 ui_actiondata( package, szSelfRegModules, uirow);
3680 msiobj_release( &uirow->hdr );
3681 msi_free( uipath );
3682 /* FIXME: call ui_progress? */
3684 return ERROR_SUCCESS;
3687 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3689 UINT rc;
3690 MSIQUERY * view;
3691 static const WCHAR ExecSeqQuery[] =
3692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3693 '`','S','e','l','f','R','e','g','`',0};
3695 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3696 if (rc != ERROR_SUCCESS)
3698 TRACE("no SelfReg table\n");
3699 return ERROR_SUCCESS;
3702 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3703 msiobj_release(&view->hdr);
3705 return ERROR_SUCCESS;
3708 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3710 MSIFEATURE *feature;
3711 UINT rc;
3712 HKEY hkey;
3713 HKEY userdata = NULL;
3715 if (!msi_check_publish(package))
3716 return ERROR_SUCCESS;
3718 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3719 &hkey, TRUE);
3720 if (rc != ERROR_SUCCESS)
3721 goto end;
3723 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3724 &userdata, TRUE);
3725 if (rc != ERROR_SUCCESS)
3726 goto end;
3728 /* here the guids are base 85 encoded */
3729 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3731 ComponentList *cl;
3732 LPWSTR data = NULL;
3733 GUID clsid;
3734 INT size;
3735 BOOL absent = FALSE;
3736 MSIRECORD *uirow;
3738 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3739 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3740 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3741 absent = TRUE;
3743 size = 1;
3744 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3746 size += 21;
3748 if (feature->Feature_Parent)
3749 size += strlenW( feature->Feature_Parent )+2;
3751 data = msi_alloc(size * sizeof(WCHAR));
3753 data[0] = 0;
3754 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3756 MSICOMPONENT* component = cl->component;
3757 WCHAR buf[21];
3759 buf[0] = 0;
3760 if (component->ComponentId)
3762 TRACE("From %s\n",debugstr_w(component->ComponentId));
3763 CLSIDFromString(component->ComponentId, &clsid);
3764 encode_base85_guid(&clsid,buf);
3765 TRACE("to %s\n",debugstr_w(buf));
3766 strcatW(data,buf);
3770 if (feature->Feature_Parent)
3772 static const WCHAR sep[] = {'\2',0};
3773 strcatW(data,sep);
3774 strcatW(data,feature->Feature_Parent);
3777 msi_reg_set_val_str( userdata, feature->Feature, data );
3778 msi_free(data);
3780 size = 0;
3781 if (feature->Feature_Parent)
3782 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3783 if (!absent)
3785 size += sizeof(WCHAR);
3786 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3787 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3789 else
3791 size += 2*sizeof(WCHAR);
3792 data = msi_alloc(size);
3793 data[0] = 0x6;
3794 data[1] = 0;
3795 if (feature->Feature_Parent)
3796 strcpyW( &data[1], feature->Feature_Parent );
3797 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3798 (LPBYTE)data,size);
3799 msi_free(data);
3802 /* the UI chunk */
3803 uirow = MSI_CreateRecord( 1 );
3804 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3805 ui_actiondata( package, szPublishFeatures, uirow);
3806 msiobj_release( &uirow->hdr );
3807 /* FIXME: call ui_progress? */
3810 end:
3811 RegCloseKey(hkey);
3812 RegCloseKey(userdata);
3813 return rc;
3816 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3818 UINT r;
3819 HKEY hkey;
3821 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3823 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3824 &hkey, FALSE);
3825 if (r == ERROR_SUCCESS)
3827 RegDeleteValueW(hkey, feature->Feature);
3828 RegCloseKey(hkey);
3831 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3832 &hkey, FALSE);
3833 if (r == ERROR_SUCCESS)
3835 RegDeleteValueW(hkey, feature->Feature);
3836 RegCloseKey(hkey);
3839 return ERROR_SUCCESS;
3842 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3844 MSIFEATURE *feature;
3846 if (!msi_check_unpublish(package))
3847 return ERROR_SUCCESS;
3849 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3851 msi_unpublish_feature(package, feature);
3854 return ERROR_SUCCESS;
3857 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
3859 LPWSTR prop, val, key;
3860 SYSTEMTIME systime;
3861 DWORD size, langid;
3862 WCHAR date[9];
3863 LPWSTR buffer;
3865 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
3866 static const WCHAR szWindowsInstaller[] =
3867 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3868 static const WCHAR modpath_fmt[] =
3869 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3870 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3871 static const WCHAR szModifyPath[] =
3872 {'M','o','d','i','f','y','P','a','t','h',0};
3873 static const WCHAR szUninstallString[] =
3874 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3875 static const WCHAR szEstimatedSize[] =
3876 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3877 static const WCHAR szProductLanguage[] =
3878 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3879 static const WCHAR szProductVersion[] =
3880 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3881 static const WCHAR szProductName[] =
3882 {'P','r','o','d','u','c','t','N','a','m','e',0};
3883 static const WCHAR szDisplayName[] =
3884 {'D','i','s','p','l','a','y','N','a','m','e',0};
3885 static const WCHAR szDisplayVersion[] =
3886 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
3887 static const WCHAR szManufacturer[] =
3888 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
3890 static const LPCSTR propval[] = {
3891 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3892 "ARPCONTACT", "Contact",
3893 "ARPCOMMENTS", "Comments",
3894 "ProductName", "DisplayName",
3895 "ProductVersion", "DisplayVersion",
3896 "ARPHELPLINK", "HelpLink",
3897 "ARPHELPTELEPHONE", "HelpTelephone",
3898 "ARPINSTALLLOCATION", "InstallLocation",
3899 "SourceDir", "InstallSource",
3900 "Manufacturer", "Publisher",
3901 "ARPREADME", "Readme",
3902 "ARPSIZE", "Size",
3903 "ARPURLINFOABOUT", "URLInfoAbout",
3904 "ARPURLUPDATEINFO", "URLUpdateInfo",
3905 NULL,
3907 const LPCSTR *p = propval;
3909 while (*p)
3911 prop = strdupAtoW(*p++);
3912 key = strdupAtoW(*p++);
3913 val = msi_dup_property(package, prop);
3914 msi_reg_set_val_str(hkey, key, val);
3915 msi_free(val);
3916 msi_free(key);
3917 msi_free(prop);
3920 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
3922 size = deformat_string(package, modpath_fmt, &buffer);
3923 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3924 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
3925 msi_free(buffer);
3927 /* FIXME: Write real Estimated Size when we have it */
3928 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
3930 buffer = msi_dup_property(package, szProductName);
3931 msi_reg_set_val_str(hkey, szDisplayName, buffer);
3932 msi_free(buffer);
3934 buffer = msi_dup_property(package, cszSourceDir);
3935 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
3936 msi_free(buffer);
3938 buffer = msi_dup_property(package, szManufacturer);
3939 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
3940 msi_free(buffer);
3942 GetLocalTime(&systime);
3943 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
3944 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
3946 langid = msi_get_property_int(package, szProductLanguage, 0);
3947 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3949 buffer = msi_dup_property(package, szProductVersion);
3950 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
3951 if (buffer)
3953 DWORD verdword = msi_version_str_to_dword(buffer);
3955 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3956 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
3957 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
3958 msi_free(buffer);
3961 return ERROR_SUCCESS;
3964 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3966 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3967 LPWSTR upgrade_code;
3968 HKEY hkey, props;
3969 HKEY upgrade;
3970 UINT rc;
3972 static const WCHAR szUpgradeCode[] = {
3973 'U','p','g','r','a','d','e','C','o','d','e',0};
3975 /* FIXME: also need to publish if the product is in advertise mode */
3976 if (!msi_check_publish(package))
3977 return ERROR_SUCCESS;
3979 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
3980 if (rc != ERROR_SUCCESS)
3981 return rc;
3983 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
3984 NULL, &props, TRUE);
3985 if (rc != ERROR_SUCCESS)
3986 goto done;
3988 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
3989 msi_free( package->db->localfile );
3990 package->db->localfile = NULL;
3992 rc = msi_publish_install_properties(package, hkey);
3993 if (rc != ERROR_SUCCESS)
3994 goto done;
3996 rc = msi_publish_install_properties(package, props);
3997 if (rc != ERROR_SUCCESS)
3998 goto done;
4000 upgrade_code = msi_dup_property(package, szUpgradeCode);
4001 if (upgrade_code)
4003 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4004 squash_guid(package->ProductCode, squashed_pc);
4005 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4006 RegCloseKey(upgrade);
4007 msi_free(upgrade_code);
4010 done:
4011 RegCloseKey(hkey);
4013 return ERROR_SUCCESS;
4016 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4018 return execute_script(package,INSTALL_SCRIPT);
4021 static UINT msi_unpublish_product(MSIPACKAGE *package)
4023 LPWSTR upgrade;
4024 LPWSTR remove = NULL;
4025 LPWSTR *features = NULL;
4026 BOOL full_uninstall = TRUE;
4027 MSIFEATURE *feature;
4029 static const WCHAR szUpgradeCode[] =
4030 {'U','p','g','r','a','d','e','C','o','d','e',0};
4032 remove = msi_dup_property(package, szRemove);
4033 if (!remove)
4034 return ERROR_SUCCESS;
4036 features = msi_split_string(remove, ',');
4037 if (!features)
4039 msi_free(remove);
4040 ERR("REMOVE feature list is empty!\n");
4041 return ERROR_FUNCTION_FAILED;
4044 if (!lstrcmpW(features[0], szAll))
4045 full_uninstall = TRUE;
4046 else
4048 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4050 if (feature->Action != INSTALLSTATE_ABSENT)
4051 full_uninstall = FALSE;
4055 if (!full_uninstall)
4056 goto done;
4058 MSIREG_DeleteProductKey(package->ProductCode);
4059 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4060 MSIREG_DeleteUninstallKey(package->ProductCode);
4062 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4064 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4065 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4067 else
4069 MSIREG_DeleteUserProductKey(package->ProductCode);
4070 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4073 upgrade = msi_dup_property(package, szUpgradeCode);
4074 if (upgrade)
4076 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4077 msi_free(upgrade);
4080 done:
4081 msi_free(remove);
4082 msi_free(features);
4083 return ERROR_SUCCESS;
4086 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4088 UINT rc;
4090 rc = msi_unpublish_product(package);
4091 if (rc != ERROR_SUCCESS)
4092 return rc;
4094 /* turn off scheduling */
4095 package->script->CurrentlyScripting= FALSE;
4097 /* first do the same as an InstallExecute */
4098 rc = ACTION_InstallExecute(package);
4099 if (rc != ERROR_SUCCESS)
4100 return rc;
4102 /* then handle Commit Actions */
4103 rc = execute_script(package,COMMIT_SCRIPT);
4105 return rc;
4108 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4110 static const WCHAR RunOnce[] = {
4111 'S','o','f','t','w','a','r','e','\\',
4112 'M','i','c','r','o','s','o','f','t','\\',
4113 'W','i','n','d','o','w','s','\\',
4114 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4115 'R','u','n','O','n','c','e',0};
4116 static const WCHAR InstallRunOnce[] = {
4117 'S','o','f','t','w','a','r','e','\\',
4118 'M','i','c','r','o','s','o','f','t','\\',
4119 'W','i','n','d','o','w','s','\\',
4120 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4121 'I','n','s','t','a','l','l','e','r','\\',
4122 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4124 static const WCHAR msiexec_fmt[] = {
4125 '%','s',
4126 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4127 '\"','%','s','\"',0};
4128 static const WCHAR install_fmt[] = {
4129 '/','I',' ','\"','%','s','\"',' ',
4130 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4131 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4132 WCHAR buffer[256], sysdir[MAX_PATH];
4133 HKEY hkey;
4134 WCHAR squished_pc[100];
4136 squash_guid(package->ProductCode,squished_pc);
4138 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4139 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4140 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4141 squished_pc);
4143 msi_reg_set_val_str( hkey, squished_pc, buffer );
4144 RegCloseKey(hkey);
4146 TRACE("Reboot command %s\n",debugstr_w(buffer));
4148 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4149 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4151 msi_reg_set_val_str( hkey, squished_pc, buffer );
4152 RegCloseKey(hkey);
4154 return ERROR_INSTALL_SUSPEND;
4157 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4159 DWORD attrib;
4160 UINT rc;
4163 * We are currently doing what should be done here in the top level Install
4164 * however for Administrative and uninstalls this step will be needed
4166 if (!package->PackagePath)
4167 return ERROR_SUCCESS;
4169 msi_set_sourcedir_props(package, TRUE);
4171 attrib = GetFileAttributesW(package->db->path);
4172 if (attrib == INVALID_FILE_ATTRIBUTES)
4174 LPWSTR prompt;
4175 LPWSTR msg;
4176 DWORD size = 0;
4178 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4179 package->Context, MSICODE_PRODUCT,
4180 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4181 if (rc == ERROR_MORE_DATA)
4183 prompt = msi_alloc(size * sizeof(WCHAR));
4184 MsiSourceListGetInfoW(package->ProductCode, NULL,
4185 package->Context, MSICODE_PRODUCT,
4186 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4188 else
4189 prompt = strdupW(package->db->path);
4191 msg = generate_error_string(package,1302,1,prompt);
4192 while(attrib == INVALID_FILE_ATTRIBUTES)
4194 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4195 if (rc == IDCANCEL)
4197 rc = ERROR_INSTALL_USEREXIT;
4198 break;
4200 attrib = GetFileAttributesW(package->db->path);
4202 msi_free(prompt);
4203 rc = ERROR_SUCCESS;
4205 else
4206 return ERROR_SUCCESS;
4208 return rc;
4211 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4213 HKEY hkey=0;
4214 LPWSTR buffer;
4215 LPWSTR productid;
4216 UINT rc,i;
4218 static const WCHAR szPropKeys[][80] =
4220 {'P','r','o','d','u','c','t','I','D',0},
4221 {'U','S','E','R','N','A','M','E',0},
4222 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4223 {0},
4226 static const WCHAR szRegKeys[][80] =
4228 {'P','r','o','d','u','c','t','I','D',0},
4229 {'R','e','g','O','w','n','e','r',0},
4230 {'R','e','g','C','o','m','p','a','n','y',0},
4231 {0},
4234 if (msi_check_unpublish(package))
4236 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4237 return ERROR_SUCCESS;
4240 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4241 if (!productid)
4242 return ERROR_SUCCESS;
4244 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4245 NULL, &hkey, TRUE);
4246 if (rc != ERROR_SUCCESS)
4247 goto end;
4249 for( i = 0; szPropKeys[i][0]; i++ )
4251 buffer = msi_dup_property( package, szPropKeys[i] );
4252 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4253 msi_free( buffer );
4256 end:
4257 msi_free(productid);
4258 RegCloseKey(hkey);
4260 /* FIXME: call ui_actiondata */
4262 return rc;
4266 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4268 UINT rc;
4270 package->script->InWhatSequence |= SEQUENCE_EXEC;
4271 rc = ACTION_ProcessExecSequence(package,FALSE);
4272 return rc;
4276 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4278 MSIPACKAGE *package = param;
4279 LPCWSTR compgroupid=NULL;
4280 LPCWSTR feature=NULL;
4281 LPCWSTR text = NULL;
4282 LPCWSTR qualifier = NULL;
4283 LPCWSTR component = NULL;
4284 LPWSTR advertise = NULL;
4285 LPWSTR output = NULL;
4286 HKEY hkey;
4287 UINT rc = ERROR_SUCCESS;
4288 MSICOMPONENT *comp;
4289 DWORD sz = 0;
4290 MSIRECORD *uirow;
4292 component = MSI_RecordGetString(rec,3);
4293 comp = get_loaded_component(package,component);
4295 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4296 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4297 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4299 TRACE("Skipping: Component %s not scheduled for install\n",
4300 debugstr_w(component));
4302 return ERROR_SUCCESS;
4305 compgroupid = MSI_RecordGetString(rec,1);
4306 qualifier = MSI_RecordGetString(rec,2);
4308 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4309 if (rc != ERROR_SUCCESS)
4310 goto end;
4312 text = MSI_RecordGetString(rec,4);
4313 feature = MSI_RecordGetString(rec,5);
4315 advertise = create_component_advertise_string(package, comp, feature);
4317 sz = strlenW(advertise);
4319 if (text)
4320 sz += lstrlenW(text);
4322 sz+=3;
4323 sz *= sizeof(WCHAR);
4325 output = msi_alloc_zero(sz);
4326 strcpyW(output,advertise);
4327 msi_free(advertise);
4329 if (text)
4330 strcatW(output,text);
4332 msi_reg_set_val_multi_str( hkey, qualifier, output );
4334 end:
4335 RegCloseKey(hkey);
4336 msi_free(output);
4338 /* the UI chunk */
4339 uirow = MSI_CreateRecord( 2 );
4340 MSI_RecordSetStringW( uirow, 1, compgroupid );
4341 MSI_RecordSetStringW( uirow, 2, qualifier);
4342 ui_actiondata( package, szPublishComponents, uirow);
4343 msiobj_release( &uirow->hdr );
4344 /* FIXME: call ui_progress? */
4346 return rc;
4350 * At present I am ignorning the advertised components part of this and only
4351 * focusing on the qualified component sets
4353 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4355 UINT rc;
4356 MSIQUERY * view;
4357 static const WCHAR ExecSeqQuery[] =
4358 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4359 '`','P','u','b','l','i','s','h',
4360 'C','o','m','p','o','n','e','n','t','`',0};
4362 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4363 if (rc != ERROR_SUCCESS)
4364 return ERROR_SUCCESS;
4366 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4367 msiobj_release(&view->hdr);
4369 return rc;
4372 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4374 MSIPACKAGE *package = param;
4375 MSIRECORD *row;
4376 MSIFILE *file;
4377 SC_HANDLE hscm, service = NULL;
4378 LPCWSTR comp, depends, pass;
4379 LPWSTR name = NULL, disp = NULL;
4380 LPCWSTR load_order, serv_name, key;
4381 DWORD serv_type, start_type;
4382 DWORD err_control;
4384 static const WCHAR query[] =
4385 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4386 '`','C','o','m','p','o','n','e','n','t','`',' ',
4387 'W','H','E','R','E',' ',
4388 '`','C','o','m','p','o','n','e','n','t','`',' ',
4389 '=','\'','%','s','\'',0};
4391 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4392 if (!hscm)
4394 ERR("Failed to open the SC Manager!\n");
4395 goto done;
4398 start_type = MSI_RecordGetInteger(rec, 5);
4399 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4400 goto done;
4402 depends = MSI_RecordGetString(rec, 8);
4403 if (depends && *depends)
4404 FIXME("Dependency list unhandled!\n");
4406 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4407 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4408 serv_type = MSI_RecordGetInteger(rec, 4);
4409 err_control = MSI_RecordGetInteger(rec, 6);
4410 load_order = MSI_RecordGetString(rec, 7);
4411 serv_name = MSI_RecordGetString(rec, 9);
4412 pass = MSI_RecordGetString(rec, 10);
4413 comp = MSI_RecordGetString(rec, 12);
4415 /* fetch the service path */
4416 row = MSI_QueryGetRecord(package->db, query, comp);
4417 if (!row)
4419 ERR("Control query failed!\n");
4420 goto done;
4423 key = MSI_RecordGetString(row, 6);
4425 file = get_loaded_file(package, key);
4426 msiobj_release(&row->hdr);
4427 if (!file)
4429 ERR("Failed to load the service file\n");
4430 goto done;
4433 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4434 start_type, err_control, file->TargetPath,
4435 load_order, NULL, NULL, serv_name, pass);
4436 if (!service)
4438 if (GetLastError() != ERROR_SERVICE_EXISTS)
4439 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4442 done:
4443 CloseServiceHandle(service);
4444 CloseServiceHandle(hscm);
4445 msi_free(name);
4446 msi_free(disp);
4448 return ERROR_SUCCESS;
4451 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4453 UINT rc;
4454 MSIQUERY * view;
4455 static const WCHAR ExecSeqQuery[] =
4456 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4457 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4459 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4460 if (rc != ERROR_SUCCESS)
4461 return ERROR_SUCCESS;
4463 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4464 msiobj_release(&view->hdr);
4466 return rc;
4469 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4470 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4472 LPCWSTR *vector, *temp_vector;
4473 LPWSTR p, q;
4474 DWORD sep_len;
4476 static const WCHAR separator[] = {'[','~',']',0};
4478 *numargs = 0;
4479 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4481 if (!args)
4482 return NULL;
4484 vector = msi_alloc(sizeof(LPWSTR));
4485 if (!vector)
4486 return NULL;
4488 p = args;
4491 (*numargs)++;
4492 vector[*numargs - 1] = p;
4494 if ((q = strstrW(p, separator)))
4496 *q = '\0';
4498 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4499 if (!temp_vector)
4501 msi_free(vector);
4502 return NULL;
4504 vector = temp_vector;
4506 p = q + sep_len;
4508 } while (q);
4510 return vector;
4513 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4515 MSIPACKAGE *package = param;
4516 MSICOMPONENT *comp;
4517 SC_HANDLE scm, service = NULL;
4518 LPCWSTR *vector = NULL;
4519 LPWSTR name, args;
4520 DWORD event, numargs;
4521 UINT r = ERROR_FUNCTION_FAILED;
4523 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4524 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4525 return ERROR_SUCCESS;
4527 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4528 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4529 event = MSI_RecordGetInteger(rec, 3);
4531 if (!(event & msidbServiceControlEventStart))
4532 return ERROR_SUCCESS;
4534 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4535 if (!scm)
4537 ERR("Failed to open the service control manager\n");
4538 goto done;
4541 service = OpenServiceW(scm, name, SERVICE_START);
4542 if (!service)
4544 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4545 goto done;
4548 vector = msi_service_args_to_vector(args, &numargs);
4550 if (!StartServiceW(service, numargs, vector) &&
4551 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4553 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4554 goto done;
4557 r = ERROR_SUCCESS;
4559 done:
4560 CloseServiceHandle(service);
4561 CloseServiceHandle(scm);
4563 msi_free(name);
4564 msi_free(args);
4565 msi_free(vector);
4566 return r;
4569 static UINT ACTION_StartServices( MSIPACKAGE *package )
4571 UINT rc;
4572 MSIQUERY *view;
4574 static const WCHAR query[] = {
4575 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4576 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4578 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4579 if (rc != ERROR_SUCCESS)
4580 return ERROR_SUCCESS;
4582 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4583 msiobj_release(&view->hdr);
4585 return rc;
4588 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4590 DWORD i, needed, count;
4591 ENUM_SERVICE_STATUSW *dependencies;
4592 SERVICE_STATUS ss;
4593 SC_HANDLE depserv;
4595 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4596 0, &needed, &count))
4597 return TRUE;
4599 if (GetLastError() != ERROR_MORE_DATA)
4600 return FALSE;
4602 dependencies = msi_alloc(needed);
4603 if (!dependencies)
4604 return FALSE;
4606 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4607 needed, &needed, &count))
4608 goto error;
4610 for (i = 0; i < count; i++)
4612 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4613 SERVICE_STOP | SERVICE_QUERY_STATUS);
4614 if (!depserv)
4615 goto error;
4617 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4618 goto error;
4621 return TRUE;
4623 error:
4624 msi_free(dependencies);
4625 return FALSE;
4628 static UINT stop_service( LPCWSTR name )
4630 SC_HANDLE scm = NULL, service = NULL;
4631 SERVICE_STATUS status;
4632 SERVICE_STATUS_PROCESS ssp;
4633 DWORD needed;
4635 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4636 if (!scm)
4638 WARN("Failed to open the SCM: %d\n", GetLastError());
4639 goto done;
4642 service = OpenServiceW(scm, name,
4643 SERVICE_STOP |
4644 SERVICE_QUERY_STATUS |
4645 SERVICE_ENUMERATE_DEPENDENTS);
4646 if (!service)
4648 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4649 goto done;
4652 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4653 sizeof(SERVICE_STATUS_PROCESS), &needed))
4655 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4656 goto done;
4659 if (ssp.dwCurrentState == SERVICE_STOPPED)
4660 goto done;
4662 stop_service_dependents(scm, service);
4664 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4665 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4667 done:
4668 CloseServiceHandle(service);
4669 CloseServiceHandle(scm);
4671 return ERROR_SUCCESS;
4674 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4676 MSIPACKAGE *package = param;
4677 MSICOMPONENT *comp;
4678 LPWSTR name;
4679 DWORD event;
4681 event = MSI_RecordGetInteger( rec, 3 );
4682 if (!(event & msidbServiceControlEventStop))
4683 return ERROR_SUCCESS;
4685 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4686 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4687 return ERROR_SUCCESS;
4689 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4690 stop_service( name );
4691 msi_free( name );
4693 return ERROR_SUCCESS;
4696 static UINT ACTION_StopServices( MSIPACKAGE *package )
4698 UINT rc;
4699 MSIQUERY *view;
4701 static const WCHAR query[] = {
4702 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4703 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4705 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4706 if (rc != ERROR_SUCCESS)
4707 return ERROR_SUCCESS;
4709 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4710 msiobj_release(&view->hdr);
4712 return rc;
4715 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
4717 MSIPACKAGE *package = param;
4718 MSICOMPONENT *comp;
4719 LPWSTR name = NULL;
4720 DWORD event;
4721 SC_HANDLE scm = NULL, service = NULL;
4723 event = MSI_RecordGetInteger( rec, 3 );
4724 if (!(event & msidbServiceControlEventDelete))
4725 return ERROR_SUCCESS;
4727 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
4728 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4729 return ERROR_SUCCESS;
4731 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
4732 stop_service( name );
4734 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
4735 if (!scm)
4737 WARN("Failed to open the SCM: %d\n", GetLastError());
4738 goto done;
4741 service = OpenServiceW( scm, name, DELETE );
4742 if (!service)
4744 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
4745 goto done;
4748 if (!DeleteService( service ))
4749 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
4751 done:
4752 CloseServiceHandle( service );
4753 CloseServiceHandle( scm );
4754 msi_free( name );
4756 return ERROR_SUCCESS;
4759 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4761 UINT rc;
4762 MSIQUERY *view;
4764 static const WCHAR query[] = {
4765 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4766 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4768 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4769 if (rc != ERROR_SUCCESS)
4770 return ERROR_SUCCESS;
4772 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
4773 msiobj_release( &view->hdr );
4775 return rc;
4778 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4780 MSIFILE *file;
4782 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4784 if (!lstrcmpW(file->File, filename))
4785 return file;
4788 return NULL;
4791 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4793 MSIPACKAGE *package = param;
4794 LPWSTR driver, driver_path, ptr;
4795 WCHAR outpath[MAX_PATH];
4796 MSIFILE *driver_file, *setup_file;
4797 LPCWSTR desc;
4798 DWORD len, usage;
4799 UINT r = ERROR_SUCCESS;
4801 static const WCHAR driver_fmt[] = {
4802 'D','r','i','v','e','r','=','%','s',0};
4803 static const WCHAR setup_fmt[] = {
4804 'S','e','t','u','p','=','%','s',0};
4805 static const WCHAR usage_fmt[] = {
4806 'F','i','l','e','U','s','a','g','e','=','1',0};
4808 desc = MSI_RecordGetString(rec, 3);
4810 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4811 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4813 if (!driver_file || !setup_file)
4815 ERR("ODBC Driver entry not found!\n");
4816 return ERROR_FUNCTION_FAILED;
4819 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4820 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4821 lstrlenW(usage_fmt) + 1;
4822 driver = msi_alloc(len * sizeof(WCHAR));
4823 if (!driver)
4824 return ERROR_OUTOFMEMORY;
4826 ptr = driver;
4827 lstrcpyW(ptr, desc);
4828 ptr += lstrlenW(ptr) + 1;
4830 sprintfW(ptr, driver_fmt, driver_file->FileName);
4831 ptr += lstrlenW(ptr) + 1;
4833 sprintfW(ptr, setup_fmt, setup_file->FileName);
4834 ptr += lstrlenW(ptr) + 1;
4836 lstrcpyW(ptr, usage_fmt);
4837 ptr += lstrlenW(ptr) + 1;
4838 *ptr = '\0';
4840 driver_path = strdupW(driver_file->TargetPath);
4841 ptr = strrchrW(driver_path, '\\');
4842 if (ptr) *ptr = '\0';
4844 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4845 NULL, ODBC_INSTALL_COMPLETE, &usage))
4847 ERR("Failed to install SQL driver!\n");
4848 r = ERROR_FUNCTION_FAILED;
4851 msi_free(driver);
4852 msi_free(driver_path);
4854 return r;
4857 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4859 MSIPACKAGE *package = param;
4860 LPWSTR translator, translator_path, ptr;
4861 WCHAR outpath[MAX_PATH];
4862 MSIFILE *translator_file, *setup_file;
4863 LPCWSTR desc;
4864 DWORD len, usage;
4865 UINT r = ERROR_SUCCESS;
4867 static const WCHAR translator_fmt[] = {
4868 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4869 static const WCHAR setup_fmt[] = {
4870 'S','e','t','u','p','=','%','s',0};
4872 desc = MSI_RecordGetString(rec, 3);
4874 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4875 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4877 if (!translator_file || !setup_file)
4879 ERR("ODBC Translator entry not found!\n");
4880 return ERROR_FUNCTION_FAILED;
4883 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4884 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4885 translator = msi_alloc(len * sizeof(WCHAR));
4886 if (!translator)
4887 return ERROR_OUTOFMEMORY;
4889 ptr = translator;
4890 lstrcpyW(ptr, desc);
4891 ptr += lstrlenW(ptr) + 1;
4893 sprintfW(ptr, translator_fmt, translator_file->FileName);
4894 ptr += lstrlenW(ptr) + 1;
4896 sprintfW(ptr, setup_fmt, setup_file->FileName);
4897 ptr += lstrlenW(ptr) + 1;
4898 *ptr = '\0';
4900 translator_path = strdupW(translator_file->TargetPath);
4901 ptr = strrchrW(translator_path, '\\');
4902 if (ptr) *ptr = '\0';
4904 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4905 NULL, ODBC_INSTALL_COMPLETE, &usage))
4907 ERR("Failed to install SQL translator!\n");
4908 r = ERROR_FUNCTION_FAILED;
4911 msi_free(translator);
4912 msi_free(translator_path);
4914 return r;
4917 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4919 LPWSTR attrs;
4920 LPCWSTR desc, driver;
4921 WORD request = ODBC_ADD_SYS_DSN;
4922 INT registration;
4923 DWORD len;
4924 UINT r = ERROR_SUCCESS;
4926 static const WCHAR attrs_fmt[] = {
4927 'D','S','N','=','%','s',0 };
4929 desc = MSI_RecordGetString(rec, 3);
4930 driver = MSI_RecordGetString(rec, 4);
4931 registration = MSI_RecordGetInteger(rec, 5);
4933 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4934 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4936 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4937 attrs = msi_alloc(len * sizeof(WCHAR));
4938 if (!attrs)
4939 return ERROR_OUTOFMEMORY;
4941 sprintfW(attrs, attrs_fmt, desc);
4942 attrs[len - 1] = '\0';
4944 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4946 ERR("Failed to install SQL data source!\n");
4947 r = ERROR_FUNCTION_FAILED;
4950 msi_free(attrs);
4952 return r;
4955 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4957 UINT rc;
4958 MSIQUERY *view;
4960 static const WCHAR driver_query[] = {
4961 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4962 'O','D','B','C','D','r','i','v','e','r',0 };
4964 static const WCHAR translator_query[] = {
4965 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4966 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4968 static const WCHAR source_query[] = {
4969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4970 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4972 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4973 if (rc != ERROR_SUCCESS)
4974 return ERROR_SUCCESS;
4976 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4977 msiobj_release(&view->hdr);
4979 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4980 if (rc != ERROR_SUCCESS)
4981 return ERROR_SUCCESS;
4983 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4984 msiobj_release(&view->hdr);
4986 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
4987 if (rc != ERROR_SUCCESS)
4988 return ERROR_SUCCESS;
4990 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
4991 msiobj_release(&view->hdr);
4993 return rc;
4996 #define ENV_ACT_SETALWAYS 0x1
4997 #define ENV_ACT_SETABSENT 0x2
4998 #define ENV_ACT_REMOVE 0x4
4999 #define ENV_ACT_REMOVEMATCH 0x8
5001 #define ENV_MOD_MACHINE 0x20000000
5002 #define ENV_MOD_APPEND 0x40000000
5003 #define ENV_MOD_PREFIX 0x80000000
5004 #define ENV_MOD_MASK 0xC0000000
5006 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5008 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5010 LPCWSTR cptr = *name;
5012 static const WCHAR prefix[] = {'[','~',']',0};
5013 static const int prefix_len = 3;
5015 *flags = 0;
5016 while (*cptr)
5018 if (*cptr == '=')
5019 *flags |= ENV_ACT_SETALWAYS;
5020 else if (*cptr == '+')
5021 *flags |= ENV_ACT_SETABSENT;
5022 else if (*cptr == '-')
5023 *flags |= ENV_ACT_REMOVE;
5024 else if (*cptr == '!')
5025 *flags |= ENV_ACT_REMOVEMATCH;
5026 else if (*cptr == '*')
5027 *flags |= ENV_MOD_MACHINE;
5028 else
5029 break;
5031 cptr++;
5032 (*name)++;
5035 if (!*cptr)
5037 ERR("Missing environment variable\n");
5038 return ERROR_FUNCTION_FAILED;
5041 if (*value)
5043 LPCWSTR ptr = *value;
5044 if (!strncmpW(ptr, prefix, prefix_len))
5046 if (ptr[prefix_len] == szSemiColon[0])
5048 *flags |= ENV_MOD_APPEND;
5049 *value += lstrlenW(prefix);
5051 else
5053 *value = NULL;
5056 else if (lstrlenW(*value) >= prefix_len)
5058 ptr += lstrlenW(ptr) - prefix_len;
5059 if (!lstrcmpW(ptr, prefix))
5061 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5063 *flags |= ENV_MOD_PREFIX;
5064 /* the "[~]" will be removed by deformat_string */;
5066 else
5068 *value = NULL;
5074 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5075 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5076 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5077 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5079 ERR("Invalid flags: %08x\n", *flags);
5080 return ERROR_FUNCTION_FAILED;
5083 if (!*flags)
5084 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5086 return ERROR_SUCCESS;
5089 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5091 MSIPACKAGE *package = param;
5092 LPCWSTR name, value;
5093 LPWSTR data = NULL, newval = NULL;
5094 LPWSTR deformatted = NULL, ptr;
5095 DWORD flags, type, size;
5096 LONG res;
5097 HKEY env = NULL, root;
5098 LPCWSTR environment;
5100 static const WCHAR user_env[] =
5101 {'E','n','v','i','r','o','n','m','e','n','t',0};
5102 static const WCHAR machine_env[] =
5103 {'S','y','s','t','e','m','\\',
5104 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5105 'C','o','n','t','r','o','l','\\',
5106 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5107 'E','n','v','i','r','o','n','m','e','n','t',0};
5109 name = MSI_RecordGetString(rec, 2);
5110 value = MSI_RecordGetString(rec, 3);
5112 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5114 res = env_set_flags(&name, &value, &flags);
5115 if (res != ERROR_SUCCESS || !value)
5116 goto done;
5118 if (value && !deformat_string(package, value, &deformatted))
5120 res = ERROR_OUTOFMEMORY;
5121 goto done;
5124 value = deformatted;
5126 if (flags & ENV_MOD_MACHINE)
5128 environment = machine_env;
5129 root = HKEY_LOCAL_MACHINE;
5131 else
5133 environment = user_env;
5134 root = HKEY_CURRENT_USER;
5137 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5138 KEY_ALL_ACCESS, NULL, &env, NULL);
5139 if (res != ERROR_SUCCESS)
5140 goto done;
5142 if (flags & ENV_ACT_REMOVE)
5143 FIXME("Not removing environment variable on uninstall!\n");
5145 size = 0;
5146 type = REG_SZ;
5147 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5148 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5149 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5150 goto done;
5152 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5154 /* Nothing to do. */
5155 if (!value)
5157 res = ERROR_SUCCESS;
5158 goto done;
5161 /* If we are appending but the string was empty, strip ; */
5162 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5164 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5165 newval = strdupW(value);
5166 if (!newval)
5168 res = ERROR_OUTOFMEMORY;
5169 goto done;
5172 else
5174 /* Contrary to MSDN, +-variable to [~];path works */
5175 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5177 res = ERROR_SUCCESS;
5178 goto done;
5181 data = msi_alloc(size);
5182 if (!data)
5184 RegCloseKey(env);
5185 return ERROR_OUTOFMEMORY;
5188 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5189 if (res != ERROR_SUCCESS)
5190 goto done;
5192 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5194 res = RegDeleteKeyW(env, name);
5195 goto done;
5198 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5199 if (flags & ENV_MOD_MASK)
5201 DWORD mod_size;
5202 int multiplier = 0;
5203 if (flags & ENV_MOD_APPEND) multiplier++;
5204 if (flags & ENV_MOD_PREFIX) multiplier++;
5205 mod_size = lstrlenW(value) * multiplier;
5206 size += mod_size * sizeof(WCHAR);
5209 newval = msi_alloc(size);
5210 ptr = newval;
5211 if (!newval)
5213 res = ERROR_OUTOFMEMORY;
5214 goto done;
5217 if (flags & ENV_MOD_PREFIX)
5219 lstrcpyW(newval, value);
5220 ptr = newval + lstrlenW(value);
5223 lstrcpyW(ptr, data);
5225 if (flags & ENV_MOD_APPEND)
5227 lstrcatW(newval, value);
5230 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5231 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5233 done:
5234 if (env) RegCloseKey(env);
5235 msi_free(deformatted);
5236 msi_free(data);
5237 msi_free(newval);
5238 return res;
5241 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5243 UINT rc;
5244 MSIQUERY * view;
5245 static const WCHAR ExecSeqQuery[] =
5246 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5247 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5248 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5249 if (rc != ERROR_SUCCESS)
5250 return ERROR_SUCCESS;
5252 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5253 msiobj_release(&view->hdr);
5255 return rc;
5258 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5260 typedef struct
5262 struct list entry;
5263 LPWSTR sourcename;
5264 LPWSTR destname;
5265 LPWSTR source;
5266 LPWSTR dest;
5267 } FILE_LIST;
5269 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5271 BOOL ret;
5273 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5274 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5276 WARN("Source or dest is directory, not moving\n");
5277 return FALSE;
5280 if (options == msidbMoveFileOptionsMove)
5282 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5283 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5284 if (!ret)
5286 WARN("MoveFile failed: %d\n", GetLastError());
5287 return FALSE;
5290 else
5292 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5293 ret = CopyFileW(source, dest, FALSE);
5294 if (!ret)
5296 WARN("CopyFile failed: %d\n", GetLastError());
5297 return FALSE;
5301 return TRUE;
5304 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5306 LPWSTR path, ptr;
5307 DWORD dirlen, pathlen;
5309 ptr = strrchrW(wildcard, '\\');
5310 dirlen = ptr - wildcard + 1;
5312 pathlen = dirlen + lstrlenW(filename) + 1;
5313 path = msi_alloc(pathlen * sizeof(WCHAR));
5315 lstrcpynW(path, wildcard, dirlen + 1);
5316 lstrcatW(path, filename);
5318 return path;
5321 static void free_file_entry(FILE_LIST *file)
5323 msi_free(file->source);
5324 msi_free(file->dest);
5325 msi_free(file);
5328 static void free_list(FILE_LIST *list)
5330 while (!list_empty(&list->entry))
5332 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5334 list_remove(&file->entry);
5335 free_file_entry(file);
5339 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5341 FILE_LIST *new, *file;
5342 LPWSTR ptr, filename;
5343 DWORD size;
5345 new = msi_alloc_zero(sizeof(FILE_LIST));
5346 if (!new)
5347 return FALSE;
5349 new->source = strdupW(source);
5350 ptr = strrchrW(dest, '\\') + 1;
5351 filename = strrchrW(new->source, '\\') + 1;
5353 new->sourcename = filename;
5355 if (*ptr)
5356 new->destname = ptr;
5357 else
5358 new->destname = new->sourcename;
5360 size = (ptr - dest) + lstrlenW(filename) + 1;
5361 new->dest = msi_alloc(size * sizeof(WCHAR));
5362 if (!new->dest)
5364 free_file_entry(new);
5365 return FALSE;
5368 lstrcpynW(new->dest, dest, ptr - dest + 1);
5369 lstrcatW(new->dest, filename);
5371 if (list_empty(&files->entry))
5373 list_add_head(&files->entry, &new->entry);
5374 return TRUE;
5377 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5379 if (lstrcmpW(source, file->source) < 0)
5381 list_add_before(&file->entry, &new->entry);
5382 return TRUE;
5386 list_add_after(&file->entry, &new->entry);
5387 return TRUE;
5390 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5392 WIN32_FIND_DATAW wfd;
5393 HANDLE hfile;
5394 LPWSTR path;
5395 BOOL res;
5396 FILE_LIST files, *file;
5397 DWORD size;
5399 hfile = FindFirstFileW(source, &wfd);
5400 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5402 list_init(&files.entry);
5404 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5406 if (is_dot_dir(wfd.cFileName)) continue;
5408 path = wildcard_to_file(source, wfd.cFileName);
5409 if (!path)
5411 res = FALSE;
5412 goto done;
5415 add_wildcard(&files, path, dest);
5416 msi_free(path);
5419 /* no files match the wildcard */
5420 if (list_empty(&files.entry))
5421 goto done;
5423 /* only the first wildcard match gets renamed to dest */
5424 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5425 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5426 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5427 if (!file->dest)
5429 res = FALSE;
5430 goto done;
5433 /* file->dest may be shorter after the reallocation, so add a NULL
5434 * terminator. This is needed for the call to strrchrW, as there will no
5435 * longer be a NULL terminator within the bounds of the allocation in this case.
5437 file->dest[size - 1] = '\0';
5438 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5440 while (!list_empty(&files.entry))
5442 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5444 msi_move_file(file->source, file->dest, options);
5446 list_remove(&file->entry);
5447 free_file_entry(file);
5450 res = TRUE;
5452 done:
5453 free_list(&files);
5454 FindClose(hfile);
5455 return res;
5458 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5460 MSIPACKAGE *package = param;
5461 MSICOMPONENT *comp;
5462 LPCWSTR sourcename;
5463 LPWSTR destname = NULL;
5464 LPWSTR sourcedir = NULL, destdir = NULL;
5465 LPWSTR source = NULL, dest = NULL;
5466 int options;
5467 DWORD size;
5468 BOOL ret, wildcards;
5470 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5471 if (!comp || !comp->Enabled ||
5472 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5474 TRACE("Component not set for install, not moving file\n");
5475 return ERROR_SUCCESS;
5478 sourcename = MSI_RecordGetString(rec, 3);
5479 options = MSI_RecordGetInteger(rec, 7);
5481 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5482 if (!sourcedir)
5483 goto done;
5485 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5486 if (!destdir)
5487 goto done;
5489 if (!sourcename)
5491 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5492 goto done;
5494 source = strdupW(sourcedir);
5495 if (!source)
5496 goto done;
5498 else
5500 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5501 source = msi_alloc(size * sizeof(WCHAR));
5502 if (!source)
5503 goto done;
5505 lstrcpyW(source, sourcedir);
5506 if (source[lstrlenW(source) - 1] != '\\')
5507 lstrcatW(source, szBackSlash);
5508 lstrcatW(source, sourcename);
5511 wildcards = strchrW(source, '*') || strchrW(source, '?');
5513 if (MSI_RecordIsNull(rec, 4))
5515 if (!wildcards)
5517 destname = strdupW(sourcename);
5518 if (!destname)
5519 goto done;
5522 else
5524 destname = strdupW(MSI_RecordGetString(rec, 4));
5525 if (destname)
5526 reduce_to_longfilename(destname);
5529 size = 0;
5530 if (destname)
5531 size = lstrlenW(destname);
5533 size += lstrlenW(destdir) + 2;
5534 dest = msi_alloc(size * sizeof(WCHAR));
5535 if (!dest)
5536 goto done;
5538 lstrcpyW(dest, destdir);
5539 if (dest[lstrlenW(dest) - 1] != '\\')
5540 lstrcatW(dest, szBackSlash);
5542 if (destname)
5543 lstrcatW(dest, destname);
5545 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5547 ret = CreateDirectoryW(destdir, NULL);
5548 if (!ret)
5550 WARN("CreateDirectory failed: %d\n", GetLastError());
5551 return ERROR_SUCCESS;
5555 if (!wildcards)
5556 msi_move_file(source, dest, options);
5557 else
5558 move_files_wildcard(source, dest, options);
5560 done:
5561 msi_free(sourcedir);
5562 msi_free(destdir);
5563 msi_free(destname);
5564 msi_free(source);
5565 msi_free(dest);
5567 return ERROR_SUCCESS;
5570 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5572 UINT rc;
5573 MSIQUERY *view;
5575 static const WCHAR ExecSeqQuery[] =
5576 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5577 '`','M','o','v','e','F','i','l','e','`',0};
5579 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5580 if (rc != ERROR_SUCCESS)
5581 return ERROR_SUCCESS;
5583 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5584 msiobj_release(&view->hdr);
5586 return rc;
5589 typedef struct tagMSIASSEMBLY
5591 struct list entry;
5592 MSICOMPONENT *component;
5593 MSIFEATURE *feature;
5594 MSIFILE *file;
5595 LPWSTR manifest;
5596 LPWSTR application;
5597 DWORD attributes;
5598 BOOL installed;
5599 } MSIASSEMBLY;
5601 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5602 DWORD dwReserved);
5603 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5604 LPVOID pvReserved, HMODULE *phModDll);
5606 static BOOL init_functionpointers(void)
5608 HRESULT hr;
5609 HMODULE hfusion;
5610 HMODULE hmscoree;
5612 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5614 hmscoree = LoadLibraryA("mscoree.dll");
5615 if (!hmscoree)
5617 WARN("mscoree.dll not available\n");
5618 return FALSE;
5621 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5622 if (!pLoadLibraryShim)
5624 WARN("LoadLibraryShim not available\n");
5625 FreeLibrary(hmscoree);
5626 return FALSE;
5629 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5630 if (FAILED(hr))
5632 WARN("fusion.dll not available\n");
5633 FreeLibrary(hmscoree);
5634 return FALSE;
5637 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5639 FreeLibrary(hmscoree);
5640 return TRUE;
5643 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5644 LPWSTR path)
5646 IAssemblyCache *cache;
5647 HRESULT hr;
5648 UINT r = ERROR_FUNCTION_FAILED;
5650 TRACE("installing assembly: %s\n", debugstr_w(path));
5652 if (assembly->feature)
5653 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5655 if (assembly->manifest)
5656 FIXME("Manifest unhandled\n");
5658 if (assembly->application)
5660 FIXME("Assembly should be privately installed\n");
5661 return ERROR_SUCCESS;
5664 if (assembly->attributes == msidbAssemblyAttributesWin32)
5666 FIXME("Win32 assemblies not handled\n");
5667 return ERROR_SUCCESS;
5670 hr = pCreateAssemblyCache(&cache, 0);
5671 if (FAILED(hr))
5672 goto done;
5674 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5675 if (FAILED(hr))
5676 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5678 r = ERROR_SUCCESS;
5680 done:
5681 IAssemblyCache_Release(cache);
5682 return r;
5685 typedef struct tagASSEMBLY_LIST
5687 MSIPACKAGE *package;
5688 IAssemblyCache *cache;
5689 struct list *assemblies;
5690 } ASSEMBLY_LIST;
5692 typedef struct tagASSEMBLY_NAME
5694 LPWSTR name;
5695 LPWSTR version;
5696 LPWSTR culture;
5697 LPWSTR pubkeytoken;
5698 } ASSEMBLY_NAME;
5700 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5702 ASSEMBLY_NAME *asmname = param;
5703 LPCWSTR name = MSI_RecordGetString(rec, 2);
5704 LPWSTR val = msi_dup_record_field(rec, 3);
5706 static const WCHAR Name[] = {'N','a','m','e',0};
5707 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5708 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5709 static const WCHAR PublicKeyToken[] = {
5710 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5712 if (!strcmpiW(name, Name))
5713 asmname->name = val;
5714 else if (!strcmpiW(name, Version))
5715 asmname->version = val;
5716 else if (!strcmpiW(name, Culture))
5717 asmname->culture = val;
5718 else if (!strcmpiW(name, PublicKeyToken))
5719 asmname->pubkeytoken = val;
5720 else
5721 msi_free(val);
5723 return ERROR_SUCCESS;
5726 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5728 if (!*str)
5730 *size = lstrlenW(append) + 1;
5731 *str = msi_alloc((*size) * sizeof(WCHAR));
5732 lstrcpyW(*str, append);
5733 return;
5736 (*size) += lstrlenW(append);
5737 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5738 lstrcatW(*str, append);
5741 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5742 MSICOMPONENT *comp)
5744 ASSEMBLY_INFO asminfo;
5745 ASSEMBLY_NAME name;
5746 MSIQUERY *view;
5747 LPWSTR disp;
5748 DWORD size;
5749 BOOL found;
5750 UINT r;
5752 static const WCHAR separator[] = {',',' ',0};
5753 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5754 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5755 static const WCHAR PublicKeyToken[] = {
5756 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5757 static const WCHAR query[] = {
5758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5759 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5760 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5761 '=','\'','%','s','\'',0};
5763 disp = NULL;
5764 found = FALSE;
5765 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5766 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5768 r = MSI_OpenQuery(db, &view, query, comp->Component);
5769 if (r != ERROR_SUCCESS)
5770 return ERROR_SUCCESS;
5772 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5773 msiobj_release(&view->hdr);
5775 if (!name.name)
5777 ERR("No assembly name specified!\n");
5778 goto done;
5781 append_str(&disp, &size, name.name);
5783 if (name.version)
5785 append_str(&disp, &size, separator);
5786 append_str(&disp, &size, Version);
5787 append_str(&disp, &size, name.version);
5790 if (name.culture)
5792 append_str(&disp, &size, separator);
5793 append_str(&disp, &size, Culture);
5794 append_str(&disp, &size, name.culture);
5797 if (name.pubkeytoken)
5799 append_str(&disp, &size, separator);
5800 append_str(&disp, &size, PublicKeyToken);
5801 append_str(&disp, &size, name.pubkeytoken);
5804 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5805 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5806 disp, &asminfo);
5807 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5809 done:
5810 msi_free(disp);
5811 msi_free(name.name);
5812 msi_free(name.version);
5813 msi_free(name.culture);
5814 msi_free(name.pubkeytoken);
5816 return found;
5819 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5821 ASSEMBLY_LIST *list = param;
5822 MSIASSEMBLY *assembly;
5824 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5825 if (!assembly)
5826 return ERROR_OUTOFMEMORY;
5828 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5830 if (!assembly->component || !assembly->component->Enabled ||
5831 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5833 TRACE("Component not set for install, not publishing assembly\n");
5834 msi_free(assembly);
5835 return ERROR_SUCCESS;
5838 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5839 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5841 if (!assembly->file)
5843 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5844 return ERROR_FUNCTION_FAILED;
5847 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5848 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5849 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5851 if (assembly->application)
5853 WCHAR version[24];
5854 DWORD size = sizeof(version)/sizeof(WCHAR);
5856 /* FIXME: we should probably check the manifest file here */
5858 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
5859 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
5861 assembly->installed = TRUE;
5864 else
5865 assembly->installed = check_assembly_installed(list->package->db,
5866 list->cache,
5867 assembly->component);
5869 list_add_head(list->assemblies, &assembly->entry);
5870 return ERROR_SUCCESS;
5873 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5875 IAssemblyCache *cache = NULL;
5876 ASSEMBLY_LIST list;
5877 MSIQUERY *view;
5878 HRESULT hr;
5879 UINT r;
5881 static const WCHAR query[] =
5882 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5883 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5885 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5886 if (r != ERROR_SUCCESS)
5887 return ERROR_SUCCESS;
5889 hr = pCreateAssemblyCache(&cache, 0);
5890 if (FAILED(hr))
5891 return ERROR_FUNCTION_FAILED;
5893 list.package = package;
5894 list.cache = cache;
5895 list.assemblies = assemblies;
5897 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5898 msiobj_release(&view->hdr);
5900 IAssemblyCache_Release(cache);
5902 return r;
5905 static void free_assemblies(struct list *assemblies)
5907 struct list *item, *cursor;
5909 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5911 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5913 list_remove(&assembly->entry);
5914 msi_free(assembly->application);
5915 msi_free(assembly->manifest);
5916 msi_free(assembly);
5920 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5922 MSIASSEMBLY *assembly;
5924 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5926 if (!lstrcmpW(assembly->file->File, file))
5928 *out = assembly;
5929 return TRUE;
5933 return FALSE;
5936 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5937 LPWSTR *path, DWORD *attrs, PVOID user)
5939 MSIASSEMBLY *assembly;
5940 WCHAR temppath[MAX_PATH];
5941 struct list *assemblies = user;
5942 UINT r;
5944 if (!find_assembly(assemblies, file, &assembly))
5945 return FALSE;
5947 GetTempPathW(MAX_PATH, temppath);
5948 PathAddBackslashW(temppath);
5949 lstrcatW(temppath, assembly->file->FileName);
5951 if (action == MSICABEXTRACT_BEGINEXTRACT)
5953 if (assembly->installed)
5954 return FALSE;
5956 *path = strdupW(temppath);
5957 *attrs = assembly->file->Attributes;
5959 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5961 assembly->installed = TRUE;
5963 r = install_assembly(package, assembly, temppath);
5964 if (r != ERROR_SUCCESS)
5965 ERR("Failed to install assembly\n");
5968 return TRUE;
5971 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5973 UINT r;
5974 struct list assemblies = LIST_INIT(assemblies);
5975 MSIASSEMBLY *assembly;
5976 MSIMEDIAINFO *mi;
5978 if (!init_functionpointers() || !pCreateAssemblyCache)
5979 return ERROR_FUNCTION_FAILED;
5981 r = load_assemblies(package, &assemblies);
5982 if (r != ERROR_SUCCESS)
5983 goto done;
5985 if (list_empty(&assemblies))
5986 goto done;
5988 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5989 if (!mi)
5991 r = ERROR_OUTOFMEMORY;
5992 goto done;
5995 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5997 if (assembly->installed && !mi->is_continuous)
5998 continue;
6000 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6001 (assembly->file->IsCompressed && !mi->is_extracted))
6003 MSICABDATA data;
6005 r = ready_media(package, assembly->file, mi);
6006 if (r != ERROR_SUCCESS)
6008 ERR("Failed to ready media\n");
6009 break;
6012 data.mi = mi;
6013 data.package = package;
6014 data.cb = installassembly_cb;
6015 data.user = &assemblies;
6017 if (assembly->file->IsCompressed &&
6018 !msi_cabextract(package, mi, &data))
6020 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6021 r = ERROR_FUNCTION_FAILED;
6022 break;
6026 if (!assembly->file->IsCompressed)
6028 LPWSTR source = resolve_file_source(package, assembly->file);
6030 r = install_assembly(package, assembly, source);
6031 if (r != ERROR_SUCCESS)
6032 ERR("Failed to install assembly\n");
6034 msi_free(source);
6037 /* FIXME: write Installer assembly reg values */
6040 done:
6041 free_assemblies(&assemblies);
6042 return r;
6045 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6047 TRACE("\n");
6048 package->need_reboot = 1;
6049 return ERROR_SUCCESS;
6052 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6053 LPCSTR action, LPCWSTR table )
6055 static const WCHAR query[] = {
6056 'S','E','L','E','C','T',' ','*',' ',
6057 'F','R','O','M',' ','`','%','s','`',0 };
6058 MSIQUERY *view = NULL;
6059 DWORD count = 0;
6060 UINT r;
6062 r = MSI_OpenQuery( package->db, &view, query, table );
6063 if (r == ERROR_SUCCESS)
6065 r = MSI_IterateRecords(view, &count, NULL, package);
6066 msiobj_release(&view->hdr);
6069 if (count)
6070 FIXME("%s -> %u ignored %s table values\n",
6071 action, count, debugstr_w(table));
6073 return ERROR_SUCCESS;
6076 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6078 TRACE("%p\n", package);
6079 return ERROR_SUCCESS;
6082 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6084 static const WCHAR table[] =
6085 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6086 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6089 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6091 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6092 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6095 static UINT ACTION_BindImage( MSIPACKAGE *package )
6097 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6098 return msi_unimplemented_action_stub( package, "BindImage", table );
6101 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6103 static const WCHAR table[] = {
6104 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6105 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6108 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6110 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6111 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6114 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6116 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6117 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6120 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6122 static const WCHAR table[] = {
6123 'P','r','o','d','u','c','t','I','D',0 };
6124 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6127 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6129 static const WCHAR table[] = {
6130 'E','n','v','i','r','o','n','m','e','n','t',0 };
6131 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6134 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6136 static const WCHAR table[] = {
6137 'M','s','i','A','s','s','e','m','b','l','y',0 };
6138 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6141 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6143 static const WCHAR table[] = { 'F','o','n','t',0 };
6144 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6147 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6149 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6150 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6153 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6155 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6156 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6159 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6161 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6162 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6165 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6167 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6168 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6171 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6173 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6174 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6177 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6179 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6180 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6183 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6185 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6186 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6189 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6191 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6192 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6195 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6197 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6198 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6201 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6203 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6204 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6207 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6209 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6210 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6213 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6215 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6216 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6219 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6221 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6222 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6225 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6227 static const WCHAR table[] = { 'M','I','M','E',0 };
6228 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6231 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6233 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6234 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6237 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6239 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6240 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6243 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6245 static const struct
6247 const WCHAR *action;
6248 UINT (*handler)(MSIPACKAGE *);
6250 StandardActions[] =
6252 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6253 { szAppSearch, ACTION_AppSearch },
6254 { szBindImage, ACTION_BindImage },
6255 { szCCPSearch, ACTION_CCPSearch },
6256 { szCostFinalize, ACTION_CostFinalize },
6257 { szCostInitialize, ACTION_CostInitialize },
6258 { szCreateFolders, ACTION_CreateFolders },
6259 { szCreateShortcuts, ACTION_CreateShortcuts },
6260 { szDeleteServices, ACTION_DeleteServices },
6261 { szDisableRollback, NULL },
6262 { szDuplicateFiles, ACTION_DuplicateFiles },
6263 { szExecuteAction, ACTION_ExecuteAction },
6264 { szFileCost, ACTION_FileCost },
6265 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6266 { szForceReboot, ACTION_ForceReboot },
6267 { szInstallAdminPackage, NULL },
6268 { szInstallExecute, ACTION_InstallExecute },
6269 { szInstallExecuteAgain, ACTION_InstallExecute },
6270 { szInstallFiles, ACTION_InstallFiles},
6271 { szInstallFinalize, ACTION_InstallFinalize },
6272 { szInstallInitialize, ACTION_InstallInitialize },
6273 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6274 { szInstallValidate, ACTION_InstallValidate },
6275 { szIsolateComponents, ACTION_IsolateComponents },
6276 { szLaunchConditions, ACTION_LaunchConditions },
6277 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6278 { szMoveFiles, ACTION_MoveFiles },
6279 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6280 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6281 { szInstallODBC, ACTION_InstallODBC },
6282 { szInstallServices, ACTION_InstallServices },
6283 { szPatchFiles, ACTION_PatchFiles },
6284 { szProcessComponents, ACTION_ProcessComponents },
6285 { szPublishComponents, ACTION_PublishComponents },
6286 { szPublishFeatures, ACTION_PublishFeatures },
6287 { szPublishProduct, ACTION_PublishProduct },
6288 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6289 { szRegisterComPlus, ACTION_RegisterComPlus},
6290 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6291 { szRegisterFonts, ACTION_RegisterFonts },
6292 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6293 { szRegisterProduct, ACTION_RegisterProduct },
6294 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6295 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6296 { szRegisterUser, ACTION_RegisterUser },
6297 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6298 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6299 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6300 { szRemoveFiles, ACTION_RemoveFiles },
6301 { szRemoveFolders, ACTION_RemoveFolders },
6302 { szRemoveIniValues, ACTION_RemoveIniValues },
6303 { szRemoveODBC, ACTION_RemoveODBC },
6304 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6305 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6306 { szResolveSource, ACTION_ResolveSource },
6307 { szRMCCPSearch, ACTION_RMCCPSearch },
6308 { szScheduleReboot, ACTION_ScheduleReboot },
6309 { szSelfRegModules, ACTION_SelfRegModules },
6310 { szSelfUnregModules, ACTION_SelfUnregModules },
6311 { szSetODBCFolders, ACTION_SetODBCFolders },
6312 { szStartServices, ACTION_StartServices },
6313 { szStopServices, ACTION_StopServices },
6314 { szUnpublishComponents, ACTION_UnpublishComponents },
6315 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6316 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6317 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6318 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6319 { szUnregisterFonts, ACTION_UnregisterFonts },
6320 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6321 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6322 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6323 { szValidateProductID, ACTION_ValidateProductID },
6324 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6325 { szWriteIniValues, ACTION_WriteIniValues },
6326 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6327 { NULL, NULL },
6330 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6331 UINT* rc, BOOL force )
6333 BOOL ret = FALSE;
6334 BOOL run = force;
6335 int i;
6337 if (!run && !package->script->CurrentlyScripting)
6338 run = TRUE;
6340 if (!run)
6342 if (strcmpW(action,szInstallFinalize) == 0 ||
6343 strcmpW(action,szInstallExecute) == 0 ||
6344 strcmpW(action,szInstallExecuteAgain) == 0)
6345 run = TRUE;
6348 i = 0;
6349 while (StandardActions[i].action != NULL)
6351 if (strcmpW(StandardActions[i].action, action)==0)
6353 if (!run)
6355 ui_actioninfo(package, action, TRUE, 0);
6356 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6357 ui_actioninfo(package, action, FALSE, *rc);
6359 else
6361 ui_actionstart(package, action);
6362 if (StandardActions[i].handler)
6364 *rc = StandardActions[i].handler(package);
6366 else
6368 FIXME("unhandled standard action %s\n",debugstr_w(action));
6369 *rc = ERROR_SUCCESS;
6372 ret = TRUE;
6373 break;
6375 i++;
6377 return ret;
6380 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6382 UINT rc = ERROR_SUCCESS;
6383 BOOL handled;
6385 TRACE("Performing action (%s)\n", debugstr_w(action));
6387 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6389 if (!handled)
6390 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6392 if (!handled)
6394 WARN("unhandled msi action %s\n", debugstr_w(action));
6395 rc = ERROR_FUNCTION_NOT_CALLED;
6398 return rc;
6401 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6403 UINT rc = ERROR_SUCCESS;
6404 BOOL handled = FALSE;
6406 TRACE("Performing action (%s)\n", debugstr_w(action));
6408 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6410 if (!handled)
6411 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6413 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6414 handled = TRUE;
6416 if (!handled)
6418 WARN("unhandled msi action %s\n", debugstr_w(action));
6419 rc = ERROR_FUNCTION_NOT_CALLED;
6422 return rc;
6425 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6427 UINT rc = ERROR_SUCCESS;
6428 MSIRECORD *row;
6430 static const WCHAR ExecSeqQuery[] =
6431 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6432 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6433 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6434 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6435 static const WCHAR UISeqQuery[] =
6436 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6437 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6438 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6439 ' ', '=',' ','%','i',0};
6441 if (needs_ui_sequence(package))
6442 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6443 else
6444 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6446 if (row)
6448 LPCWSTR action, cond;
6450 TRACE("Running the actions\n");
6452 /* check conditions */
6453 cond = MSI_RecordGetString(row, 2);
6455 /* this is a hack to skip errors in the condition code */
6456 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6458 msiobj_release(&row->hdr);
6459 return ERROR_SUCCESS;
6462 action = MSI_RecordGetString(row, 1);
6463 if (!action)
6465 ERR("failed to fetch action\n");
6466 msiobj_release(&row->hdr);
6467 return ERROR_FUNCTION_FAILED;
6470 if (needs_ui_sequence(package))
6471 rc = ACTION_PerformUIAction(package, action, -1);
6472 else
6473 rc = ACTION_PerformAction(package, action, -1, FALSE);
6475 msiobj_release(&row->hdr);
6478 return rc;
6481 /****************************************************
6482 * TOP level entry points
6483 *****************************************************/
6485 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6486 LPCWSTR szCommandLine )
6488 UINT rc;
6489 BOOL ui_exists;
6491 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6492 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6494 MSI_SetPropertyW(package, szAction, szInstall);
6496 package->script->InWhatSequence = SEQUENCE_INSTALL;
6498 if (szPackagePath)
6500 LPWSTR p, dir;
6501 LPCWSTR file;
6503 dir = strdupW(szPackagePath);
6504 p = strrchrW(dir, '\\');
6505 if (p)
6507 *(++p) = 0;
6508 file = szPackagePath + (p - dir);
6510 else
6512 msi_free(dir);
6513 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6514 GetCurrentDirectoryW(MAX_PATH, dir);
6515 lstrcatW(dir, szBackSlash);
6516 file = szPackagePath;
6519 msi_free( package->PackagePath );
6520 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6521 if (!package->PackagePath)
6523 msi_free(dir);
6524 return ERROR_OUTOFMEMORY;
6527 lstrcpyW(package->PackagePath, dir);
6528 lstrcatW(package->PackagePath, file);
6529 msi_free(dir);
6531 msi_set_sourcedir_props(package, FALSE);
6534 msi_parse_command_line( package, szCommandLine, FALSE );
6536 msi_apply_transforms( package );
6537 msi_apply_patches( package );
6539 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6541 TRACE("setting reinstall property\n");
6542 MSI_SetPropertyW( package, szReinstall, szAll );
6545 /* properties may have been added by a transform */
6546 msi_clone_properties( package );
6547 msi_set_context( package );
6549 if (needs_ui_sequence( package))
6551 package->script->InWhatSequence |= SEQUENCE_UI;
6552 rc = ACTION_ProcessUISequence(package);
6553 ui_exists = ui_sequence_exists(package);
6554 if (rc == ERROR_SUCCESS || !ui_exists)
6556 package->script->InWhatSequence |= SEQUENCE_EXEC;
6557 rc = ACTION_ProcessExecSequence(package, ui_exists);
6560 else
6561 rc = ACTION_ProcessExecSequence(package, FALSE);
6563 package->script->CurrentlyScripting = FALSE;
6565 /* process the ending type action */
6566 if (rc == ERROR_SUCCESS)
6567 ACTION_PerformActionSequence(package, -1);
6568 else if (rc == ERROR_INSTALL_USEREXIT)
6569 ACTION_PerformActionSequence(package, -2);
6570 else if (rc == ERROR_INSTALL_SUSPEND)
6571 ACTION_PerformActionSequence(package, -4);
6572 else /* failed */
6573 ACTION_PerformActionSequence(package, -3);
6575 /* finish up running custom actions */
6576 ACTION_FinishCustomActions(package);
6578 if (rc == ERROR_SUCCESS && package->need_reboot)
6579 return ERROR_SUCCESS_REBOOT_REQUIRED;
6581 return rc;