msi: Publish the UpgradeCode in PublishProduct.
[wine/gsoc_dplay.git] / dlls / msi / action.c
blob6761b39cf8484f8f49515920d6e46387c86e8ae4
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 * Prototypes
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137 'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163 'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211 'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223 'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
236 struct _actions {
237 LPCWSTR action;
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
243 * helper functions
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
253 MSIRECORD * row;
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
256 if (!row)
257 return;
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
263 UINT rc)
265 MSIRECORD * row;
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268 '%','s', '.',0};
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272 '%','i','.',0};
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 WCHAR message[1024];
276 WCHAR timet[0x100];
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279 if (start)
280 sprintfW(message,template_s,timet,action);
281 else
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
293 LPCWSTR ptr,ptr2;
294 BOOL quote;
295 DWORD len;
296 LPWSTR prop = NULL, val = NULL;
298 if (!szCommandLine)
299 return ERROR_SUCCESS;
301 ptr = szCommandLine;
303 while (*ptr)
305 if (*ptr==' ')
307 ptr++;
308 continue;
311 TRACE("Looking at %s\n",debugstr_w(ptr));
313 ptr2 = strchrW(ptr,'=');
314 if (!ptr2)
316 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
317 break;
320 quote = FALSE;
322 len = ptr2-ptr;
323 prop = msi_alloc((len+1)*sizeof(WCHAR));
324 memcpy(prop,ptr,len*sizeof(WCHAR));
325 prop[len]=0;
326 ptr2++;
328 len = 0;
329 ptr = ptr2;
330 while (*ptr && (quote || (!quote && *ptr!=' ')))
332 if (*ptr == '"')
333 quote = !quote;
334 ptr++;
335 len++;
338 if (*ptr2=='"')
340 ptr2++;
341 len -= 2;
343 val = msi_alloc((len+1)*sizeof(WCHAR));
344 memcpy(val,ptr2,len*sizeof(WCHAR));
345 val[len] = 0;
347 if (lstrlenW(prop) > 0)
349 TRACE("Found commandline property (%s) = (%s)\n",
350 debugstr_w(prop), debugstr_w(val));
351 MSI_SetPropertyW(package,prop,val);
353 msi_free(val);
354 msi_free(prop);
357 return ERROR_SUCCESS;
361 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
363 LPCWSTR pc;
364 LPWSTR p, *ret = NULL;
365 UINT count = 0;
367 if (!str)
368 return ret;
370 /* count the number of substrings */
371 for ( pc = str, count = 0; pc; count++ )
373 pc = strchrW( pc, sep );
374 if (pc)
375 pc++;
378 /* allocate space for an array of substring pointers and the substrings */
379 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
380 (lstrlenW(str)+1) * sizeof(WCHAR) );
381 if (!ret)
382 return ret;
384 /* copy the string and set the pointers */
385 p = (LPWSTR) &ret[count+1];
386 lstrcpyW( p, str );
387 for( count = 0; (ret[count] = p); count++ )
389 p = strchrW( p, sep );
390 if (p)
391 *p++ = 0;
394 return ret;
397 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
399 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
400 LPWSTR prod_code, patch_product;
401 UINT ret;
403 prod_code = msi_dup_property( package, szProductCode );
404 patch_product = msi_get_suminfo_product( patch );
406 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
408 if ( strstrW( patch_product, prod_code ) )
409 ret = ERROR_SUCCESS;
410 else
411 ret = ERROR_FUNCTION_FAILED;
413 msi_free( patch_product );
414 msi_free( prod_code );
416 return ret;
419 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
420 MSIDATABASE *patch_db, LPCWSTR name )
422 UINT ret = ERROR_FUNCTION_FAILED;
423 IStorage *stg = NULL;
424 HRESULT r;
426 TRACE("%p %s\n", package, debugstr_w(name) );
428 if (*name++ != ':')
430 ERR("expected a colon in %s\n", debugstr_w(name));
431 return ERROR_FUNCTION_FAILED;
434 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
435 if (SUCCEEDED(r))
437 ret = msi_check_transform_applicable( package, stg );
438 if (ret == ERROR_SUCCESS)
439 msi_table_apply_transform( package->db, stg );
440 else
441 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
442 IStorage_Release( stg );
444 else
445 ERR("failed to open substorage %s\n", debugstr_w(name));
447 return ERROR_SUCCESS;
450 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
452 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
453 LPWSTR guid_list, *guids, product_code;
454 UINT i, ret = ERROR_FUNCTION_FAILED;
456 product_code = msi_dup_property( package, szProdCode );
457 if (!product_code)
459 /* FIXME: the property ProductCode should be written into the DB somewhere */
460 ERR("no product code to check\n");
461 return ERROR_SUCCESS;
464 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
465 guids = msi_split_string( guid_list, ';' );
466 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
468 if (!lstrcmpW( guids[i], product_code ))
469 ret = ERROR_SUCCESS;
471 msi_free( guids );
472 msi_free( guid_list );
473 msi_free( product_code );
475 return ret;
478 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
480 MSISUMMARYINFO *si;
481 LPWSTR str, *substorage;
482 UINT i, r = ERROR_SUCCESS;
484 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
485 if (!si)
486 return ERROR_FUNCTION_FAILED;
488 msi_check_patch_applicable( package, si );
490 /* enumerate the substorage */
491 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
492 substorage = msi_split_string( str, ';' );
493 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
494 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
495 msi_free( substorage );
496 msi_free( str );
498 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
500 msiobj_release( &si->hdr );
502 return r;
505 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
507 MSIDATABASE *patch_db = NULL;
508 UINT r;
510 TRACE("%p %s\n", package, debugstr_w( file ) );
512 /* FIXME:
513 * We probably want to make sure we only open a patch collection here.
514 * Patch collections (.msp) and databases (.msi) have different GUIDs
515 * but currently MSI_OpenDatabaseW will accept both.
517 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
518 if ( r != ERROR_SUCCESS )
520 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
521 return r;
524 msi_parse_patch_summary( package, patch_db );
527 * There might be a CAB file in the patch package,
528 * so append it to the list of storage to search for streams.
530 append_storage_to_db( package->db, patch_db->storage );
532 msiobj_release( &patch_db->hdr );
534 return ERROR_SUCCESS;
537 /* get the PATCH property, and apply all the patches it specifies */
538 static UINT msi_apply_patches( MSIPACKAGE *package )
540 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
541 LPWSTR patch_list, *patches;
542 UINT i, r = ERROR_SUCCESS;
544 patch_list = msi_dup_property( package, szPatch );
546 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
548 patches = msi_split_string( patch_list, ';' );
549 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
550 r = msi_apply_patch_package( package, patches[i] );
552 msi_free( patches );
553 msi_free( patch_list );
555 return r;
558 static UINT msi_apply_transforms( MSIPACKAGE *package )
560 static const WCHAR szTransforms[] = {
561 'T','R','A','N','S','F','O','R','M','S',0 };
562 LPWSTR xform_list, *xforms;
563 UINT i, r = ERROR_SUCCESS;
565 xform_list = msi_dup_property( package, szTransforms );
566 xforms = msi_split_string( xform_list, ';' );
568 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
570 if (xforms[i][0] == ':')
571 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
572 else
573 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
576 msi_free( xforms );
577 msi_free( xform_list );
579 return r;
582 static BOOL ui_sequence_exists( MSIPACKAGE *package )
584 MSIQUERY *view;
585 UINT rc;
587 static const WCHAR ExecSeqQuery [] =
588 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l',
590 'U','I','S','e','q','u','e','n','c','e','`',
591 ' ','W','H','E','R','E',' ',
592 '`','S','e','q','u','e','n','c','e','`',' ',
593 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
594 '`','S','e','q','u','e','n','c','e','`',0};
596 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
597 if (rc == ERROR_SUCCESS)
599 msiobj_release(&view->hdr);
600 return TRUE;
603 return FALSE;
606 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
608 LPWSTR p, db;
609 LPWSTR source, check;
610 DWORD len;
612 static const WCHAR szOriginalDatabase[] =
613 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
615 db = msi_dup_property( package, szOriginalDatabase );
616 if (!db)
617 return ERROR_OUTOFMEMORY;
619 p = strrchrW( db, '\\' );
620 if (!p)
622 p = strrchrW( db, '/' );
623 if (!p)
625 msi_free(db);
626 return ERROR_SUCCESS;
630 len = p - db + 2;
631 source = msi_alloc( len * sizeof(WCHAR) );
632 lstrcpynW( source, db, len );
634 check = msi_dup_property( package, cszSourceDir );
635 if (!check || replace)
636 MSI_SetPropertyW( package, cszSourceDir, source );
638 msi_free( check );
640 check = msi_dup_property( package, cszSOURCEDIR );
641 if (!check || replace)
642 MSI_SetPropertyW( package, cszSOURCEDIR, source );
644 msi_free( check );
645 msi_free( source );
646 msi_free( db );
648 return ERROR_SUCCESS;
651 static UINT msi_set_context(MSIPACKAGE *package)
653 WCHAR val[10];
654 DWORD sz = 10;
655 DWORD num;
656 UINT r;
658 static const WCHAR szOne[] = {'1',0};
659 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
661 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
663 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
664 if (r == ERROR_SUCCESS)
666 num = atolW(val);
667 if (num == 1 || num == 2)
668 package->Context = MSIINSTALLCONTEXT_MACHINE;
671 MSI_SetPropertyW(package, szAllUsers, szOne);
672 return ERROR_SUCCESS;
675 /****************************************************
676 * TOP level entry points
677 *****************************************************/
679 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
680 LPCWSTR szCommandLine )
682 UINT rc;
683 BOOL ui = FALSE, ui_exists;
684 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
685 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
686 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
688 MSI_SetPropertyW(package, szAction, szInstall);
690 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
692 package->script->InWhatSequence = SEQUENCE_INSTALL;
694 if (szPackagePath)
696 LPWSTR p, dir;
697 LPCWSTR file;
699 dir = strdupW(szPackagePath);
700 p = strrchrW(dir, '\\');
701 if (p)
703 *(++p) = 0;
704 file = szPackagePath + (p - dir);
706 else
708 msi_free(dir);
709 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
710 GetCurrentDirectoryW(MAX_PATH, dir);
711 lstrcatW(dir, cszbs);
712 file = szPackagePath;
715 msi_free( package->PackagePath );
716 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
717 if (!package->PackagePath)
719 msi_free(dir);
720 return ERROR_OUTOFMEMORY;
723 lstrcpyW(package->PackagePath, dir);
724 lstrcatW(package->PackagePath, file);
725 msi_free(dir);
727 msi_set_sourcedir_props(package, FALSE);
730 msi_parse_command_line( package, szCommandLine );
732 msi_apply_transforms( package );
733 msi_apply_patches( package );
735 /* properties may have been added by a transform */
736 msi_clone_properties( package );
737 msi_set_context( package );
739 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
741 package->script->InWhatSequence |= SEQUENCE_UI;
742 rc = ACTION_ProcessUISequence(package);
743 ui = TRUE;
744 ui_exists = ui_sequence_exists(package);
745 if (rc == ERROR_SUCCESS || !ui_exists)
747 package->script->InWhatSequence |= SEQUENCE_EXEC;
748 rc = ACTION_ProcessExecSequence(package,ui_exists);
751 else
752 rc = ACTION_ProcessExecSequence(package,FALSE);
754 package->script->CurrentlyScripting= FALSE;
756 /* process the ending type action */
757 if (rc == ERROR_SUCCESS)
758 ACTION_PerformActionSequence(package,-1,ui);
759 else if (rc == ERROR_INSTALL_USEREXIT)
760 ACTION_PerformActionSequence(package,-2,ui);
761 else if (rc == ERROR_INSTALL_SUSPEND)
762 ACTION_PerformActionSequence(package,-4,ui);
763 else /* failed */
764 ACTION_PerformActionSequence(package,-3,ui);
766 /* finish up running custom actions */
767 ACTION_FinishCustomActions(package);
769 return rc;
772 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
774 UINT rc = ERROR_SUCCESS;
775 MSIRECORD * row = 0;
776 static const WCHAR ExecSeqQuery[] =
777 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
778 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
779 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
780 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
782 static const WCHAR UISeqQuery[] =
783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
784 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
785 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
786 ' ', '=',' ','%','i',0};
788 if (UI)
789 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
790 else
791 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
793 if (row)
795 LPCWSTR action, cond;
797 TRACE("Running the actions\n");
799 /* check conditions */
800 cond = MSI_RecordGetString(row,2);
802 /* this is a hack to skip errors in the condition code */
803 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
804 goto end;
806 action = MSI_RecordGetString(row,1);
807 if (!action)
809 ERR("failed to fetch action\n");
810 rc = ERROR_FUNCTION_FAILED;
811 goto end;
814 if (UI)
815 rc = ACTION_PerformUIAction(package,action,-1);
816 else
817 rc = ACTION_PerformAction(package,action,-1,FALSE);
818 end:
819 msiobj_release(&row->hdr);
821 else
822 rc = ERROR_SUCCESS;
824 return rc;
827 typedef struct {
828 MSIPACKAGE* package;
829 BOOL UI;
830 } iterate_action_param;
832 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
834 iterate_action_param *iap= (iterate_action_param*)param;
835 UINT rc;
836 LPCWSTR cond, action;
838 action = MSI_RecordGetString(row,1);
839 if (!action)
841 ERR("Error is retrieving action name\n");
842 return ERROR_FUNCTION_FAILED;
845 /* check conditions */
846 cond = MSI_RecordGetString(row,2);
848 /* this is a hack to skip errors in the condition code */
849 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
851 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
852 return ERROR_SUCCESS;
855 if (iap->UI)
856 rc = ACTION_PerformUIAction(iap->package,action,-1);
857 else
858 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
860 msi_dialog_check_messages( NULL );
862 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
863 rc = iap->package->CurrentInstallState;
865 if (rc == ERROR_FUNCTION_NOT_CALLED)
866 rc = ERROR_SUCCESS;
868 if (rc != ERROR_SUCCESS)
869 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
871 return rc;
874 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
876 MSIQUERY * view;
877 UINT r;
878 static const WCHAR query[] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','%','s','`',
881 ' ','W','H','E','R','E',' ',
882 '`','S','e','q','u','e','n','c','e','`',' ',
883 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
884 '`','S','e','q','u','e','n','c','e','`',0};
885 iterate_action_param iap;
888 * FIXME: probably should be checking UILevel in the
889 * ACTION_PerformUIAction/ACTION_PerformAction
890 * rather than saving the UI level here. Those
891 * two functions can be merged too.
893 iap.package = package;
894 iap.UI = TRUE;
896 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
898 r = MSI_OpenQuery( package->db, &view, query, szTable );
899 if (r == ERROR_SUCCESS)
901 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
902 msiobj_release(&view->hdr);
905 return r;
908 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
910 MSIQUERY * view;
911 UINT rc;
912 static const WCHAR ExecSeqQuery[] =
913 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
914 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
915 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
917 'O','R','D','E','R',' ', 'B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0 };
919 MSIRECORD * row = 0;
920 static const WCHAR IVQuery[] =
921 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
922 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
923 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
924 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
925 ' ','\'', 'I','n','s','t','a','l','l',
926 'V','a','l','i','d','a','t','e','\'', 0};
927 INT seq = 0;
928 iterate_action_param iap;
930 iap.package = package;
931 iap.UI = FALSE;
933 if (package->script->ExecuteSequenceRun)
935 TRACE("Execute Sequence already Run\n");
936 return ERROR_SUCCESS;
939 package->script->ExecuteSequenceRun = TRUE;
941 /* get the sequence number */
942 if (UIran)
944 row = MSI_QueryGetRecord(package->db, IVQuery);
945 if( !row )
946 return ERROR_FUNCTION_FAILED;
947 seq = MSI_RecordGetInteger(row,1);
948 msiobj_release(&row->hdr);
951 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
952 if (rc == ERROR_SUCCESS)
954 TRACE("Running the actions\n");
956 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
957 msiobj_release(&view->hdr);
960 return rc;
963 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
965 MSIQUERY * view;
966 UINT rc;
967 static const WCHAR ExecSeqQuery [] =
968 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
969 '`','I','n','s','t','a','l','l',
970 'U','I','S','e','q','u','e','n','c','e','`',
971 ' ','W','H','E','R','E',' ',
972 '`','S','e','q','u','e','n','c','e','`',' ',
973 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
974 '`','S','e','q','u','e','n','c','e','`',0};
975 iterate_action_param iap;
977 iap.package = package;
978 iap.UI = TRUE;
980 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
982 if (rc == ERROR_SUCCESS)
984 TRACE("Running the actions\n");
986 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
987 msiobj_release(&view->hdr);
990 return rc;
993 /********************************************************
994 * ACTION helper functions and functions that perform the actions
995 *******************************************************/
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 WARN("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 WARN("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 if ( !name )
1279 return NULL;
1281 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1283 if ( !lstrcmpW( feature->Feature, name ) )
1284 return feature;
1287 return NULL;
1290 static UINT load_feature(MSIRECORD * row, LPVOID param)
1292 MSIPACKAGE* package = (MSIPACKAGE*)param;
1293 MSIFEATURE* feature;
1294 static const WCHAR Query1[] =
1295 {'S','E','L','E','C','T',' ',
1296 '`','C','o','m','p','o','n','e','n','t','_','`',
1297 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1298 'C','o','m','p','o','n','e','n','t','s','`',' ',
1299 'W','H','E','R','E',' ',
1300 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1301 MSIQUERY * view;
1302 UINT rc;
1303 _ilfs ilfs;
1305 /* fill in the data */
1307 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1308 if (!feature)
1309 return ERROR_NOT_ENOUGH_MEMORY;
1311 list_init( &feature->Children );
1312 list_init( &feature->Components );
1314 feature->Feature = msi_dup_record_field( row, 1 );
1316 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1318 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1319 feature->Title = msi_dup_record_field( row, 3 );
1320 feature->Description = msi_dup_record_field( row, 4 );
1322 if (!MSI_RecordIsNull(row,5))
1323 feature->Display = MSI_RecordGetInteger(row,5);
1325 feature->Level= MSI_RecordGetInteger(row,6);
1326 feature->Directory = msi_dup_record_field( row, 7 );
1327 feature->Attributes = MSI_RecordGetInteger(row,8);
1329 feature->Installed = INSTALLSTATE_UNKNOWN;
1330 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1332 list_add_tail( &package->features, &feature->entry );
1334 /* load feature components */
1336 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1337 if (rc != ERROR_SUCCESS)
1338 return ERROR_SUCCESS;
1340 ilfs.package = package;
1341 ilfs.feature = feature;
1343 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1344 msiobj_release(&view->hdr);
1346 return ERROR_SUCCESS;
1349 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1351 MSIPACKAGE* package = (MSIPACKAGE*)param;
1352 MSIFEATURE *parent, *child;
1354 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1355 if (!child)
1356 return ERROR_FUNCTION_FAILED;
1358 if (!child->Feature_Parent)
1359 return ERROR_SUCCESS;
1361 parent = find_feature_by_name( package, child->Feature_Parent );
1362 if (!parent)
1363 return ERROR_FUNCTION_FAILED;
1365 add_feature_child( parent, child );
1366 return ERROR_SUCCESS;
1369 static UINT load_all_features( MSIPACKAGE *package )
1371 static const WCHAR query[] = {
1372 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1373 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1374 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1375 MSIQUERY *view;
1376 UINT r;
1378 if (!list_empty(&package->features))
1379 return ERROR_SUCCESS;
1381 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1382 if (r != ERROR_SUCCESS)
1383 return r;
1385 r = MSI_IterateRecords( view, NULL, load_feature, package );
1386 if (r != ERROR_SUCCESS)
1387 return r;
1389 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1390 msiobj_release( &view->hdr );
1392 return r;
1395 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1397 if (!p)
1398 return p;
1399 p = strchrW(p, ch);
1400 if (!p)
1401 return p;
1402 *p = 0;
1403 return p+1;
1406 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1408 static const WCHAR query[] = {
1409 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1410 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1411 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1412 MSIQUERY *view = NULL;
1413 MSIRECORD *row = NULL;
1414 UINT r;
1416 TRACE("%s\n", debugstr_w(file->File));
1418 r = MSI_OpenQuery(package->db, &view, query, file->File);
1419 if (r != ERROR_SUCCESS)
1420 goto done;
1422 r = MSI_ViewExecute(view, NULL);
1423 if (r != ERROR_SUCCESS)
1424 goto done;
1426 r = MSI_ViewFetch(view, &row);
1427 if (r != ERROR_SUCCESS)
1428 goto done;
1430 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1431 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1432 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1433 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1434 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1436 done:
1437 if (view) msiobj_release(&view->hdr);
1438 if (row) msiobj_release(&row->hdr);
1439 return r;
1442 static UINT load_file(MSIRECORD *row, LPVOID param)
1444 MSIPACKAGE* package = (MSIPACKAGE*)param;
1445 LPCWSTR component;
1446 MSIFILE *file;
1448 /* fill in the data */
1450 file = msi_alloc_zero( sizeof (MSIFILE) );
1451 if (!file)
1452 return ERROR_NOT_ENOUGH_MEMORY;
1454 file->File = msi_dup_record_field( row, 1 );
1456 component = MSI_RecordGetString( row, 2 );
1457 file->Component = get_loaded_component( package, component );
1459 if (!file->Component)
1460 ERR("Unfound Component %s\n",debugstr_w(component));
1462 file->FileName = msi_dup_record_field( row, 3 );
1463 reduce_to_longfilename( file->FileName );
1465 file->ShortName = msi_dup_record_field( row, 3 );
1466 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1468 file->FileSize = MSI_RecordGetInteger( row, 4 );
1469 file->Version = msi_dup_record_field( row, 5 );
1470 file->Language = msi_dup_record_field( row, 6 );
1471 file->Attributes = MSI_RecordGetInteger( row, 7 );
1472 file->Sequence = MSI_RecordGetInteger( row, 8 );
1474 file->state = msifs_invalid;
1476 /* if the compressed bits are not set in the file attributes,
1477 * then read the information from the package word count property
1479 if (file->Attributes & msidbFileAttributesCompressed)
1481 file->IsCompressed = TRUE;
1483 else if (file->Attributes & msidbFileAttributesNoncompressed)
1485 file->IsCompressed = FALSE;
1487 else
1489 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1492 if (!file->IsCompressed)
1494 LPWSTR p, path;
1496 p = resolve_folder(package, file->Component->Directory,
1497 TRUE, FALSE, TRUE, NULL);
1498 path = build_directory_name(2, p, file->ShortName);
1500 if (file->LongName &&
1501 GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
1503 msi_free(path);
1504 path = build_directory_name(2, p, file->LongName);
1507 file->SourcePath = path;
1508 msi_free(p);
1511 load_file_hash(package, file);
1513 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1515 list_add_tail( &package->files, &file->entry );
1517 return ERROR_SUCCESS;
1520 static UINT load_all_files(MSIPACKAGE *package)
1522 MSIQUERY * view;
1523 UINT rc;
1524 static const WCHAR Query[] =
1525 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1526 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1527 '`','S','e','q','u','e','n','c','e','`', 0};
1529 if (!list_empty(&package->files))
1530 return ERROR_SUCCESS;
1532 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1533 if (rc != ERROR_SUCCESS)
1534 return ERROR_SUCCESS;
1536 rc = MSI_IterateRecords(view, NULL, load_file, package);
1537 msiobj_release(&view->hdr);
1539 return ERROR_SUCCESS;
1542 static UINT load_folder( MSIRECORD *row, LPVOID param )
1544 MSIPACKAGE *package = param;
1545 static const WCHAR szDot[] = { '.',0 };
1546 static WCHAR szEmpty[] = { 0 };
1547 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1548 MSIFOLDER *folder;
1550 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1551 if (!folder)
1552 return ERROR_NOT_ENOUGH_MEMORY;
1554 folder->Directory = msi_dup_record_field( row, 1 );
1556 TRACE("%s\n", debugstr_w(folder->Directory));
1558 p = msi_dup_record_field(row, 3);
1560 /* split src and target dir */
1561 tgt_short = p;
1562 src_short = folder_split_path( p, ':' );
1564 /* split the long and short paths */
1565 tgt_long = folder_split_path( tgt_short, '|' );
1566 src_long = folder_split_path( src_short, '|' );
1568 /* check for no-op dirs */
1569 if (!lstrcmpW(szDot, tgt_short))
1570 tgt_short = szEmpty;
1571 if (!lstrcmpW(szDot, src_short))
1572 src_short = szEmpty;
1574 if (!tgt_long)
1575 tgt_long = tgt_short;
1577 if (!src_short) {
1578 src_short = tgt_short;
1579 src_long = tgt_long;
1582 if (!src_long)
1583 src_long = src_short;
1585 /* FIXME: use the target short path too */
1586 folder->TargetDefault = strdupW(tgt_long);
1587 folder->SourceShortPath = strdupW(src_short);
1588 folder->SourceLongPath = strdupW(src_long);
1589 msi_free(p);
1591 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1592 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1593 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1595 folder->Parent = msi_dup_record_field( row, 2 );
1597 folder->Property = msi_dup_property( package, folder->Directory );
1599 list_add_tail( &package->folders, &folder->entry );
1601 TRACE("returning %p\n", folder);
1603 return ERROR_SUCCESS;
1606 static UINT load_all_folders( MSIPACKAGE *package )
1608 static const WCHAR query[] = {
1609 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1610 '`','D','i','r','e','c','t','o','r','y','`',0 };
1611 MSIQUERY *view;
1612 UINT r;
1614 if (!list_empty(&package->folders))
1615 return ERROR_SUCCESS;
1617 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1618 if (r != ERROR_SUCCESS)
1619 return r;
1621 r = MSI_IterateRecords(view, NULL, load_folder, package);
1622 msiobj_release(&view->hdr);
1623 return r;
1627 * I am not doing any of the costing functionality yet.
1628 * Mostly looking at doing the Component and Feature loading
1630 * The native MSI does A LOT of modification to tables here. Mostly adding
1631 * a lot of temporary columns to the Feature and Component tables.
1633 * note: Native msi also tracks the short filename. But I am only going to
1634 * track the long ones. Also looking at this directory table
1635 * it appears that the directory table does not get the parents
1636 * resolved base on property only based on their entries in the
1637 * directory table.
1639 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1641 static const WCHAR szCosting[] =
1642 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1643 static const WCHAR szZero[] = { '0', 0 };
1645 MSI_SetPropertyW(package, szCosting, szZero);
1646 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1648 load_all_folders( package );
1649 load_all_components( package );
1650 load_all_features( package );
1651 load_all_files( package );
1653 return ERROR_SUCCESS;
1656 static UINT execute_script(MSIPACKAGE *package, UINT script )
1658 UINT i;
1659 UINT rc = ERROR_SUCCESS;
1661 TRACE("Executing Script %i\n",script);
1663 if (!package->script)
1665 ERR("no script!\n");
1666 return ERROR_FUNCTION_FAILED;
1669 for (i = 0; i < package->script->ActionCount[script]; i++)
1671 LPWSTR action;
1672 action = package->script->Actions[script][i];
1673 ui_actionstart(package, action);
1674 TRACE("Executing Action (%s)\n",debugstr_w(action));
1675 rc = ACTION_PerformAction(package, action, script, TRUE);
1676 if (rc != ERROR_SUCCESS)
1677 break;
1679 msi_free_action_script(package, script);
1680 return rc;
1683 static UINT ACTION_FileCost(MSIPACKAGE *package)
1685 return ERROR_SUCCESS;
1688 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1690 MSICOMPONENT *comp;
1692 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1694 INSTALLSTATE res;
1696 if (!comp->ComponentId)
1697 continue;
1699 res = MsiGetComponentPathW( package->ProductCode,
1700 comp->ComponentId, NULL, NULL);
1701 if (res < 0)
1702 res = INSTALLSTATE_ABSENT;
1703 comp->Installed = res;
1707 /* scan for and update current install states */
1708 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1710 MSICOMPONENT *comp;
1711 MSIFEATURE *feature;
1713 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1715 ComponentList *cl;
1716 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1718 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1720 comp= cl->component;
1722 if (!comp->ComponentId)
1724 res = INSTALLSTATE_ABSENT;
1725 break;
1728 if (res == INSTALLSTATE_ABSENT)
1729 res = comp->Installed;
1730 else
1732 if (res == comp->Installed)
1733 continue;
1735 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1736 res != INSTALLSTATE_SOURCE)
1738 res = INSTALLSTATE_INCOMPLETE;
1742 feature->Installed = res;
1746 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1747 INSTALLSTATE state)
1749 static const WCHAR all[]={'A','L','L',0};
1750 LPWSTR override;
1751 MSIFEATURE *feature;
1753 override = msi_dup_property( package, property );
1754 if (!override)
1755 return FALSE;
1757 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1759 if (strcmpiW(override,all)==0)
1760 msi_feature_set_state( feature, state );
1761 else
1763 LPWSTR ptr = override;
1764 LPWSTR ptr2 = strchrW(override,',');
1766 while (ptr)
1768 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1769 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1771 msi_feature_set_state( feature, state );
1772 break;
1774 if (ptr2)
1776 ptr=ptr2+1;
1777 ptr2 = strchrW(ptr,',');
1779 else
1780 break;
1784 msi_free(override);
1786 return TRUE;
1789 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1791 int install_level;
1792 static const WCHAR szlevel[] =
1793 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1794 static const WCHAR szAddLocal[] =
1795 {'A','D','D','L','O','C','A','L',0};
1796 static const WCHAR szAddSource[] =
1797 {'A','D','D','S','O','U','R','C','E',0};
1798 static const WCHAR szRemove[] =
1799 {'R','E','M','O','V','E',0};
1800 static const WCHAR szReinstall[] =
1801 {'R','E','I','N','S','T','A','L','L',0};
1802 BOOL override = FALSE;
1803 MSICOMPONENT* component;
1804 MSIFEATURE *feature;
1807 /* I do not know if this is where it should happen.. but */
1809 TRACE("Checking Install Level\n");
1811 install_level = msi_get_property_int( package, szlevel, 1 );
1813 /* ok here is the _real_ rub
1814 * all these activation/deactivation things happen in order and things
1815 * later on the list override things earlier on the list.
1816 * 1) INSTALLLEVEL processing
1817 * 2) ADDLOCAL
1818 * 3) REMOVE
1819 * 4) ADDSOURCE
1820 * 5) ADDDEFAULT
1821 * 6) REINSTALL
1822 * 7) COMPADDLOCAL
1823 * 8) COMPADDSOURCE
1824 * 9) FILEADDLOCAL
1825 * 10) FILEADDSOURCE
1826 * 11) FILEADDDEFAULT
1828 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1829 * REMOVE are the big ones, since we don't handle administrative installs
1830 * yet anyway.
1832 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1833 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1834 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1835 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1837 if (!override)
1839 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1841 BOOL feature_state = ((feature->Level > 0) &&
1842 (feature->Level <= install_level));
1844 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1846 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1847 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1848 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1849 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1850 else
1851 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1855 /* disable child features of unselected parent features */
1856 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1858 FeatureList *fl;
1860 if (feature->Level > 0 && feature->Level <= install_level)
1861 continue;
1863 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1864 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1867 else
1869 /* set the Preselected Property */
1870 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1871 static const WCHAR szOne[] = { '1', 0 };
1873 MSI_SetPropertyW(package,szPreselected,szOne);
1877 * now we want to enable or disable components base on feature
1880 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1882 ComponentList *cl;
1884 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1885 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1887 if (!feature->Level)
1888 continue;
1890 /* features with components that have compressed files are made local */
1891 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1893 if (cl->component->Enabled &&
1894 cl->component->ForceLocalState &&
1895 feature->Action == INSTALLSTATE_SOURCE)
1897 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1898 break;
1902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1904 component = cl->component;
1906 if (!component->Enabled)
1907 continue;
1909 switch (feature->Action)
1911 case INSTALLSTATE_ABSENT:
1912 component->anyAbsent = 1;
1913 break;
1914 case INSTALLSTATE_ADVERTISED:
1915 component->hasAdvertiseFeature = 1;
1916 break;
1917 case INSTALLSTATE_SOURCE:
1918 component->hasSourceFeature = 1;
1919 break;
1920 case INSTALLSTATE_LOCAL:
1921 component->hasLocalFeature = 1;
1922 break;
1923 case INSTALLSTATE_DEFAULT:
1924 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1925 component->hasAdvertiseFeature = 1;
1926 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1927 component->hasSourceFeature = 1;
1928 else
1929 component->hasLocalFeature = 1;
1930 break;
1931 default:
1932 break;
1937 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1939 /* if the component isn't enabled, leave it alone */
1940 if (!component->Enabled)
1941 continue;
1943 /* check if it's local or source */
1944 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1945 (component->hasLocalFeature || component->hasSourceFeature))
1947 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1948 !component->ForceLocalState)
1949 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1950 else
1951 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1952 continue;
1955 /* if any feature is local, the component must be local too */
1956 if (component->hasLocalFeature)
1958 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1959 continue;
1962 if (component->hasSourceFeature)
1964 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1965 continue;
1968 if (component->hasAdvertiseFeature)
1970 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1971 continue;
1974 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1975 if (component->anyAbsent)
1976 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1979 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1981 if (component->Action == INSTALLSTATE_DEFAULT)
1983 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1984 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1987 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1988 debugstr_w(component->Component), component->Installed, component->Action);
1992 return ERROR_SUCCESS;
1995 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1997 MSIPACKAGE *package = (MSIPACKAGE*)param;
1998 LPCWSTR name;
1999 LPWSTR path;
2000 MSIFOLDER *f;
2002 name = MSI_RecordGetString(row,1);
2004 f = get_loaded_folder(package, name);
2005 if (!f) return ERROR_SUCCESS;
2007 /* reset the ResolvedTarget */
2008 msi_free(f->ResolvedTarget);
2009 f->ResolvedTarget = NULL;
2011 /* This helper function now does ALL the work */
2012 TRACE("Dir %s ...\n",debugstr_w(name));
2013 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2014 TRACE("resolves to %s\n",debugstr_w(path));
2015 msi_free(path);
2017 return ERROR_SUCCESS;
2020 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2022 MSIPACKAGE *package = (MSIPACKAGE*)param;
2023 LPCWSTR name;
2024 MSIFEATURE *feature;
2026 name = MSI_RecordGetString( row, 1 );
2028 feature = get_loaded_feature( package, name );
2029 if (!feature)
2030 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2031 else
2033 LPCWSTR Condition;
2034 Condition = MSI_RecordGetString(row,3);
2036 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2038 int level = MSI_RecordGetInteger(row,2);
2039 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2040 feature->Level = level;
2043 return ERROR_SUCCESS;
2046 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2048 static const WCHAR name_fmt[] =
2049 {'%','u','.','%','u','.','%','u','.','%','u',0};
2050 static const WCHAR name[] = {'\\',0};
2051 VS_FIXEDFILEINFO *lpVer;
2052 WCHAR filever[0x100];
2053 LPVOID version;
2054 DWORD versize;
2055 DWORD handle;
2056 UINT sz;
2058 TRACE("%s\n", debugstr_w(filename));
2060 versize = GetFileVersionInfoSizeW( filename, &handle );
2061 if (!versize)
2062 return NULL;
2064 version = msi_alloc( versize );
2065 GetFileVersionInfoW( filename, 0, versize, version );
2067 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2069 msi_free( version );
2070 return NULL;
2073 sprintfW( filever, name_fmt,
2074 HIWORD(lpVer->dwFileVersionMS),
2075 LOWORD(lpVer->dwFileVersionMS),
2076 HIWORD(lpVer->dwFileVersionLS),
2077 LOWORD(lpVer->dwFileVersionLS));
2079 msi_free( version );
2081 return strdupW( filever );
2084 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2086 LPWSTR file_version;
2087 MSIFILE *file;
2089 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2091 MSICOMPONENT* comp = file->Component;
2092 LPWSTR p;
2094 if (!comp)
2095 continue;
2097 if (file->IsCompressed)
2098 comp->ForceLocalState = TRUE;
2100 /* calculate target */
2101 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2103 msi_free(file->TargetPath);
2105 TRACE("file %s is named %s\n",
2106 debugstr_w(file->File), debugstr_w(file->FileName));
2108 file->TargetPath = build_directory_name(2, p, file->FileName);
2110 msi_free(p);
2112 TRACE("file %s resolves to %s\n",
2113 debugstr_w(file->File), debugstr_w(file->TargetPath));
2115 /* don't check files of components that aren't installed */
2116 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2117 comp->Installed == INSTALLSTATE_ABSENT)
2119 file->state = msifs_missing; /* assume files are missing */
2120 continue;
2123 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2125 file->state = msifs_missing;
2126 comp->Cost += file->FileSize;
2127 comp->Installed = INSTALLSTATE_INCOMPLETE;
2128 continue;
2131 if (file->Version &&
2132 (file_version = msi_get_disk_file_version( file->TargetPath )))
2134 TRACE("new %s old %s\n", debugstr_w(file->Version),
2135 debugstr_w(file_version));
2136 /* FIXME: seems like a bad way to compare version numbers */
2137 if (lstrcmpiW(file_version, file->Version)<0)
2139 file->state = msifs_overwrite;
2140 comp->Cost += file->FileSize;
2141 comp->Installed = INSTALLSTATE_INCOMPLETE;
2143 else
2144 file->state = msifs_present;
2145 msi_free( file_version );
2147 else
2148 file->state = msifs_present;
2151 return ERROR_SUCCESS;
2155 * A lot is done in this function aside from just the costing.
2156 * The costing needs to be implemented at some point but for now I am going
2157 * to focus on the directory building
2160 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2162 static const WCHAR ExecSeqQuery[] =
2163 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2164 '`','D','i','r','e','c','t','o','r','y','`',0};
2165 static const WCHAR ConditionQuery[] =
2166 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2167 '`','C','o','n','d','i','t','i','o','n','`',0};
2168 static const WCHAR szCosting[] =
2169 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2170 static const WCHAR szlevel[] =
2171 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2172 static const WCHAR szOutOfDiskSpace[] =
2173 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2174 static const WCHAR szOne[] = { '1', 0 };
2175 static const WCHAR szZero[] = { '0', 0 };
2176 MSICOMPONENT *comp;
2177 UINT rc;
2178 MSIQUERY * view;
2179 LPWSTR level;
2181 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2182 return ERROR_SUCCESS;
2184 TRACE("Building Directory properties\n");
2186 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2187 if (rc == ERROR_SUCCESS)
2189 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2190 package);
2191 msiobj_release(&view->hdr);
2194 /* read components states from the registry */
2195 ACTION_GetComponentInstallStates(package);
2197 TRACE("File calculations\n");
2198 msi_check_file_install_states( package );
2200 TRACE("Evaluating Condition Table\n");
2202 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2203 if (rc == ERROR_SUCCESS)
2205 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2206 package);
2207 msiobj_release(&view->hdr);
2210 TRACE("Enabling or Disabling Components\n");
2211 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2213 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2215 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2216 comp->Enabled = FALSE;
2220 MSI_SetPropertyW(package,szCosting,szOne);
2221 /* set default run level if not set */
2222 level = msi_dup_property( package, szlevel );
2223 if (!level)
2224 MSI_SetPropertyW(package,szlevel, szOne);
2225 msi_free(level);
2227 /* FIXME: check volume disk space */
2228 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2230 ACTION_UpdateFeatureInstallStates(package);
2232 return MSI_SetFeatureStates(package);
2235 /* OK this value is "interpreted" and then formatted based on the
2236 first few characters */
2237 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2238 DWORD *size)
2240 LPSTR data = NULL;
2242 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2244 if (value[1]=='x')
2246 LPWSTR ptr;
2247 CHAR byte[5];
2248 LPWSTR deformated = NULL;
2249 int count;
2251 deformat_string(package, &value[2], &deformated);
2253 /* binary value type */
2254 ptr = deformated;
2255 *type = REG_BINARY;
2256 if (strlenW(ptr)%2)
2257 *size = (strlenW(ptr)/2)+1;
2258 else
2259 *size = strlenW(ptr)/2;
2261 data = msi_alloc(*size);
2263 byte[0] = '0';
2264 byte[1] = 'x';
2265 byte[4] = 0;
2266 count = 0;
2267 /* if uneven pad with a zero in front */
2268 if (strlenW(ptr)%2)
2270 byte[2]= '0';
2271 byte[3]= *ptr;
2272 ptr++;
2273 data[count] = (BYTE)strtol(byte,NULL,0);
2274 count ++;
2275 TRACE("Uneven byte count\n");
2277 while (*ptr)
2279 byte[2]= *ptr;
2280 ptr++;
2281 byte[3]= *ptr;
2282 ptr++;
2283 data[count] = (BYTE)strtol(byte,NULL,0);
2284 count ++;
2286 msi_free(deformated);
2288 TRACE("Data %i bytes(%i)\n",*size,count);
2290 else
2292 LPWSTR deformated;
2293 LPWSTR p;
2294 DWORD d = 0;
2295 deformat_string(package, &value[1], &deformated);
2297 *type=REG_DWORD;
2298 *size = sizeof(DWORD);
2299 data = msi_alloc(*size);
2300 p = deformated;
2301 if (*p == '-')
2302 p++;
2303 while (*p)
2305 if ( (*p < '0') || (*p > '9') )
2306 break;
2307 d *= 10;
2308 d += (*p - '0');
2309 p++;
2311 if (deformated[0] == '-')
2312 d = -d;
2313 *(LPDWORD)data = d;
2314 TRACE("DWORD %i\n",*(LPDWORD)data);
2316 msi_free(deformated);
2319 else
2321 static const WCHAR szMulti[] = {'[','~',']',0};
2322 LPCWSTR ptr;
2323 *type=REG_SZ;
2325 if (value[0]=='#')
2327 if (value[1]=='%')
2329 ptr = &value[2];
2330 *type=REG_EXPAND_SZ;
2332 else
2333 ptr = &value[1];
2335 else
2336 ptr=value;
2338 if (strstrW(value,szMulti))
2339 *type = REG_MULTI_SZ;
2341 /* remove initial delimiter */
2342 if (!strncmpW(value, szMulti, 3))
2343 ptr = value + 3;
2345 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2347 /* add double NULL terminator */
2348 if (*type == REG_MULTI_SZ)
2350 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2351 data = msi_realloc_zero(data, *size);
2354 return data;
2357 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2359 MSIPACKAGE *package = (MSIPACKAGE*)param;
2360 static const WCHAR szHCR[] =
2361 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2362 'R','O','O','T','\\',0};
2363 static const WCHAR szHCU[] =
2364 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2365 'U','S','E','R','\\',0};
2366 static const WCHAR szHLM[] =
2367 {'H','K','E','Y','_','L','O','C','A','L','_',
2368 'M','A','C','H','I','N','E','\\',0};
2369 static const WCHAR szHU[] =
2370 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2372 LPSTR value_data = NULL;
2373 HKEY root_key, hkey;
2374 DWORD type,size;
2375 LPWSTR deformated;
2376 LPCWSTR szRoot, component, name, key, value;
2377 MSICOMPONENT *comp;
2378 MSIRECORD * uirow;
2379 LPWSTR uikey;
2380 INT root;
2381 BOOL check_first = FALSE;
2382 UINT rc;
2384 ui_progress(package,2,0,0,0);
2386 value = NULL;
2387 key = NULL;
2388 uikey = NULL;
2389 name = NULL;
2391 component = MSI_RecordGetString(row, 6);
2392 comp = get_loaded_component(package,component);
2393 if (!comp)
2394 return ERROR_SUCCESS;
2396 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2398 TRACE("Skipping write due to disabled component %s\n",
2399 debugstr_w(component));
2401 comp->Action = comp->Installed;
2403 return ERROR_SUCCESS;
2406 comp->Action = INSTALLSTATE_LOCAL;
2408 name = MSI_RecordGetString(row, 4);
2409 if( MSI_RecordIsNull(row,5) && name )
2411 /* null values can have special meanings */
2412 if (name[0]=='-' && name[1] == 0)
2413 return ERROR_SUCCESS;
2414 else if ((name[0]=='+' && name[1] == 0) ||
2415 (name[0] == '*' && name[1] == 0))
2416 name = NULL;
2417 check_first = TRUE;
2420 root = MSI_RecordGetInteger(row,2);
2421 key = MSI_RecordGetString(row, 3);
2423 /* get the root key */
2424 switch (root)
2426 case -1:
2428 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2429 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2430 if (all_users && all_users[0] == '1')
2432 root_key = HKEY_LOCAL_MACHINE;
2433 szRoot = szHLM;
2435 else
2437 root_key = HKEY_CURRENT_USER;
2438 szRoot = szHCU;
2440 msi_free(all_users);
2442 break;
2443 case 0: root_key = HKEY_CLASSES_ROOT;
2444 szRoot = szHCR;
2445 break;
2446 case 1: root_key = HKEY_CURRENT_USER;
2447 szRoot = szHCU;
2448 break;
2449 case 2: root_key = HKEY_LOCAL_MACHINE;
2450 szRoot = szHLM;
2451 break;
2452 case 3: root_key = HKEY_USERS;
2453 szRoot = szHU;
2454 break;
2455 default:
2456 ERR("Unknown root %i\n",root);
2457 root_key=NULL;
2458 szRoot = NULL;
2459 break;
2461 if (!root_key)
2462 return ERROR_SUCCESS;
2464 deformat_string(package, key , &deformated);
2465 size = strlenW(deformated) + strlenW(szRoot) + 1;
2466 uikey = msi_alloc(size*sizeof(WCHAR));
2467 strcpyW(uikey,szRoot);
2468 strcatW(uikey,deformated);
2470 if (RegCreateKeyW( root_key, deformated, &hkey))
2472 ERR("Could not create key %s\n",debugstr_w(deformated));
2473 msi_free(deformated);
2474 msi_free(uikey);
2475 return ERROR_SUCCESS;
2477 msi_free(deformated);
2479 value = MSI_RecordGetString(row,5);
2480 if (value)
2481 value_data = parse_value(package, value, &type, &size);
2482 else
2484 static const WCHAR szEmpty[] = {0};
2485 value_data = (LPSTR)strdupW(szEmpty);
2486 size = sizeof(szEmpty);
2487 type = REG_SZ;
2490 deformat_string(package, name, &deformated);
2492 if (!check_first)
2494 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2495 debugstr_w(uikey));
2496 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2498 else
2500 DWORD sz = 0;
2501 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2502 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2504 TRACE("value %s of %s checked already exists\n",
2505 debugstr_w(deformated), debugstr_w(uikey));
2507 else
2509 TRACE("Checked and setting value %s of %s\n",
2510 debugstr_w(deformated), debugstr_w(uikey));
2511 if (deformated || size)
2512 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2515 RegCloseKey(hkey);
2517 uirow = MSI_CreateRecord(3);
2518 MSI_RecordSetStringW(uirow,2,deformated);
2519 MSI_RecordSetStringW(uirow,1,uikey);
2521 if (type == REG_SZ)
2522 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2523 else
2524 MSI_RecordSetStringW(uirow,3,value);
2526 ui_actiondata(package,szWriteRegistryValues,uirow);
2527 msiobj_release( &uirow->hdr );
2529 msi_free(value_data);
2530 msi_free(deformated);
2531 msi_free(uikey);
2533 return ERROR_SUCCESS;
2536 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2538 UINT rc;
2539 MSIQUERY * view;
2540 static const WCHAR ExecSeqQuery[] =
2541 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2542 '`','R','e','g','i','s','t','r','y','`',0 };
2544 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2545 if (rc != ERROR_SUCCESS)
2546 return ERROR_SUCCESS;
2548 /* increment progress bar each time action data is sent */
2549 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2551 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2553 msiobj_release(&view->hdr);
2554 return rc;
2557 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2559 package->script->CurrentlyScripting = TRUE;
2561 return ERROR_SUCCESS;
2565 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2567 MSICOMPONENT *comp;
2568 DWORD progress = 0;
2569 DWORD total = 0;
2570 static const WCHAR q1[]=
2571 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2572 '`','R','e','g','i','s','t','r','y','`',0};
2573 UINT rc;
2574 MSIQUERY * view;
2575 MSIFEATURE *feature;
2576 MSIFILE *file;
2578 TRACE("InstallValidate\n");
2580 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2581 if (rc == ERROR_SUCCESS)
2583 MSI_IterateRecords( view, &progress, NULL, package );
2584 msiobj_release( &view->hdr );
2585 total += progress * REG_PROGRESS_VALUE;
2588 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2589 total += COMPONENT_PROGRESS_VALUE;
2591 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2592 total += file->FileSize;
2594 ui_progress(package,0,total,0,0);
2596 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2598 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2599 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2600 feature->ActionRequest);
2603 return ERROR_SUCCESS;
2606 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2608 MSIPACKAGE* package = (MSIPACKAGE*)param;
2609 LPCWSTR cond = NULL;
2610 LPCWSTR message = NULL;
2611 UINT r;
2613 static const WCHAR title[]=
2614 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2616 cond = MSI_RecordGetString(row,1);
2618 r = MSI_EvaluateConditionW(package,cond);
2619 if (r == MSICONDITION_FALSE)
2621 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2623 LPWSTR deformated;
2624 message = MSI_RecordGetString(row,2);
2625 deformat_string(package,message,&deformated);
2626 MessageBoxW(NULL,deformated,title,MB_OK);
2627 msi_free(deformated);
2630 return ERROR_INSTALL_FAILURE;
2633 return ERROR_SUCCESS;
2636 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2638 UINT rc;
2639 MSIQUERY * view = NULL;
2640 static const WCHAR ExecSeqQuery[] =
2641 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2642 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2644 TRACE("Checking launch conditions\n");
2646 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2647 if (rc != ERROR_SUCCESS)
2648 return ERROR_SUCCESS;
2650 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2651 msiobj_release(&view->hdr);
2653 return rc;
2656 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2659 if (!cmp->KeyPath)
2660 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2662 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2664 MSIRECORD * row = 0;
2665 UINT root,len;
2666 LPWSTR deformated,buffer,deformated_name;
2667 LPCWSTR key,name;
2668 static const WCHAR ExecSeqQuery[] =
2669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2670 '`','R','e','g','i','s','t','r','y','`',' ',
2671 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2672 ' ','=',' ' ,'\'','%','s','\'',0 };
2673 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2674 static const WCHAR fmt2[]=
2675 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2677 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2678 if (!row)
2679 return NULL;
2681 root = MSI_RecordGetInteger(row,2);
2682 key = MSI_RecordGetString(row, 3);
2683 name = MSI_RecordGetString(row, 4);
2684 deformat_string(package, key , &deformated);
2685 deformat_string(package, name, &deformated_name);
2687 len = strlenW(deformated) + 6;
2688 if (deformated_name)
2689 len+=strlenW(deformated_name);
2691 buffer = msi_alloc( len *sizeof(WCHAR));
2693 if (deformated_name)
2694 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2695 else
2696 sprintfW(buffer,fmt,root,deformated);
2698 msi_free(deformated);
2699 msi_free(deformated_name);
2700 msiobj_release(&row->hdr);
2702 return buffer;
2704 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2706 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2707 return NULL;
2709 else
2711 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2713 if (file)
2714 return strdupW( file->TargetPath );
2716 return NULL;
2719 static HKEY openSharedDLLsKey(void)
2721 HKEY hkey=0;
2722 static const WCHAR path[] =
2723 {'S','o','f','t','w','a','r','e','\\',
2724 'M','i','c','r','o','s','o','f','t','\\',
2725 'W','i','n','d','o','w','s','\\',
2726 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2727 'S','h','a','r','e','d','D','L','L','s',0};
2729 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2730 return hkey;
2733 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2735 HKEY hkey;
2736 DWORD count=0;
2737 DWORD type;
2738 DWORD sz = sizeof(count);
2739 DWORD rc;
2741 hkey = openSharedDLLsKey();
2742 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2743 if (rc != ERROR_SUCCESS)
2744 count = 0;
2745 RegCloseKey(hkey);
2746 return count;
2749 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2751 HKEY hkey;
2753 hkey = openSharedDLLsKey();
2754 if (count > 0)
2755 msi_reg_set_val_dword( hkey, path, count );
2756 else
2757 RegDeleteValueW(hkey,path);
2758 RegCloseKey(hkey);
2759 return count;
2763 * Return TRUE if the count should be written out and FALSE if not
2765 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2767 MSIFEATURE *feature;
2768 INT count = 0;
2769 BOOL write = FALSE;
2771 /* only refcount DLLs */
2772 if (comp->KeyPath == NULL ||
2773 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2774 comp->Attributes & msidbComponentAttributesODBCDataSource)
2775 write = FALSE;
2776 else
2778 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2779 write = (count > 0);
2781 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2782 write = TRUE;
2785 /* increment counts */
2786 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2788 ComponentList *cl;
2790 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2791 continue;
2793 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2795 if ( cl->component == comp )
2796 count++;
2800 /* decrement counts */
2801 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2803 ComponentList *cl;
2805 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2806 continue;
2808 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2810 if ( cl->component == comp )
2811 count--;
2815 /* ref count all the files in the component */
2816 if (write)
2818 MSIFILE *file;
2820 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2822 if (file->Component == comp)
2823 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2827 /* add a count for permanent */
2828 if (comp->Attributes & msidbComponentAttributesPermanent)
2829 count ++;
2831 comp->RefCount = count;
2833 if (write)
2834 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2838 * Ok further analysis makes me think that this work is
2839 * actually done in the PublishComponents and PublishFeatures
2840 * step, and not here. It appears like the keypath and all that is
2841 * resolved in this step, however actually written in the Publish steps.
2842 * But we will leave it here for now because it is unclear
2844 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2846 WCHAR squished_pc[GUID_SIZE];
2847 WCHAR squished_cc[GUID_SIZE];
2848 UINT rc;
2849 MSICOMPONENT *comp;
2850 HKEY hkey;
2852 TRACE("\n");
2854 /* writes the Component values to the registry */
2856 squash_guid(package->ProductCode,squished_pc);
2857 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2859 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2861 MSIRECORD * uirow;
2863 ui_progress(package,2,0,0,0);
2864 if (!comp->ComponentId)
2865 continue;
2867 squash_guid(comp->ComponentId,squished_cc);
2869 msi_free(comp->FullKeypath);
2870 comp->FullKeypath = resolve_keypath( package, comp );
2872 /* do the refcounting */
2873 ACTION_RefCountComponent( package, comp );
2875 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2876 debugstr_w(comp->Component),
2877 debugstr_w(squished_cc),
2878 debugstr_w(comp->FullKeypath),
2879 comp->RefCount);
2881 * Write the keypath out if the component is to be registered
2882 * and delete the key if the component is to be unregistered
2884 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2886 if (!comp->FullKeypath)
2887 continue;
2889 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2890 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2891 else
2892 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2894 if (rc != ERROR_SUCCESS)
2895 continue;
2897 if (comp->Attributes & msidbComponentAttributesPermanent)
2899 static const WCHAR szPermKey[] =
2900 { '0','0','0','0','0','0','0','0','0','0','0','0',
2901 '0','0','0','0','0','0','0','0','0','0','0','0',
2902 '0','0','0','0','0','0','0','0',0 };
2904 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2907 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2908 RegCloseKey(hkey);
2910 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2912 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2913 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2914 else
2915 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2918 /* UI stuff */
2919 uirow = MSI_CreateRecord(3);
2920 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2921 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2922 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2923 ui_actiondata(package,szProcessComponents,uirow);
2924 msiobj_release( &uirow->hdr );
2927 return ERROR_SUCCESS;
2930 typedef struct {
2931 CLSID clsid;
2932 LPWSTR source;
2934 LPWSTR path;
2935 ITypeLib *ptLib;
2936 } typelib_struct;
2938 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2939 LPWSTR lpszName, LONG_PTR lParam)
2941 TLIBATTR *attr;
2942 typelib_struct *tl_struct = (typelib_struct*) lParam;
2943 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2944 int sz;
2945 HRESULT res;
2947 if (!IS_INTRESOURCE(lpszName))
2949 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2950 return TRUE;
2953 sz = strlenW(tl_struct->source)+4;
2954 sz *= sizeof(WCHAR);
2956 if ((INT_PTR)lpszName == 1)
2957 tl_struct->path = strdupW(tl_struct->source);
2958 else
2960 tl_struct->path = msi_alloc(sz);
2961 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2964 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2965 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2966 if (!SUCCEEDED(res))
2968 msi_free(tl_struct->path);
2969 tl_struct->path = NULL;
2971 return TRUE;
2974 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2975 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2977 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2978 return FALSE;
2981 msi_free(tl_struct->path);
2982 tl_struct->path = NULL;
2984 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2985 ITypeLib_Release(tl_struct->ptLib);
2987 return TRUE;
2990 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2992 MSIPACKAGE* package = (MSIPACKAGE*)param;
2993 LPCWSTR component;
2994 MSICOMPONENT *comp;
2995 MSIFILE *file;
2996 typelib_struct tl_struct;
2997 HMODULE module;
2998 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3000 component = MSI_RecordGetString(row,3);
3001 comp = get_loaded_component(package,component);
3002 if (!comp)
3003 return ERROR_SUCCESS;
3005 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3007 TRACE("Skipping typelib reg due to disabled component\n");
3009 comp->Action = comp->Installed;
3011 return ERROR_SUCCESS;
3014 comp->Action = INSTALLSTATE_LOCAL;
3016 file = get_loaded_file( package, comp->KeyPath );
3017 if (!file)
3018 return ERROR_SUCCESS;
3020 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3021 if (module)
3023 LPCWSTR guid;
3024 guid = MSI_RecordGetString(row,1);
3025 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3026 tl_struct.source = strdupW( file->TargetPath );
3027 tl_struct.path = NULL;
3029 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3030 (LONG_PTR)&tl_struct);
3032 if (tl_struct.path)
3034 LPWSTR help = NULL;
3035 LPCWSTR helpid;
3036 HRESULT res;
3038 helpid = MSI_RecordGetString(row,6);
3040 if (helpid)
3041 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3042 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3043 msi_free(help);
3045 if (!SUCCEEDED(res))
3046 ERR("Failed to register type library %s\n",
3047 debugstr_w(tl_struct.path));
3048 else
3050 ui_actiondata(package,szRegisterTypeLibraries,row);
3052 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3055 ITypeLib_Release(tl_struct.ptLib);
3056 msi_free(tl_struct.path);
3058 else
3059 ERR("Failed to load type library %s\n",
3060 debugstr_w(tl_struct.source));
3062 FreeLibrary(module);
3063 msi_free(tl_struct.source);
3065 else
3066 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3068 return ERROR_SUCCESS;
3071 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3074 * OK this is a bit confusing.. I am given a _Component key and I believe
3075 * that the file that is being registered as a type library is the "key file
3076 * of that component" which I interpret to mean "The file in the KeyPath of
3077 * that component".
3079 UINT rc;
3080 MSIQUERY * view;
3081 static const WCHAR Query[] =
3082 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3083 '`','T','y','p','e','L','i','b','`',0};
3085 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3086 if (rc != ERROR_SUCCESS)
3087 return ERROR_SUCCESS;
3089 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3090 msiobj_release(&view->hdr);
3091 return rc;
3094 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3096 MSIPACKAGE *package = (MSIPACKAGE*)param;
3097 LPWSTR target_file, target_folder, filename;
3098 LPCWSTR buffer, extension;
3099 MSICOMPONENT *comp;
3100 static const WCHAR szlnk[]={'.','l','n','k',0};
3101 IShellLinkW *sl = NULL;
3102 IPersistFile *pf = NULL;
3103 HRESULT res;
3105 buffer = MSI_RecordGetString(row,4);
3106 comp = get_loaded_component(package,buffer);
3107 if (!comp)
3108 return ERROR_SUCCESS;
3110 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3112 TRACE("Skipping shortcut creation due to disabled component\n");
3114 comp->Action = comp->Installed;
3116 return ERROR_SUCCESS;
3119 comp->Action = INSTALLSTATE_LOCAL;
3121 ui_actiondata(package,szCreateShortcuts,row);
3123 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3124 &IID_IShellLinkW, (LPVOID *) &sl );
3126 if (FAILED( res ))
3128 ERR("CLSID_ShellLink not available\n");
3129 goto err;
3132 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3133 if (FAILED( res ))
3135 ERR("QueryInterface(IID_IPersistFile) failed\n");
3136 goto err;
3139 buffer = MSI_RecordGetString(row,2);
3140 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3142 /* may be needed because of a bug somewhere else */
3143 create_full_pathW(target_folder);
3145 filename = msi_dup_record_field( row, 3 );
3146 reduce_to_longfilename(filename);
3148 extension = strchrW(filename,'.');
3149 if (!extension || strcmpiW(extension,szlnk))
3151 int len = strlenW(filename);
3152 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3153 memcpy(filename + len, szlnk, sizeof(szlnk));
3155 target_file = build_directory_name(2, target_folder, filename);
3156 msi_free(target_folder);
3157 msi_free(filename);
3159 buffer = MSI_RecordGetString(row,5);
3160 if (strchrW(buffer,'['))
3162 LPWSTR deformated;
3163 deformat_string(package,buffer,&deformated);
3164 IShellLinkW_SetPath(sl,deformated);
3165 msi_free(deformated);
3167 else
3169 FIXME("poorly handled shortcut format, advertised shortcut\n");
3170 IShellLinkW_SetPath(sl,comp->FullKeypath);
3173 if (!MSI_RecordIsNull(row,6))
3175 LPWSTR deformated;
3176 buffer = MSI_RecordGetString(row,6);
3177 deformat_string(package,buffer,&deformated);
3178 IShellLinkW_SetArguments(sl,deformated);
3179 msi_free(deformated);
3182 if (!MSI_RecordIsNull(row,7))
3184 buffer = MSI_RecordGetString(row,7);
3185 IShellLinkW_SetDescription(sl,buffer);
3188 if (!MSI_RecordIsNull(row,8))
3189 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3191 if (!MSI_RecordIsNull(row,9))
3193 LPWSTR Path;
3194 INT index;
3196 buffer = MSI_RecordGetString(row,9);
3198 Path = build_icon_path(package,buffer);
3199 index = MSI_RecordGetInteger(row,10);
3201 /* no value means 0 */
3202 if (index == MSI_NULL_INTEGER)
3203 index = 0;
3205 IShellLinkW_SetIconLocation(sl,Path,index);
3206 msi_free(Path);
3209 if (!MSI_RecordIsNull(row,11))
3210 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3212 if (!MSI_RecordIsNull(row,12))
3214 LPWSTR Path;
3215 buffer = MSI_RecordGetString(row,12);
3216 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3217 if (Path)
3218 IShellLinkW_SetWorkingDirectory(sl,Path);
3219 msi_free(Path);
3222 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3223 IPersistFile_Save(pf,target_file,FALSE);
3225 msi_free(target_file);
3227 err:
3228 if (pf)
3229 IPersistFile_Release( pf );
3230 if (sl)
3231 IShellLinkW_Release( sl );
3233 return ERROR_SUCCESS;
3236 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3238 UINT rc;
3239 HRESULT res;
3240 MSIQUERY * view;
3241 static const WCHAR Query[] =
3242 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3243 '`','S','h','o','r','t','c','u','t','`',0};
3245 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3246 if (rc != ERROR_SUCCESS)
3247 return ERROR_SUCCESS;
3249 res = CoInitialize( NULL );
3250 if (FAILED (res))
3252 ERR("CoInitialize failed\n");
3253 return ERROR_FUNCTION_FAILED;
3256 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3257 msiobj_release(&view->hdr);
3259 CoUninitialize();
3261 return rc;
3264 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3266 MSIPACKAGE* package = (MSIPACKAGE*)param;
3267 HANDLE the_file;
3268 LPWSTR FilePath;
3269 LPCWSTR FileName;
3270 CHAR buffer[1024];
3271 DWORD sz;
3272 UINT rc;
3273 MSIRECORD *uirow;
3275 FileName = MSI_RecordGetString(row,1);
3276 if (!FileName)
3278 ERR("Unable to get FileName\n");
3279 return ERROR_SUCCESS;
3282 FilePath = build_icon_path(package,FileName);
3284 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3286 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3287 FILE_ATTRIBUTE_NORMAL, NULL);
3289 if (the_file == INVALID_HANDLE_VALUE)
3291 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3292 msi_free(FilePath);
3293 return ERROR_SUCCESS;
3298 DWORD write;
3299 sz = 1024;
3300 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3301 if (rc != ERROR_SUCCESS)
3303 ERR("Failed to get stream\n");
3304 CloseHandle(the_file);
3305 DeleteFileW(FilePath);
3306 break;
3308 WriteFile(the_file,buffer,sz,&write,NULL);
3309 } while (sz == 1024);
3311 msi_free(FilePath);
3313 CloseHandle(the_file);
3315 uirow = MSI_CreateRecord(1);
3316 MSI_RecordSetStringW(uirow,1,FileName);
3317 ui_actiondata(package,szPublishProduct,uirow);
3318 msiobj_release( &uirow->hdr );
3320 return ERROR_SUCCESS;
3323 static UINT msi_publish_icons(MSIPACKAGE *package)
3325 UINT r;
3326 MSIQUERY *view;
3328 static const WCHAR query[]= {
3329 'S','E','L','E','C','T',' ','*',' ',
3330 'F','R','O','M',' ','`','I','c','o','n','`',0};
3332 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3333 if (r == ERROR_SUCCESS)
3335 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3336 msiobj_release(&view->hdr);
3339 return ERROR_SUCCESS;
3342 static UINT msi_publish_sourcelist(MSIPACKAGE *package)
3344 UINT r;
3345 LPWSTR buffer;
3346 MSIMEDIADISK *disk;
3347 MSISOURCELISTINFO *info;
3349 static const WCHAR szEmpty[] = {0};
3351 buffer = strrchrW(package->PackagePath, '\\') + 1;
3352 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3353 package->Context, MSICODE_PRODUCT,
3354 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3355 if (r != ERROR_SUCCESS)
3356 return r;
3358 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3359 package->Context, MSICODE_PRODUCT,
3360 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3361 if (r != ERROR_SUCCESS)
3362 return r;
3364 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3365 package->Context, MSICODE_PRODUCT,
3366 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3367 if (r != ERROR_SUCCESS)
3368 return r;
3370 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3372 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3373 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3374 info->options, info->value);
3375 else
3376 MsiSourceListSetInfoW(package->ProductCode, NULL,
3377 info->context, info->options,
3378 info->property, info->value);
3381 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3383 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3384 disk->context, disk->options,
3385 disk->disk_id, disk->volume_label, disk->disk_prompt);
3388 return ERROR_SUCCESS;
3391 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3393 MSIHANDLE hdb, suminfo;
3394 WCHAR guids[MAX_PATH];
3395 WCHAR packcode[SQUISH_GUID_SIZE];
3396 LPWSTR buffer;
3397 LPWSTR ptr;
3398 DWORD langid;
3399 DWORD size;
3400 UINT r;
3402 static const WCHAR szProductLanguage[] =
3403 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3404 static const WCHAR szARPProductIcon[] =
3405 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3406 static const WCHAR szProductVersion[] =
3407 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3408 static const WCHAR szAssignment[] =
3409 {'A','s','s','i','g','n','m','e','n','t',0};
3410 static const WCHAR szAdvertiseFlags[] =
3411 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3412 static const WCHAR szClients[] =
3413 {'C','l','i','e','n','t','s',0};
3414 static const WCHAR szColon[] = {':',0};
3416 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3417 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3418 msi_free(buffer);
3420 langid = msi_get_property_int(package, szProductLanguage, 0);
3421 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3423 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3424 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3426 /* FIXME */
3427 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3429 buffer = msi_dup_property(package, szARPProductIcon);
3430 if (buffer)
3432 LPWSTR path = build_icon_path(package,buffer);
3433 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3434 msi_free(path);
3435 msi_free(buffer);
3438 buffer = msi_dup_property(package, szProductVersion);
3439 if (buffer)
3441 DWORD verdword = msi_version_str_to_dword(buffer);
3442 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3443 msi_free(buffer);
3446 msi_reg_set_val_dword(hkey, szAssignment, 0);
3447 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3448 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3449 msi_reg_set_val_str(hkey, szClients, szColon);
3451 hdb = alloc_msihandle(&package->db->hdr);
3452 if (!hdb)
3453 return ERROR_NOT_ENOUGH_MEMORY;
3455 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3456 MsiCloseHandle(hdb);
3457 if (r != ERROR_SUCCESS)
3458 goto done;
3460 size = MAX_PATH;
3461 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3462 NULL, guids, &size);
3463 if (r != ERROR_SUCCESS)
3464 goto done;
3466 ptr = strchrW(guids, ';');
3467 if (ptr) *ptr = 0;
3468 squash_guid(guids, packcode);
3469 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3471 done:
3472 MsiCloseHandle(suminfo);
3473 return ERROR_SUCCESS;
3476 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3478 UINT r;
3479 HKEY hkey;
3480 LPWSTR upgrade;
3481 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3483 static const WCHAR szUpgradeCode[] =
3484 {'U','p','g','r','a','d','e','C','o','d','e',0};
3486 upgrade = msi_dup_property(package, szUpgradeCode);
3487 if (!upgrade)
3488 return ERROR_SUCCESS;
3490 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3491 if (r != ERROR_SUCCESS)
3492 goto done;
3494 squash_guid(package->ProductCode, squashed_pc);
3495 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3497 RegCloseKey(hkey);
3499 done:
3500 msi_free(upgrade);
3501 return r;
3504 static BOOL msi_check_publish(MSIPACKAGE *package)
3506 MSIFEATURE *feature;
3508 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3510 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3511 return TRUE;
3514 return FALSE;
3517 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3519 MSIFEATURE *feature;
3521 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3523 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3524 return FALSE;
3527 return TRUE;
3531 * 99% of the work done here is only done for
3532 * advertised installs. However this is where the
3533 * Icon table is processed and written out
3534 * so that is what I am going to do here.
3536 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3538 UINT rc;
3539 HKEY hukey=0;
3540 HKEY hudkey=0;
3541 HKEY source;
3543 static const WCHAR szSourceList[] =
3544 {'S','o','u','r','c','e','L','i','s','t',0};
3546 /* FIXME: also need to publish if the product is in advertise mode */
3547 if (!msi_check_publish(package))
3548 return ERROR_SUCCESS;
3550 /* ok there is a lot more done here but i need to figure out what */
3552 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3554 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3555 if (rc != ERROR_SUCCESS)
3556 goto end;
3558 else
3560 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3561 if (rc != ERROR_SUCCESS)
3562 goto end;
3565 rc = RegCreateKeyW(hukey, szSourceList, &source);
3566 if (rc != ERROR_SUCCESS)
3567 goto end;
3569 RegCloseKey(source);
3571 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3572 if (rc != ERROR_SUCCESS)
3573 goto end;
3575 rc = msi_publish_upgrade_code(package);
3576 if (rc != ERROR_SUCCESS)
3577 goto end;
3579 rc = msi_publish_product_properties(package, hukey);
3580 if (rc != ERROR_SUCCESS)
3581 goto end;
3583 rc = msi_publish_sourcelist(package);
3584 if (rc != ERROR_SUCCESS)
3585 goto end;
3587 rc = msi_publish_icons(package);
3589 end:
3590 RegCloseKey(hukey);
3591 RegCloseKey(hudkey);
3593 return rc;
3596 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3598 MSIPACKAGE *package = (MSIPACKAGE*)param;
3599 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3600 LPWSTR deformated_section, deformated_key, deformated_value;
3601 LPWSTR folder, fullname = NULL;
3602 MSIRECORD * uirow;
3603 INT action;
3604 MSICOMPONENT *comp;
3605 static const WCHAR szWindowsFolder[] =
3606 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3608 component = MSI_RecordGetString(row, 8);
3609 comp = get_loaded_component(package,component);
3611 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3613 TRACE("Skipping ini file due to disabled component %s\n",
3614 debugstr_w(component));
3616 comp->Action = comp->Installed;
3618 return ERROR_SUCCESS;
3621 comp->Action = INSTALLSTATE_LOCAL;
3623 identifier = MSI_RecordGetString(row,1);
3624 filename = MSI_RecordGetString(row,2);
3625 dirproperty = MSI_RecordGetString(row,3);
3626 section = MSI_RecordGetString(row,4);
3627 key = MSI_RecordGetString(row,5);
3628 value = MSI_RecordGetString(row,6);
3629 action = MSI_RecordGetInteger(row,7);
3631 deformat_string(package,section,&deformated_section);
3632 deformat_string(package,key,&deformated_key);
3633 deformat_string(package,value,&deformated_value);
3635 if (dirproperty)
3637 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3638 if (!folder)
3639 folder = msi_dup_property( package, dirproperty );
3641 else
3642 folder = msi_dup_property( package, szWindowsFolder );
3644 if (!folder)
3646 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3647 goto cleanup;
3650 fullname = build_directory_name(2, folder, filename);
3652 if (action == 0)
3654 TRACE("Adding value %s to section %s in %s\n",
3655 debugstr_w(deformated_key), debugstr_w(deformated_section),
3656 debugstr_w(fullname));
3657 WritePrivateProfileStringW(deformated_section, deformated_key,
3658 deformated_value, fullname);
3660 else if (action == 1)
3662 WCHAR returned[10];
3663 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3664 returned, 10, fullname);
3665 if (returned[0] == 0)
3667 TRACE("Adding value %s to section %s in %s\n",
3668 debugstr_w(deformated_key), debugstr_w(deformated_section),
3669 debugstr_w(fullname));
3671 WritePrivateProfileStringW(deformated_section, deformated_key,
3672 deformated_value, fullname);
3675 else if (action == 3)
3676 FIXME("Append to existing section not yet implemented\n");
3678 uirow = MSI_CreateRecord(4);
3679 MSI_RecordSetStringW(uirow,1,identifier);
3680 MSI_RecordSetStringW(uirow,2,deformated_section);
3681 MSI_RecordSetStringW(uirow,3,deformated_key);
3682 MSI_RecordSetStringW(uirow,4,deformated_value);
3683 ui_actiondata(package,szWriteIniValues,uirow);
3684 msiobj_release( &uirow->hdr );
3685 cleanup:
3686 msi_free(fullname);
3687 msi_free(folder);
3688 msi_free(deformated_key);
3689 msi_free(deformated_value);
3690 msi_free(deformated_section);
3691 return ERROR_SUCCESS;
3694 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3696 UINT rc;
3697 MSIQUERY * view;
3698 static const WCHAR ExecSeqQuery[] =
3699 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3700 '`','I','n','i','F','i','l','e','`',0};
3702 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3703 if (rc != ERROR_SUCCESS)
3705 TRACE("no IniFile table\n");
3706 return ERROR_SUCCESS;
3709 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3710 msiobj_release(&view->hdr);
3711 return rc;
3714 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3716 MSIPACKAGE *package = (MSIPACKAGE*)param;
3717 LPCWSTR filename;
3718 LPWSTR FullName;
3719 MSIFILE *file;
3720 DWORD len;
3721 static const WCHAR ExeStr[] =
3722 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3723 static const WCHAR close[] = {'\"',0};
3724 STARTUPINFOW si;
3725 PROCESS_INFORMATION info;
3726 BOOL brc;
3727 MSIRECORD *uirow;
3728 LPWSTR uipath, p;
3730 memset(&si,0,sizeof(STARTUPINFOW));
3732 filename = MSI_RecordGetString(row,1);
3733 file = get_loaded_file( package, filename );
3735 if (!file)
3737 ERR("Unable to find file id %s\n",debugstr_w(filename));
3738 return ERROR_SUCCESS;
3741 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3743 FullName = msi_alloc(len*sizeof(WCHAR));
3744 strcpyW(FullName,ExeStr);
3745 strcatW( FullName, file->TargetPath );
3746 strcatW(FullName,close);
3748 TRACE("Registering %s\n",debugstr_w(FullName));
3749 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3750 &si, &info);
3752 if (brc)
3753 msi_dialog_check_messages(info.hProcess);
3755 msi_free(FullName);
3757 /* the UI chunk */
3758 uirow = MSI_CreateRecord( 2 );
3759 uipath = strdupW( file->TargetPath );
3760 p = strrchrW(uipath,'\\');
3761 if (p)
3762 p[0]=0;
3763 MSI_RecordSetStringW( uirow, 1, &p[1] );
3764 MSI_RecordSetStringW( uirow, 2, uipath);
3765 ui_actiondata( package, szSelfRegModules, uirow);
3766 msiobj_release( &uirow->hdr );
3767 msi_free( uipath );
3768 /* FIXME: call ui_progress? */
3770 return ERROR_SUCCESS;
3773 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3775 UINT rc;
3776 MSIQUERY * view;
3777 static const WCHAR ExecSeqQuery[] =
3778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3779 '`','S','e','l','f','R','e','g','`',0};
3781 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3782 if (rc != ERROR_SUCCESS)
3784 TRACE("no SelfReg table\n");
3785 return ERROR_SUCCESS;
3788 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3789 msiobj_release(&view->hdr);
3791 return ERROR_SUCCESS;
3794 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3796 MSIFEATURE *feature;
3797 UINT rc;
3798 HKEY hkey;
3799 HKEY userdata;
3801 if (!msi_check_publish(package))
3802 return ERROR_SUCCESS;
3804 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3806 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3807 &hkey, TRUE);
3808 if (rc != ERROR_SUCCESS)
3809 goto end;
3811 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3812 &userdata, TRUE);
3813 if (rc != ERROR_SUCCESS)
3814 goto end;
3816 else
3818 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3819 if (rc != ERROR_SUCCESS)
3820 goto end;
3822 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3823 &userdata, TRUE);
3824 if (rc != ERROR_SUCCESS)
3825 goto end;
3828 /* here the guids are base 85 encoded */
3829 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3831 ComponentList *cl;
3832 LPWSTR data = NULL;
3833 GUID clsid;
3834 INT size;
3835 BOOL absent = FALSE;
3836 MSIRECORD *uirow;
3838 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3839 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3840 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3841 absent = TRUE;
3843 size = 1;
3844 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3846 size += 21;
3848 if (feature->Feature_Parent)
3849 size += strlenW( feature->Feature_Parent )+2;
3851 data = msi_alloc(size * sizeof(WCHAR));
3853 data[0] = 0;
3854 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3856 MSICOMPONENT* component = cl->component;
3857 WCHAR buf[21];
3859 buf[0] = 0;
3860 if (component->ComponentId)
3862 TRACE("From %s\n",debugstr_w(component->ComponentId));
3863 CLSIDFromString(component->ComponentId, &clsid);
3864 encode_base85_guid(&clsid,buf);
3865 TRACE("to %s\n",debugstr_w(buf));
3866 strcatW(data,buf);
3870 if (feature->Feature_Parent)
3872 static const WCHAR sep[] = {'\2',0};
3873 strcatW(data,sep);
3874 strcatW(data,feature->Feature_Parent);
3877 msi_reg_set_val_str( userdata, feature->Feature, data );
3878 msi_free(data);
3880 size = 0;
3881 if (feature->Feature_Parent)
3882 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3883 if (!absent)
3885 static const WCHAR emptyW[] = {0};
3886 size += sizeof(WCHAR);
3887 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3888 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3890 else
3892 size += 2*sizeof(WCHAR);
3893 data = msi_alloc(size);
3894 data[0] = 0x6;
3895 data[1] = 0;
3896 if (feature->Feature_Parent)
3897 strcpyW( &data[1], feature->Feature_Parent );
3898 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3899 (LPBYTE)data,size);
3900 msi_free(data);
3903 /* the UI chunk */
3904 uirow = MSI_CreateRecord( 1 );
3905 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3906 ui_actiondata( package, szPublishFeatures, uirow);
3907 msiobj_release( &uirow->hdr );
3908 /* FIXME: call ui_progress? */
3911 end:
3912 RegCloseKey(hkey);
3913 RegCloseKey(userdata);
3914 return rc;
3917 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3919 UINT r;
3920 HKEY hkey;
3922 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3924 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3925 if (r == ERROR_SUCCESS)
3927 RegDeleteValueW(hkey, feature->Feature);
3928 RegCloseKey(hkey);
3931 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3932 if (r == ERROR_SUCCESS)
3934 RegDeleteValueW(hkey, feature->Feature);
3935 RegCloseKey(hkey);
3938 return ERROR_SUCCESS;
3941 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3943 MSIFEATURE *feature;
3945 if (!msi_check_unpublish(package))
3946 return ERROR_SUCCESS;
3948 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3950 msi_unpublish_feature(package, feature);
3953 return ERROR_SUCCESS;
3956 static UINT msi_get_local_package_name( LPWSTR path )
3958 static const WCHAR szInstaller[] = {
3959 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3960 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3961 DWORD time, len, i;
3962 HANDLE handle;
3964 time = GetTickCount();
3965 GetWindowsDirectoryW( path, MAX_PATH );
3966 lstrcatW( path, szInstaller );
3967 CreateDirectoryW( path, NULL );
3969 len = lstrlenW(path);
3970 for (i=0; i<0x10000; i++)
3972 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3973 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3974 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3975 if (handle != INVALID_HANDLE_VALUE)
3977 CloseHandle(handle);
3978 break;
3980 if (GetLastError() != ERROR_FILE_EXISTS &&
3981 GetLastError() != ERROR_SHARING_VIOLATION)
3982 return ERROR_FUNCTION_FAILED;
3985 return ERROR_SUCCESS;
3988 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3990 WCHAR packagefile[MAX_PATH];
3991 UINT r;
3993 r = msi_get_local_package_name( packagefile );
3994 if (r != ERROR_SUCCESS)
3995 return r;
3997 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3999 r = CopyFileW( package->db->path, packagefile, FALSE);
4001 if (!r)
4003 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4004 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4005 return ERROR_FUNCTION_FAILED;
4008 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4010 return ERROR_SUCCESS;
4013 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4015 LPWSTR prop, val, key;
4016 SYSTEMTIME systime;
4017 DWORD size, langid;
4018 WCHAR date[9];
4019 LPWSTR buffer;
4021 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4022 static const WCHAR szWindowsInstaller[] =
4023 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4024 static const WCHAR modpath_fmt[] =
4025 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4026 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4027 static const WCHAR szModifyPath[] =
4028 {'M','o','d','i','f','y','P','a','t','h',0};
4029 static const WCHAR szUninstallString[] =
4030 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4031 static const WCHAR szEstimatedSize[] =
4032 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4033 static const WCHAR szProductLanguage[] =
4034 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4035 static const WCHAR szProductVersion[] =
4036 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4037 static const WCHAR szProductName[] =
4038 {'P','r','o','d','u','c','t','N','a','m','e',0};
4039 static const WCHAR szDisplayName[] =
4040 {'D','i','s','p','l','a','y','N','a','m','e',0};
4041 static const WCHAR szDisplayVersion[] =
4042 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4043 static const WCHAR szManufacturer[] =
4044 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4046 static const LPCSTR propval[] = {
4047 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4048 "ARPCONTACT", "Contact",
4049 "ARPCOMMENTS", "Comments",
4050 "ProductName", "DisplayName",
4051 "ProductVersion", "DisplayVersion",
4052 "ARPHELPLINK", "HelpLink",
4053 "ARPHELPTELEPHONE", "HelpTelephone",
4054 "ARPINSTALLLOCATION", "InstallLocation",
4055 "SourceDir", "InstallSource",
4056 "Manufacturer", "Publisher",
4057 "ARPREADME", "Readme",
4058 "ARPSIZE", "Size",
4059 "ARPURLINFOABOUT", "URLInfoAbout",
4060 "ARPURLUPDATEINFO", "URLUpdateInfo",
4061 NULL,
4063 const LPCSTR *p = propval;
4065 while (*p)
4067 prop = strdupAtoW(*p++);
4068 key = strdupAtoW(*p++);
4069 val = msi_dup_property(package, prop);
4070 msi_reg_set_val_str(hkey, key, val);
4071 msi_free(val);
4072 msi_free(key);
4073 msi_free(prop);
4076 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4078 size = deformat_string(package, modpath_fmt, &buffer);
4079 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4080 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4081 msi_free(buffer);
4083 /* FIXME: Write real Estimated Size when we have it */
4084 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4086 buffer = msi_dup_property(package, szProductName);
4087 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4088 msi_free(buffer);
4090 buffer = msi_dup_property(package, cszSourceDir);
4091 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4092 msi_free(buffer);
4094 buffer = msi_dup_property(package, szManufacturer);
4095 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4096 msi_free(buffer);
4098 GetLocalTime(&systime);
4099 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4100 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4102 langid = msi_get_property_int(package, szProductLanguage, 0);
4103 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4105 buffer = msi_dup_property(package, szProductVersion);
4106 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4107 if (buffer)
4109 DWORD verdword = msi_version_str_to_dword(buffer);
4111 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4112 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4113 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4114 msi_free(buffer);
4117 return ERROR_SUCCESS;
4120 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4122 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4123 LPWSTR upgrade_code;
4124 HKEY hkey, props;
4125 HKEY upgrade;
4126 UINT rc;
4128 static const WCHAR szUpgradeCode[] = {
4129 'U','p','g','r','a','d','e','C','o','d','e',0};
4131 /* FIXME: also need to publish if the product is in advertise mode */
4132 if (!msi_check_publish(package))
4133 return ERROR_SUCCESS;
4135 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4136 if (rc != ERROR_SUCCESS)
4137 return rc;
4139 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4141 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4142 if (rc != ERROR_SUCCESS)
4143 goto done;
4145 else
4147 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4148 if (rc != ERROR_SUCCESS)
4149 goto done;
4152 msi_make_package_local(package, props);
4154 rc = msi_publish_install_properties(package, hkey);
4155 if (rc != ERROR_SUCCESS)
4156 goto done;
4158 rc = msi_publish_install_properties(package, props);
4159 if (rc != ERROR_SUCCESS)
4160 goto done;
4162 upgrade_code = msi_dup_property(package, szUpgradeCode);
4163 if (upgrade_code)
4165 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4166 squash_guid(package->ProductCode, squashed_pc);
4167 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4168 RegCloseKey(upgrade);
4169 msi_free(upgrade_code);
4172 done:
4173 RegCloseKey(hkey);
4175 return ERROR_SUCCESS;
4178 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4180 return execute_script(package,INSTALL_SCRIPT);
4183 static UINT msi_unpublish_product(MSIPACKAGE *package)
4185 LPWSTR upgrade;
4186 LPWSTR remove = NULL;
4187 LPWSTR *features = NULL;
4188 BOOL full_uninstall = TRUE;
4189 MSIFEATURE *feature;
4191 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4192 static const WCHAR szAll[] = {'A','L','L',0};
4193 static const WCHAR szUpgradeCode[] =
4194 {'U','p','g','r','a','d','e','C','o','d','e',0};
4196 remove = msi_dup_property(package, szRemove);
4197 if (!remove)
4198 return ERROR_SUCCESS;
4200 features = msi_split_string(remove, ',');
4201 if (!features)
4203 msi_free(remove);
4204 ERR("REMOVE feature list is empty!\n");
4205 return ERROR_FUNCTION_FAILED;
4208 if (!lstrcmpW(features[0], szAll))
4209 full_uninstall = TRUE;
4210 else
4212 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4214 if (feature->Action != INSTALLSTATE_ABSENT)
4215 full_uninstall = FALSE;
4219 if (!full_uninstall)
4220 goto done;
4222 MSIREG_DeleteProductKey(package->ProductCode);
4223 MSIREG_DeleteUserProductKey(package->ProductCode);
4224 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4225 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4226 MSIREG_DeleteUninstallKey(package->ProductCode);
4228 upgrade = msi_dup_property(package, szUpgradeCode);
4229 if (upgrade)
4231 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4232 msi_free(upgrade);
4235 done:
4236 msi_free(remove);
4237 msi_free(features);
4238 return ERROR_SUCCESS;
4241 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4243 UINT rc;
4245 rc = msi_unpublish_product(package);
4246 if (rc != ERROR_SUCCESS)
4247 return rc;
4249 /* turn off scheduling */
4250 package->script->CurrentlyScripting= FALSE;
4252 /* first do the same as an InstallExecute */
4253 rc = ACTION_InstallExecute(package);
4254 if (rc != ERROR_SUCCESS)
4255 return rc;
4257 /* then handle Commit Actions */
4258 rc = execute_script(package,COMMIT_SCRIPT);
4260 return rc;
4263 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4265 static const WCHAR RunOnce[] = {
4266 'S','o','f','t','w','a','r','e','\\',
4267 'M','i','c','r','o','s','o','f','t','\\',
4268 'W','i','n','d','o','w','s','\\',
4269 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4270 'R','u','n','O','n','c','e',0};
4271 static const WCHAR InstallRunOnce[] = {
4272 'S','o','f','t','w','a','r','e','\\',
4273 'M','i','c','r','o','s','o','f','t','\\',
4274 'W','i','n','d','o','w','s','\\',
4275 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4276 'I','n','s','t','a','l','l','e','r','\\',
4277 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4279 static const WCHAR msiexec_fmt[] = {
4280 '%','s',
4281 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4282 '\"','%','s','\"',0};
4283 static const WCHAR install_fmt[] = {
4284 '/','I',' ','\"','%','s','\"',' ',
4285 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4286 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4287 WCHAR buffer[256], sysdir[MAX_PATH];
4288 HKEY hkey;
4289 WCHAR squished_pc[100];
4291 squash_guid(package->ProductCode,squished_pc);
4293 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4294 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4295 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4296 squished_pc);
4298 msi_reg_set_val_str( hkey, squished_pc, buffer );
4299 RegCloseKey(hkey);
4301 TRACE("Reboot command %s\n",debugstr_w(buffer));
4303 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4304 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4306 msi_reg_set_val_str( hkey, squished_pc, buffer );
4307 RegCloseKey(hkey);
4309 return ERROR_INSTALL_SUSPEND;
4312 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4314 DWORD attrib;
4315 UINT rc;
4318 * We are currently doing what should be done here in the top level Install
4319 * however for Administrative and uninstalls this step will be needed
4321 if (!package->PackagePath)
4322 return ERROR_SUCCESS;
4324 msi_set_sourcedir_props(package, TRUE);
4326 attrib = GetFileAttributesW(package->db->path);
4327 if (attrib == INVALID_FILE_ATTRIBUTES)
4329 LPWSTR prompt;
4330 LPWSTR msg;
4331 DWORD size = 0;
4333 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4334 package->Context, MSICODE_PRODUCT,
4335 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4336 if (rc == ERROR_MORE_DATA)
4338 prompt = msi_alloc(size * sizeof(WCHAR));
4339 MsiSourceListGetInfoW(package->ProductCode, NULL,
4340 package->Context, MSICODE_PRODUCT,
4341 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4343 else
4344 prompt = strdupW(package->db->path);
4346 msg = generate_error_string(package,1302,1,prompt);
4347 while(attrib == INVALID_FILE_ATTRIBUTES)
4349 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4350 if (rc == IDCANCEL)
4352 rc = ERROR_INSTALL_USEREXIT;
4353 break;
4355 attrib = GetFileAttributesW(package->db->path);
4357 msi_free(prompt);
4358 rc = ERROR_SUCCESS;
4360 else
4361 return ERROR_SUCCESS;
4363 return rc;
4366 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4368 HKEY hkey=0;
4369 LPWSTR buffer;
4370 LPWSTR productid;
4371 UINT rc,i;
4373 static const WCHAR szPropKeys[][80] =
4375 {'P','r','o','d','u','c','t','I','D',0},
4376 {'U','S','E','R','N','A','M','E',0},
4377 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4378 {0},
4381 static const WCHAR szRegKeys[][80] =
4383 {'P','r','o','d','u','c','t','I','D',0},
4384 {'R','e','g','O','w','n','e','r',0},
4385 {'R','e','g','C','o','m','p','a','n','y',0},
4386 {0},
4389 if (msi_check_unpublish(package))
4391 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4392 return ERROR_SUCCESS;
4395 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4396 if (!productid)
4397 return ERROR_SUCCESS;
4399 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4400 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4401 else
4402 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4404 if (rc != ERROR_SUCCESS)
4405 goto end;
4407 for( i = 0; szPropKeys[i][0]; i++ )
4409 buffer = msi_dup_property( package, szPropKeys[i] );
4410 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4411 msi_free( buffer );
4414 end:
4415 msi_free(productid);
4416 RegCloseKey(hkey);
4418 /* FIXME: call ui_actiondata */
4420 return rc;
4424 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4426 UINT rc;
4428 package->script->InWhatSequence |= SEQUENCE_EXEC;
4429 rc = ACTION_ProcessExecSequence(package,FALSE);
4430 return rc;
4434 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4436 MSIPACKAGE *package = (MSIPACKAGE*)param;
4437 LPCWSTR compgroupid=NULL;
4438 LPCWSTR feature=NULL;
4439 LPCWSTR text = NULL;
4440 LPCWSTR qualifier = NULL;
4441 LPCWSTR component = NULL;
4442 LPWSTR advertise = NULL;
4443 LPWSTR output = NULL;
4444 HKEY hkey;
4445 UINT rc = ERROR_SUCCESS;
4446 MSICOMPONENT *comp;
4447 DWORD sz = 0;
4448 MSIRECORD *uirow;
4450 component = MSI_RecordGetString(rec,3);
4451 comp = get_loaded_component(package,component);
4453 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4454 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4455 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4457 TRACE("Skipping: Component %s not scheduled for install\n",
4458 debugstr_w(component));
4460 return ERROR_SUCCESS;
4463 compgroupid = MSI_RecordGetString(rec,1);
4464 qualifier = MSI_RecordGetString(rec,2);
4466 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4467 if (rc != ERROR_SUCCESS)
4468 goto end;
4470 text = MSI_RecordGetString(rec,4);
4471 feature = MSI_RecordGetString(rec,5);
4473 advertise = create_component_advertise_string(package, comp, feature);
4475 sz = strlenW(advertise);
4477 if (text)
4478 sz += lstrlenW(text);
4480 sz+=3;
4481 sz *= sizeof(WCHAR);
4483 output = msi_alloc_zero(sz);
4484 strcpyW(output,advertise);
4485 msi_free(advertise);
4487 if (text)
4488 strcatW(output,text);
4490 msi_reg_set_val_multi_str( hkey, qualifier, output );
4492 end:
4493 RegCloseKey(hkey);
4494 msi_free(output);
4496 /* the UI chunk */
4497 uirow = MSI_CreateRecord( 2 );
4498 MSI_RecordSetStringW( uirow, 1, compgroupid );
4499 MSI_RecordSetStringW( uirow, 2, qualifier);
4500 ui_actiondata( package, szPublishComponents, uirow);
4501 msiobj_release( &uirow->hdr );
4502 /* FIXME: call ui_progress? */
4504 return rc;
4508 * At present I am ignorning the advertised components part of this and only
4509 * focusing on the qualified component sets
4511 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4513 UINT rc;
4514 MSIQUERY * view;
4515 static const WCHAR ExecSeqQuery[] =
4516 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4517 '`','P','u','b','l','i','s','h',
4518 'C','o','m','p','o','n','e','n','t','`',0};
4520 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4521 if (rc != ERROR_SUCCESS)
4522 return ERROR_SUCCESS;
4524 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4525 msiobj_release(&view->hdr);
4527 return rc;
4530 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4532 MSIPACKAGE *package = (MSIPACKAGE*)param;
4533 MSIRECORD *row;
4534 MSIFILE *file;
4535 SC_HANDLE hscm, service = NULL;
4536 LPCWSTR comp, depends, pass;
4537 LPWSTR name = NULL, disp = NULL;
4538 LPCWSTR load_order, serv_name, key;
4539 DWORD serv_type, start_type;
4540 DWORD err_control;
4542 static const WCHAR query[] =
4543 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4544 '`','C','o','m','p','o','n','e','n','t','`',' ',
4545 'W','H','E','R','E',' ',
4546 '`','C','o','m','p','o','n','e','n','t','`',' ',
4547 '=','\'','%','s','\'',0};
4549 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4550 if (!hscm)
4552 ERR("Failed to open the SC Manager!\n");
4553 goto done;
4556 start_type = MSI_RecordGetInteger(rec, 5);
4557 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4558 goto done;
4560 depends = MSI_RecordGetString(rec, 8);
4561 if (depends && *depends)
4562 FIXME("Dependency list unhandled!\n");
4564 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4565 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4566 serv_type = MSI_RecordGetInteger(rec, 4);
4567 err_control = MSI_RecordGetInteger(rec, 6);
4568 load_order = MSI_RecordGetString(rec, 7);
4569 serv_name = MSI_RecordGetString(rec, 9);
4570 pass = MSI_RecordGetString(rec, 10);
4571 comp = MSI_RecordGetString(rec, 12);
4573 /* fetch the service path */
4574 row = MSI_QueryGetRecord(package->db, query, comp);
4575 if (!row)
4577 ERR("Control query failed!\n");
4578 goto done;
4581 key = MSI_RecordGetString(row, 6);
4583 file = get_loaded_file(package, key);
4584 msiobj_release(&row->hdr);
4585 if (!file)
4587 ERR("Failed to load the service file\n");
4588 goto done;
4591 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4592 start_type, err_control, file->TargetPath,
4593 load_order, NULL, NULL, serv_name, pass);
4594 if (!service)
4596 if (GetLastError() != ERROR_SERVICE_EXISTS)
4597 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4600 done:
4601 CloseServiceHandle(service);
4602 CloseServiceHandle(hscm);
4603 msi_free(name);
4604 msi_free(disp);
4606 return ERROR_SUCCESS;
4609 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4611 UINT rc;
4612 MSIQUERY * view;
4613 static const WCHAR ExecSeqQuery[] =
4614 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4615 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4617 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4618 if (rc != ERROR_SUCCESS)
4619 return ERROR_SUCCESS;
4621 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4622 msiobj_release(&view->hdr);
4624 return rc;
4627 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4628 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4630 LPCWSTR *vector, *temp_vector;
4631 LPWSTR p, q;
4632 DWORD sep_len;
4634 static const WCHAR separator[] = {'[','~',']',0};
4636 *numargs = 0;
4637 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4639 if (!args)
4640 return NULL;
4642 vector = msi_alloc(sizeof(LPWSTR));
4643 if (!vector)
4644 return NULL;
4646 p = args;
4649 (*numargs)++;
4650 vector[*numargs - 1] = p;
4652 if ((q = strstrW(p, separator)))
4654 *q = '\0';
4656 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4657 if (!temp_vector)
4659 msi_free(vector);
4660 return NULL;
4662 vector = temp_vector;
4664 p = q + sep_len;
4666 } while (q);
4668 return vector;
4671 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4673 MSIPACKAGE *package = (MSIPACKAGE *)param;
4674 MSICOMPONENT *comp;
4675 SC_HANDLE scm, service = NULL;
4676 LPCWSTR name, *vector = NULL;
4677 LPWSTR args;
4678 DWORD event, numargs;
4679 UINT r = ERROR_FUNCTION_FAILED;
4681 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4682 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4683 return ERROR_SUCCESS;
4685 name = MSI_RecordGetString(rec, 2);
4686 event = MSI_RecordGetInteger(rec, 3);
4687 args = strdupW(MSI_RecordGetString(rec, 4));
4689 if (!(event & msidbServiceControlEventStart))
4690 return ERROR_SUCCESS;
4692 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4693 if (!scm)
4695 ERR("Failed to open the service control manager\n");
4696 goto done;
4699 service = OpenServiceW(scm, name, SERVICE_START);
4700 if (!service)
4702 ERR("Failed to open service %s\n", debugstr_w(name));
4703 goto done;
4706 vector = msi_service_args_to_vector(args, &numargs);
4708 if (!StartServiceW(service, numargs, vector))
4710 ERR("Failed to start service %s\n", debugstr_w(name));
4711 goto done;
4714 r = ERROR_SUCCESS;
4716 done:
4717 CloseServiceHandle(service);
4718 CloseServiceHandle(scm);
4720 msi_free(args);
4721 msi_free(vector);
4722 return r;
4725 static UINT ACTION_StartServices( MSIPACKAGE *package )
4727 UINT rc;
4728 MSIQUERY *view;
4730 static const WCHAR query[] = {
4731 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4732 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4734 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4735 if (rc != ERROR_SUCCESS)
4736 return ERROR_SUCCESS;
4738 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4739 msiobj_release(&view->hdr);
4741 return rc;
4744 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4746 DWORD i, needed, count;
4747 ENUM_SERVICE_STATUSW *dependencies;
4748 SERVICE_STATUS ss;
4749 SC_HANDLE depserv;
4751 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4752 0, &needed, &count))
4753 return TRUE;
4755 if (GetLastError() != ERROR_MORE_DATA)
4756 return FALSE;
4758 dependencies = msi_alloc(needed);
4759 if (!dependencies)
4760 return FALSE;
4762 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4763 needed, &needed, &count))
4764 goto error;
4766 for (i = 0; i < count; i++)
4768 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4769 SERVICE_STOP | SERVICE_QUERY_STATUS);
4770 if (!depserv)
4771 goto error;
4773 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4774 goto error;
4777 return TRUE;
4779 error:
4780 msi_free(dependencies);
4781 return FALSE;
4784 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4786 MSIPACKAGE *package = (MSIPACKAGE *)param;
4787 MSICOMPONENT *comp;
4788 SERVICE_STATUS status;
4789 SERVICE_STATUS_PROCESS ssp;
4790 SC_HANDLE scm = NULL, service = NULL;
4791 LPWSTR name, args;
4792 DWORD event, needed;
4794 event = MSI_RecordGetInteger(rec, 3);
4795 if (!(event & msidbServiceControlEventStop))
4796 return ERROR_SUCCESS;
4798 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4799 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4800 return ERROR_SUCCESS;
4802 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4803 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4804 args = strdupW(MSI_RecordGetString(rec, 4));
4806 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4807 if (!scm)
4809 WARN("Failed to open the SCM: %d\n", GetLastError());
4810 goto done;
4813 service = OpenServiceW(scm, name,
4814 SERVICE_STOP |
4815 SERVICE_QUERY_STATUS |
4816 SERVICE_ENUMERATE_DEPENDENTS);
4817 if (!service)
4819 WARN("Failed to open service (%s): %d\n",
4820 debugstr_w(name), GetLastError());
4821 goto done;
4824 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4825 sizeof(SERVICE_STATUS_PROCESS), &needed))
4827 WARN("Failed to query service status (%s): %d\n",
4828 debugstr_w(name), GetLastError());
4829 goto done;
4832 if (ssp.dwCurrentState == SERVICE_STOPPED)
4833 goto done;
4835 stop_service_dependents(scm, service);
4837 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4838 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4840 done:
4841 CloseServiceHandle(service);
4842 CloseServiceHandle(scm);
4843 msi_free(name);
4844 msi_free(args);
4846 return ERROR_SUCCESS;
4849 static UINT ACTION_StopServices( MSIPACKAGE *package )
4851 UINT rc;
4852 MSIQUERY *view;
4854 static const WCHAR query[] = {
4855 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4856 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4858 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4859 if (rc != ERROR_SUCCESS)
4860 return ERROR_SUCCESS;
4862 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4863 msiobj_release(&view->hdr);
4865 return rc;
4868 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4870 MSIFILE *file;
4872 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4874 if (!lstrcmpW(file->File, filename))
4875 return file;
4878 return NULL;
4881 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4883 MSIPACKAGE *package = (MSIPACKAGE*)param;
4884 LPWSTR driver, driver_path, ptr;
4885 WCHAR outpath[MAX_PATH];
4886 MSIFILE *driver_file, *setup_file;
4887 LPCWSTR desc;
4888 DWORD len, usage;
4889 UINT r = ERROR_SUCCESS;
4891 static const WCHAR driver_fmt[] = {
4892 'D','r','i','v','e','r','=','%','s',0};
4893 static const WCHAR setup_fmt[] = {
4894 'S','e','t','u','p','=','%','s',0};
4895 static const WCHAR usage_fmt[] = {
4896 'F','i','l','e','U','s','a','g','e','=','1',0};
4898 desc = MSI_RecordGetString(rec, 3);
4900 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4901 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4903 if (!driver_file || !setup_file)
4905 ERR("ODBC Driver entry not found!\n");
4906 return ERROR_FUNCTION_FAILED;
4909 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4910 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4911 lstrlenW(usage_fmt) + 1;
4912 driver = msi_alloc(len * sizeof(WCHAR));
4913 if (!driver)
4914 return ERROR_OUTOFMEMORY;
4916 ptr = driver;
4917 lstrcpyW(ptr, desc);
4918 ptr += lstrlenW(ptr) + 1;
4920 sprintfW(ptr, driver_fmt, driver_file->FileName);
4921 ptr += lstrlenW(ptr) + 1;
4923 sprintfW(ptr, setup_fmt, setup_file->FileName);
4924 ptr += lstrlenW(ptr) + 1;
4926 lstrcpyW(ptr, usage_fmt);
4927 ptr += lstrlenW(ptr) + 1;
4928 *ptr = '\0';
4930 driver_path = strdupW(driver_file->TargetPath);
4931 ptr = strrchrW(driver_path, '\\');
4932 if (ptr) *ptr = '\0';
4934 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4935 NULL, ODBC_INSTALL_COMPLETE, &usage))
4937 ERR("Failed to install SQL driver!\n");
4938 r = ERROR_FUNCTION_FAILED;
4941 msi_free(driver);
4942 msi_free(driver_path);
4944 return r;
4947 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4949 MSIPACKAGE *package = (MSIPACKAGE*)param;
4950 LPWSTR translator, translator_path, ptr;
4951 WCHAR outpath[MAX_PATH];
4952 MSIFILE *translator_file, *setup_file;
4953 LPCWSTR desc;
4954 DWORD len, usage;
4955 UINT r = ERROR_SUCCESS;
4957 static const WCHAR translator_fmt[] = {
4958 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4959 static const WCHAR setup_fmt[] = {
4960 'S','e','t','u','p','=','%','s',0};
4962 desc = MSI_RecordGetString(rec, 3);
4964 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4965 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4967 if (!translator_file || !setup_file)
4969 ERR("ODBC Translator entry not found!\n");
4970 return ERROR_FUNCTION_FAILED;
4973 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4974 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4975 translator = msi_alloc(len * sizeof(WCHAR));
4976 if (!translator)
4977 return ERROR_OUTOFMEMORY;
4979 ptr = translator;
4980 lstrcpyW(ptr, desc);
4981 ptr += lstrlenW(ptr) + 1;
4983 sprintfW(ptr, translator_fmt, translator_file->FileName);
4984 ptr += lstrlenW(ptr) + 1;
4986 sprintfW(ptr, setup_fmt, setup_file->FileName);
4987 ptr += lstrlenW(ptr) + 1;
4988 *ptr = '\0';
4990 translator_path = strdupW(translator_file->TargetPath);
4991 ptr = strrchrW(translator_path, '\\');
4992 if (ptr) *ptr = '\0';
4994 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4995 NULL, ODBC_INSTALL_COMPLETE, &usage))
4997 ERR("Failed to install SQL translator!\n");
4998 r = ERROR_FUNCTION_FAILED;
5001 msi_free(translator);
5002 msi_free(translator_path);
5004 return r;
5007 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5009 LPWSTR attrs;
5010 LPCWSTR desc, driver;
5011 WORD request = ODBC_ADD_SYS_DSN;
5012 INT registration;
5013 DWORD len;
5014 UINT r = ERROR_SUCCESS;
5016 static const WCHAR attrs_fmt[] = {
5017 'D','S','N','=','%','s',0 };
5019 desc = MSI_RecordGetString(rec, 3);
5020 driver = MSI_RecordGetString(rec, 4);
5021 registration = MSI_RecordGetInteger(rec, 5);
5023 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5024 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5026 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5027 attrs = msi_alloc(len * sizeof(WCHAR));
5028 if (!attrs)
5029 return ERROR_OUTOFMEMORY;
5031 sprintfW(attrs, attrs_fmt, desc);
5032 attrs[len - 1] = '\0';
5034 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5036 ERR("Failed to install SQL data source!\n");
5037 r = ERROR_FUNCTION_FAILED;
5040 msi_free(attrs);
5042 return r;
5045 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5047 UINT rc;
5048 MSIQUERY *view;
5050 static const WCHAR driver_query[] = {
5051 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5052 'O','D','B','C','D','r','i','v','e','r',0 };
5054 static const WCHAR translator_query[] = {
5055 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5056 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5058 static const WCHAR source_query[] = {
5059 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5060 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5062 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5063 if (rc != ERROR_SUCCESS)
5064 return ERROR_SUCCESS;
5066 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5067 msiobj_release(&view->hdr);
5069 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5070 if (rc != ERROR_SUCCESS)
5071 return ERROR_SUCCESS;
5073 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5074 msiobj_release(&view->hdr);
5076 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5077 if (rc != ERROR_SUCCESS)
5078 return ERROR_SUCCESS;
5080 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5081 msiobj_release(&view->hdr);
5083 return rc;
5086 #define ENV_ACT_SETALWAYS 0x1
5087 #define ENV_ACT_SETABSENT 0x2
5088 #define ENV_ACT_REMOVE 0x4
5089 #define ENV_ACT_REMOVEMATCH 0x8
5091 #define ENV_MOD_MACHINE 0x20000000
5092 #define ENV_MOD_APPEND 0x40000000
5093 #define ENV_MOD_PREFIX 0x80000000
5094 #define ENV_MOD_MASK 0xC0000000
5096 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5098 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5100 LPCWSTR cptr = *name;
5101 LPCWSTR ptr = *value;
5103 static const WCHAR prefix[] = {'[','~',']',0};
5104 static const int prefix_len = 3;
5106 *flags = 0;
5107 while (*cptr)
5109 if (*cptr == '=')
5110 *flags |= ENV_ACT_SETALWAYS;
5111 else if (*cptr == '+')
5112 *flags |= ENV_ACT_SETABSENT;
5113 else if (*cptr == '-')
5114 *flags |= ENV_ACT_REMOVE;
5115 else if (*cptr == '!')
5116 *flags |= ENV_ACT_REMOVEMATCH;
5117 else if (*cptr == '*')
5118 *flags |= ENV_MOD_MACHINE;
5119 else
5120 break;
5122 cptr++;
5123 (*name)++;
5126 if (!*cptr)
5128 ERR("Missing environment variable\n");
5129 return ERROR_FUNCTION_FAILED;
5132 if (!strncmpW(ptr, prefix, prefix_len))
5134 *flags |= ENV_MOD_APPEND;
5135 *value += lstrlenW(prefix);
5137 else if (lstrlenW(*value) >= prefix_len)
5139 ptr += lstrlenW(ptr) - prefix_len;
5140 if (!lstrcmpW(ptr, prefix))
5142 *flags |= ENV_MOD_PREFIX;
5143 /* the "[~]" will be removed by deformat_string */;
5147 if (!*flags ||
5148 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5149 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5150 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5151 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5153 ERR("Invalid flags: %08x\n", *flags);
5154 return ERROR_FUNCTION_FAILED;
5157 return ERROR_SUCCESS;
5160 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5162 MSIPACKAGE *package = param;
5163 LPCWSTR name, value;
5164 LPWSTR data = NULL, newval = NULL;
5165 LPWSTR deformatted = NULL, ptr;
5166 DWORD flags, type, size;
5167 LONG res;
5168 HKEY env = NULL, root;
5169 LPCWSTR environment;
5171 static const WCHAR user_env[] =
5172 {'E','n','v','i','r','o','n','m','e','n','t',0};
5173 static const WCHAR machine_env[] =
5174 {'S','y','s','t','e','m','\\',
5175 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5176 'C','o','n','t','r','o','l','\\',
5177 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5178 'E','n','v','i','r','o','n','m','e','n','t',0};
5179 static const WCHAR semicolon[] = {';',0};
5181 name = MSI_RecordGetString(rec, 2);
5182 value = MSI_RecordGetString(rec, 3);
5184 res = env_set_flags(&name, &value, &flags);
5185 if (res != ERROR_SUCCESS)
5186 goto done;
5188 deformat_string(package, value, &deformatted);
5189 if (!deformatted)
5191 res = ERROR_OUTOFMEMORY;
5192 goto done;
5195 value = deformatted;
5197 if (flags & ENV_MOD_MACHINE)
5199 environment = machine_env;
5200 root = HKEY_LOCAL_MACHINE;
5202 else
5204 environment = user_env;
5205 root = HKEY_CURRENT_USER;
5208 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5209 KEY_ALL_ACCESS, NULL, &env, NULL);
5210 if (res != ERROR_SUCCESS)
5211 goto done;
5213 if (flags & ENV_ACT_REMOVE)
5214 FIXME("Not removing environment variable on uninstall!\n");
5216 size = 0;
5217 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5218 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5219 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5220 goto done;
5222 if (res != ERROR_FILE_NOT_FOUND)
5224 if (flags & ENV_ACT_SETABSENT)
5226 res = ERROR_SUCCESS;
5227 goto done;
5230 data = msi_alloc(size);
5231 if (!data)
5233 RegCloseKey(env);
5234 return ERROR_OUTOFMEMORY;
5237 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5238 if (res != ERROR_SUCCESS)
5239 goto done;
5241 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5243 res = RegDeleteKeyW(env, name);
5244 goto done;
5247 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5248 newval = msi_alloc(size);
5249 ptr = newval;
5250 if (!newval)
5252 res = ERROR_OUTOFMEMORY;
5253 goto done;
5256 if (!(flags & ENV_MOD_MASK))
5257 lstrcpyW(newval, value);
5258 else
5260 if (flags & ENV_MOD_PREFIX)
5262 lstrcpyW(newval, value);
5263 lstrcatW(newval, semicolon);
5264 ptr = newval + lstrlenW(value) + 1;
5267 lstrcpyW(ptr, data);
5269 if (flags & ENV_MOD_APPEND)
5271 lstrcatW(newval, semicolon);
5272 lstrcatW(newval, value);
5276 else
5278 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5279 newval = msi_alloc(size);
5280 if (!newval)
5282 res = ERROR_OUTOFMEMORY;
5283 goto done;
5286 lstrcpyW(newval, value);
5289 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5290 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5292 done:
5293 if (env) RegCloseKey(env);
5294 msi_free(deformatted);
5295 msi_free(data);
5296 msi_free(newval);
5297 return res;
5300 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5302 UINT rc;
5303 MSIQUERY * view;
5304 static const WCHAR ExecSeqQuery[] =
5305 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5306 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5307 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5308 if (rc != ERROR_SUCCESS)
5309 return ERROR_SUCCESS;
5311 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5312 msiobj_release(&view->hdr);
5314 return rc;
5317 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5319 typedef struct
5321 struct list entry;
5322 LPWSTR sourcename;
5323 LPWSTR destname;
5324 LPWSTR source;
5325 LPWSTR dest;
5326 } FILE_LIST;
5328 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5330 BOOL ret;
5332 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5333 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5335 WARN("Source or dest is directory, not moving\n");
5336 return FALSE;
5339 if (options == msidbMoveFileOptionsMove)
5341 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5342 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5343 if (!ret)
5345 WARN("MoveFile failed: %d\n", GetLastError());
5346 return FALSE;
5349 else
5351 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5352 ret = CopyFileW(source, dest, FALSE);
5353 if (!ret)
5355 WARN("CopyFile failed: %d\n", GetLastError());
5356 return FALSE;
5360 return TRUE;
5363 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5365 LPWSTR path, ptr;
5366 DWORD dirlen, pathlen;
5368 ptr = strrchrW(wildcard, '\\');
5369 dirlen = ptr - wildcard + 1;
5371 pathlen = dirlen + lstrlenW(filename) + 1;
5372 path = msi_alloc(pathlen * sizeof(WCHAR));
5374 lstrcpynW(path, wildcard, dirlen + 1);
5375 lstrcatW(path, filename);
5377 return path;
5380 static void free_file_entry(FILE_LIST *file)
5382 msi_free(file->source);
5383 msi_free(file->dest);
5384 msi_free(file);
5387 static void free_list(FILE_LIST *list)
5389 while (!list_empty(&list->entry))
5391 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5393 list_remove(&file->entry);
5394 free_file_entry(file);
5398 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5400 FILE_LIST *new, *file;
5401 LPWSTR ptr, filename;
5402 DWORD size;
5404 new = msi_alloc_zero(sizeof(FILE_LIST));
5405 if (!new)
5406 return FALSE;
5408 new->source = strdupW(source);
5409 ptr = strrchrW(dest, '\\') + 1;
5410 filename = strrchrW(new->source, '\\') + 1;
5412 new->sourcename = filename;
5414 if (*ptr)
5415 new->destname = ptr;
5416 else
5417 new->destname = new->sourcename;
5419 size = (ptr - dest) + lstrlenW(filename) + 1;
5420 new->dest = msi_alloc(size * sizeof(WCHAR));
5421 if (!new->dest)
5423 free_file_entry(new);
5424 return FALSE;
5427 lstrcpynW(new->dest, dest, ptr - dest + 1);
5428 lstrcatW(new->dest, filename);
5430 if (list_empty(&files->entry))
5432 list_add_head(&files->entry, &new->entry);
5433 return TRUE;
5436 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5438 if (lstrcmpW(source, file->source) < 0)
5440 list_add_before(&file->entry, &new->entry);
5441 return TRUE;
5445 list_add_after(&file->entry, &new->entry);
5446 return TRUE;
5449 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5451 WIN32_FIND_DATAW wfd;
5452 HANDLE hfile;
5453 LPWSTR path;
5454 BOOL res;
5455 FILE_LIST files, *file;
5456 DWORD size;
5458 hfile = FindFirstFileW(source, &wfd);
5459 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5461 list_init(&files.entry);
5463 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5465 if (is_dot_dir(wfd.cFileName)) continue;
5467 path = wildcard_to_file(source, wfd.cFileName);
5468 if (!path)
5470 res = FALSE;
5471 goto done;
5474 add_wildcard(&files, path, dest);
5475 msi_free(path);
5478 /* no files match the wildcard */
5479 if (list_empty(&files.entry))
5480 goto done;
5482 /* only the first wildcard match gets renamed to dest */
5483 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5484 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5485 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5486 if (!file->dest)
5488 res = FALSE;
5489 goto done;
5492 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5494 while (!list_empty(&files.entry))
5496 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5498 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5500 list_remove(&file->entry);
5501 free_file_entry(file);
5504 res = TRUE;
5506 done:
5507 free_list(&files);
5508 FindClose(hfile);
5509 return res;
5512 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5514 MSIPACKAGE *package = param;
5515 MSICOMPONENT *comp;
5516 LPCWSTR sourcename, destname;
5517 LPWSTR sourcedir = NULL, destdir = NULL;
5518 LPWSTR source = NULL, dest = NULL;
5519 int options;
5520 DWORD size;
5521 BOOL ret, wildcards;
5523 static const WCHAR backslash[] = {'\\',0};
5525 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5526 if (!comp || !comp->Enabled ||
5527 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5529 TRACE("Component not set for install, not moving file\n");
5530 return ERROR_SUCCESS;
5533 sourcename = MSI_RecordGetString(rec, 3);
5534 destname = MSI_RecordGetString(rec, 4);
5535 options = MSI_RecordGetInteger(rec, 7);
5537 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5538 if (!sourcedir)
5539 goto done;
5541 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5542 if (!destdir)
5543 goto done;
5545 if (!sourcename)
5547 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5548 goto done;
5550 source = strdupW(sourcedir);
5551 if (!source)
5552 goto done;
5554 else
5556 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5557 source = msi_alloc(size * sizeof(WCHAR));
5558 if (!source)
5559 goto done;
5561 lstrcpyW(source, sourcedir);
5562 if (source[lstrlenW(source) - 1] != '\\')
5563 lstrcatW(source, backslash);
5564 lstrcatW(source, sourcename);
5567 wildcards = strchrW(source, '*') || strchrW(source, '?');
5569 if (!destname && !wildcards)
5571 destname = strdupW(sourcename);
5572 if (!destname)
5573 goto done;
5576 size = 0;
5577 if (destname)
5578 size = lstrlenW(destname);
5580 size += lstrlenW(destdir) + 2;
5581 dest = msi_alloc(size * sizeof(WCHAR));
5582 if (!dest)
5583 goto done;
5585 lstrcpyW(dest, destdir);
5586 if (dest[lstrlenW(dest) - 1] != '\\')
5587 lstrcatW(dest, backslash);
5589 if (destname)
5590 lstrcatW(dest, destname);
5592 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5594 ret = CreateDirectoryW(destdir, NULL);
5595 if (!ret)
5597 WARN("CreateDirectory failed: %d\n", GetLastError());
5598 return ERROR_SUCCESS;
5602 if (!wildcards)
5603 msi_move_file(source, dest, options);
5604 else
5605 move_files_wildcard(source, dest, options);
5607 done:
5608 msi_free(sourcedir);
5609 msi_free(destdir);
5610 msi_free(source);
5611 msi_free(dest);
5613 return ERROR_SUCCESS;
5616 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5618 UINT rc;
5619 MSIQUERY *view;
5621 static const WCHAR ExecSeqQuery[] =
5622 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5623 '`','M','o','v','e','F','i','l','e','`',0};
5625 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5626 if (rc != ERROR_SUCCESS)
5627 return ERROR_SUCCESS;
5629 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5630 msiobj_release(&view->hdr);
5632 return rc;
5635 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5636 DWORD dwReserved);
5637 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5638 LPVOID pvReserved, HMODULE *phModDll);
5640 static BOOL init_functionpointers(void)
5642 HRESULT hr;
5643 HMODULE hfusion;
5644 HMODULE hmscoree;
5646 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5648 hmscoree = LoadLibraryA("mscoree.dll");
5649 if (!hmscoree)
5651 WARN("mscoree.dll not available\n");
5652 return FALSE;
5655 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5656 if (!pLoadLibraryShim)
5658 WARN("LoadLibraryShim not available\n");
5659 FreeLibrary(hmscoree);
5660 return FALSE;
5663 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5664 if (FAILED(hr))
5666 WARN("fusion.dll not available\n");
5667 FreeLibrary(hmscoree);
5668 return FALSE;
5671 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5673 FreeLibrary(hmscoree);
5674 return TRUE;
5677 static UINT install_assembly(LPWSTR path)
5679 IAssemblyCache *cache;
5680 HRESULT hr;
5681 UINT r = ERROR_FUNCTION_FAILED;
5683 if (!init_functionpointers() || !pCreateAssemblyCache)
5684 return ERROR_FUNCTION_FAILED;
5686 hr = pCreateAssemblyCache(&cache, 0);
5687 if (FAILED(hr))
5688 goto done;
5690 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5691 if (FAILED(hr))
5692 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5694 r = ERROR_SUCCESS;
5696 done:
5697 IAssemblyCache_Release(cache);
5698 return r;
5701 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5703 MSIPACKAGE *package = param;
5704 MSICOMPONENT *comp;
5705 MSIFEATURE *feature;
5706 MSIFILE *file;
5707 WCHAR path[MAX_PATH];
5708 LPCWSTR app;
5709 DWORD attr;
5710 UINT r;
5712 comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5713 if (!comp || !comp->Enabled ||
5714 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5716 ERR("Component not set for install, not publishing assembly\n");
5717 return ERROR_SUCCESS;
5720 feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5721 if (feature)
5722 msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5724 if (MSI_RecordGetString(rec, 3))
5725 FIXME("Manifest unhandled\n");
5727 app = MSI_RecordGetString(rec, 4);
5728 if (app)
5730 FIXME("Assembly should be privately installed\n");
5731 return ERROR_SUCCESS;
5734 attr = MSI_RecordGetInteger(rec, 5);
5735 if (attr == msidbAssemblyAttributesWin32)
5737 FIXME("Win32 assemblies not handled\n");
5738 return ERROR_SUCCESS;
5741 /* FIXME: extract all files belonging to this component */
5742 file = msi_find_file(package, comp->KeyPath);
5743 if (!file)
5745 ERR("File %s not found\n", debugstr_w(comp->KeyPath));
5746 return ERROR_FUNCTION_FAILED;
5749 GetTempPathW(MAX_PATH, path);
5751 if (file->IsCompressed)
5753 r = msi_extract_file(package, file, path);
5754 if (r != ERROR_SUCCESS)
5756 ERR("Failed to extract temporary assembly\n");
5757 return r;
5760 PathAddBackslashW(path);
5761 lstrcatW(path, file->FileName);
5763 else
5765 PathAddBackslashW(path);
5766 lstrcatW(path, file->FileName);
5768 if (!CopyFileW(file->SourcePath, path, FALSE))
5770 ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5771 return ERROR_FUNCTION_FAILED;
5775 r = install_assembly(path);
5776 if (r != ERROR_SUCCESS)
5777 ERR("Failed to install assembly\n");
5779 /* FIXME: write Installer assembly reg values */
5781 return r;
5784 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5786 UINT rc;
5787 MSIQUERY *view;
5789 static const WCHAR ExecSeqQuery[] =
5790 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5791 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5793 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5794 if (rc != ERROR_SUCCESS)
5795 return ERROR_SUCCESS;
5797 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5798 msiobj_release(&view->hdr);
5800 return rc;
5803 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5804 LPCSTR action, LPCWSTR table )
5806 static const WCHAR query[] = {
5807 'S','E','L','E','C','T',' ','*',' ',
5808 'F','R','O','M',' ','`','%','s','`',0 };
5809 MSIQUERY *view = NULL;
5810 DWORD count = 0;
5811 UINT r;
5813 r = MSI_OpenQuery( package->db, &view, query, table );
5814 if (r == ERROR_SUCCESS)
5816 r = MSI_IterateRecords(view, &count, NULL, package);
5817 msiobj_release(&view->hdr);
5820 if (count)
5821 FIXME("%s -> %u ignored %s table values\n",
5822 action, count, debugstr_w(table));
5824 return ERROR_SUCCESS;
5827 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5829 TRACE("%p\n", package);
5830 return ERROR_SUCCESS;
5833 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5835 static const WCHAR table[] =
5836 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5837 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5840 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5842 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5843 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5846 static UINT ACTION_BindImage( MSIPACKAGE *package )
5848 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5849 return msi_unimplemented_action_stub( package, "BindImage", table );
5852 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5854 static const WCHAR table[] = {
5855 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5856 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5859 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5861 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5862 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5865 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5867 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5868 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5871 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5873 static const WCHAR table[] = {
5874 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5875 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5877 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5879 static const WCHAR table[] = {
5880 'P','r','o','d','u','c','t','I','D',0 };
5881 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5884 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5886 static const WCHAR table[] = {
5887 'E','n','v','i','r','o','n','m','e','n','t',0 };
5888 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5891 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5893 static const WCHAR table[] = {
5894 'M','s','i','A','s','s','e','m','b','l','y',0 };
5895 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5898 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5900 static const WCHAR table[] = { 'F','o','n','t',0 };
5901 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5904 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5906 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5907 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5910 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5912 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5913 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5916 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5918 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5919 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5922 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5924 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5925 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5928 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5930 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5931 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5934 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5936 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5937 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5940 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5942 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5943 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5946 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5948 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5949 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5952 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5954 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5955 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5958 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5960 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5961 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5964 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5966 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5967 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5970 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5972 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5973 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5976 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5978 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5979 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5982 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5984 static const WCHAR table[] = { 'M','I','M','E',0 };
5985 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5988 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5990 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5991 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5994 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5996 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5997 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6000 static const struct _actions StandardActions[] = {
6001 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6002 { szAppSearch, ACTION_AppSearch },
6003 { szBindImage, ACTION_BindImage },
6004 { szCCPSearch, ACTION_CCPSearch },
6005 { szCostFinalize, ACTION_CostFinalize },
6006 { szCostInitialize, ACTION_CostInitialize },
6007 { szCreateFolders, ACTION_CreateFolders },
6008 { szCreateShortcuts, ACTION_CreateShortcuts },
6009 { szDeleteServices, ACTION_DeleteServices },
6010 { szDisableRollback, NULL },
6011 { szDuplicateFiles, ACTION_DuplicateFiles },
6012 { szExecuteAction, ACTION_ExecuteAction },
6013 { szFileCost, ACTION_FileCost },
6014 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6015 { szForceReboot, ACTION_ForceReboot },
6016 { szInstallAdminPackage, NULL },
6017 { szInstallExecute, ACTION_InstallExecute },
6018 { szInstallExecuteAgain, ACTION_InstallExecute },
6019 { szInstallFiles, ACTION_InstallFiles},
6020 { szInstallFinalize, ACTION_InstallFinalize },
6021 { szInstallInitialize, ACTION_InstallInitialize },
6022 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6023 { szInstallValidate, ACTION_InstallValidate },
6024 { szIsolateComponents, ACTION_IsolateComponents },
6025 { szLaunchConditions, ACTION_LaunchConditions },
6026 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6027 { szMoveFiles, ACTION_MoveFiles },
6028 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6029 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6030 { szInstallODBC, ACTION_InstallODBC },
6031 { szInstallServices, ACTION_InstallServices },
6032 { szPatchFiles, ACTION_PatchFiles },
6033 { szProcessComponents, ACTION_ProcessComponents },
6034 { szPublishComponents, ACTION_PublishComponents },
6035 { szPublishFeatures, ACTION_PublishFeatures },
6036 { szPublishProduct, ACTION_PublishProduct },
6037 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6038 { szRegisterComPlus, ACTION_RegisterComPlus},
6039 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6040 { szRegisterFonts, ACTION_RegisterFonts },
6041 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6042 { szRegisterProduct, ACTION_RegisterProduct },
6043 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6044 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6045 { szRegisterUser, ACTION_RegisterUser },
6046 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6047 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6048 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6049 { szRemoveFiles, ACTION_RemoveFiles },
6050 { szRemoveFolders, ACTION_RemoveFolders },
6051 { szRemoveIniValues, ACTION_RemoveIniValues },
6052 { szRemoveODBC, ACTION_RemoveODBC },
6053 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6054 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6055 { szResolveSource, ACTION_ResolveSource },
6056 { szRMCCPSearch, ACTION_RMCCPSearch },
6057 { szScheduleReboot, NULL },
6058 { szSelfRegModules, ACTION_SelfRegModules },
6059 { szSelfUnregModules, ACTION_SelfUnregModules },
6060 { szSetODBCFolders, NULL },
6061 { szStartServices, ACTION_StartServices },
6062 { szStopServices, ACTION_StopServices },
6063 { szUnpublishComponents, ACTION_UnpublishComponents },
6064 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6065 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6066 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6067 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6068 { szUnregisterFonts, ACTION_UnregisterFonts },
6069 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6070 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6071 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6072 { szValidateProductID, ACTION_ValidateProductID },
6073 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6074 { szWriteIniValues, ACTION_WriteIniValues },
6075 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6076 { NULL, NULL },
6079 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6080 UINT* rc, BOOL force )
6082 BOOL ret = FALSE;
6083 BOOL run = force;
6084 int i;
6086 if (!run && !package->script->CurrentlyScripting)
6087 run = TRUE;
6089 if (!run)
6091 if (strcmpW(action,szInstallFinalize) == 0 ||
6092 strcmpW(action,szInstallExecute) == 0 ||
6093 strcmpW(action,szInstallExecuteAgain) == 0)
6094 run = TRUE;
6097 i = 0;
6098 while (StandardActions[i].action != NULL)
6100 if (strcmpW(StandardActions[i].action, action)==0)
6102 if (!run)
6104 ui_actioninfo(package, action, TRUE, 0);
6105 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6106 ui_actioninfo(package, action, FALSE, *rc);
6108 else
6110 ui_actionstart(package, action);
6111 if (StandardActions[i].handler)
6113 *rc = StandardActions[i].handler(package);
6115 else
6117 FIXME("unhandled standard action %s\n",debugstr_w(action));
6118 *rc = ERROR_SUCCESS;
6121 ret = TRUE;
6122 break;
6124 i++;
6126 return ret;