push 1439a81556dec7242b4effc273edbc16b1d3ce68
[wine/hacks.git] / dlls / msi / action.c
blob2d2fe6c1ce356ca7d7d2e7bdcc8c5dba5b25592b
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 & msidbSumInfoSourceTypeCompressed;
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 ITypeLib *tlib;
2998 HMODULE module;
2999 HRESULT hr;
3001 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3003 component = MSI_RecordGetString(row,3);
3004 comp = get_loaded_component(package,component);
3005 if (!comp)
3006 return ERROR_SUCCESS;
3008 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3010 TRACE("Skipping typelib reg due to disabled component\n");
3012 comp->Action = comp->Installed;
3014 return ERROR_SUCCESS;
3017 comp->Action = INSTALLSTATE_LOCAL;
3019 file = get_loaded_file( package, comp->KeyPath );
3020 if (!file)
3021 return ERROR_SUCCESS;
3023 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3024 if (module)
3026 LPCWSTR guid;
3027 guid = MSI_RecordGetString(row,1);
3028 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3029 tl_struct.source = strdupW( file->TargetPath );
3030 tl_struct.path = NULL;
3032 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3033 (LONG_PTR)&tl_struct);
3035 if (tl_struct.path)
3037 LPWSTR help = NULL;
3038 LPCWSTR helpid;
3039 HRESULT res;
3041 helpid = MSI_RecordGetString(row,6);
3043 if (helpid)
3044 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3045 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3046 msi_free(help);
3048 if (!SUCCEEDED(res))
3049 ERR("Failed to register type library %s\n",
3050 debugstr_w(tl_struct.path));
3051 else
3053 ui_actiondata(package,szRegisterTypeLibraries,row);
3055 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3058 ITypeLib_Release(tl_struct.ptLib);
3059 msi_free(tl_struct.path);
3061 else
3062 ERR("Failed to load type library %s\n",
3063 debugstr_w(tl_struct.source));
3065 FreeLibrary(module);
3066 msi_free(tl_struct.source);
3068 else
3070 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3071 if (FAILED(hr))
3073 ERR("Failed to load type library: %08x\n", hr);
3074 return ERROR_FUNCTION_FAILED;
3077 ITypeLib_Release(tlib);
3080 return ERROR_SUCCESS;
3083 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3086 * OK this is a bit confusing.. I am given a _Component key and I believe
3087 * that the file that is being registered as a type library is the "key file
3088 * of that component" which I interpret to mean "The file in the KeyPath of
3089 * that component".
3091 UINT rc;
3092 MSIQUERY * view;
3093 static const WCHAR Query[] =
3094 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3095 '`','T','y','p','e','L','i','b','`',0};
3097 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3098 if (rc != ERROR_SUCCESS)
3099 return ERROR_SUCCESS;
3101 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3102 msiobj_release(&view->hdr);
3103 return rc;
3106 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3108 MSIPACKAGE *package = (MSIPACKAGE*)param;
3109 LPWSTR target_file, target_folder, filename;
3110 LPCWSTR buffer, extension;
3111 MSICOMPONENT *comp;
3112 static const WCHAR szlnk[]={'.','l','n','k',0};
3113 IShellLinkW *sl = NULL;
3114 IPersistFile *pf = NULL;
3115 HRESULT res;
3117 buffer = MSI_RecordGetString(row,4);
3118 comp = get_loaded_component(package,buffer);
3119 if (!comp)
3120 return ERROR_SUCCESS;
3122 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3124 TRACE("Skipping shortcut creation due to disabled component\n");
3126 comp->Action = comp->Installed;
3128 return ERROR_SUCCESS;
3131 comp->Action = INSTALLSTATE_LOCAL;
3133 ui_actiondata(package,szCreateShortcuts,row);
3135 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3136 &IID_IShellLinkW, (LPVOID *) &sl );
3138 if (FAILED( res ))
3140 ERR("CLSID_ShellLink not available\n");
3141 goto err;
3144 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3145 if (FAILED( res ))
3147 ERR("QueryInterface(IID_IPersistFile) failed\n");
3148 goto err;
3151 buffer = MSI_RecordGetString(row,2);
3152 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3154 /* may be needed because of a bug somewhere else */
3155 create_full_pathW(target_folder);
3157 filename = msi_dup_record_field( row, 3 );
3158 reduce_to_longfilename(filename);
3160 extension = strchrW(filename,'.');
3161 if (!extension || strcmpiW(extension,szlnk))
3163 int len = strlenW(filename);
3164 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3165 memcpy(filename + len, szlnk, sizeof(szlnk));
3167 target_file = build_directory_name(2, target_folder, filename);
3168 msi_free(target_folder);
3169 msi_free(filename);
3171 buffer = MSI_RecordGetString(row,5);
3172 if (strchrW(buffer,'['))
3174 LPWSTR deformated;
3175 deformat_string(package,buffer,&deformated);
3176 IShellLinkW_SetPath(sl,deformated);
3177 msi_free(deformated);
3179 else
3181 FIXME("poorly handled shortcut format, advertised shortcut\n");
3182 IShellLinkW_SetPath(sl,comp->FullKeypath);
3185 if (!MSI_RecordIsNull(row,6))
3187 LPWSTR deformated;
3188 buffer = MSI_RecordGetString(row,6);
3189 deformat_string(package,buffer,&deformated);
3190 IShellLinkW_SetArguments(sl,deformated);
3191 msi_free(deformated);
3194 if (!MSI_RecordIsNull(row,7))
3196 buffer = MSI_RecordGetString(row,7);
3197 IShellLinkW_SetDescription(sl,buffer);
3200 if (!MSI_RecordIsNull(row,8))
3201 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3203 if (!MSI_RecordIsNull(row,9))
3205 LPWSTR Path;
3206 INT index;
3208 buffer = MSI_RecordGetString(row,9);
3210 Path = build_icon_path(package,buffer);
3211 index = MSI_RecordGetInteger(row,10);
3213 /* no value means 0 */
3214 if (index == MSI_NULL_INTEGER)
3215 index = 0;
3217 IShellLinkW_SetIconLocation(sl,Path,index);
3218 msi_free(Path);
3221 if (!MSI_RecordIsNull(row,11))
3222 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3224 if (!MSI_RecordIsNull(row,12))
3226 LPWSTR Path;
3227 buffer = MSI_RecordGetString(row,12);
3228 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3229 if (Path)
3230 IShellLinkW_SetWorkingDirectory(sl,Path);
3231 msi_free(Path);
3234 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3235 IPersistFile_Save(pf,target_file,FALSE);
3237 msi_free(target_file);
3239 err:
3240 if (pf)
3241 IPersistFile_Release( pf );
3242 if (sl)
3243 IShellLinkW_Release( sl );
3245 return ERROR_SUCCESS;
3248 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3250 UINT rc;
3251 HRESULT res;
3252 MSIQUERY * view;
3253 static const WCHAR Query[] =
3254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3255 '`','S','h','o','r','t','c','u','t','`',0};
3257 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3258 if (rc != ERROR_SUCCESS)
3259 return ERROR_SUCCESS;
3261 res = CoInitialize( NULL );
3262 if (FAILED (res))
3264 ERR("CoInitialize failed\n");
3265 return ERROR_FUNCTION_FAILED;
3268 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3269 msiobj_release(&view->hdr);
3271 CoUninitialize();
3273 return rc;
3276 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3278 MSIPACKAGE* package = (MSIPACKAGE*)param;
3279 HANDLE the_file;
3280 LPWSTR FilePath;
3281 LPCWSTR FileName;
3282 CHAR buffer[1024];
3283 DWORD sz;
3284 UINT rc;
3285 MSIRECORD *uirow;
3287 FileName = MSI_RecordGetString(row,1);
3288 if (!FileName)
3290 ERR("Unable to get FileName\n");
3291 return ERROR_SUCCESS;
3294 FilePath = build_icon_path(package,FileName);
3296 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3298 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3299 FILE_ATTRIBUTE_NORMAL, NULL);
3301 if (the_file == INVALID_HANDLE_VALUE)
3303 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3304 msi_free(FilePath);
3305 return ERROR_SUCCESS;
3310 DWORD write;
3311 sz = 1024;
3312 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3313 if (rc != ERROR_SUCCESS)
3315 ERR("Failed to get stream\n");
3316 CloseHandle(the_file);
3317 DeleteFileW(FilePath);
3318 break;
3320 WriteFile(the_file,buffer,sz,&write,NULL);
3321 } while (sz == 1024);
3323 msi_free(FilePath);
3325 CloseHandle(the_file);
3327 uirow = MSI_CreateRecord(1);
3328 MSI_RecordSetStringW(uirow,1,FileName);
3329 ui_actiondata(package,szPublishProduct,uirow);
3330 msiobj_release( &uirow->hdr );
3332 return ERROR_SUCCESS;
3335 static UINT msi_publish_icons(MSIPACKAGE *package)
3337 UINT r;
3338 MSIQUERY *view;
3340 static const WCHAR query[]= {
3341 'S','E','L','E','C','T',' ','*',' ',
3342 'F','R','O','M',' ','`','I','c','o','n','`',0};
3344 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3345 if (r == ERROR_SUCCESS)
3347 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3348 msiobj_release(&view->hdr);
3351 return ERROR_SUCCESS;
3354 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3356 UINT r;
3357 HKEY source;
3358 LPWSTR buffer;
3359 MSIMEDIADISK *disk;
3360 MSISOURCELISTINFO *info;
3362 static const WCHAR szEmpty[] = {0};
3363 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3365 r = RegCreateKeyW(hkey, szSourceList, &source);
3366 if (r != ERROR_SUCCESS)
3367 return r;
3369 RegCloseKey(source);
3371 buffer = strrchrW(package->PackagePath, '\\') + 1;
3372 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3373 package->Context, MSICODE_PRODUCT,
3374 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3375 if (r != ERROR_SUCCESS)
3376 return r;
3378 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3379 package->Context, MSICODE_PRODUCT,
3380 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3381 if (r != ERROR_SUCCESS)
3382 return r;
3384 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3385 package->Context, MSICODE_PRODUCT,
3386 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3387 if (r != ERROR_SUCCESS)
3388 return r;
3390 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3392 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3393 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3394 info->options, info->value);
3395 else
3396 MsiSourceListSetInfoW(package->ProductCode, NULL,
3397 info->context, info->options,
3398 info->property, info->value);
3401 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3403 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3404 disk->context, disk->options,
3405 disk->disk_id, disk->volume_label, disk->disk_prompt);
3408 return ERROR_SUCCESS;
3411 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3413 MSIHANDLE hdb, suminfo;
3414 WCHAR guids[MAX_PATH];
3415 WCHAR packcode[SQUISH_GUID_SIZE];
3416 LPWSTR buffer;
3417 LPWSTR ptr;
3418 DWORD langid;
3419 DWORD size;
3420 UINT r;
3422 static const WCHAR szProductLanguage[] =
3423 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3424 static const WCHAR szARPProductIcon[] =
3425 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3426 static const WCHAR szProductVersion[] =
3427 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3428 static const WCHAR szAssignment[] =
3429 {'A','s','s','i','g','n','m','e','n','t',0};
3430 static const WCHAR szAdvertiseFlags[] =
3431 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3432 static const WCHAR szClients[] =
3433 {'C','l','i','e','n','t','s',0};
3434 static const WCHAR szColon[] = {':',0};
3436 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3437 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3438 msi_free(buffer);
3440 langid = msi_get_property_int(package, szProductLanguage, 0);
3441 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3443 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3444 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3446 /* FIXME */
3447 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3449 buffer = msi_dup_property(package, szARPProductIcon);
3450 if (buffer)
3452 LPWSTR path = build_icon_path(package,buffer);
3453 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3454 msi_free(path);
3455 msi_free(buffer);
3458 buffer = msi_dup_property(package, szProductVersion);
3459 if (buffer)
3461 DWORD verdword = msi_version_str_to_dword(buffer);
3462 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3463 msi_free(buffer);
3466 msi_reg_set_val_dword(hkey, szAssignment, 0);
3467 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3468 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3469 msi_reg_set_val_str(hkey, szClients, szColon);
3471 hdb = alloc_msihandle(&package->db->hdr);
3472 if (!hdb)
3473 return ERROR_NOT_ENOUGH_MEMORY;
3475 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3476 MsiCloseHandle(hdb);
3477 if (r != ERROR_SUCCESS)
3478 goto done;
3480 size = MAX_PATH;
3481 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3482 NULL, guids, &size);
3483 if (r != ERROR_SUCCESS)
3484 goto done;
3486 ptr = strchrW(guids, ';');
3487 if (ptr) *ptr = 0;
3488 squash_guid(guids, packcode);
3489 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3491 done:
3492 MsiCloseHandle(suminfo);
3493 return ERROR_SUCCESS;
3496 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3498 UINT r;
3499 HKEY hkey;
3500 LPWSTR upgrade;
3501 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3503 static const WCHAR szUpgradeCode[] =
3504 {'U','p','g','r','a','d','e','C','o','d','e',0};
3506 upgrade = msi_dup_property(package, szUpgradeCode);
3507 if (!upgrade)
3508 return ERROR_SUCCESS;
3510 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3512 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3513 if (r != ERROR_SUCCESS)
3514 goto done;
3516 else
3518 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3519 if (r != ERROR_SUCCESS)
3520 goto done;
3523 squash_guid(package->ProductCode, squashed_pc);
3524 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3526 RegCloseKey(hkey);
3528 done:
3529 msi_free(upgrade);
3530 return r;
3533 static BOOL msi_check_publish(MSIPACKAGE *package)
3535 MSIFEATURE *feature;
3537 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3539 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3540 return TRUE;
3543 return FALSE;
3546 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3548 MSIFEATURE *feature;
3550 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3552 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3553 return FALSE;
3556 return TRUE;
3560 * 99% of the work done here is only done for
3561 * advertised installs. However this is where the
3562 * Icon table is processed and written out
3563 * so that is what I am going to do here.
3565 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3567 UINT rc;
3568 HKEY hukey=0;
3569 HKEY hudkey=0;
3571 /* FIXME: also need to publish if the product is in advertise mode */
3572 if (!msi_check_publish(package))
3573 return ERROR_SUCCESS;
3575 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3577 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3578 if (rc != ERROR_SUCCESS)
3579 goto end;
3581 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3582 if (rc != ERROR_SUCCESS)
3583 goto end;
3585 else
3587 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3588 if (rc != ERROR_SUCCESS)
3589 goto end;
3591 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3592 if (rc != ERROR_SUCCESS)
3593 goto end;
3596 rc = msi_publish_upgrade_code(package);
3597 if (rc != ERROR_SUCCESS)
3598 goto end;
3600 rc = msi_publish_product_properties(package, hukey);
3601 if (rc != ERROR_SUCCESS)
3602 goto end;
3604 rc = msi_publish_sourcelist(package, hukey);
3605 if (rc != ERROR_SUCCESS)
3606 goto end;
3608 rc = msi_publish_icons(package);
3610 end:
3611 RegCloseKey(hukey);
3612 RegCloseKey(hudkey);
3614 return rc;
3617 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3619 MSIPACKAGE *package = (MSIPACKAGE*)param;
3620 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3621 LPWSTR deformated_section, deformated_key, deformated_value;
3622 LPWSTR folder, fullname = NULL;
3623 MSIRECORD * uirow;
3624 INT action;
3625 MSICOMPONENT *comp;
3626 static const WCHAR szWindowsFolder[] =
3627 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3629 component = MSI_RecordGetString(row, 8);
3630 comp = get_loaded_component(package,component);
3632 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3634 TRACE("Skipping ini file due to disabled component %s\n",
3635 debugstr_w(component));
3637 comp->Action = comp->Installed;
3639 return ERROR_SUCCESS;
3642 comp->Action = INSTALLSTATE_LOCAL;
3644 identifier = MSI_RecordGetString(row,1);
3645 filename = MSI_RecordGetString(row,2);
3646 dirproperty = MSI_RecordGetString(row,3);
3647 section = MSI_RecordGetString(row,4);
3648 key = MSI_RecordGetString(row,5);
3649 value = MSI_RecordGetString(row,6);
3650 action = MSI_RecordGetInteger(row,7);
3652 deformat_string(package,section,&deformated_section);
3653 deformat_string(package,key,&deformated_key);
3654 deformat_string(package,value,&deformated_value);
3656 if (dirproperty)
3658 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3659 if (!folder)
3660 folder = msi_dup_property( package, dirproperty );
3662 else
3663 folder = msi_dup_property( package, szWindowsFolder );
3665 if (!folder)
3667 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3668 goto cleanup;
3671 fullname = build_directory_name(2, folder, filename);
3673 if (action == 0)
3675 TRACE("Adding value %s to section %s in %s\n",
3676 debugstr_w(deformated_key), debugstr_w(deformated_section),
3677 debugstr_w(fullname));
3678 WritePrivateProfileStringW(deformated_section, deformated_key,
3679 deformated_value, fullname);
3681 else if (action == 1)
3683 WCHAR returned[10];
3684 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3685 returned, 10, fullname);
3686 if (returned[0] == 0)
3688 TRACE("Adding value %s to section %s in %s\n",
3689 debugstr_w(deformated_key), debugstr_w(deformated_section),
3690 debugstr_w(fullname));
3692 WritePrivateProfileStringW(deformated_section, deformated_key,
3693 deformated_value, fullname);
3696 else if (action == 3)
3697 FIXME("Append to existing section not yet implemented\n");
3699 uirow = MSI_CreateRecord(4);
3700 MSI_RecordSetStringW(uirow,1,identifier);
3701 MSI_RecordSetStringW(uirow,2,deformated_section);
3702 MSI_RecordSetStringW(uirow,3,deformated_key);
3703 MSI_RecordSetStringW(uirow,4,deformated_value);
3704 ui_actiondata(package,szWriteIniValues,uirow);
3705 msiobj_release( &uirow->hdr );
3706 cleanup:
3707 msi_free(fullname);
3708 msi_free(folder);
3709 msi_free(deformated_key);
3710 msi_free(deformated_value);
3711 msi_free(deformated_section);
3712 return ERROR_SUCCESS;
3715 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3717 UINT rc;
3718 MSIQUERY * view;
3719 static const WCHAR ExecSeqQuery[] =
3720 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3721 '`','I','n','i','F','i','l','e','`',0};
3723 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3724 if (rc != ERROR_SUCCESS)
3726 TRACE("no IniFile table\n");
3727 return ERROR_SUCCESS;
3730 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3731 msiobj_release(&view->hdr);
3732 return rc;
3735 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3737 MSIPACKAGE *package = (MSIPACKAGE*)param;
3738 LPCWSTR filename;
3739 LPWSTR FullName;
3740 MSIFILE *file;
3741 DWORD len;
3742 static const WCHAR ExeStr[] =
3743 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3744 static const WCHAR close[] = {'\"',0};
3745 STARTUPINFOW si;
3746 PROCESS_INFORMATION info;
3747 BOOL brc;
3748 MSIRECORD *uirow;
3749 LPWSTR uipath, p;
3751 memset(&si,0,sizeof(STARTUPINFOW));
3753 filename = MSI_RecordGetString(row,1);
3754 file = get_loaded_file( package, filename );
3756 if (!file)
3758 ERR("Unable to find file id %s\n",debugstr_w(filename));
3759 return ERROR_SUCCESS;
3762 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3764 FullName = msi_alloc(len*sizeof(WCHAR));
3765 strcpyW(FullName,ExeStr);
3766 strcatW( FullName, file->TargetPath );
3767 strcatW(FullName,close);
3769 TRACE("Registering %s\n",debugstr_w(FullName));
3770 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3771 &si, &info);
3773 if (brc)
3774 msi_dialog_check_messages(info.hProcess);
3776 msi_free(FullName);
3778 /* the UI chunk */
3779 uirow = MSI_CreateRecord( 2 );
3780 uipath = strdupW( file->TargetPath );
3781 p = strrchrW(uipath,'\\');
3782 if (p)
3783 p[0]=0;
3784 MSI_RecordSetStringW( uirow, 1, &p[1] );
3785 MSI_RecordSetStringW( uirow, 2, uipath);
3786 ui_actiondata( package, szSelfRegModules, uirow);
3787 msiobj_release( &uirow->hdr );
3788 msi_free( uipath );
3789 /* FIXME: call ui_progress? */
3791 return ERROR_SUCCESS;
3794 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3796 UINT rc;
3797 MSIQUERY * view;
3798 static const WCHAR ExecSeqQuery[] =
3799 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3800 '`','S','e','l','f','R','e','g','`',0};
3802 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3803 if (rc != ERROR_SUCCESS)
3805 TRACE("no SelfReg table\n");
3806 return ERROR_SUCCESS;
3809 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3810 msiobj_release(&view->hdr);
3812 return ERROR_SUCCESS;
3815 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3817 MSIFEATURE *feature;
3818 UINT rc;
3819 HKEY hkey;
3820 HKEY userdata;
3822 if (!msi_check_publish(package))
3823 return ERROR_SUCCESS;
3825 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3827 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3828 &hkey, TRUE);
3829 if (rc != ERROR_SUCCESS)
3830 goto end;
3832 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3833 &userdata, TRUE);
3834 if (rc != ERROR_SUCCESS)
3835 goto end;
3837 else
3839 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3840 if (rc != ERROR_SUCCESS)
3841 goto end;
3843 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3844 &userdata, TRUE);
3845 if (rc != ERROR_SUCCESS)
3846 goto end;
3849 /* here the guids are base 85 encoded */
3850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3852 ComponentList *cl;
3853 LPWSTR data = NULL;
3854 GUID clsid;
3855 INT size;
3856 BOOL absent = FALSE;
3857 MSIRECORD *uirow;
3859 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3860 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3861 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3862 absent = TRUE;
3864 size = 1;
3865 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3867 size += 21;
3869 if (feature->Feature_Parent)
3870 size += strlenW( feature->Feature_Parent )+2;
3872 data = msi_alloc(size * sizeof(WCHAR));
3874 data[0] = 0;
3875 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3877 MSICOMPONENT* component = cl->component;
3878 WCHAR buf[21];
3880 buf[0] = 0;
3881 if (component->ComponentId)
3883 TRACE("From %s\n",debugstr_w(component->ComponentId));
3884 CLSIDFromString(component->ComponentId, &clsid);
3885 encode_base85_guid(&clsid,buf);
3886 TRACE("to %s\n",debugstr_w(buf));
3887 strcatW(data,buf);
3891 if (feature->Feature_Parent)
3893 static const WCHAR sep[] = {'\2',0};
3894 strcatW(data,sep);
3895 strcatW(data,feature->Feature_Parent);
3898 msi_reg_set_val_str( userdata, feature->Feature, data );
3899 msi_free(data);
3901 size = 0;
3902 if (feature->Feature_Parent)
3903 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3904 if (!absent)
3906 static const WCHAR emptyW[] = {0};
3907 size += sizeof(WCHAR);
3908 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3909 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3911 else
3913 size += 2*sizeof(WCHAR);
3914 data = msi_alloc(size);
3915 data[0] = 0x6;
3916 data[1] = 0;
3917 if (feature->Feature_Parent)
3918 strcpyW( &data[1], feature->Feature_Parent );
3919 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3920 (LPBYTE)data,size);
3921 msi_free(data);
3924 /* the UI chunk */
3925 uirow = MSI_CreateRecord( 1 );
3926 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3927 ui_actiondata( package, szPublishFeatures, uirow);
3928 msiobj_release( &uirow->hdr );
3929 /* FIXME: call ui_progress? */
3932 end:
3933 RegCloseKey(hkey);
3934 RegCloseKey(userdata);
3935 return rc;
3938 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3940 UINT r;
3941 HKEY hkey;
3943 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3945 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3946 if (r == ERROR_SUCCESS)
3948 RegDeleteValueW(hkey, feature->Feature);
3949 RegCloseKey(hkey);
3952 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3953 if (r == ERROR_SUCCESS)
3955 RegDeleteValueW(hkey, feature->Feature);
3956 RegCloseKey(hkey);
3959 return ERROR_SUCCESS;
3962 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3964 MSIFEATURE *feature;
3966 if (!msi_check_unpublish(package))
3967 return ERROR_SUCCESS;
3969 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3971 msi_unpublish_feature(package, feature);
3974 return ERROR_SUCCESS;
3977 static UINT msi_get_local_package_name( LPWSTR path )
3979 static const WCHAR szInstaller[] = {
3980 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3981 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3982 DWORD time, len, i;
3983 HANDLE handle;
3985 time = GetTickCount();
3986 GetWindowsDirectoryW( path, MAX_PATH );
3987 lstrcatW( path, szInstaller );
3988 CreateDirectoryW( path, NULL );
3990 len = lstrlenW(path);
3991 for (i=0; i<0x10000; i++)
3993 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3994 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3995 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3996 if (handle != INVALID_HANDLE_VALUE)
3998 CloseHandle(handle);
3999 break;
4001 if (GetLastError() != ERROR_FILE_EXISTS &&
4002 GetLastError() != ERROR_SHARING_VIOLATION)
4003 return ERROR_FUNCTION_FAILED;
4006 return ERROR_SUCCESS;
4009 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4011 WCHAR packagefile[MAX_PATH];
4012 UINT r;
4014 r = msi_get_local_package_name( packagefile );
4015 if (r != ERROR_SUCCESS)
4016 return r;
4018 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4020 r = CopyFileW( package->db->path, packagefile, FALSE);
4022 if (!r)
4024 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4025 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4026 return ERROR_FUNCTION_FAILED;
4029 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4031 return ERROR_SUCCESS;
4034 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4036 LPWSTR prop, val, key;
4037 SYSTEMTIME systime;
4038 DWORD size, langid;
4039 WCHAR date[9];
4040 LPWSTR buffer;
4042 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4043 static const WCHAR szWindowsInstaller[] =
4044 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4045 static const WCHAR modpath_fmt[] =
4046 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4047 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4048 static const WCHAR szModifyPath[] =
4049 {'M','o','d','i','f','y','P','a','t','h',0};
4050 static const WCHAR szUninstallString[] =
4051 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4052 static const WCHAR szEstimatedSize[] =
4053 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4054 static const WCHAR szProductLanguage[] =
4055 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4056 static const WCHAR szProductVersion[] =
4057 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4058 static const WCHAR szProductName[] =
4059 {'P','r','o','d','u','c','t','N','a','m','e',0};
4060 static const WCHAR szDisplayName[] =
4061 {'D','i','s','p','l','a','y','N','a','m','e',0};
4062 static const WCHAR szDisplayVersion[] =
4063 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4064 static const WCHAR szManufacturer[] =
4065 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4067 static const LPCSTR propval[] = {
4068 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4069 "ARPCONTACT", "Contact",
4070 "ARPCOMMENTS", "Comments",
4071 "ProductName", "DisplayName",
4072 "ProductVersion", "DisplayVersion",
4073 "ARPHELPLINK", "HelpLink",
4074 "ARPHELPTELEPHONE", "HelpTelephone",
4075 "ARPINSTALLLOCATION", "InstallLocation",
4076 "SourceDir", "InstallSource",
4077 "Manufacturer", "Publisher",
4078 "ARPREADME", "Readme",
4079 "ARPSIZE", "Size",
4080 "ARPURLINFOABOUT", "URLInfoAbout",
4081 "ARPURLUPDATEINFO", "URLUpdateInfo",
4082 NULL,
4084 const LPCSTR *p = propval;
4086 while (*p)
4088 prop = strdupAtoW(*p++);
4089 key = strdupAtoW(*p++);
4090 val = msi_dup_property(package, prop);
4091 msi_reg_set_val_str(hkey, key, val);
4092 msi_free(val);
4093 msi_free(key);
4094 msi_free(prop);
4097 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4099 size = deformat_string(package, modpath_fmt, &buffer);
4100 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4101 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4102 msi_free(buffer);
4104 /* FIXME: Write real Estimated Size when we have it */
4105 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4107 buffer = msi_dup_property(package, szProductName);
4108 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4109 msi_free(buffer);
4111 buffer = msi_dup_property(package, cszSourceDir);
4112 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4113 msi_free(buffer);
4115 buffer = msi_dup_property(package, szManufacturer);
4116 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4117 msi_free(buffer);
4119 GetLocalTime(&systime);
4120 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4121 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4123 langid = msi_get_property_int(package, szProductLanguage, 0);
4124 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4126 buffer = msi_dup_property(package, szProductVersion);
4127 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4128 if (buffer)
4130 DWORD verdword = msi_version_str_to_dword(buffer);
4132 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4133 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4134 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4135 msi_free(buffer);
4138 return ERROR_SUCCESS;
4141 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4143 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4144 LPWSTR upgrade_code;
4145 HKEY hkey, props;
4146 HKEY upgrade;
4147 UINT rc;
4149 static const WCHAR szUpgradeCode[] = {
4150 'U','p','g','r','a','d','e','C','o','d','e',0};
4152 /* FIXME: also need to publish if the product is in advertise mode */
4153 if (!msi_check_publish(package))
4154 return ERROR_SUCCESS;
4156 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4157 if (rc != ERROR_SUCCESS)
4158 return rc;
4160 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4162 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4163 if (rc != ERROR_SUCCESS)
4164 goto done;
4166 else
4168 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4169 if (rc != ERROR_SUCCESS)
4170 goto done;
4173 msi_make_package_local(package, props);
4175 rc = msi_publish_install_properties(package, hkey);
4176 if (rc != ERROR_SUCCESS)
4177 goto done;
4179 rc = msi_publish_install_properties(package, props);
4180 if (rc != ERROR_SUCCESS)
4181 goto done;
4183 upgrade_code = msi_dup_property(package, szUpgradeCode);
4184 if (upgrade_code)
4186 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4187 squash_guid(package->ProductCode, squashed_pc);
4188 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4189 RegCloseKey(upgrade);
4190 msi_free(upgrade_code);
4193 done:
4194 RegCloseKey(hkey);
4196 return ERROR_SUCCESS;
4199 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4201 return execute_script(package,INSTALL_SCRIPT);
4204 static UINT msi_unpublish_product(MSIPACKAGE *package)
4206 LPWSTR upgrade;
4207 LPWSTR remove = NULL;
4208 LPWSTR *features = NULL;
4209 BOOL full_uninstall = TRUE;
4210 MSIFEATURE *feature;
4212 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4213 static const WCHAR szAll[] = {'A','L','L',0};
4214 static const WCHAR szUpgradeCode[] =
4215 {'U','p','g','r','a','d','e','C','o','d','e',0};
4217 remove = msi_dup_property(package, szRemove);
4218 if (!remove)
4219 return ERROR_SUCCESS;
4221 features = msi_split_string(remove, ',');
4222 if (!features)
4224 msi_free(remove);
4225 ERR("REMOVE feature list is empty!\n");
4226 return ERROR_FUNCTION_FAILED;
4229 if (!lstrcmpW(features[0], szAll))
4230 full_uninstall = TRUE;
4231 else
4233 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4235 if (feature->Action != INSTALLSTATE_ABSENT)
4236 full_uninstall = FALSE;
4240 if (!full_uninstall)
4241 goto done;
4243 MSIREG_DeleteProductKey(package->ProductCode);
4244 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4245 MSIREG_DeleteUninstallKey(package->ProductCode);
4247 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4249 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4250 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4252 else
4254 MSIREG_DeleteUserProductKey(package->ProductCode);
4255 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4258 upgrade = msi_dup_property(package, szUpgradeCode);
4259 if (upgrade)
4261 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4262 msi_free(upgrade);
4265 done:
4266 msi_free(remove);
4267 msi_free(features);
4268 return ERROR_SUCCESS;
4271 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4273 UINT rc;
4275 rc = msi_unpublish_product(package);
4276 if (rc != ERROR_SUCCESS)
4277 return rc;
4279 /* turn off scheduling */
4280 package->script->CurrentlyScripting= FALSE;
4282 /* first do the same as an InstallExecute */
4283 rc = ACTION_InstallExecute(package);
4284 if (rc != ERROR_SUCCESS)
4285 return rc;
4287 /* then handle Commit Actions */
4288 rc = execute_script(package,COMMIT_SCRIPT);
4290 return rc;
4293 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4295 static const WCHAR RunOnce[] = {
4296 'S','o','f','t','w','a','r','e','\\',
4297 'M','i','c','r','o','s','o','f','t','\\',
4298 'W','i','n','d','o','w','s','\\',
4299 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4300 'R','u','n','O','n','c','e',0};
4301 static const WCHAR InstallRunOnce[] = {
4302 'S','o','f','t','w','a','r','e','\\',
4303 'M','i','c','r','o','s','o','f','t','\\',
4304 'W','i','n','d','o','w','s','\\',
4305 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4306 'I','n','s','t','a','l','l','e','r','\\',
4307 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4309 static const WCHAR msiexec_fmt[] = {
4310 '%','s',
4311 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4312 '\"','%','s','\"',0};
4313 static const WCHAR install_fmt[] = {
4314 '/','I',' ','\"','%','s','\"',' ',
4315 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4316 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4317 WCHAR buffer[256], sysdir[MAX_PATH];
4318 HKEY hkey;
4319 WCHAR squished_pc[100];
4321 squash_guid(package->ProductCode,squished_pc);
4323 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4324 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4325 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4326 squished_pc);
4328 msi_reg_set_val_str( hkey, squished_pc, buffer );
4329 RegCloseKey(hkey);
4331 TRACE("Reboot command %s\n",debugstr_w(buffer));
4333 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4334 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4336 msi_reg_set_val_str( hkey, squished_pc, buffer );
4337 RegCloseKey(hkey);
4339 return ERROR_INSTALL_SUSPEND;
4342 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4344 DWORD attrib;
4345 UINT rc;
4348 * We are currently doing what should be done here in the top level Install
4349 * however for Administrative and uninstalls this step will be needed
4351 if (!package->PackagePath)
4352 return ERROR_SUCCESS;
4354 msi_set_sourcedir_props(package, TRUE);
4356 attrib = GetFileAttributesW(package->db->path);
4357 if (attrib == INVALID_FILE_ATTRIBUTES)
4359 LPWSTR prompt;
4360 LPWSTR msg;
4361 DWORD size = 0;
4363 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4364 package->Context, MSICODE_PRODUCT,
4365 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4366 if (rc == ERROR_MORE_DATA)
4368 prompt = msi_alloc(size * sizeof(WCHAR));
4369 MsiSourceListGetInfoW(package->ProductCode, NULL,
4370 package->Context, MSICODE_PRODUCT,
4371 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4373 else
4374 prompt = strdupW(package->db->path);
4376 msg = generate_error_string(package,1302,1,prompt);
4377 while(attrib == INVALID_FILE_ATTRIBUTES)
4379 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4380 if (rc == IDCANCEL)
4382 rc = ERROR_INSTALL_USEREXIT;
4383 break;
4385 attrib = GetFileAttributesW(package->db->path);
4387 msi_free(prompt);
4388 rc = ERROR_SUCCESS;
4390 else
4391 return ERROR_SUCCESS;
4393 return rc;
4396 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4398 HKEY hkey=0;
4399 LPWSTR buffer;
4400 LPWSTR productid;
4401 UINT rc,i;
4403 static const WCHAR szPropKeys[][80] =
4405 {'P','r','o','d','u','c','t','I','D',0},
4406 {'U','S','E','R','N','A','M','E',0},
4407 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4408 {0},
4411 static const WCHAR szRegKeys[][80] =
4413 {'P','r','o','d','u','c','t','I','D',0},
4414 {'R','e','g','O','w','n','e','r',0},
4415 {'R','e','g','C','o','m','p','a','n','y',0},
4416 {0},
4419 if (msi_check_unpublish(package))
4421 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4422 return ERROR_SUCCESS;
4425 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4426 if (!productid)
4427 return ERROR_SUCCESS;
4429 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4430 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4431 else
4432 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4434 if (rc != ERROR_SUCCESS)
4435 goto end;
4437 for( i = 0; szPropKeys[i][0]; i++ )
4439 buffer = msi_dup_property( package, szPropKeys[i] );
4440 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4441 msi_free( buffer );
4444 end:
4445 msi_free(productid);
4446 RegCloseKey(hkey);
4448 /* FIXME: call ui_actiondata */
4450 return rc;
4454 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4456 UINT rc;
4458 package->script->InWhatSequence |= SEQUENCE_EXEC;
4459 rc = ACTION_ProcessExecSequence(package,FALSE);
4460 return rc;
4464 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4466 MSIPACKAGE *package = (MSIPACKAGE*)param;
4467 LPCWSTR compgroupid=NULL;
4468 LPCWSTR feature=NULL;
4469 LPCWSTR text = NULL;
4470 LPCWSTR qualifier = NULL;
4471 LPCWSTR component = NULL;
4472 LPWSTR advertise = NULL;
4473 LPWSTR output = NULL;
4474 HKEY hkey;
4475 UINT rc = ERROR_SUCCESS;
4476 MSICOMPONENT *comp;
4477 DWORD sz = 0;
4478 MSIRECORD *uirow;
4480 component = MSI_RecordGetString(rec,3);
4481 comp = get_loaded_component(package,component);
4483 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4484 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4485 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4487 TRACE("Skipping: Component %s not scheduled for install\n",
4488 debugstr_w(component));
4490 return ERROR_SUCCESS;
4493 compgroupid = MSI_RecordGetString(rec,1);
4494 qualifier = MSI_RecordGetString(rec,2);
4496 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4497 if (rc != ERROR_SUCCESS)
4498 goto end;
4500 text = MSI_RecordGetString(rec,4);
4501 feature = MSI_RecordGetString(rec,5);
4503 advertise = create_component_advertise_string(package, comp, feature);
4505 sz = strlenW(advertise);
4507 if (text)
4508 sz += lstrlenW(text);
4510 sz+=3;
4511 sz *= sizeof(WCHAR);
4513 output = msi_alloc_zero(sz);
4514 strcpyW(output,advertise);
4515 msi_free(advertise);
4517 if (text)
4518 strcatW(output,text);
4520 msi_reg_set_val_multi_str( hkey, qualifier, output );
4522 end:
4523 RegCloseKey(hkey);
4524 msi_free(output);
4526 /* the UI chunk */
4527 uirow = MSI_CreateRecord( 2 );
4528 MSI_RecordSetStringW( uirow, 1, compgroupid );
4529 MSI_RecordSetStringW( uirow, 2, qualifier);
4530 ui_actiondata( package, szPublishComponents, uirow);
4531 msiobj_release( &uirow->hdr );
4532 /* FIXME: call ui_progress? */
4534 return rc;
4538 * At present I am ignorning the advertised components part of this and only
4539 * focusing on the qualified component sets
4541 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4543 UINT rc;
4544 MSIQUERY * view;
4545 static const WCHAR ExecSeqQuery[] =
4546 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547 '`','P','u','b','l','i','s','h',
4548 'C','o','m','p','o','n','e','n','t','`',0};
4550 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4551 if (rc != ERROR_SUCCESS)
4552 return ERROR_SUCCESS;
4554 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4555 msiobj_release(&view->hdr);
4557 return rc;
4560 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4562 MSIPACKAGE *package = (MSIPACKAGE*)param;
4563 MSIRECORD *row;
4564 MSIFILE *file;
4565 SC_HANDLE hscm, service = NULL;
4566 LPCWSTR comp, depends, pass;
4567 LPWSTR name = NULL, disp = NULL;
4568 LPCWSTR load_order, serv_name, key;
4569 DWORD serv_type, start_type;
4570 DWORD err_control;
4572 static const WCHAR query[] =
4573 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4574 '`','C','o','m','p','o','n','e','n','t','`',' ',
4575 'W','H','E','R','E',' ',
4576 '`','C','o','m','p','o','n','e','n','t','`',' ',
4577 '=','\'','%','s','\'',0};
4579 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4580 if (!hscm)
4582 ERR("Failed to open the SC Manager!\n");
4583 goto done;
4586 start_type = MSI_RecordGetInteger(rec, 5);
4587 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4588 goto done;
4590 depends = MSI_RecordGetString(rec, 8);
4591 if (depends && *depends)
4592 FIXME("Dependency list unhandled!\n");
4594 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4595 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4596 serv_type = MSI_RecordGetInteger(rec, 4);
4597 err_control = MSI_RecordGetInteger(rec, 6);
4598 load_order = MSI_RecordGetString(rec, 7);
4599 serv_name = MSI_RecordGetString(rec, 9);
4600 pass = MSI_RecordGetString(rec, 10);
4601 comp = MSI_RecordGetString(rec, 12);
4603 /* fetch the service path */
4604 row = MSI_QueryGetRecord(package->db, query, comp);
4605 if (!row)
4607 ERR("Control query failed!\n");
4608 goto done;
4611 key = MSI_RecordGetString(row, 6);
4613 file = get_loaded_file(package, key);
4614 msiobj_release(&row->hdr);
4615 if (!file)
4617 ERR("Failed to load the service file\n");
4618 goto done;
4621 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4622 start_type, err_control, file->TargetPath,
4623 load_order, NULL, NULL, serv_name, pass);
4624 if (!service)
4626 if (GetLastError() != ERROR_SERVICE_EXISTS)
4627 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4630 done:
4631 CloseServiceHandle(service);
4632 CloseServiceHandle(hscm);
4633 msi_free(name);
4634 msi_free(disp);
4636 return ERROR_SUCCESS;
4639 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4641 UINT rc;
4642 MSIQUERY * view;
4643 static const WCHAR ExecSeqQuery[] =
4644 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4645 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4647 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4648 if (rc != ERROR_SUCCESS)
4649 return ERROR_SUCCESS;
4651 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4652 msiobj_release(&view->hdr);
4654 return rc;
4657 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4658 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4660 LPCWSTR *vector, *temp_vector;
4661 LPWSTR p, q;
4662 DWORD sep_len;
4664 static const WCHAR separator[] = {'[','~',']',0};
4666 *numargs = 0;
4667 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4669 if (!args)
4670 return NULL;
4672 vector = msi_alloc(sizeof(LPWSTR));
4673 if (!vector)
4674 return NULL;
4676 p = args;
4679 (*numargs)++;
4680 vector[*numargs - 1] = p;
4682 if ((q = strstrW(p, separator)))
4684 *q = '\0';
4686 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4687 if (!temp_vector)
4689 msi_free(vector);
4690 return NULL;
4692 vector = temp_vector;
4694 p = q + sep_len;
4696 } while (q);
4698 return vector;
4701 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4703 MSIPACKAGE *package = (MSIPACKAGE *)param;
4704 MSICOMPONENT *comp;
4705 SC_HANDLE scm, service = NULL;
4706 LPCWSTR name, *vector = NULL;
4707 LPWSTR args;
4708 DWORD event, numargs;
4709 UINT r = ERROR_FUNCTION_FAILED;
4711 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4712 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4713 return ERROR_SUCCESS;
4715 name = MSI_RecordGetString(rec, 2);
4716 event = MSI_RecordGetInteger(rec, 3);
4717 args = strdupW(MSI_RecordGetString(rec, 4));
4719 if (!(event & msidbServiceControlEventStart))
4720 return ERROR_SUCCESS;
4722 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4723 if (!scm)
4725 ERR("Failed to open the service control manager\n");
4726 goto done;
4729 service = OpenServiceW(scm, name, SERVICE_START);
4730 if (!service)
4732 ERR("Failed to open service %s\n", debugstr_w(name));
4733 goto done;
4736 vector = msi_service_args_to_vector(args, &numargs);
4738 if (!StartServiceW(service, numargs, vector))
4740 ERR("Failed to start service %s\n", debugstr_w(name));
4741 goto done;
4744 r = ERROR_SUCCESS;
4746 done:
4747 CloseServiceHandle(service);
4748 CloseServiceHandle(scm);
4750 msi_free(args);
4751 msi_free(vector);
4752 return r;
4755 static UINT ACTION_StartServices( MSIPACKAGE *package )
4757 UINT rc;
4758 MSIQUERY *view;
4760 static const WCHAR query[] = {
4761 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4762 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4764 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4765 if (rc != ERROR_SUCCESS)
4766 return ERROR_SUCCESS;
4768 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4769 msiobj_release(&view->hdr);
4771 return rc;
4774 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4776 DWORD i, needed, count;
4777 ENUM_SERVICE_STATUSW *dependencies;
4778 SERVICE_STATUS ss;
4779 SC_HANDLE depserv;
4781 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4782 0, &needed, &count))
4783 return TRUE;
4785 if (GetLastError() != ERROR_MORE_DATA)
4786 return FALSE;
4788 dependencies = msi_alloc(needed);
4789 if (!dependencies)
4790 return FALSE;
4792 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4793 needed, &needed, &count))
4794 goto error;
4796 for (i = 0; i < count; i++)
4798 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4799 SERVICE_STOP | SERVICE_QUERY_STATUS);
4800 if (!depserv)
4801 goto error;
4803 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4804 goto error;
4807 return TRUE;
4809 error:
4810 msi_free(dependencies);
4811 return FALSE;
4814 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4816 MSIPACKAGE *package = (MSIPACKAGE *)param;
4817 MSICOMPONENT *comp;
4818 SERVICE_STATUS status;
4819 SERVICE_STATUS_PROCESS ssp;
4820 SC_HANDLE scm = NULL, service = NULL;
4821 LPWSTR name, args;
4822 DWORD event, needed;
4824 event = MSI_RecordGetInteger(rec, 3);
4825 if (!(event & msidbServiceControlEventStop))
4826 return ERROR_SUCCESS;
4828 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4829 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4830 return ERROR_SUCCESS;
4832 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4833 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4834 args = strdupW(MSI_RecordGetString(rec, 4));
4836 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4837 if (!scm)
4839 WARN("Failed to open the SCM: %d\n", GetLastError());
4840 goto done;
4843 service = OpenServiceW(scm, name,
4844 SERVICE_STOP |
4845 SERVICE_QUERY_STATUS |
4846 SERVICE_ENUMERATE_DEPENDENTS);
4847 if (!service)
4849 WARN("Failed to open service (%s): %d\n",
4850 debugstr_w(name), GetLastError());
4851 goto done;
4854 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4855 sizeof(SERVICE_STATUS_PROCESS), &needed))
4857 WARN("Failed to query service status (%s): %d\n",
4858 debugstr_w(name), GetLastError());
4859 goto done;
4862 if (ssp.dwCurrentState == SERVICE_STOPPED)
4863 goto done;
4865 stop_service_dependents(scm, service);
4867 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4868 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4870 done:
4871 CloseServiceHandle(service);
4872 CloseServiceHandle(scm);
4873 msi_free(name);
4874 msi_free(args);
4876 return ERROR_SUCCESS;
4879 static UINT ACTION_StopServices( MSIPACKAGE *package )
4881 UINT rc;
4882 MSIQUERY *view;
4884 static const WCHAR query[] = {
4885 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4886 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4888 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4889 if (rc != ERROR_SUCCESS)
4890 return ERROR_SUCCESS;
4892 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4893 msiobj_release(&view->hdr);
4895 return rc;
4898 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4900 MSIFILE *file;
4902 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4904 if (!lstrcmpW(file->File, filename))
4905 return file;
4908 return NULL;
4911 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4913 MSIPACKAGE *package = (MSIPACKAGE*)param;
4914 LPWSTR driver, driver_path, ptr;
4915 WCHAR outpath[MAX_PATH];
4916 MSIFILE *driver_file, *setup_file;
4917 LPCWSTR desc;
4918 DWORD len, usage;
4919 UINT r = ERROR_SUCCESS;
4921 static const WCHAR driver_fmt[] = {
4922 'D','r','i','v','e','r','=','%','s',0};
4923 static const WCHAR setup_fmt[] = {
4924 'S','e','t','u','p','=','%','s',0};
4925 static const WCHAR usage_fmt[] = {
4926 'F','i','l','e','U','s','a','g','e','=','1',0};
4928 desc = MSI_RecordGetString(rec, 3);
4930 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4931 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4933 if (!driver_file || !setup_file)
4935 ERR("ODBC Driver entry not found!\n");
4936 return ERROR_FUNCTION_FAILED;
4939 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4940 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4941 lstrlenW(usage_fmt) + 1;
4942 driver = msi_alloc(len * sizeof(WCHAR));
4943 if (!driver)
4944 return ERROR_OUTOFMEMORY;
4946 ptr = driver;
4947 lstrcpyW(ptr, desc);
4948 ptr += lstrlenW(ptr) + 1;
4950 sprintfW(ptr, driver_fmt, driver_file->FileName);
4951 ptr += lstrlenW(ptr) + 1;
4953 sprintfW(ptr, setup_fmt, setup_file->FileName);
4954 ptr += lstrlenW(ptr) + 1;
4956 lstrcpyW(ptr, usage_fmt);
4957 ptr += lstrlenW(ptr) + 1;
4958 *ptr = '\0';
4960 driver_path = strdupW(driver_file->TargetPath);
4961 ptr = strrchrW(driver_path, '\\');
4962 if (ptr) *ptr = '\0';
4964 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4965 NULL, ODBC_INSTALL_COMPLETE, &usage))
4967 ERR("Failed to install SQL driver!\n");
4968 r = ERROR_FUNCTION_FAILED;
4971 msi_free(driver);
4972 msi_free(driver_path);
4974 return r;
4977 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4979 MSIPACKAGE *package = (MSIPACKAGE*)param;
4980 LPWSTR translator, translator_path, ptr;
4981 WCHAR outpath[MAX_PATH];
4982 MSIFILE *translator_file, *setup_file;
4983 LPCWSTR desc;
4984 DWORD len, usage;
4985 UINT r = ERROR_SUCCESS;
4987 static const WCHAR translator_fmt[] = {
4988 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4989 static const WCHAR setup_fmt[] = {
4990 'S','e','t','u','p','=','%','s',0};
4992 desc = MSI_RecordGetString(rec, 3);
4994 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4995 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4997 if (!translator_file || !setup_file)
4999 ERR("ODBC Translator entry not found!\n");
5000 return ERROR_FUNCTION_FAILED;
5003 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5004 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5005 translator = msi_alloc(len * sizeof(WCHAR));
5006 if (!translator)
5007 return ERROR_OUTOFMEMORY;
5009 ptr = translator;
5010 lstrcpyW(ptr, desc);
5011 ptr += lstrlenW(ptr) + 1;
5013 sprintfW(ptr, translator_fmt, translator_file->FileName);
5014 ptr += lstrlenW(ptr) + 1;
5016 sprintfW(ptr, setup_fmt, setup_file->FileName);
5017 ptr += lstrlenW(ptr) + 1;
5018 *ptr = '\0';
5020 translator_path = strdupW(translator_file->TargetPath);
5021 ptr = strrchrW(translator_path, '\\');
5022 if (ptr) *ptr = '\0';
5024 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5025 NULL, ODBC_INSTALL_COMPLETE, &usage))
5027 ERR("Failed to install SQL translator!\n");
5028 r = ERROR_FUNCTION_FAILED;
5031 msi_free(translator);
5032 msi_free(translator_path);
5034 return r;
5037 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5039 LPWSTR attrs;
5040 LPCWSTR desc, driver;
5041 WORD request = ODBC_ADD_SYS_DSN;
5042 INT registration;
5043 DWORD len;
5044 UINT r = ERROR_SUCCESS;
5046 static const WCHAR attrs_fmt[] = {
5047 'D','S','N','=','%','s',0 };
5049 desc = MSI_RecordGetString(rec, 3);
5050 driver = MSI_RecordGetString(rec, 4);
5051 registration = MSI_RecordGetInteger(rec, 5);
5053 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5054 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5056 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5057 attrs = msi_alloc(len * sizeof(WCHAR));
5058 if (!attrs)
5059 return ERROR_OUTOFMEMORY;
5061 sprintfW(attrs, attrs_fmt, desc);
5062 attrs[len - 1] = '\0';
5064 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5066 ERR("Failed to install SQL data source!\n");
5067 r = ERROR_FUNCTION_FAILED;
5070 msi_free(attrs);
5072 return r;
5075 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5077 UINT rc;
5078 MSIQUERY *view;
5080 static const WCHAR driver_query[] = {
5081 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5082 'O','D','B','C','D','r','i','v','e','r',0 };
5084 static const WCHAR translator_query[] = {
5085 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5086 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5088 static const WCHAR source_query[] = {
5089 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5090 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5092 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5093 if (rc != ERROR_SUCCESS)
5094 return ERROR_SUCCESS;
5096 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5097 msiobj_release(&view->hdr);
5099 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5100 if (rc != ERROR_SUCCESS)
5101 return ERROR_SUCCESS;
5103 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5104 msiobj_release(&view->hdr);
5106 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5107 if (rc != ERROR_SUCCESS)
5108 return ERROR_SUCCESS;
5110 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5111 msiobj_release(&view->hdr);
5113 return rc;
5116 #define ENV_ACT_SETALWAYS 0x1
5117 #define ENV_ACT_SETABSENT 0x2
5118 #define ENV_ACT_REMOVE 0x4
5119 #define ENV_ACT_REMOVEMATCH 0x8
5121 #define ENV_MOD_MACHINE 0x20000000
5122 #define ENV_MOD_APPEND 0x40000000
5123 #define ENV_MOD_PREFIX 0x80000000
5124 #define ENV_MOD_MASK 0xC0000000
5126 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5128 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5130 LPCWSTR cptr = *name;
5131 LPCWSTR ptr = *value;
5133 static const WCHAR prefix[] = {'[','~',']',0};
5134 static const int prefix_len = 3;
5136 *flags = 0;
5137 while (*cptr)
5139 if (*cptr == '=')
5140 *flags |= ENV_ACT_SETALWAYS;
5141 else if (*cptr == '+')
5142 *flags |= ENV_ACT_SETABSENT;
5143 else if (*cptr == '-')
5144 *flags |= ENV_ACT_REMOVE;
5145 else if (*cptr == '!')
5146 *flags |= ENV_ACT_REMOVEMATCH;
5147 else if (*cptr == '*')
5148 *flags |= ENV_MOD_MACHINE;
5149 else
5150 break;
5152 cptr++;
5153 (*name)++;
5156 if (!*cptr)
5158 ERR("Missing environment variable\n");
5159 return ERROR_FUNCTION_FAILED;
5162 if (!strncmpW(ptr, prefix, prefix_len))
5164 *flags |= ENV_MOD_APPEND;
5165 *value += lstrlenW(prefix);
5167 else if (lstrlenW(*value) >= prefix_len)
5169 ptr += lstrlenW(ptr) - prefix_len;
5170 if (!lstrcmpW(ptr, prefix))
5172 *flags |= ENV_MOD_PREFIX;
5173 /* the "[~]" will be removed by deformat_string */;
5177 if (!*flags ||
5178 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5179 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5180 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5181 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5183 ERR("Invalid flags: %08x\n", *flags);
5184 return ERROR_FUNCTION_FAILED;
5187 return ERROR_SUCCESS;
5190 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5192 MSIPACKAGE *package = param;
5193 LPCWSTR name, value;
5194 LPWSTR data = NULL, newval = NULL;
5195 LPWSTR deformatted = NULL, ptr;
5196 DWORD flags, type, size;
5197 LONG res;
5198 HKEY env = NULL, root;
5199 LPCWSTR environment;
5201 static const WCHAR user_env[] =
5202 {'E','n','v','i','r','o','n','m','e','n','t',0};
5203 static const WCHAR machine_env[] =
5204 {'S','y','s','t','e','m','\\',
5205 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5206 'C','o','n','t','r','o','l','\\',
5207 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5208 'E','n','v','i','r','o','n','m','e','n','t',0};
5209 static const WCHAR semicolon[] = {';',0};
5211 name = MSI_RecordGetString(rec, 2);
5212 value = MSI_RecordGetString(rec, 3);
5214 res = env_set_flags(&name, &value, &flags);
5215 if (res != ERROR_SUCCESS)
5216 goto done;
5218 deformat_string(package, value, &deformatted);
5219 if (!deformatted)
5221 res = ERROR_OUTOFMEMORY;
5222 goto done;
5225 value = deformatted;
5227 if (flags & ENV_MOD_MACHINE)
5229 environment = machine_env;
5230 root = HKEY_LOCAL_MACHINE;
5232 else
5234 environment = user_env;
5235 root = HKEY_CURRENT_USER;
5238 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5239 KEY_ALL_ACCESS, NULL, &env, NULL);
5240 if (res != ERROR_SUCCESS)
5241 goto done;
5243 if (flags & ENV_ACT_REMOVE)
5244 FIXME("Not removing environment variable on uninstall!\n");
5246 size = 0;
5247 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5248 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5249 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5250 goto done;
5252 if (res != ERROR_FILE_NOT_FOUND)
5254 if (flags & ENV_ACT_SETABSENT)
5256 res = ERROR_SUCCESS;
5257 goto done;
5260 data = msi_alloc(size);
5261 if (!data)
5263 RegCloseKey(env);
5264 return ERROR_OUTOFMEMORY;
5267 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5268 if (res != ERROR_SUCCESS)
5269 goto done;
5271 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5273 res = RegDeleteKeyW(env, name);
5274 goto done;
5277 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5278 newval = msi_alloc(size);
5279 ptr = newval;
5280 if (!newval)
5282 res = ERROR_OUTOFMEMORY;
5283 goto done;
5286 if (!(flags & ENV_MOD_MASK))
5287 lstrcpyW(newval, value);
5288 else
5290 if (flags & ENV_MOD_PREFIX)
5292 lstrcpyW(newval, value);
5293 lstrcatW(newval, semicolon);
5294 ptr = newval + lstrlenW(value) + 1;
5297 lstrcpyW(ptr, data);
5299 if (flags & ENV_MOD_APPEND)
5301 lstrcatW(newval, semicolon);
5302 lstrcatW(newval, value);
5306 else
5308 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5309 newval = msi_alloc(size);
5310 if (!newval)
5312 res = ERROR_OUTOFMEMORY;
5313 goto done;
5316 lstrcpyW(newval, value);
5319 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5320 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5322 done:
5323 if (env) RegCloseKey(env);
5324 msi_free(deformatted);
5325 msi_free(data);
5326 msi_free(newval);
5327 return res;
5330 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5332 UINT rc;
5333 MSIQUERY * view;
5334 static const WCHAR ExecSeqQuery[] =
5335 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5336 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5337 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5338 if (rc != ERROR_SUCCESS)
5339 return ERROR_SUCCESS;
5341 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5342 msiobj_release(&view->hdr);
5344 return rc;
5347 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5349 typedef struct
5351 struct list entry;
5352 LPWSTR sourcename;
5353 LPWSTR destname;
5354 LPWSTR source;
5355 LPWSTR dest;
5356 } FILE_LIST;
5358 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5360 BOOL ret;
5362 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5363 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5365 WARN("Source or dest is directory, not moving\n");
5366 return FALSE;
5369 if (options == msidbMoveFileOptionsMove)
5371 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5372 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5373 if (!ret)
5375 WARN("MoveFile failed: %d\n", GetLastError());
5376 return FALSE;
5379 else
5381 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5382 ret = CopyFileW(source, dest, FALSE);
5383 if (!ret)
5385 WARN("CopyFile failed: %d\n", GetLastError());
5386 return FALSE;
5390 return TRUE;
5393 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5395 LPWSTR path, ptr;
5396 DWORD dirlen, pathlen;
5398 ptr = strrchrW(wildcard, '\\');
5399 dirlen = ptr - wildcard + 1;
5401 pathlen = dirlen + lstrlenW(filename) + 1;
5402 path = msi_alloc(pathlen * sizeof(WCHAR));
5404 lstrcpynW(path, wildcard, dirlen + 1);
5405 lstrcatW(path, filename);
5407 return path;
5410 static void free_file_entry(FILE_LIST *file)
5412 msi_free(file->source);
5413 msi_free(file->dest);
5414 msi_free(file);
5417 static void free_list(FILE_LIST *list)
5419 while (!list_empty(&list->entry))
5421 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5423 list_remove(&file->entry);
5424 free_file_entry(file);
5428 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5430 FILE_LIST *new, *file;
5431 LPWSTR ptr, filename;
5432 DWORD size;
5434 new = msi_alloc_zero(sizeof(FILE_LIST));
5435 if (!new)
5436 return FALSE;
5438 new->source = strdupW(source);
5439 ptr = strrchrW(dest, '\\') + 1;
5440 filename = strrchrW(new->source, '\\') + 1;
5442 new->sourcename = filename;
5444 if (*ptr)
5445 new->destname = ptr;
5446 else
5447 new->destname = new->sourcename;
5449 size = (ptr - dest) + lstrlenW(filename) + 1;
5450 new->dest = msi_alloc(size * sizeof(WCHAR));
5451 if (!new->dest)
5453 free_file_entry(new);
5454 return FALSE;
5457 lstrcpynW(new->dest, dest, ptr - dest + 1);
5458 lstrcatW(new->dest, filename);
5460 if (list_empty(&files->entry))
5462 list_add_head(&files->entry, &new->entry);
5463 return TRUE;
5466 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5468 if (lstrcmpW(source, file->source) < 0)
5470 list_add_before(&file->entry, &new->entry);
5471 return TRUE;
5475 list_add_after(&file->entry, &new->entry);
5476 return TRUE;
5479 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5481 WIN32_FIND_DATAW wfd;
5482 HANDLE hfile;
5483 LPWSTR path;
5484 BOOL res;
5485 FILE_LIST files, *file;
5486 DWORD size;
5488 hfile = FindFirstFileW(source, &wfd);
5489 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5491 list_init(&files.entry);
5493 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5495 if (is_dot_dir(wfd.cFileName)) continue;
5497 path = wildcard_to_file(source, wfd.cFileName);
5498 if (!path)
5500 res = FALSE;
5501 goto done;
5504 add_wildcard(&files, path, dest);
5505 msi_free(path);
5508 /* no files match the wildcard */
5509 if (list_empty(&files.entry))
5510 goto done;
5512 /* only the first wildcard match gets renamed to dest */
5513 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5514 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5515 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5516 if (!file->dest)
5518 res = FALSE;
5519 goto done;
5522 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5524 while (!list_empty(&files.entry))
5526 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5528 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5530 list_remove(&file->entry);
5531 free_file_entry(file);
5534 res = TRUE;
5536 done:
5537 free_list(&files);
5538 FindClose(hfile);
5539 return res;
5542 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5544 MSIPACKAGE *package = param;
5545 MSICOMPONENT *comp;
5546 LPCWSTR sourcename, destname;
5547 LPWSTR sourcedir = NULL, destdir = NULL;
5548 LPWSTR source = NULL, dest = NULL;
5549 int options;
5550 DWORD size;
5551 BOOL ret, wildcards;
5553 static const WCHAR backslash[] = {'\\',0};
5555 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5556 if (!comp || !comp->Enabled ||
5557 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5559 TRACE("Component not set for install, not moving file\n");
5560 return ERROR_SUCCESS;
5563 sourcename = MSI_RecordGetString(rec, 3);
5564 destname = MSI_RecordGetString(rec, 4);
5565 options = MSI_RecordGetInteger(rec, 7);
5567 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5568 if (!sourcedir)
5569 goto done;
5571 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5572 if (!destdir)
5573 goto done;
5575 if (!sourcename)
5577 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5578 goto done;
5580 source = strdupW(sourcedir);
5581 if (!source)
5582 goto done;
5584 else
5586 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5587 source = msi_alloc(size * sizeof(WCHAR));
5588 if (!source)
5589 goto done;
5591 lstrcpyW(source, sourcedir);
5592 if (source[lstrlenW(source) - 1] != '\\')
5593 lstrcatW(source, backslash);
5594 lstrcatW(source, sourcename);
5597 wildcards = strchrW(source, '*') || strchrW(source, '?');
5599 if (!destname && !wildcards)
5601 destname = strdupW(sourcename);
5602 if (!destname)
5603 goto done;
5606 size = 0;
5607 if (destname)
5608 size = lstrlenW(destname);
5610 size += lstrlenW(destdir) + 2;
5611 dest = msi_alloc(size * sizeof(WCHAR));
5612 if (!dest)
5613 goto done;
5615 lstrcpyW(dest, destdir);
5616 if (dest[lstrlenW(dest) - 1] != '\\')
5617 lstrcatW(dest, backslash);
5619 if (destname)
5620 lstrcatW(dest, destname);
5622 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5624 ret = CreateDirectoryW(destdir, NULL);
5625 if (!ret)
5627 WARN("CreateDirectory failed: %d\n", GetLastError());
5628 return ERROR_SUCCESS;
5632 if (!wildcards)
5633 msi_move_file(source, dest, options);
5634 else
5635 move_files_wildcard(source, dest, options);
5637 done:
5638 msi_free(sourcedir);
5639 msi_free(destdir);
5640 msi_free(source);
5641 msi_free(dest);
5643 return ERROR_SUCCESS;
5646 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5648 UINT rc;
5649 MSIQUERY *view;
5651 static const WCHAR ExecSeqQuery[] =
5652 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5653 '`','M','o','v','e','F','i','l','e','`',0};
5655 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5656 if (rc != ERROR_SUCCESS)
5657 return ERROR_SUCCESS;
5659 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5660 msiobj_release(&view->hdr);
5662 return rc;
5665 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5666 DWORD dwReserved);
5667 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5668 LPVOID pvReserved, HMODULE *phModDll);
5670 static BOOL init_functionpointers(void)
5672 HRESULT hr;
5673 HMODULE hfusion;
5674 HMODULE hmscoree;
5676 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5678 hmscoree = LoadLibraryA("mscoree.dll");
5679 if (!hmscoree)
5681 WARN("mscoree.dll not available\n");
5682 return FALSE;
5685 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5686 if (!pLoadLibraryShim)
5688 WARN("LoadLibraryShim not available\n");
5689 FreeLibrary(hmscoree);
5690 return FALSE;
5693 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5694 if (FAILED(hr))
5696 WARN("fusion.dll not available\n");
5697 FreeLibrary(hmscoree);
5698 return FALSE;
5701 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5703 FreeLibrary(hmscoree);
5704 return TRUE;
5707 static UINT install_assembly(LPWSTR path)
5709 IAssemblyCache *cache;
5710 HRESULT hr;
5711 UINT r = ERROR_FUNCTION_FAILED;
5713 if (!init_functionpointers() || !pCreateAssemblyCache)
5714 return ERROR_FUNCTION_FAILED;
5716 hr = pCreateAssemblyCache(&cache, 0);
5717 if (FAILED(hr))
5718 goto done;
5720 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5721 if (FAILED(hr))
5722 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5724 r = ERROR_SUCCESS;
5726 done:
5727 IAssemblyCache_Release(cache);
5728 return r;
5731 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5733 MSIPACKAGE *package = param;
5734 MSICOMPONENT *comp;
5735 MSIFEATURE *feature;
5736 MSIFILE *file;
5737 WCHAR path[MAX_PATH];
5738 LPCWSTR app;
5739 DWORD attr;
5740 UINT r;
5742 comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5743 if (!comp || !comp->Enabled ||
5744 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5746 TRACE("Component not set for install, not publishing assembly\n");
5747 return ERROR_SUCCESS;
5750 feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5751 if (feature)
5752 msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5754 if (MSI_RecordGetString(rec, 3))
5755 FIXME("Manifest unhandled\n");
5757 app = MSI_RecordGetString(rec, 4);
5758 if (app)
5760 FIXME("Assembly should be privately installed\n");
5761 return ERROR_SUCCESS;
5764 attr = MSI_RecordGetInteger(rec, 5);
5765 if (attr == msidbAssemblyAttributesWin32)
5767 FIXME("Win32 assemblies not handled\n");
5768 return ERROR_SUCCESS;
5771 /* FIXME: extract all files belonging to this component */
5772 file = msi_find_file(package, comp->KeyPath);
5773 if (!file)
5775 ERR("File %s not found\n", debugstr_w(comp->KeyPath));
5776 return ERROR_FUNCTION_FAILED;
5779 GetTempPathW(MAX_PATH, path);
5781 if (file->IsCompressed)
5783 r = msi_extract_file(package, file, path);
5784 if (r != ERROR_SUCCESS)
5786 ERR("Failed to extract temporary assembly\n");
5787 return r;
5790 PathAddBackslashW(path);
5791 lstrcatW(path, file->FileName);
5793 else
5795 PathAddBackslashW(path);
5796 lstrcatW(path, file->FileName);
5798 if (!CopyFileW(file->SourcePath, path, FALSE))
5800 ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5801 return ERROR_FUNCTION_FAILED;
5805 r = install_assembly(path);
5806 if (r != ERROR_SUCCESS)
5807 ERR("Failed to install assembly\n");
5809 /* FIXME: write Installer assembly reg values */
5811 return r;
5814 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5816 UINT rc;
5817 MSIQUERY *view;
5819 static const WCHAR ExecSeqQuery[] =
5820 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5821 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5823 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5824 if (rc != ERROR_SUCCESS)
5825 return ERROR_SUCCESS;
5827 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5828 msiobj_release(&view->hdr);
5830 return rc;
5833 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5834 LPCSTR action, LPCWSTR table )
5836 static const WCHAR query[] = {
5837 'S','E','L','E','C','T',' ','*',' ',
5838 'F','R','O','M',' ','`','%','s','`',0 };
5839 MSIQUERY *view = NULL;
5840 DWORD count = 0;
5841 UINT r;
5843 r = MSI_OpenQuery( package->db, &view, query, table );
5844 if (r == ERROR_SUCCESS)
5846 r = MSI_IterateRecords(view, &count, NULL, package);
5847 msiobj_release(&view->hdr);
5850 if (count)
5851 FIXME("%s -> %u ignored %s table values\n",
5852 action, count, debugstr_w(table));
5854 return ERROR_SUCCESS;
5857 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5859 TRACE("%p\n", package);
5860 return ERROR_SUCCESS;
5863 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5865 static const WCHAR table[] =
5866 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5867 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5870 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5872 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5873 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5876 static UINT ACTION_BindImage( MSIPACKAGE *package )
5878 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5879 return msi_unimplemented_action_stub( package, "BindImage", table );
5882 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5884 static const WCHAR table[] = {
5885 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5886 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5889 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5891 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5892 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5895 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5897 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5898 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5901 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5903 static const WCHAR table[] = {
5904 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5905 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5907 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5909 static const WCHAR table[] = {
5910 'P','r','o','d','u','c','t','I','D',0 };
5911 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5914 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5916 static const WCHAR table[] = {
5917 'E','n','v','i','r','o','n','m','e','n','t',0 };
5918 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5921 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5923 static const WCHAR table[] = {
5924 'M','s','i','A','s','s','e','m','b','l','y',0 };
5925 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5928 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5930 static const WCHAR table[] = { 'F','o','n','t',0 };
5931 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5934 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5936 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5937 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5940 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5942 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5943 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5946 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5948 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5949 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5952 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5954 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5955 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5958 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5960 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5961 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5964 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5966 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5967 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5970 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5972 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5973 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5976 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5978 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5979 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5982 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5984 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5985 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5988 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5990 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5991 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5994 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5996 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5997 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6000 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6002 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6003 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6006 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6008 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6009 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6012 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6014 static const WCHAR table[] = { 'M','I','M','E',0 };
6015 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6018 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6020 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6021 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6024 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6026 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6027 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6030 static const struct _actions StandardActions[] = {
6031 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6032 { szAppSearch, ACTION_AppSearch },
6033 { szBindImage, ACTION_BindImage },
6034 { szCCPSearch, ACTION_CCPSearch },
6035 { szCostFinalize, ACTION_CostFinalize },
6036 { szCostInitialize, ACTION_CostInitialize },
6037 { szCreateFolders, ACTION_CreateFolders },
6038 { szCreateShortcuts, ACTION_CreateShortcuts },
6039 { szDeleteServices, ACTION_DeleteServices },
6040 { szDisableRollback, NULL },
6041 { szDuplicateFiles, ACTION_DuplicateFiles },
6042 { szExecuteAction, ACTION_ExecuteAction },
6043 { szFileCost, ACTION_FileCost },
6044 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6045 { szForceReboot, ACTION_ForceReboot },
6046 { szInstallAdminPackage, NULL },
6047 { szInstallExecute, ACTION_InstallExecute },
6048 { szInstallExecuteAgain, ACTION_InstallExecute },
6049 { szInstallFiles, ACTION_InstallFiles},
6050 { szInstallFinalize, ACTION_InstallFinalize },
6051 { szInstallInitialize, ACTION_InstallInitialize },
6052 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6053 { szInstallValidate, ACTION_InstallValidate },
6054 { szIsolateComponents, ACTION_IsolateComponents },
6055 { szLaunchConditions, ACTION_LaunchConditions },
6056 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6057 { szMoveFiles, ACTION_MoveFiles },
6058 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6059 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6060 { szInstallODBC, ACTION_InstallODBC },
6061 { szInstallServices, ACTION_InstallServices },
6062 { szPatchFiles, ACTION_PatchFiles },
6063 { szProcessComponents, ACTION_ProcessComponents },
6064 { szPublishComponents, ACTION_PublishComponents },
6065 { szPublishFeatures, ACTION_PublishFeatures },
6066 { szPublishProduct, ACTION_PublishProduct },
6067 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6068 { szRegisterComPlus, ACTION_RegisterComPlus},
6069 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6070 { szRegisterFonts, ACTION_RegisterFonts },
6071 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6072 { szRegisterProduct, ACTION_RegisterProduct },
6073 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6074 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6075 { szRegisterUser, ACTION_RegisterUser },
6076 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6077 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6078 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6079 { szRemoveFiles, ACTION_RemoveFiles },
6080 { szRemoveFolders, ACTION_RemoveFolders },
6081 { szRemoveIniValues, ACTION_RemoveIniValues },
6082 { szRemoveODBC, ACTION_RemoveODBC },
6083 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6084 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6085 { szResolveSource, ACTION_ResolveSource },
6086 { szRMCCPSearch, ACTION_RMCCPSearch },
6087 { szScheduleReboot, NULL },
6088 { szSelfRegModules, ACTION_SelfRegModules },
6089 { szSelfUnregModules, ACTION_SelfUnregModules },
6090 { szSetODBCFolders, NULL },
6091 { szStartServices, ACTION_StartServices },
6092 { szStopServices, ACTION_StopServices },
6093 { szUnpublishComponents, ACTION_UnpublishComponents },
6094 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6095 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6096 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6097 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6098 { szUnregisterFonts, ACTION_UnregisterFonts },
6099 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6100 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6101 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6102 { szValidateProductID, ACTION_ValidateProductID },
6103 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6104 { szWriteIniValues, ACTION_WriteIniValues },
6105 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6106 { NULL, NULL },
6109 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6110 UINT* rc, BOOL force )
6112 BOOL ret = FALSE;
6113 BOOL run = force;
6114 int i;
6116 if (!run && !package->script->CurrentlyScripting)
6117 run = TRUE;
6119 if (!run)
6121 if (strcmpW(action,szInstallFinalize) == 0 ||
6122 strcmpW(action,szInstallExecute) == 0 ||
6123 strcmpW(action,szInstallExecuteAgain) == 0)
6124 run = TRUE;
6127 i = 0;
6128 while (StandardActions[i].action != NULL)
6130 if (strcmpW(StandardActions[i].action, action)==0)
6132 if (!run)
6134 ui_actioninfo(package, action, TRUE, 0);
6135 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6136 ui_actioninfo(package, action, FALSE, *rc);
6138 else
6140 ui_actionstart(package, action);
6141 if (StandardActions[i].handler)
6143 *rc = StandardActions[i].handler(package);
6145 else
6147 FIXME("unhandled standard action %s\n",debugstr_w(action));
6148 *rc = ERROR_SUCCESS;
6151 ret = TRUE;
6152 break;
6154 i++;
6156 return ret;