push 788b87a47b004852a8afcb3465898bd5465a2225
[wine/hacks.git] / dlls / msi / action.c
blobf319427e01d5c0298cb449189d4bf4e6ef2eb3ed
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 /****************************************************
652 * TOP level entry points
653 *****************************************************/
655 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
656 LPCWSTR szCommandLine )
658 UINT rc;
659 BOOL ui = FALSE, ui_exists;
660 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
661 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
662 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
664 MSI_SetPropertyW(package, szAction, szInstall);
666 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
668 package->script->InWhatSequence = SEQUENCE_INSTALL;
670 if (szPackagePath)
672 LPWSTR p, dir;
673 LPCWSTR file;
675 dir = strdupW(szPackagePath);
676 p = strrchrW(dir, '\\');
677 if (p)
679 *(++p) = 0;
680 file = szPackagePath + (p - dir);
682 else
684 msi_free(dir);
685 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
686 GetCurrentDirectoryW(MAX_PATH, dir);
687 lstrcatW(dir, cszbs);
688 file = szPackagePath;
691 msi_free( package->PackagePath );
692 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
693 if (!package->PackagePath)
695 msi_free(dir);
696 return ERROR_OUTOFMEMORY;
699 lstrcpyW(package->PackagePath, dir);
700 lstrcatW(package->PackagePath, file);
701 msi_free(dir);
703 msi_set_sourcedir_props(package, FALSE);
706 msi_parse_command_line( package, szCommandLine );
708 msi_apply_transforms( package );
709 msi_apply_patches( package );
711 /* properties may have been added by a transform */
712 msi_clone_properties( package );
714 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
716 package->script->InWhatSequence |= SEQUENCE_UI;
717 rc = ACTION_ProcessUISequence(package);
718 ui = TRUE;
719 ui_exists = ui_sequence_exists(package);
720 if (rc == ERROR_SUCCESS || !ui_exists)
722 package->script->InWhatSequence |= SEQUENCE_EXEC;
723 rc = ACTION_ProcessExecSequence(package,ui_exists);
726 else
727 rc = ACTION_ProcessExecSequence(package,FALSE);
729 package->script->CurrentlyScripting= FALSE;
731 /* process the ending type action */
732 if (rc == ERROR_SUCCESS)
733 ACTION_PerformActionSequence(package,-1,ui);
734 else if (rc == ERROR_INSTALL_USEREXIT)
735 ACTION_PerformActionSequence(package,-2,ui);
736 else if (rc == ERROR_INSTALL_SUSPEND)
737 ACTION_PerformActionSequence(package,-4,ui);
738 else /* failed */
739 ACTION_PerformActionSequence(package,-3,ui);
741 /* finish up running custom actions */
742 ACTION_FinishCustomActions(package);
744 return rc;
747 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
749 UINT rc = ERROR_SUCCESS;
750 MSIRECORD * row = 0;
751 static const WCHAR ExecSeqQuery[] =
752 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
753 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
754 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
755 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
757 static const WCHAR UISeqQuery[] =
758 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
759 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
760 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
761 ' ', '=',' ','%','i',0};
763 if (UI)
764 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
765 else
766 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
768 if (row)
770 LPCWSTR action, cond;
772 TRACE("Running the actions\n");
774 /* check conditions */
775 cond = MSI_RecordGetString(row,2);
777 /* this is a hack to skip errors in the condition code */
778 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
779 goto end;
781 action = MSI_RecordGetString(row,1);
782 if (!action)
784 ERR("failed to fetch action\n");
785 rc = ERROR_FUNCTION_FAILED;
786 goto end;
789 if (UI)
790 rc = ACTION_PerformUIAction(package,action,-1);
791 else
792 rc = ACTION_PerformAction(package,action,-1,FALSE);
793 end:
794 msiobj_release(&row->hdr);
796 else
797 rc = ERROR_SUCCESS;
799 return rc;
802 typedef struct {
803 MSIPACKAGE* package;
804 BOOL UI;
805 } iterate_action_param;
807 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
809 iterate_action_param *iap= (iterate_action_param*)param;
810 UINT rc;
811 LPCWSTR cond, action;
813 action = MSI_RecordGetString(row,1);
814 if (!action)
816 ERR("Error is retrieving action name\n");
817 return ERROR_FUNCTION_FAILED;
820 /* check conditions */
821 cond = MSI_RecordGetString(row,2);
823 /* this is a hack to skip errors in the condition code */
824 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
826 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
827 return ERROR_SUCCESS;
830 if (iap->UI)
831 rc = ACTION_PerformUIAction(iap->package,action,-1);
832 else
833 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
835 msi_dialog_check_messages( NULL );
837 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
838 rc = iap->package->CurrentInstallState;
840 if (rc == ERROR_FUNCTION_NOT_CALLED)
841 rc = ERROR_SUCCESS;
843 if (rc != ERROR_SUCCESS)
844 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
846 return rc;
849 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
851 MSIQUERY * view;
852 UINT r;
853 static const WCHAR query[] =
854 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
855 '`','%','s','`',
856 ' ','W','H','E','R','E',' ',
857 '`','S','e','q','u','e','n','c','e','`',' ',
858 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
859 '`','S','e','q','u','e','n','c','e','`',0};
860 iterate_action_param iap;
863 * FIXME: probably should be checking UILevel in the
864 * ACTION_PerformUIAction/ACTION_PerformAction
865 * rather than saving the UI level here. Those
866 * two functions can be merged too.
868 iap.package = package;
869 iap.UI = TRUE;
871 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
873 r = MSI_OpenQuery( package->db, &view, query, szTable );
874 if (r == ERROR_SUCCESS)
876 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
877 msiobj_release(&view->hdr);
880 return r;
883 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
885 MSIQUERY * view;
886 UINT rc;
887 static const WCHAR ExecSeqQuery[] =
888 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
889 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
890 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
891 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
892 'O','R','D','E','R',' ', 'B','Y',' ',
893 '`','S','e','q','u','e','n','c','e','`',0 };
894 MSIRECORD * row = 0;
895 static const WCHAR IVQuery[] =
896 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
897 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
898 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
899 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
900 ' ','\'', 'I','n','s','t','a','l','l',
901 'V','a','l','i','d','a','t','e','\'', 0};
902 INT seq = 0;
903 iterate_action_param iap;
905 iap.package = package;
906 iap.UI = FALSE;
908 if (package->script->ExecuteSequenceRun)
910 TRACE("Execute Sequence already Run\n");
911 return ERROR_SUCCESS;
914 package->script->ExecuteSequenceRun = TRUE;
916 /* get the sequence number */
917 if (UIran)
919 row = MSI_QueryGetRecord(package->db, IVQuery);
920 if( !row )
921 return ERROR_FUNCTION_FAILED;
922 seq = MSI_RecordGetInteger(row,1);
923 msiobj_release(&row->hdr);
926 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
927 if (rc == ERROR_SUCCESS)
929 TRACE("Running the actions\n");
931 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
932 msiobj_release(&view->hdr);
935 return rc;
938 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
940 MSIQUERY * view;
941 UINT rc;
942 static const WCHAR ExecSeqQuery [] =
943 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
944 '`','I','n','s','t','a','l','l',
945 'U','I','S','e','q','u','e','n','c','e','`',
946 ' ','W','H','E','R','E',' ',
947 '`','S','e','q','u','e','n','c','e','`',' ',
948 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
949 '`','S','e','q','u','e','n','c','e','`',0};
950 iterate_action_param iap;
952 iap.package = package;
953 iap.UI = TRUE;
955 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
957 if (rc == ERROR_SUCCESS)
959 TRACE("Running the actions\n");
961 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
962 msiobj_release(&view->hdr);
965 return rc;
968 /********************************************************
969 * ACTION helper functions and functions that perform the actions
970 *******************************************************/
971 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
972 UINT* rc, UINT script, BOOL force )
974 BOOL ret=FALSE;
975 UINT arc;
977 arc = ACTION_CustomAction(package, action, script, force);
979 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
981 *rc = arc;
982 ret = TRUE;
984 return ret;
988 * A lot of actions are really important even if they don't do anything
989 * explicit... Lots of properties are set at the beginning of the installation
990 * CostFinalize does a bunch of work to translate the directories and such
992 * But until I get write access to the database that is hard, so I am going to
993 * hack it to see if I can get something to run.
995 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
997 UINT rc = ERROR_SUCCESS;
998 BOOL handled;
1000 TRACE("Performing action (%s)\n",debugstr_w(action));
1002 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1004 if (!handled)
1005 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1007 if (!handled)
1009 WARN("unhandled msi action %s\n",debugstr_w(action));
1010 rc = ERROR_FUNCTION_NOT_CALLED;
1013 return rc;
1016 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1018 UINT rc = ERROR_SUCCESS;
1019 BOOL handled = FALSE;
1021 TRACE("Performing action (%s)\n",debugstr_w(action));
1023 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1025 if (!handled)
1026 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1028 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1029 handled = TRUE;
1031 if (!handled)
1033 WARN("unhandled msi action %s\n",debugstr_w(action));
1034 rc = ERROR_FUNCTION_NOT_CALLED;
1037 return rc;
1042 * Actual Action Handlers
1045 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1047 MSIPACKAGE *package = (MSIPACKAGE*)param;
1048 LPCWSTR dir;
1049 LPWSTR full_path;
1050 MSIRECORD *uirow;
1051 MSIFOLDER *folder;
1053 dir = MSI_RecordGetString(row,1);
1054 if (!dir)
1056 ERR("Unable to get folder id\n");
1057 return ERROR_SUCCESS;
1060 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1061 if (!full_path)
1063 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1064 return ERROR_SUCCESS;
1067 TRACE("Folder is %s\n",debugstr_w(full_path));
1069 /* UI stuff */
1070 uirow = MSI_CreateRecord(1);
1071 MSI_RecordSetStringW(uirow,1,full_path);
1072 ui_actiondata(package,szCreateFolders,uirow);
1073 msiobj_release( &uirow->hdr );
1075 if (folder->State == 0)
1076 create_full_pathW(full_path);
1078 folder->State = 3;
1080 msi_free(full_path);
1081 return ERROR_SUCCESS;
1084 /* FIXME: probably should merge this with the above function */
1085 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1087 UINT rc = ERROR_SUCCESS;
1088 MSIFOLDER *folder;
1089 LPWSTR install_path;
1091 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1092 if (!install_path)
1093 return ERROR_FUNCTION_FAILED;
1095 /* create the path */
1096 if (folder->State == 0)
1098 create_full_pathW(install_path);
1099 folder->State = 2;
1101 msi_free(install_path);
1103 return rc;
1106 UINT msi_create_component_directories( MSIPACKAGE *package )
1108 MSICOMPONENT *comp;
1110 /* create all the folders required by the components are going to install */
1111 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1113 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1114 continue;
1115 msi_create_directory( package, comp->Directory );
1118 return ERROR_SUCCESS;
1122 * Also we cannot enable/disable components either, so for now I am just going
1123 * to do all the directories for all the components.
1125 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1127 static const WCHAR ExecSeqQuery[] =
1128 {'S','E','L','E','C','T',' ',
1129 '`','D','i','r','e','c','t','o','r','y','_','`',
1130 ' ','F','R','O','M',' ',
1131 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1132 UINT rc;
1133 MSIQUERY *view;
1135 /* create all the empty folders specified in the CreateFolder table */
1136 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1137 if (rc != ERROR_SUCCESS)
1138 return ERROR_SUCCESS;
1140 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1141 msiobj_release(&view->hdr);
1143 msi_create_component_directories( package );
1145 return rc;
1148 static UINT load_component( MSIRECORD *row, LPVOID param )
1150 MSIPACKAGE *package = param;
1151 MSICOMPONENT *comp;
1153 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1154 if (!comp)
1155 return ERROR_FUNCTION_FAILED;
1157 list_add_tail( &package->components, &comp->entry );
1159 /* fill in the data */
1160 comp->Component = msi_dup_record_field( row, 1 );
1162 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1164 comp->ComponentId = msi_dup_record_field( row, 2 );
1165 comp->Directory = msi_dup_record_field( row, 3 );
1166 comp->Attributes = MSI_RecordGetInteger(row,4);
1167 comp->Condition = msi_dup_record_field( row, 5 );
1168 comp->KeyPath = msi_dup_record_field( row, 6 );
1170 comp->Installed = INSTALLSTATE_UNKNOWN;
1171 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1173 return ERROR_SUCCESS;
1176 static UINT load_all_components( MSIPACKAGE *package )
1178 static const WCHAR query[] = {
1179 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1180 '`','C','o','m','p','o','n','e','n','t','`',0 };
1181 MSIQUERY *view;
1182 UINT r;
1184 if (!list_empty(&package->components))
1185 return ERROR_SUCCESS;
1187 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1188 if (r != ERROR_SUCCESS)
1189 return r;
1191 r = MSI_IterateRecords(view, NULL, load_component, package);
1192 msiobj_release(&view->hdr);
1193 return r;
1196 typedef struct {
1197 MSIPACKAGE *package;
1198 MSIFEATURE *feature;
1199 } _ilfs;
1201 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1203 ComponentList *cl;
1205 cl = msi_alloc( sizeof (*cl) );
1206 if ( !cl )
1207 return ERROR_NOT_ENOUGH_MEMORY;
1208 cl->component = comp;
1209 list_add_tail( &feature->Components, &cl->entry );
1211 return ERROR_SUCCESS;
1214 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1216 FeatureList *fl;
1218 fl = msi_alloc( sizeof(*fl) );
1219 if ( !fl )
1220 return ERROR_NOT_ENOUGH_MEMORY;
1221 fl->feature = child;
1222 list_add_tail( &parent->Children, &fl->entry );
1224 return ERROR_SUCCESS;
1227 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1229 _ilfs* ilfs= (_ilfs*)param;
1230 LPCWSTR component;
1231 MSICOMPONENT *comp;
1233 component = MSI_RecordGetString(row,1);
1235 /* check to see if the component is already loaded */
1236 comp = get_loaded_component( ilfs->package, component );
1237 if (!comp)
1239 ERR("unknown component %s\n", debugstr_w(component));
1240 return ERROR_FUNCTION_FAILED;
1243 add_feature_component( ilfs->feature, comp );
1244 comp->Enabled = TRUE;
1246 return ERROR_SUCCESS;
1249 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1251 MSIFEATURE *feature;
1253 if ( !name )
1254 return NULL;
1256 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1258 if ( !lstrcmpW( feature->Feature, name ) )
1259 return feature;
1262 return NULL;
1265 static UINT load_feature(MSIRECORD * row, LPVOID param)
1267 MSIPACKAGE* package = (MSIPACKAGE*)param;
1268 MSIFEATURE* feature;
1269 static const WCHAR Query1[] =
1270 {'S','E','L','E','C','T',' ',
1271 '`','C','o','m','p','o','n','e','n','t','_','`',
1272 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1273 'C','o','m','p','o','n','e','n','t','s','`',' ',
1274 'W','H','E','R','E',' ',
1275 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1276 MSIQUERY * view;
1277 UINT rc;
1278 _ilfs ilfs;
1280 /* fill in the data */
1282 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1283 if (!feature)
1284 return ERROR_NOT_ENOUGH_MEMORY;
1286 list_init( &feature->Children );
1287 list_init( &feature->Components );
1289 feature->Feature = msi_dup_record_field( row, 1 );
1291 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1293 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1294 feature->Title = msi_dup_record_field( row, 3 );
1295 feature->Description = msi_dup_record_field( row, 4 );
1297 if (!MSI_RecordIsNull(row,5))
1298 feature->Display = MSI_RecordGetInteger(row,5);
1300 feature->Level= MSI_RecordGetInteger(row,6);
1301 feature->Directory = msi_dup_record_field( row, 7 );
1302 feature->Attributes = MSI_RecordGetInteger(row,8);
1304 feature->Installed = INSTALLSTATE_UNKNOWN;
1305 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1307 list_add_tail( &package->features, &feature->entry );
1309 /* load feature components */
1311 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1312 if (rc != ERROR_SUCCESS)
1313 return ERROR_SUCCESS;
1315 ilfs.package = package;
1316 ilfs.feature = feature;
1318 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1319 msiobj_release(&view->hdr);
1321 return ERROR_SUCCESS;
1324 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1326 MSIPACKAGE* package = (MSIPACKAGE*)param;
1327 MSIFEATURE *parent, *child;
1329 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1330 if (!child)
1331 return ERROR_FUNCTION_FAILED;
1333 if (!child->Feature_Parent)
1334 return ERROR_SUCCESS;
1336 parent = find_feature_by_name( package, child->Feature_Parent );
1337 if (!parent)
1338 return ERROR_FUNCTION_FAILED;
1340 add_feature_child( parent, child );
1341 return ERROR_SUCCESS;
1344 static UINT load_all_features( MSIPACKAGE *package )
1346 static const WCHAR query[] = {
1347 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1348 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1349 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1350 MSIQUERY *view;
1351 UINT r;
1353 if (!list_empty(&package->features))
1354 return ERROR_SUCCESS;
1356 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1357 if (r != ERROR_SUCCESS)
1358 return r;
1360 r = MSI_IterateRecords( view, NULL, load_feature, package );
1361 if (r != ERROR_SUCCESS)
1362 return r;
1364 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1365 msiobj_release( &view->hdr );
1367 return r;
1370 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1372 if (!p)
1373 return p;
1374 p = strchrW(p, ch);
1375 if (!p)
1376 return p;
1377 *p = 0;
1378 return p+1;
1381 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1385 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1386 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1387 MSIQUERY *view = NULL;
1388 MSIRECORD *row = NULL;
1389 UINT r;
1391 TRACE("%s\n", debugstr_w(file->File));
1393 r = MSI_OpenQuery(package->db, &view, query, file->File);
1394 if (r != ERROR_SUCCESS)
1395 goto done;
1397 r = MSI_ViewExecute(view, NULL);
1398 if (r != ERROR_SUCCESS)
1399 goto done;
1401 r = MSI_ViewFetch(view, &row);
1402 if (r != ERROR_SUCCESS)
1403 goto done;
1405 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1406 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1407 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1408 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1409 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1411 done:
1412 if (view) msiobj_release(&view->hdr);
1413 if (row) msiobj_release(&row->hdr);
1414 return r;
1417 static UINT load_file(MSIRECORD *row, LPVOID param)
1419 MSIPACKAGE* package = (MSIPACKAGE*)param;
1420 LPCWSTR component;
1421 MSIFILE *file;
1423 /* fill in the data */
1425 file = msi_alloc_zero( sizeof (MSIFILE) );
1426 if (!file)
1427 return ERROR_NOT_ENOUGH_MEMORY;
1429 file->File = msi_dup_record_field( row, 1 );
1431 component = MSI_RecordGetString( row, 2 );
1432 file->Component = get_loaded_component( package, component );
1434 if (!file->Component)
1435 ERR("Unfound Component %s\n",debugstr_w(component));
1437 file->FileName = msi_dup_record_field( row, 3 );
1438 reduce_to_longfilename( file->FileName );
1440 file->ShortName = msi_dup_record_field( row, 3 );
1441 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1443 file->FileSize = MSI_RecordGetInteger( row, 4 );
1444 file->Version = msi_dup_record_field( row, 5 );
1445 file->Language = msi_dup_record_field( row, 6 );
1446 file->Attributes = MSI_RecordGetInteger( row, 7 );
1447 file->Sequence = MSI_RecordGetInteger( row, 8 );
1449 file->state = msifs_invalid;
1451 /* if the compressed bits are not set in the file attributes,
1452 * then read the information from the package word count property
1454 if (file->Attributes & msidbFileAttributesCompressed)
1456 file->IsCompressed = TRUE;
1458 else if (file->Attributes & msidbFileAttributesNoncompressed)
1460 file->IsCompressed = FALSE;
1462 else
1464 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1467 if (!file->IsCompressed)
1469 LPWSTR p, path;
1471 p = resolve_folder(package, file->Component->Directory,
1472 TRUE, FALSE, TRUE, NULL);
1473 path = build_directory_name(2, p, file->ShortName);
1475 if (file->LongName &&
1476 GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
1478 msi_free(path);
1479 path = build_directory_name(2, p, file->LongName);
1482 file->SourcePath = path;
1483 msi_free(p);
1486 load_file_hash(package, file);
1488 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1490 list_add_tail( &package->files, &file->entry );
1492 return ERROR_SUCCESS;
1495 static UINT load_all_files(MSIPACKAGE *package)
1497 MSIQUERY * view;
1498 UINT rc;
1499 static const WCHAR Query[] =
1500 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1501 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1502 '`','S','e','q','u','e','n','c','e','`', 0};
1504 if (!list_empty(&package->files))
1505 return ERROR_SUCCESS;
1507 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1508 if (rc != ERROR_SUCCESS)
1509 return ERROR_SUCCESS;
1511 rc = MSI_IterateRecords(view, NULL, load_file, package);
1512 msiobj_release(&view->hdr);
1514 return ERROR_SUCCESS;
1517 static UINT load_folder( MSIRECORD *row, LPVOID param )
1519 MSIPACKAGE *package = param;
1520 static const WCHAR szDot[] = { '.',0 };
1521 static WCHAR szEmpty[] = { 0 };
1522 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1523 MSIFOLDER *folder;
1525 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1526 if (!folder)
1527 return ERROR_NOT_ENOUGH_MEMORY;
1529 folder->Directory = msi_dup_record_field( row, 1 );
1531 TRACE("%s\n", debugstr_w(folder->Directory));
1533 p = msi_dup_record_field(row, 3);
1535 /* split src and target dir */
1536 tgt_short = p;
1537 src_short = folder_split_path( p, ':' );
1539 /* split the long and short paths */
1540 tgt_long = folder_split_path( tgt_short, '|' );
1541 src_long = folder_split_path( src_short, '|' );
1543 /* check for no-op dirs */
1544 if (!lstrcmpW(szDot, tgt_short))
1545 tgt_short = szEmpty;
1546 if (!lstrcmpW(szDot, src_short))
1547 src_short = szEmpty;
1549 if (!tgt_long)
1550 tgt_long = tgt_short;
1552 if (!src_short) {
1553 src_short = tgt_short;
1554 src_long = tgt_long;
1557 if (!src_long)
1558 src_long = src_short;
1560 /* FIXME: use the target short path too */
1561 folder->TargetDefault = strdupW(tgt_long);
1562 folder->SourceShortPath = strdupW(src_short);
1563 folder->SourceLongPath = strdupW(src_long);
1564 msi_free(p);
1566 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1567 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1568 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1570 folder->Parent = msi_dup_record_field( row, 2 );
1572 folder->Property = msi_dup_property( package, folder->Directory );
1574 list_add_tail( &package->folders, &folder->entry );
1576 TRACE("returning %p\n", folder);
1578 return ERROR_SUCCESS;
1581 static UINT load_all_folders( MSIPACKAGE *package )
1583 static const WCHAR query[] = {
1584 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1585 '`','D','i','r','e','c','t','o','r','y','`',0 };
1586 MSIQUERY *view;
1587 UINT r;
1589 if (!list_empty(&package->folders))
1590 return ERROR_SUCCESS;
1592 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1593 if (r != ERROR_SUCCESS)
1594 return r;
1596 r = MSI_IterateRecords(view, NULL, load_folder, package);
1597 msiobj_release(&view->hdr);
1598 return r;
1602 * I am not doing any of the costing functionality yet.
1603 * Mostly looking at doing the Component and Feature loading
1605 * The native MSI does A LOT of modification to tables here. Mostly adding
1606 * a lot of temporary columns to the Feature and Component tables.
1608 * note: Native msi also tracks the short filename. But I am only going to
1609 * track the long ones. Also looking at this directory table
1610 * it appears that the directory table does not get the parents
1611 * resolved base on property only based on their entries in the
1612 * directory table.
1614 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1616 static const WCHAR szCosting[] =
1617 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1618 static const WCHAR szZero[] = { '0', 0 };
1620 MSI_SetPropertyW(package, szCosting, szZero);
1621 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1623 load_all_folders( package );
1624 load_all_components( package );
1625 load_all_features( package );
1626 load_all_files( package );
1628 return ERROR_SUCCESS;
1631 static UINT execute_script(MSIPACKAGE *package, UINT script )
1633 UINT i;
1634 UINT rc = ERROR_SUCCESS;
1636 TRACE("Executing Script %i\n",script);
1638 if (!package->script)
1640 ERR("no script!\n");
1641 return ERROR_FUNCTION_FAILED;
1644 for (i = 0; i < package->script->ActionCount[script]; i++)
1646 LPWSTR action;
1647 action = package->script->Actions[script][i];
1648 ui_actionstart(package, action);
1649 TRACE("Executing Action (%s)\n",debugstr_w(action));
1650 rc = ACTION_PerformAction(package, action, script, TRUE);
1651 if (rc != ERROR_SUCCESS)
1652 break;
1654 msi_free_action_script(package, script);
1655 return rc;
1658 static UINT ACTION_FileCost(MSIPACKAGE *package)
1660 return ERROR_SUCCESS;
1663 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1665 MSICOMPONENT *comp;
1667 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1669 INSTALLSTATE res;
1671 if (!comp->ComponentId)
1672 continue;
1674 res = MsiGetComponentPathW( package->ProductCode,
1675 comp->ComponentId, NULL, NULL);
1676 if (res < 0)
1677 res = INSTALLSTATE_ABSENT;
1678 comp->Installed = res;
1682 /* scan for and update current install states */
1683 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1685 MSICOMPONENT *comp;
1686 MSIFEATURE *feature;
1688 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1690 ComponentList *cl;
1691 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1693 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1695 comp= cl->component;
1697 if (!comp->ComponentId)
1699 res = INSTALLSTATE_ABSENT;
1700 break;
1703 if (res == INSTALLSTATE_ABSENT)
1704 res = comp->Installed;
1705 else
1707 if (res == comp->Installed)
1708 continue;
1710 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1711 res != INSTALLSTATE_SOURCE)
1713 res = INSTALLSTATE_INCOMPLETE;
1717 feature->Installed = res;
1721 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1722 INSTALLSTATE state)
1724 static const WCHAR all[]={'A','L','L',0};
1725 LPWSTR override;
1726 MSIFEATURE *feature;
1728 override = msi_dup_property( package, property );
1729 if (!override)
1730 return FALSE;
1732 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1734 if (strcmpiW(override,all)==0)
1735 msi_feature_set_state( feature, state );
1736 else
1738 LPWSTR ptr = override;
1739 LPWSTR ptr2 = strchrW(override,',');
1741 while (ptr)
1743 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1744 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1746 msi_feature_set_state( feature, state );
1747 break;
1749 if (ptr2)
1751 ptr=ptr2+1;
1752 ptr2 = strchrW(ptr,',');
1754 else
1755 break;
1759 msi_free(override);
1761 return TRUE;
1764 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1766 int install_level;
1767 static const WCHAR szlevel[] =
1768 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1769 static const WCHAR szAddLocal[] =
1770 {'A','D','D','L','O','C','A','L',0};
1771 static const WCHAR szAddSource[] =
1772 {'A','D','D','S','O','U','R','C','E',0};
1773 static const WCHAR szRemove[] =
1774 {'R','E','M','O','V','E',0};
1775 static const WCHAR szReinstall[] =
1776 {'R','E','I','N','S','T','A','L','L',0};
1777 BOOL override = FALSE;
1778 MSICOMPONENT* component;
1779 MSIFEATURE *feature;
1782 /* I do not know if this is where it should happen.. but */
1784 TRACE("Checking Install Level\n");
1786 install_level = msi_get_property_int( package, szlevel, 1 );
1788 /* ok here is the _real_ rub
1789 * all these activation/deactivation things happen in order and things
1790 * later on the list override things earlier on the list.
1791 * 1) INSTALLLEVEL processing
1792 * 2) ADDLOCAL
1793 * 3) REMOVE
1794 * 4) ADDSOURCE
1795 * 5) ADDDEFAULT
1796 * 6) REINSTALL
1797 * 7) COMPADDLOCAL
1798 * 8) COMPADDSOURCE
1799 * 9) FILEADDLOCAL
1800 * 10) FILEADDSOURCE
1801 * 11) FILEADDDEFAULT
1803 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1804 * REMOVE are the big ones, since we don't handle administrative installs
1805 * yet anyway.
1807 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1808 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1809 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1810 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1812 if (!override)
1814 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1816 BOOL feature_state = ((feature->Level > 0) &&
1817 (feature->Level <= install_level));
1819 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1821 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1822 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1823 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1824 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1825 else
1826 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1830 /* disable child features of unselected parent features */
1831 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1833 FeatureList *fl;
1835 if (feature->Level > 0 && feature->Level <= install_level)
1836 continue;
1838 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1839 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1842 else
1844 /* set the Preselected Property */
1845 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1846 static const WCHAR szOne[] = { '1', 0 };
1848 MSI_SetPropertyW(package,szPreselected,szOne);
1852 * now we want to enable or disable components base on feature
1855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1857 ComponentList *cl;
1859 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1860 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1862 if (!feature->Level)
1863 continue;
1865 /* features with components that have compressed files are made local */
1866 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1868 if (cl->component->Enabled &&
1869 cl->component->ForceLocalState &&
1870 feature->Action == INSTALLSTATE_SOURCE)
1872 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1873 break;
1877 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1879 component = cl->component;
1881 if (!component->Enabled)
1882 continue;
1884 switch (feature->Action)
1886 case INSTALLSTATE_ABSENT:
1887 component->anyAbsent = 1;
1888 break;
1889 case INSTALLSTATE_ADVERTISED:
1890 component->hasAdvertiseFeature = 1;
1891 break;
1892 case INSTALLSTATE_SOURCE:
1893 component->hasSourceFeature = 1;
1894 break;
1895 case INSTALLSTATE_LOCAL:
1896 component->hasLocalFeature = 1;
1897 break;
1898 case INSTALLSTATE_DEFAULT:
1899 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1900 component->hasAdvertiseFeature = 1;
1901 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1902 component->hasSourceFeature = 1;
1903 else
1904 component->hasLocalFeature = 1;
1905 break;
1906 default:
1907 break;
1912 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1914 /* if the component isn't enabled, leave it alone */
1915 if (!component->Enabled)
1916 continue;
1918 /* check if it's local or source */
1919 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1920 (component->hasLocalFeature || component->hasSourceFeature))
1922 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1923 !component->ForceLocalState)
1924 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1925 else
1926 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1927 continue;
1930 /* if any feature is local, the component must be local too */
1931 if (component->hasLocalFeature)
1933 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1934 continue;
1937 if (component->hasSourceFeature)
1939 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1940 continue;
1943 if (component->hasAdvertiseFeature)
1945 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1946 continue;
1949 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1950 if (component->anyAbsent)
1951 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1954 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1956 if (component->Action == INSTALLSTATE_DEFAULT)
1958 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1959 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1962 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1963 debugstr_w(component->Component), component->Installed, component->Action);
1967 return ERROR_SUCCESS;
1970 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1972 MSIPACKAGE *package = (MSIPACKAGE*)param;
1973 LPCWSTR name;
1974 LPWSTR path;
1975 MSIFOLDER *f;
1977 name = MSI_RecordGetString(row,1);
1979 f = get_loaded_folder(package, name);
1980 if (!f) return ERROR_SUCCESS;
1982 /* reset the ResolvedTarget */
1983 msi_free(f->ResolvedTarget);
1984 f->ResolvedTarget = NULL;
1986 /* This helper function now does ALL the work */
1987 TRACE("Dir %s ...\n",debugstr_w(name));
1988 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1989 TRACE("resolves to %s\n",debugstr_w(path));
1990 msi_free(path);
1992 return ERROR_SUCCESS;
1995 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1997 MSIPACKAGE *package = (MSIPACKAGE*)param;
1998 LPCWSTR name;
1999 MSIFEATURE *feature;
2001 name = MSI_RecordGetString( row, 1 );
2003 feature = get_loaded_feature( package, name );
2004 if (!feature)
2005 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2006 else
2008 LPCWSTR Condition;
2009 Condition = MSI_RecordGetString(row,3);
2011 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2013 int level = MSI_RecordGetInteger(row,2);
2014 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2015 feature->Level = level;
2018 return ERROR_SUCCESS;
2021 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2023 static const WCHAR name_fmt[] =
2024 {'%','u','.','%','u','.','%','u','.','%','u',0};
2025 static const WCHAR name[] = {'\\',0};
2026 VS_FIXEDFILEINFO *lpVer;
2027 WCHAR filever[0x100];
2028 LPVOID version;
2029 DWORD versize;
2030 DWORD handle;
2031 UINT sz;
2033 TRACE("%s\n", debugstr_w(filename));
2035 versize = GetFileVersionInfoSizeW( filename, &handle );
2036 if (!versize)
2037 return NULL;
2039 version = msi_alloc( versize );
2040 GetFileVersionInfoW( filename, 0, versize, version );
2042 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2044 msi_free( version );
2045 return NULL;
2048 sprintfW( filever, name_fmt,
2049 HIWORD(lpVer->dwFileVersionMS),
2050 LOWORD(lpVer->dwFileVersionMS),
2051 HIWORD(lpVer->dwFileVersionLS),
2052 LOWORD(lpVer->dwFileVersionLS));
2054 msi_free( version );
2056 return strdupW( filever );
2059 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2061 LPWSTR file_version;
2062 MSIFILE *file;
2064 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2066 MSICOMPONENT* comp = file->Component;
2067 LPWSTR p;
2069 if (!comp)
2070 continue;
2072 if (file->IsCompressed)
2073 comp->ForceLocalState = TRUE;
2075 /* calculate target */
2076 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2078 msi_free(file->TargetPath);
2080 TRACE("file %s is named %s\n",
2081 debugstr_w(file->File), debugstr_w(file->FileName));
2083 file->TargetPath = build_directory_name(2, p, file->FileName);
2085 msi_free(p);
2087 TRACE("file %s resolves to %s\n",
2088 debugstr_w(file->File), debugstr_w(file->TargetPath));
2090 /* don't check files of components that aren't installed */
2091 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2092 comp->Installed == INSTALLSTATE_ABSENT)
2094 file->state = msifs_missing; /* assume files are missing */
2095 continue;
2098 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2100 file->state = msifs_missing;
2101 comp->Cost += file->FileSize;
2102 comp->Installed = INSTALLSTATE_INCOMPLETE;
2103 continue;
2106 if (file->Version &&
2107 (file_version = msi_get_disk_file_version( file->TargetPath )))
2109 TRACE("new %s old %s\n", debugstr_w(file->Version),
2110 debugstr_w(file_version));
2111 /* FIXME: seems like a bad way to compare version numbers */
2112 if (lstrcmpiW(file_version, file->Version)<0)
2114 file->state = msifs_overwrite;
2115 comp->Cost += file->FileSize;
2116 comp->Installed = INSTALLSTATE_INCOMPLETE;
2118 else
2119 file->state = msifs_present;
2120 msi_free( file_version );
2122 else
2123 file->state = msifs_present;
2126 return ERROR_SUCCESS;
2130 * A lot is done in this function aside from just the costing.
2131 * The costing needs to be implemented at some point but for now I am going
2132 * to focus on the directory building
2135 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2137 static const WCHAR ExecSeqQuery[] =
2138 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2139 '`','D','i','r','e','c','t','o','r','y','`',0};
2140 static const WCHAR ConditionQuery[] =
2141 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2142 '`','C','o','n','d','i','t','i','o','n','`',0};
2143 static const WCHAR szCosting[] =
2144 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2145 static const WCHAR szlevel[] =
2146 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2147 static const WCHAR szOutOfDiskSpace[] =
2148 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2149 static const WCHAR szOne[] = { '1', 0 };
2150 static const WCHAR szZero[] = { '0', 0 };
2151 MSICOMPONENT *comp;
2152 UINT rc;
2153 MSIQUERY * view;
2154 LPWSTR level;
2156 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2157 return ERROR_SUCCESS;
2159 TRACE("Building Directory properties\n");
2161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2162 if (rc == ERROR_SUCCESS)
2164 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2165 package);
2166 msiobj_release(&view->hdr);
2169 /* read components states from the registry */
2170 ACTION_GetComponentInstallStates(package);
2172 TRACE("File calculations\n");
2173 msi_check_file_install_states( package );
2175 TRACE("Evaluating Condition Table\n");
2177 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2178 if (rc == ERROR_SUCCESS)
2180 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2181 package);
2182 msiobj_release(&view->hdr);
2185 TRACE("Enabling or Disabling Components\n");
2186 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2188 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2190 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2191 comp->Enabled = FALSE;
2195 MSI_SetPropertyW(package,szCosting,szOne);
2196 /* set default run level if not set */
2197 level = msi_dup_property( package, szlevel );
2198 if (!level)
2199 MSI_SetPropertyW(package,szlevel, szOne);
2200 msi_free(level);
2202 /* FIXME: check volume disk space */
2203 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2205 ACTION_UpdateFeatureInstallStates(package);
2207 return MSI_SetFeatureStates(package);
2210 /* OK this value is "interpreted" and then formatted based on the
2211 first few characters */
2212 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2213 DWORD *size)
2215 LPSTR data = NULL;
2217 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2219 if (value[1]=='x')
2221 LPWSTR ptr;
2222 CHAR byte[5];
2223 LPWSTR deformated = NULL;
2224 int count;
2226 deformat_string(package, &value[2], &deformated);
2228 /* binary value type */
2229 ptr = deformated;
2230 *type = REG_BINARY;
2231 if (strlenW(ptr)%2)
2232 *size = (strlenW(ptr)/2)+1;
2233 else
2234 *size = strlenW(ptr)/2;
2236 data = msi_alloc(*size);
2238 byte[0] = '0';
2239 byte[1] = 'x';
2240 byte[4] = 0;
2241 count = 0;
2242 /* if uneven pad with a zero in front */
2243 if (strlenW(ptr)%2)
2245 byte[2]= '0';
2246 byte[3]= *ptr;
2247 ptr++;
2248 data[count] = (BYTE)strtol(byte,NULL,0);
2249 count ++;
2250 TRACE("Uneven byte count\n");
2252 while (*ptr)
2254 byte[2]= *ptr;
2255 ptr++;
2256 byte[3]= *ptr;
2257 ptr++;
2258 data[count] = (BYTE)strtol(byte,NULL,0);
2259 count ++;
2261 msi_free(deformated);
2263 TRACE("Data %i bytes(%i)\n",*size,count);
2265 else
2267 LPWSTR deformated;
2268 LPWSTR p;
2269 DWORD d = 0;
2270 deformat_string(package, &value[1], &deformated);
2272 *type=REG_DWORD;
2273 *size = sizeof(DWORD);
2274 data = msi_alloc(*size);
2275 p = deformated;
2276 if (*p == '-')
2277 p++;
2278 while (*p)
2280 if ( (*p < '0') || (*p > '9') )
2281 break;
2282 d *= 10;
2283 d += (*p - '0');
2284 p++;
2286 if (deformated[0] == '-')
2287 d = -d;
2288 *(LPDWORD)data = d;
2289 TRACE("DWORD %i\n",*(LPDWORD)data);
2291 msi_free(deformated);
2294 else
2296 static const WCHAR szMulti[] = {'[','~',']',0};
2297 LPCWSTR ptr;
2298 *type=REG_SZ;
2300 if (value[0]=='#')
2302 if (value[1]=='%')
2304 ptr = &value[2];
2305 *type=REG_EXPAND_SZ;
2307 else
2308 ptr = &value[1];
2310 else
2311 ptr=value;
2313 if (strstrW(value,szMulti))
2314 *type = REG_MULTI_SZ;
2316 /* remove initial delimiter */
2317 if (!strncmpW(value, szMulti, 3))
2318 ptr = value + 3;
2320 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2322 /* add double NULL terminator */
2323 if (*type == REG_MULTI_SZ)
2325 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2326 data = msi_realloc_zero(data, *size);
2329 return data;
2332 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2334 MSIPACKAGE *package = (MSIPACKAGE*)param;
2335 static const WCHAR szHCR[] =
2336 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2337 'R','O','O','T','\\',0};
2338 static const WCHAR szHCU[] =
2339 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2340 'U','S','E','R','\\',0};
2341 static const WCHAR szHLM[] =
2342 {'H','K','E','Y','_','L','O','C','A','L','_',
2343 'M','A','C','H','I','N','E','\\',0};
2344 static const WCHAR szHU[] =
2345 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2347 LPSTR value_data = NULL;
2348 HKEY root_key, hkey;
2349 DWORD type,size;
2350 LPWSTR deformated;
2351 LPCWSTR szRoot, component, name, key, value;
2352 MSICOMPONENT *comp;
2353 MSIRECORD * uirow;
2354 LPWSTR uikey;
2355 INT root;
2356 BOOL check_first = FALSE;
2357 UINT rc;
2359 ui_progress(package,2,0,0,0);
2361 value = NULL;
2362 key = NULL;
2363 uikey = NULL;
2364 name = NULL;
2366 component = MSI_RecordGetString(row, 6);
2367 comp = get_loaded_component(package,component);
2368 if (!comp)
2369 return ERROR_SUCCESS;
2371 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2373 TRACE("Skipping write due to disabled component %s\n",
2374 debugstr_w(component));
2376 comp->Action = comp->Installed;
2378 return ERROR_SUCCESS;
2381 comp->Action = INSTALLSTATE_LOCAL;
2383 name = MSI_RecordGetString(row, 4);
2384 if( MSI_RecordIsNull(row,5) && name )
2386 /* null values can have special meanings */
2387 if (name[0]=='-' && name[1] == 0)
2388 return ERROR_SUCCESS;
2389 else if ((name[0]=='+' && name[1] == 0) ||
2390 (name[0] == '*' && name[1] == 0))
2391 name = NULL;
2392 check_first = TRUE;
2395 root = MSI_RecordGetInteger(row,2);
2396 key = MSI_RecordGetString(row, 3);
2398 /* get the root key */
2399 switch (root)
2401 case -1:
2403 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2404 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2405 if (all_users && all_users[0] == '1')
2407 root_key = HKEY_LOCAL_MACHINE;
2408 szRoot = szHLM;
2410 else
2412 root_key = HKEY_CURRENT_USER;
2413 szRoot = szHCU;
2415 msi_free(all_users);
2417 break;
2418 case 0: root_key = HKEY_CLASSES_ROOT;
2419 szRoot = szHCR;
2420 break;
2421 case 1: root_key = HKEY_CURRENT_USER;
2422 szRoot = szHCU;
2423 break;
2424 case 2: root_key = HKEY_LOCAL_MACHINE;
2425 szRoot = szHLM;
2426 break;
2427 case 3: root_key = HKEY_USERS;
2428 szRoot = szHU;
2429 break;
2430 default:
2431 ERR("Unknown root %i\n",root);
2432 root_key=NULL;
2433 szRoot = NULL;
2434 break;
2436 if (!root_key)
2437 return ERROR_SUCCESS;
2439 deformat_string(package, key , &deformated);
2440 size = strlenW(deformated) + strlenW(szRoot) + 1;
2441 uikey = msi_alloc(size*sizeof(WCHAR));
2442 strcpyW(uikey,szRoot);
2443 strcatW(uikey,deformated);
2445 if (RegCreateKeyW( root_key, deformated, &hkey))
2447 ERR("Could not create key %s\n",debugstr_w(deformated));
2448 msi_free(deformated);
2449 msi_free(uikey);
2450 return ERROR_SUCCESS;
2452 msi_free(deformated);
2454 value = MSI_RecordGetString(row,5);
2455 if (value)
2456 value_data = parse_value(package, value, &type, &size);
2457 else
2459 static const WCHAR szEmpty[] = {0};
2460 value_data = (LPSTR)strdupW(szEmpty);
2461 size = sizeof(szEmpty);
2462 type = REG_SZ;
2465 deformat_string(package, name, &deformated);
2467 if (!check_first)
2469 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2470 debugstr_w(uikey));
2471 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2473 else
2475 DWORD sz = 0;
2476 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2477 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2479 TRACE("value %s of %s checked already exists\n",
2480 debugstr_w(deformated), debugstr_w(uikey));
2482 else
2484 TRACE("Checked and setting value %s of %s\n",
2485 debugstr_w(deformated), debugstr_w(uikey));
2486 if (deformated || size)
2487 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2490 RegCloseKey(hkey);
2492 uirow = MSI_CreateRecord(3);
2493 MSI_RecordSetStringW(uirow,2,deformated);
2494 MSI_RecordSetStringW(uirow,1,uikey);
2496 if (type == REG_SZ)
2497 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2498 else
2499 MSI_RecordSetStringW(uirow,3,value);
2501 ui_actiondata(package,szWriteRegistryValues,uirow);
2502 msiobj_release( &uirow->hdr );
2504 msi_free(value_data);
2505 msi_free(deformated);
2506 msi_free(uikey);
2508 return ERROR_SUCCESS;
2511 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2513 UINT rc;
2514 MSIQUERY * view;
2515 static const WCHAR ExecSeqQuery[] =
2516 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2517 '`','R','e','g','i','s','t','r','y','`',0 };
2519 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2520 if (rc != ERROR_SUCCESS)
2521 return ERROR_SUCCESS;
2523 /* increment progress bar each time action data is sent */
2524 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2526 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2528 msiobj_release(&view->hdr);
2529 return rc;
2532 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2534 package->script->CurrentlyScripting = TRUE;
2536 return ERROR_SUCCESS;
2540 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2542 MSICOMPONENT *comp;
2543 DWORD progress = 0;
2544 DWORD total = 0;
2545 static const WCHAR q1[]=
2546 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2547 '`','R','e','g','i','s','t','r','y','`',0};
2548 UINT rc;
2549 MSIQUERY * view;
2550 MSIFEATURE *feature;
2551 MSIFILE *file;
2553 TRACE("InstallValidate\n");
2555 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2556 if (rc == ERROR_SUCCESS)
2558 MSI_IterateRecords( view, &progress, NULL, package );
2559 msiobj_release( &view->hdr );
2560 total += progress * REG_PROGRESS_VALUE;
2563 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2564 total += COMPONENT_PROGRESS_VALUE;
2566 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2567 total += file->FileSize;
2569 ui_progress(package,0,total,0,0);
2571 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2573 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2574 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2575 feature->ActionRequest);
2578 return ERROR_SUCCESS;
2581 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2583 MSIPACKAGE* package = (MSIPACKAGE*)param;
2584 LPCWSTR cond = NULL;
2585 LPCWSTR message = NULL;
2586 UINT r;
2588 static const WCHAR title[]=
2589 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2591 cond = MSI_RecordGetString(row,1);
2593 r = MSI_EvaluateConditionW(package,cond);
2594 if (r == MSICONDITION_FALSE)
2596 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2598 LPWSTR deformated;
2599 message = MSI_RecordGetString(row,2);
2600 deformat_string(package,message,&deformated);
2601 MessageBoxW(NULL,deformated,title,MB_OK);
2602 msi_free(deformated);
2605 return ERROR_INSTALL_FAILURE;
2608 return ERROR_SUCCESS;
2611 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2613 UINT rc;
2614 MSIQUERY * view = NULL;
2615 static const WCHAR ExecSeqQuery[] =
2616 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2617 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2619 TRACE("Checking launch conditions\n");
2621 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2622 if (rc != ERROR_SUCCESS)
2623 return ERROR_SUCCESS;
2625 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2626 msiobj_release(&view->hdr);
2628 return rc;
2631 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2634 if (!cmp->KeyPath)
2635 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2637 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2639 MSIRECORD * row = 0;
2640 UINT root,len;
2641 LPWSTR deformated,buffer,deformated_name;
2642 LPCWSTR key,name;
2643 static const WCHAR ExecSeqQuery[] =
2644 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2645 '`','R','e','g','i','s','t','r','y','`',' ',
2646 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2647 ' ','=',' ' ,'\'','%','s','\'',0 };
2648 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2649 static const WCHAR fmt2[]=
2650 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2652 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2653 if (!row)
2654 return NULL;
2656 root = MSI_RecordGetInteger(row,2);
2657 key = MSI_RecordGetString(row, 3);
2658 name = MSI_RecordGetString(row, 4);
2659 deformat_string(package, key , &deformated);
2660 deformat_string(package, name, &deformated_name);
2662 len = strlenW(deformated) + 6;
2663 if (deformated_name)
2664 len+=strlenW(deformated_name);
2666 buffer = msi_alloc( len *sizeof(WCHAR));
2668 if (deformated_name)
2669 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2670 else
2671 sprintfW(buffer,fmt,root,deformated);
2673 msi_free(deformated);
2674 msi_free(deformated_name);
2675 msiobj_release(&row->hdr);
2677 return buffer;
2679 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2681 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2682 return NULL;
2684 else
2686 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2688 if (file)
2689 return strdupW( file->TargetPath );
2691 return NULL;
2694 static HKEY openSharedDLLsKey(void)
2696 HKEY hkey=0;
2697 static const WCHAR path[] =
2698 {'S','o','f','t','w','a','r','e','\\',
2699 'M','i','c','r','o','s','o','f','t','\\',
2700 'W','i','n','d','o','w','s','\\',
2701 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2702 'S','h','a','r','e','d','D','L','L','s',0};
2704 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2705 return hkey;
2708 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2710 HKEY hkey;
2711 DWORD count=0;
2712 DWORD type;
2713 DWORD sz = sizeof(count);
2714 DWORD rc;
2716 hkey = openSharedDLLsKey();
2717 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2718 if (rc != ERROR_SUCCESS)
2719 count = 0;
2720 RegCloseKey(hkey);
2721 return count;
2724 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2726 HKEY hkey;
2728 hkey = openSharedDLLsKey();
2729 if (count > 0)
2730 msi_reg_set_val_dword( hkey, path, count );
2731 else
2732 RegDeleteValueW(hkey,path);
2733 RegCloseKey(hkey);
2734 return count;
2738 * Return TRUE if the count should be written out and FALSE if not
2740 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2742 MSIFEATURE *feature;
2743 INT count = 0;
2744 BOOL write = FALSE;
2746 /* only refcount DLLs */
2747 if (comp->KeyPath == NULL ||
2748 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2749 comp->Attributes & msidbComponentAttributesODBCDataSource)
2750 write = FALSE;
2751 else
2753 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2754 write = (count > 0);
2756 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2757 write = TRUE;
2760 /* increment counts */
2761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2763 ComponentList *cl;
2765 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2766 continue;
2768 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2770 if ( cl->component == comp )
2771 count++;
2775 /* decrement counts */
2776 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2778 ComponentList *cl;
2780 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2781 continue;
2783 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2785 if ( cl->component == comp )
2786 count--;
2790 /* ref count all the files in the component */
2791 if (write)
2793 MSIFILE *file;
2795 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2797 if (file->Component == comp)
2798 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2802 /* add a count for permanent */
2803 if (comp->Attributes & msidbComponentAttributesPermanent)
2804 count ++;
2806 comp->RefCount = count;
2808 if (write)
2809 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2813 * Ok further analysis makes me think that this work is
2814 * actually done in the PublishComponents and PublishFeatures
2815 * step, and not here. It appears like the keypath and all that is
2816 * resolved in this step, however actually written in the Publish steps.
2817 * But we will leave it here for now because it is unclear
2819 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2821 WCHAR squished_pc[GUID_SIZE];
2822 WCHAR squished_cc[GUID_SIZE];
2823 UINT rc;
2824 MSICOMPONENT *comp;
2825 HKEY hkey=0,hkey2=0;
2827 TRACE("\n");
2829 /* writes the Component and Features values to the registry */
2831 rc = MSIREG_OpenComponents(&hkey);
2832 if (rc != ERROR_SUCCESS)
2833 return rc;
2835 squash_guid(package->ProductCode,squished_pc);
2836 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2838 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2840 MSIRECORD * uirow;
2842 ui_progress(package,2,0,0,0);
2843 if (!comp->ComponentId)
2844 continue;
2846 squash_guid(comp->ComponentId,squished_cc);
2848 msi_free(comp->FullKeypath);
2849 comp->FullKeypath = resolve_keypath( package, comp );
2851 /* do the refcounting */
2852 ACTION_RefCountComponent( package, comp );
2854 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2855 debugstr_w(comp->Component),
2856 debugstr_w(squished_cc),
2857 debugstr_w(comp->FullKeypath),
2858 comp->RefCount);
2860 * Write the keypath out if the component is to be registered
2861 * and delete the key if the component is to be unregistered
2863 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2865 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2866 if (rc != ERROR_SUCCESS)
2867 continue;
2869 if (!comp->FullKeypath)
2870 continue;
2872 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2874 if (comp->Attributes & msidbComponentAttributesPermanent)
2876 static const WCHAR szPermKey[] =
2877 { '0','0','0','0','0','0','0','0','0','0','0','0',
2878 '0','0','0','0','0','0','0','0','0','0','0','0',
2879 '0','0','0','0','0','0','0','0',0 };
2881 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2884 RegCloseKey(hkey2);
2886 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2887 if (rc != ERROR_SUCCESS)
2888 continue;
2890 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2891 RegCloseKey(hkey2);
2893 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2895 DWORD res;
2897 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2898 if (rc != ERROR_SUCCESS)
2899 continue;
2901 RegDeleteValueW(hkey2,squished_pc);
2903 /* if the key is empty delete it */
2904 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2905 RegCloseKey(hkey2);
2906 if (res == ERROR_NO_MORE_ITEMS)
2907 RegDeleteKeyW(hkey,squished_cc);
2909 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2912 /* UI stuff */
2913 uirow = MSI_CreateRecord(3);
2914 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2915 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2916 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2917 ui_actiondata(package,szProcessComponents,uirow);
2918 msiobj_release( &uirow->hdr );
2920 RegCloseKey(hkey);
2921 return rc;
2924 typedef struct {
2925 CLSID clsid;
2926 LPWSTR source;
2928 LPWSTR path;
2929 ITypeLib *ptLib;
2930 } typelib_struct;
2932 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2933 LPWSTR lpszName, LONG_PTR lParam)
2935 TLIBATTR *attr;
2936 typelib_struct *tl_struct = (typelib_struct*) lParam;
2937 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2938 int sz;
2939 HRESULT res;
2941 if (!IS_INTRESOURCE(lpszName))
2943 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2944 return TRUE;
2947 sz = strlenW(tl_struct->source)+4;
2948 sz *= sizeof(WCHAR);
2950 if ((INT_PTR)lpszName == 1)
2951 tl_struct->path = strdupW(tl_struct->source);
2952 else
2954 tl_struct->path = msi_alloc(sz);
2955 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2958 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2959 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2960 if (!SUCCEEDED(res))
2962 msi_free(tl_struct->path);
2963 tl_struct->path = NULL;
2965 return TRUE;
2968 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2969 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2971 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2972 return FALSE;
2975 msi_free(tl_struct->path);
2976 tl_struct->path = NULL;
2978 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2979 ITypeLib_Release(tl_struct->ptLib);
2981 return TRUE;
2984 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2986 MSIPACKAGE* package = (MSIPACKAGE*)param;
2987 LPCWSTR component;
2988 MSICOMPONENT *comp;
2989 MSIFILE *file;
2990 typelib_struct tl_struct;
2991 HMODULE module;
2992 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2994 component = MSI_RecordGetString(row,3);
2995 comp = get_loaded_component(package,component);
2996 if (!comp)
2997 return ERROR_SUCCESS;
2999 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3001 TRACE("Skipping typelib reg due to disabled component\n");
3003 comp->Action = comp->Installed;
3005 return ERROR_SUCCESS;
3008 comp->Action = INSTALLSTATE_LOCAL;
3010 file = get_loaded_file( package, comp->KeyPath );
3011 if (!file)
3012 return ERROR_SUCCESS;
3014 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3015 if (module)
3017 LPCWSTR guid;
3018 guid = MSI_RecordGetString(row,1);
3019 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3020 tl_struct.source = strdupW( file->TargetPath );
3021 tl_struct.path = NULL;
3023 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3024 (LONG_PTR)&tl_struct);
3026 if (tl_struct.path)
3028 LPWSTR help = NULL;
3029 LPCWSTR helpid;
3030 HRESULT res;
3032 helpid = MSI_RecordGetString(row,6);
3034 if (helpid)
3035 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3036 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3037 msi_free(help);
3039 if (!SUCCEEDED(res))
3040 ERR("Failed to register type library %s\n",
3041 debugstr_w(tl_struct.path));
3042 else
3044 ui_actiondata(package,szRegisterTypeLibraries,row);
3046 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3049 ITypeLib_Release(tl_struct.ptLib);
3050 msi_free(tl_struct.path);
3052 else
3053 ERR("Failed to load type library %s\n",
3054 debugstr_w(tl_struct.source));
3056 FreeLibrary(module);
3057 msi_free(tl_struct.source);
3059 else
3060 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3062 return ERROR_SUCCESS;
3065 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3068 * OK this is a bit confusing.. I am given a _Component key and I believe
3069 * that the file that is being registered as a type library is the "key file
3070 * of that component" which I interpret to mean "The file in the KeyPath of
3071 * that component".
3073 UINT rc;
3074 MSIQUERY * view;
3075 static const WCHAR Query[] =
3076 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3077 '`','T','y','p','e','L','i','b','`',0};
3079 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3080 if (rc != ERROR_SUCCESS)
3081 return ERROR_SUCCESS;
3083 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3084 msiobj_release(&view->hdr);
3085 return rc;
3088 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3090 MSIPACKAGE *package = (MSIPACKAGE*)param;
3091 LPWSTR target_file, target_folder, filename;
3092 LPCWSTR buffer, extension;
3093 MSICOMPONENT *comp;
3094 static const WCHAR szlnk[]={'.','l','n','k',0};
3095 IShellLinkW *sl = NULL;
3096 IPersistFile *pf = NULL;
3097 HRESULT res;
3099 buffer = MSI_RecordGetString(row,4);
3100 comp = get_loaded_component(package,buffer);
3101 if (!comp)
3102 return ERROR_SUCCESS;
3104 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3106 TRACE("Skipping shortcut creation due to disabled component\n");
3108 comp->Action = comp->Installed;
3110 return ERROR_SUCCESS;
3113 comp->Action = INSTALLSTATE_LOCAL;
3115 ui_actiondata(package,szCreateShortcuts,row);
3117 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3118 &IID_IShellLinkW, (LPVOID *) &sl );
3120 if (FAILED( res ))
3122 ERR("CLSID_ShellLink not available\n");
3123 goto err;
3126 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3127 if (FAILED( res ))
3129 ERR("QueryInterface(IID_IPersistFile) failed\n");
3130 goto err;
3133 buffer = MSI_RecordGetString(row,2);
3134 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3136 /* may be needed because of a bug somewhere else */
3137 create_full_pathW(target_folder);
3139 filename = msi_dup_record_field( row, 3 );
3140 reduce_to_longfilename(filename);
3142 extension = strchrW(filename,'.');
3143 if (!extension || strcmpiW(extension,szlnk))
3145 int len = strlenW(filename);
3146 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3147 memcpy(filename + len, szlnk, sizeof(szlnk));
3149 target_file = build_directory_name(2, target_folder, filename);
3150 msi_free(target_folder);
3151 msi_free(filename);
3153 buffer = MSI_RecordGetString(row,5);
3154 if (strchrW(buffer,'['))
3156 LPWSTR deformated;
3157 deformat_string(package,buffer,&deformated);
3158 IShellLinkW_SetPath(sl,deformated);
3159 msi_free(deformated);
3161 else
3163 FIXME("poorly handled shortcut format, advertised shortcut\n");
3164 IShellLinkW_SetPath(sl,comp->FullKeypath);
3167 if (!MSI_RecordIsNull(row,6))
3169 LPWSTR deformated;
3170 buffer = MSI_RecordGetString(row,6);
3171 deformat_string(package,buffer,&deformated);
3172 IShellLinkW_SetArguments(sl,deformated);
3173 msi_free(deformated);
3176 if (!MSI_RecordIsNull(row,7))
3178 buffer = MSI_RecordGetString(row,7);
3179 IShellLinkW_SetDescription(sl,buffer);
3182 if (!MSI_RecordIsNull(row,8))
3183 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3185 if (!MSI_RecordIsNull(row,9))
3187 LPWSTR Path;
3188 INT index;
3190 buffer = MSI_RecordGetString(row,9);
3192 Path = build_icon_path(package,buffer);
3193 index = MSI_RecordGetInteger(row,10);
3195 /* no value means 0 */
3196 if (index == MSI_NULL_INTEGER)
3197 index = 0;
3199 IShellLinkW_SetIconLocation(sl,Path,index);
3200 msi_free(Path);
3203 if (!MSI_RecordIsNull(row,11))
3204 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3206 if (!MSI_RecordIsNull(row,12))
3208 LPWSTR Path;
3209 buffer = MSI_RecordGetString(row,12);
3210 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3211 if (Path)
3212 IShellLinkW_SetWorkingDirectory(sl,Path);
3213 msi_free(Path);
3216 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3217 IPersistFile_Save(pf,target_file,FALSE);
3219 msi_free(target_file);
3221 err:
3222 if (pf)
3223 IPersistFile_Release( pf );
3224 if (sl)
3225 IShellLinkW_Release( sl );
3227 return ERROR_SUCCESS;
3230 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3232 UINT rc;
3233 HRESULT res;
3234 MSIQUERY * view;
3235 static const WCHAR Query[] =
3236 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3237 '`','S','h','o','r','t','c','u','t','`',0};
3239 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3240 if (rc != ERROR_SUCCESS)
3241 return ERROR_SUCCESS;
3243 res = CoInitialize( NULL );
3244 if (FAILED (res))
3246 ERR("CoInitialize failed\n");
3247 return ERROR_FUNCTION_FAILED;
3250 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3251 msiobj_release(&view->hdr);
3253 CoUninitialize();
3255 return rc;
3258 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3260 MSIPACKAGE* package = (MSIPACKAGE*)param;
3261 HANDLE the_file;
3262 LPWSTR FilePath;
3263 LPCWSTR FileName;
3264 CHAR buffer[1024];
3265 DWORD sz;
3266 UINT rc;
3267 MSIRECORD *uirow;
3269 FileName = MSI_RecordGetString(row,1);
3270 if (!FileName)
3272 ERR("Unable to get FileName\n");
3273 return ERROR_SUCCESS;
3276 FilePath = build_icon_path(package,FileName);
3278 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3280 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3281 FILE_ATTRIBUTE_NORMAL, NULL);
3283 if (the_file == INVALID_HANDLE_VALUE)
3285 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3286 msi_free(FilePath);
3287 return ERROR_SUCCESS;
3292 DWORD write;
3293 sz = 1024;
3294 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3295 if (rc != ERROR_SUCCESS)
3297 ERR("Failed to get stream\n");
3298 CloseHandle(the_file);
3299 DeleteFileW(FilePath);
3300 break;
3302 WriteFile(the_file,buffer,sz,&write,NULL);
3303 } while (sz == 1024);
3305 msi_free(FilePath);
3307 CloseHandle(the_file);
3309 uirow = MSI_CreateRecord(1);
3310 MSI_RecordSetStringW(uirow,1,FileName);
3311 ui_actiondata(package,szPublishProduct,uirow);
3312 msiobj_release( &uirow->hdr );
3314 return ERROR_SUCCESS;
3317 static BOOL msi_check_publish(MSIPACKAGE *package)
3319 MSIFEATURE *feature;
3321 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3323 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3324 return TRUE;
3327 return FALSE;
3331 * 99% of the work done here is only done for
3332 * advertised installs. However this is where the
3333 * Icon table is processed and written out
3334 * so that is what I am going to do here.
3336 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3338 UINT rc;
3339 LPWSTR packname;
3340 MSIQUERY * view;
3341 MSISOURCELISTINFO *info;
3342 MSIMEDIADISK *disk;
3343 static const WCHAR Query[]=
3344 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3345 '`','I','c','o','n','`',0};
3346 /* for registry stuff */
3347 HKEY hkey=0;
3348 HKEY hukey=0;
3349 HKEY hudkey=0, props=0;
3350 HKEY source;
3351 static const WCHAR szProductLanguage[] =
3352 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3353 static const WCHAR szARPProductIcon[] =
3354 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3355 static const WCHAR szProductVersion[] =
3356 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3357 static const WCHAR szSourceList[] =
3358 {'S','o','u','r','c','e','L','i','s','t',0};
3359 static const WCHAR szEmpty[] = {0};
3360 DWORD langid;
3361 LPWSTR buffer;
3362 DWORD size;
3363 MSIHANDLE hDb, hSumInfo;
3365 /* FIXME: also need to publish if the product is in advertise mode */
3366 if (!msi_check_publish(package))
3367 return ERROR_SUCCESS;
3369 /* write out icon files */
3371 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3372 if (rc == ERROR_SUCCESS)
3374 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3375 msiobj_release(&view->hdr);
3378 /* ok there is a lot more done here but i need to figure out what */
3380 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3382 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3383 if (rc != ERROR_SUCCESS)
3384 goto end;
3386 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
3387 if (rc != ERROR_SUCCESS)
3388 goto end;
3390 else
3392 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3393 if (rc != ERROR_SUCCESS)
3394 goto end;
3396 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3397 if (rc != ERROR_SUCCESS)
3398 goto end;
3400 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3401 if (rc != ERROR_SUCCESS)
3402 goto end;
3405 rc = RegCreateKeyW(hukey, szSourceList, &source);
3406 if (rc != ERROR_SUCCESS)
3407 goto end;
3409 RegCloseKey(source);
3411 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3412 if (rc != ERROR_SUCCESS)
3413 goto end;
3415 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3416 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3417 msi_free(buffer);
3419 langid = msi_get_property_int( package, szProductLanguage, 0 );
3420 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_LANGUAGEW, langid );
3422 packname = strrchrW( package->PackagePath, '\\' ) + 1;
3423 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGENAMEW, packname );
3425 /* FIXME */
3426 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0 );
3427 msi_reg_set_val_dword( props, INSTALLPROPERTY_INSTANCETYPEW, 0 );
3429 buffer = msi_dup_property( package, szARPProductIcon );
3430 if (buffer)
3432 LPWSTR path = build_icon_path(package,buffer);
3433 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3434 msi_free( path );
3436 msi_free(buffer);
3438 buffer = msi_dup_property( package, szProductVersion );
3439 if (buffer)
3441 DWORD verdword = msi_version_str_to_dword(buffer);
3442 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_VERSIONW, verdword );
3444 msi_free(buffer);
3446 buffer = strrchrW( package->PackagePath, '\\') + 1;
3447 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3448 package->Context, MSICODE_PRODUCT,
3449 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3450 if (rc != ERROR_SUCCESS)
3451 goto end;
3453 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3454 package->Context, MSICODE_PRODUCT,
3455 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3456 if (rc != ERROR_SUCCESS)
3457 goto end;
3459 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3460 package->Context, MSICODE_PRODUCT,
3461 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3462 if (rc != ERROR_SUCCESS)
3463 goto end;
3465 /* FIXME: Need to write more keys to the user registry */
3467 hDb= alloc_msihandle( &package->db->hdr );
3468 if (!hDb) {
3469 rc = ERROR_NOT_ENOUGH_MEMORY;
3470 goto end;
3472 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3473 MsiCloseHandle(hDb);
3474 if (rc == ERROR_SUCCESS)
3476 WCHAR guidbuffer[0x200];
3477 size = 0x200;
3478 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3479 guidbuffer, &size);
3480 if (rc == ERROR_SUCCESS)
3482 /* for now we only care about the first guid */
3483 LPWSTR ptr = strchrW(guidbuffer,';');
3484 if (ptr) *ptr = 0;
3485 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, guidbuffer );
3487 else
3489 ERR("Unable to query Revision_Number...\n");
3490 rc = ERROR_SUCCESS;
3492 MsiCloseHandle(hSumInfo);
3494 else
3496 ERR("Unable to open Summary Information\n");
3497 rc = ERROR_SUCCESS;
3500 /* publish the SourceList info */
3501 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3503 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3504 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3505 info->options, info->value);
3506 else
3507 MsiSourceListSetInfoW(package->ProductCode, NULL,
3508 info->context, info->options,
3509 info->property, info->value);
3512 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3514 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3515 disk->context, disk->options,
3516 disk->disk_id, disk->volume_label, disk->disk_prompt);
3519 end:
3520 RegCloseKey(hkey);
3521 RegCloseKey(hukey);
3522 RegCloseKey(hudkey);
3523 RegCloseKey(props);
3525 return rc;
3528 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3530 MSIPACKAGE *package = (MSIPACKAGE*)param;
3531 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3532 LPWSTR deformated_section, deformated_key, deformated_value;
3533 LPWSTR folder, fullname = NULL;
3534 MSIRECORD * uirow;
3535 INT action;
3536 MSICOMPONENT *comp;
3537 static const WCHAR szWindowsFolder[] =
3538 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3540 component = MSI_RecordGetString(row, 8);
3541 comp = get_loaded_component(package,component);
3543 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3545 TRACE("Skipping ini file due to disabled component %s\n",
3546 debugstr_w(component));
3548 comp->Action = comp->Installed;
3550 return ERROR_SUCCESS;
3553 comp->Action = INSTALLSTATE_LOCAL;
3555 identifier = MSI_RecordGetString(row,1);
3556 filename = MSI_RecordGetString(row,2);
3557 dirproperty = MSI_RecordGetString(row,3);
3558 section = MSI_RecordGetString(row,4);
3559 key = MSI_RecordGetString(row,5);
3560 value = MSI_RecordGetString(row,6);
3561 action = MSI_RecordGetInteger(row,7);
3563 deformat_string(package,section,&deformated_section);
3564 deformat_string(package,key,&deformated_key);
3565 deformat_string(package,value,&deformated_value);
3567 if (dirproperty)
3569 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3570 if (!folder)
3571 folder = msi_dup_property( package, dirproperty );
3573 else
3574 folder = msi_dup_property( package, szWindowsFolder );
3576 if (!folder)
3578 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3579 goto cleanup;
3582 fullname = build_directory_name(2, folder, filename);
3584 if (action == 0)
3586 TRACE("Adding value %s to section %s in %s\n",
3587 debugstr_w(deformated_key), debugstr_w(deformated_section),
3588 debugstr_w(fullname));
3589 WritePrivateProfileStringW(deformated_section, deformated_key,
3590 deformated_value, fullname);
3592 else if (action == 1)
3594 WCHAR returned[10];
3595 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3596 returned, 10, fullname);
3597 if (returned[0] == 0)
3599 TRACE("Adding value %s to section %s in %s\n",
3600 debugstr_w(deformated_key), debugstr_w(deformated_section),
3601 debugstr_w(fullname));
3603 WritePrivateProfileStringW(deformated_section, deformated_key,
3604 deformated_value, fullname);
3607 else if (action == 3)
3608 FIXME("Append to existing section not yet implemented\n");
3610 uirow = MSI_CreateRecord(4);
3611 MSI_RecordSetStringW(uirow,1,identifier);
3612 MSI_RecordSetStringW(uirow,2,deformated_section);
3613 MSI_RecordSetStringW(uirow,3,deformated_key);
3614 MSI_RecordSetStringW(uirow,4,deformated_value);
3615 ui_actiondata(package,szWriteIniValues,uirow);
3616 msiobj_release( &uirow->hdr );
3617 cleanup:
3618 msi_free(fullname);
3619 msi_free(folder);
3620 msi_free(deformated_key);
3621 msi_free(deformated_value);
3622 msi_free(deformated_section);
3623 return ERROR_SUCCESS;
3626 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3628 UINT rc;
3629 MSIQUERY * view;
3630 static const WCHAR ExecSeqQuery[] =
3631 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3632 '`','I','n','i','F','i','l','e','`',0};
3634 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3635 if (rc != ERROR_SUCCESS)
3637 TRACE("no IniFile table\n");
3638 return ERROR_SUCCESS;
3641 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3642 msiobj_release(&view->hdr);
3643 return rc;
3646 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3648 MSIPACKAGE *package = (MSIPACKAGE*)param;
3649 LPCWSTR filename;
3650 LPWSTR FullName;
3651 MSIFILE *file;
3652 DWORD len;
3653 static const WCHAR ExeStr[] =
3654 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3655 static const WCHAR close[] = {'\"',0};
3656 STARTUPINFOW si;
3657 PROCESS_INFORMATION info;
3658 BOOL brc;
3659 MSIRECORD *uirow;
3660 LPWSTR uipath, p;
3662 memset(&si,0,sizeof(STARTUPINFOW));
3664 filename = MSI_RecordGetString(row,1);
3665 file = get_loaded_file( package, filename );
3667 if (!file)
3669 ERR("Unable to find file id %s\n",debugstr_w(filename));
3670 return ERROR_SUCCESS;
3673 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3675 FullName = msi_alloc(len*sizeof(WCHAR));
3676 strcpyW(FullName,ExeStr);
3677 strcatW( FullName, file->TargetPath );
3678 strcatW(FullName,close);
3680 TRACE("Registering %s\n",debugstr_w(FullName));
3681 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3682 &si, &info);
3684 if (brc)
3685 msi_dialog_check_messages(info.hProcess);
3687 msi_free(FullName);
3689 /* the UI chunk */
3690 uirow = MSI_CreateRecord( 2 );
3691 uipath = strdupW( file->TargetPath );
3692 p = strrchrW(uipath,'\\');
3693 if (p)
3694 p[0]=0;
3695 MSI_RecordSetStringW( uirow, 1, &p[1] );
3696 MSI_RecordSetStringW( uirow, 2, uipath);
3697 ui_actiondata( package, szSelfRegModules, uirow);
3698 msiobj_release( &uirow->hdr );
3699 msi_free( uipath );
3700 /* FIXME: call ui_progress? */
3702 return ERROR_SUCCESS;
3705 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3707 UINT rc;
3708 MSIQUERY * view;
3709 static const WCHAR ExecSeqQuery[] =
3710 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3711 '`','S','e','l','f','R','e','g','`',0};
3713 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3714 if (rc != ERROR_SUCCESS)
3716 TRACE("no SelfReg table\n");
3717 return ERROR_SUCCESS;
3720 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3721 msiobj_release(&view->hdr);
3723 return ERROR_SUCCESS;
3726 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3728 MSIFEATURE *feature;
3729 UINT rc;
3730 HKEY hkey=0;
3731 HKEY hukey=0;
3732 HKEY userdata=0;
3734 if (!msi_check_publish(package))
3735 return ERROR_SUCCESS;
3737 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3738 if (rc != ERROR_SUCCESS)
3739 goto end;
3741 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3742 if (rc != ERROR_SUCCESS)
3743 goto end;
3745 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3746 if (rc != ERROR_SUCCESS)
3747 goto end;
3749 /* here the guids are base 85 encoded */
3750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3752 ComponentList *cl;
3753 LPWSTR data = NULL;
3754 GUID clsid;
3755 INT size;
3756 BOOL absent = FALSE;
3757 MSIRECORD *uirow;
3759 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3760 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3761 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3762 absent = TRUE;
3764 size = 1;
3765 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3767 size += 21;
3769 if (feature->Feature_Parent)
3770 size += strlenW( feature->Feature_Parent )+2;
3772 data = msi_alloc(size * sizeof(WCHAR));
3774 data[0] = 0;
3775 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3777 MSICOMPONENT* component = cl->component;
3778 WCHAR buf[21];
3780 buf[0] = 0;
3781 if (component->ComponentId)
3783 TRACE("From %s\n",debugstr_w(component->ComponentId));
3784 CLSIDFromString(component->ComponentId, &clsid);
3785 encode_base85_guid(&clsid,buf);
3786 TRACE("to %s\n",debugstr_w(buf));
3787 strcatW(data,buf);
3791 if (feature->Feature_Parent)
3793 static const WCHAR sep[] = {'\2',0};
3794 strcatW(data,sep);
3795 strcatW(data,feature->Feature_Parent);
3798 msi_reg_set_val_str( hkey, feature->Feature, data );
3799 msi_reg_set_val_str( userdata, feature->Feature, data );
3800 msi_free(data);
3802 size = 0;
3803 if (feature->Feature_Parent)
3804 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3805 if (!absent)
3807 static const WCHAR emptyW[] = {0};
3808 size += sizeof(WCHAR);
3809 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3810 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3812 else
3814 size += 2*sizeof(WCHAR);
3815 data = msi_alloc(size);
3816 data[0] = 0x6;
3817 data[1] = 0;
3818 if (feature->Feature_Parent)
3819 strcpyW( &data[1], feature->Feature_Parent );
3820 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3821 (LPBYTE)data,size);
3822 msi_free(data);
3825 /* the UI chunk */
3826 uirow = MSI_CreateRecord( 1 );
3827 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3828 ui_actiondata( package, szPublishFeatures, uirow);
3829 msiobj_release( &uirow->hdr );
3830 /* FIXME: call ui_progress? */
3833 end:
3834 RegCloseKey(hkey);
3835 RegCloseKey(hukey);
3836 return rc;
3839 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3841 UINT r;
3842 HKEY hkey;
3844 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3846 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3847 if (r == ERROR_SUCCESS)
3849 RegDeleteValueW(hkey, feature->Feature);
3850 RegCloseKey(hkey);
3853 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3854 if (r == ERROR_SUCCESS)
3856 RegDeleteValueW(hkey, feature->Feature);
3857 RegCloseKey(hkey);
3860 return ERROR_SUCCESS;
3863 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3865 MSIFEATURE *feature;
3867 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3869 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3870 return FALSE;
3873 return TRUE;
3876 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3878 MSIFEATURE *feature;
3880 if (!msi_check_unpublish(package))
3881 return ERROR_SUCCESS;
3883 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3885 msi_unpublish_feature(package, feature);
3888 return ERROR_SUCCESS;
3891 static UINT msi_get_local_package_name( LPWSTR path )
3893 static const WCHAR szInstaller[] = {
3894 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3895 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3896 DWORD time, len, i;
3897 HANDLE handle;
3899 time = GetTickCount();
3900 GetWindowsDirectoryW( path, MAX_PATH );
3901 lstrcatW( path, szInstaller );
3902 CreateDirectoryW( path, NULL );
3904 len = lstrlenW(path);
3905 for (i=0; i<0x10000; i++)
3907 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3908 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3909 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3910 if (handle != INVALID_HANDLE_VALUE)
3912 CloseHandle(handle);
3913 break;
3915 if (GetLastError() != ERROR_FILE_EXISTS &&
3916 GetLastError() != ERROR_SHARING_VIOLATION)
3917 return ERROR_FUNCTION_FAILED;
3920 return ERROR_SUCCESS;
3923 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3925 WCHAR packagefile[MAX_PATH];
3926 HKEY props;
3927 UINT r;
3929 r = msi_get_local_package_name( packagefile );
3930 if (r != ERROR_SUCCESS)
3931 return r;
3933 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3935 r = CopyFileW( package->db->path, packagefile, FALSE);
3937 if (!r)
3939 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3940 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3941 return ERROR_FUNCTION_FAILED;
3944 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3946 r = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3947 if (r != ERROR_SUCCESS)
3948 return r;
3950 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3951 RegCloseKey(props);
3952 return ERROR_SUCCESS;
3955 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3957 LPWSTR prop, val, key;
3958 static const LPCSTR propval[] = {
3959 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3960 "ARPCONTACT", "Contact",
3961 "ARPCOMMENTS", "Comments",
3962 "ProductName", "DisplayName",
3963 "ProductVersion", "DisplayVersion",
3964 "ARPHELPLINK", "HelpLink",
3965 "ARPHELPTELEPHONE", "HelpTelephone",
3966 "ARPINSTALLLOCATION", "InstallLocation",
3967 "SourceDir", "InstallSource",
3968 "Manufacturer", "Publisher",
3969 "ARPREADME", "Readme",
3970 "ARPSIZE", "Size",
3971 "ARPURLINFOABOUT", "URLInfoAbout",
3972 "ARPURLUPDATEINFO", "URLUpdateInfo",
3973 NULL,
3975 const LPCSTR *p = propval;
3977 while( *p )
3979 prop = strdupAtoW( *p++ );
3980 key = strdupAtoW( *p++ );
3981 val = msi_dup_property( package, prop );
3982 msi_reg_set_val_str( hkey, key, val );
3983 msi_free(val);
3984 msi_free(key);
3985 msi_free(prop);
3987 return ERROR_SUCCESS;
3990 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3992 HKEY hkey=0;
3993 HKEY hudkey=0, props=0;
3994 LPWSTR buffer = NULL;
3995 UINT rc;
3996 DWORD size, langid;
3997 static const WCHAR szWindowsInstaller[] =
3998 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3999 static const WCHAR szUpgradeCode[] =
4000 {'U','p','g','r','a','d','e','C','o','d','e',0};
4001 static const WCHAR modpath_fmt[] =
4002 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4003 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4004 static const WCHAR szModifyPath[] =
4005 {'M','o','d','i','f','y','P','a','t','h',0};
4006 static const WCHAR szUninstallString[] =
4007 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4008 static const WCHAR szEstimatedSize[] =
4009 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4010 static const WCHAR szProductLanguage[] =
4011 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4012 static const WCHAR szProductVersion[] =
4013 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4014 static const WCHAR szProductName[] =
4015 {'P','r','o','d','u','c','t','N','a','m','e',0};
4016 static const WCHAR szDisplayName[] =
4017 {'D','i','s','p','l','a','y','N','a','m','e',0};
4018 static const WCHAR szDisplayVersion[] =
4019 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4020 static const WCHAR szManufacturer[] =
4021 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4023 SYSTEMTIME systime;
4024 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4025 LPWSTR upgrade_code;
4026 WCHAR szDate[9];
4028 /* FIXME: also need to publish if the product is in advertise mode */
4029 if (!msi_check_publish(package))
4030 return ERROR_SUCCESS;
4032 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4033 if (rc != ERROR_SUCCESS)
4034 return rc;
4036 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4038 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4039 if (rc != ERROR_SUCCESS)
4040 return rc;
4042 else
4044 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4045 if (rc != ERROR_SUCCESS)
4046 return rc;
4049 /* dump all the info i can grab */
4050 /* FIXME: Flesh out more information */
4052 msi_write_uninstall_property_vals( package, hkey );
4054 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4056 msi_make_package_local( package, hkey );
4058 /* do ModifyPath and UninstallString */
4059 size = deformat_string(package,modpath_fmt,&buffer);
4060 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4061 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4062 msi_free(buffer);
4064 /* FIXME: Write real Estimated Size when we have it */
4065 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4067 buffer = msi_dup_property( package, szProductName );
4068 msi_reg_set_val_str( props, szDisplayName, buffer );
4069 msi_free(buffer);
4071 buffer = msi_dup_property( package, cszSourceDir );
4072 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4073 msi_free(buffer);
4075 buffer = msi_dup_property( package, szManufacturer );
4076 msi_reg_set_val_str( props, INSTALLPROPERTY_PUBLISHERW, buffer);
4077 msi_free(buffer);
4079 GetLocalTime(&systime);
4080 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4081 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4082 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLDATEW, szDate );
4084 langid = msi_get_property_int( package, szProductLanguage, 0 );
4085 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4087 buffer = msi_dup_property( package, szProductVersion );
4088 msi_reg_set_val_str( props, szDisplayVersion, buffer );
4089 if (buffer)
4091 DWORD verdword = msi_version_str_to_dword(buffer);
4093 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4094 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONW, verdword );
4095 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4096 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4097 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4098 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4100 msi_free(buffer);
4102 /* Handle Upgrade Codes */
4103 upgrade_code = msi_dup_property( package, szUpgradeCode );
4104 if (upgrade_code)
4106 HKEY hkey2;
4107 WCHAR squashed[33];
4108 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4109 squash_guid(package->ProductCode,squashed);
4110 msi_reg_set_val_str( hkey2, squashed, NULL );
4111 RegCloseKey(hkey2);
4112 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4113 squash_guid(package->ProductCode,squashed);
4114 msi_reg_set_val_str( hkey2, squashed, NULL );
4115 RegCloseKey(hkey2);
4117 msi_free(upgrade_code);
4120 RegCloseKey(hkey);
4122 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4123 if (rc != ERROR_SUCCESS)
4124 return rc;
4126 RegCloseKey(hudkey);
4128 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4129 RegCloseKey(props);
4131 return ERROR_SUCCESS;
4134 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4136 return execute_script(package,INSTALL_SCRIPT);
4139 static UINT msi_unpublish_product(MSIPACKAGE *package)
4141 LPWSTR remove = NULL;
4142 LPWSTR *features = NULL;
4143 BOOL full_uninstall = TRUE;
4144 MSIFEATURE *feature;
4146 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4147 static const WCHAR szAll[] = {'A','L','L',0};
4149 remove = msi_dup_property(package, szRemove);
4150 if (!remove)
4151 return ERROR_SUCCESS;
4153 features = msi_split_string(remove, ',');
4154 if (!features)
4156 msi_free(remove);
4157 ERR("REMOVE feature list is empty!\n");
4158 return ERROR_FUNCTION_FAILED;
4161 if (!lstrcmpW(features[0], szAll))
4162 full_uninstall = TRUE;
4163 else
4165 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4167 if (feature->Action != INSTALLSTATE_ABSENT)
4168 full_uninstall = FALSE;
4172 if (!full_uninstall)
4173 goto done;
4175 MSIREG_DeleteProductKey(package->ProductCode);
4176 MSIREG_DeleteUserProductKey(package->ProductCode);
4177 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4178 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4179 MSIREG_DeleteUninstallKey(package->ProductCode);
4181 done:
4182 msi_free(remove);
4183 msi_free(features);
4184 return ERROR_SUCCESS;
4187 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4189 UINT rc;
4191 rc = msi_unpublish_product(package);
4192 if (rc != ERROR_SUCCESS)
4193 return rc;
4195 /* turn off scheduling */
4196 package->script->CurrentlyScripting= FALSE;
4198 /* first do the same as an InstallExecute */
4199 rc = ACTION_InstallExecute(package);
4200 if (rc != ERROR_SUCCESS)
4201 return rc;
4203 /* then handle Commit Actions */
4204 rc = execute_script(package,COMMIT_SCRIPT);
4206 return rc;
4209 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4211 static const WCHAR RunOnce[] = {
4212 'S','o','f','t','w','a','r','e','\\',
4213 'M','i','c','r','o','s','o','f','t','\\',
4214 'W','i','n','d','o','w','s','\\',
4215 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4216 'R','u','n','O','n','c','e',0};
4217 static const WCHAR InstallRunOnce[] = {
4218 'S','o','f','t','w','a','r','e','\\',
4219 'M','i','c','r','o','s','o','f','t','\\',
4220 'W','i','n','d','o','w','s','\\',
4221 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4222 'I','n','s','t','a','l','l','e','r','\\',
4223 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4225 static const WCHAR msiexec_fmt[] = {
4226 '%','s',
4227 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4228 '\"','%','s','\"',0};
4229 static const WCHAR install_fmt[] = {
4230 '/','I',' ','\"','%','s','\"',' ',
4231 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4232 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4233 WCHAR buffer[256], sysdir[MAX_PATH];
4234 HKEY hkey;
4235 WCHAR squished_pc[100];
4237 squash_guid(package->ProductCode,squished_pc);
4239 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4240 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4241 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4242 squished_pc);
4244 msi_reg_set_val_str( hkey, squished_pc, buffer );
4245 RegCloseKey(hkey);
4247 TRACE("Reboot command %s\n",debugstr_w(buffer));
4249 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4250 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4252 msi_reg_set_val_str( hkey, squished_pc, buffer );
4253 RegCloseKey(hkey);
4255 return ERROR_INSTALL_SUSPEND;
4258 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4260 DWORD attrib;
4261 UINT rc;
4264 * We are currently doing what should be done here in the top level Install
4265 * however for Administrative and uninstalls this step will be needed
4267 if (!package->PackagePath)
4268 return ERROR_SUCCESS;
4270 msi_set_sourcedir_props(package, TRUE);
4272 attrib = GetFileAttributesW(package->db->path);
4273 if (attrib == INVALID_FILE_ATTRIBUTES)
4275 LPWSTR prompt;
4276 LPWSTR msg;
4277 DWORD size = 0;
4279 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4280 package->Context, MSICODE_PRODUCT,
4281 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4282 if (rc == ERROR_MORE_DATA)
4284 prompt = msi_alloc(size * sizeof(WCHAR));
4285 MsiSourceListGetInfoW(package->ProductCode, NULL,
4286 package->Context, MSICODE_PRODUCT,
4287 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4289 else
4290 prompt = strdupW(package->db->path);
4292 msg = generate_error_string(package,1302,1,prompt);
4293 while(attrib == INVALID_FILE_ATTRIBUTES)
4295 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4296 if (rc == IDCANCEL)
4298 rc = ERROR_INSTALL_USEREXIT;
4299 break;
4301 attrib = GetFileAttributesW(package->db->path);
4303 msi_free(prompt);
4304 rc = ERROR_SUCCESS;
4306 else
4307 return ERROR_SUCCESS;
4309 return rc;
4312 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4314 HKEY hkey=0;
4315 LPWSTR buffer;
4316 LPWSTR productid;
4317 UINT rc,i;
4319 static const WCHAR szPropKeys[][80] =
4321 {'P','r','o','d','u','c','t','I','D',0},
4322 {'U','S','E','R','N','A','M','E',0},
4323 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4324 {0},
4327 static const WCHAR szRegKeys[][80] =
4329 {'P','r','o','d','u','c','t','I','D',0},
4330 {'R','e','g','O','w','n','e','r',0},
4331 {'R','e','g','C','o','m','p','a','n','y',0},
4332 {0},
4335 if (msi_check_unpublish(package))
4337 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4338 return ERROR_SUCCESS;
4341 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4342 if (!productid)
4343 return ERROR_SUCCESS;
4345 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4346 if (rc != ERROR_SUCCESS)
4347 goto end;
4349 for( i = 0; szPropKeys[i][0]; i++ )
4351 buffer = msi_dup_property( package, szPropKeys[i] );
4352 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4353 msi_free( buffer );
4356 end:
4357 msi_free(productid);
4358 RegCloseKey(hkey);
4360 /* FIXME: call ui_actiondata */
4362 return rc;
4366 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4368 UINT rc;
4370 package->script->InWhatSequence |= SEQUENCE_EXEC;
4371 rc = ACTION_ProcessExecSequence(package,FALSE);
4372 return rc;
4376 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4378 MSIPACKAGE *package = (MSIPACKAGE*)param;
4379 LPCWSTR compgroupid=NULL;
4380 LPCWSTR feature=NULL;
4381 LPCWSTR text = NULL;
4382 LPCWSTR qualifier = NULL;
4383 LPCWSTR component = NULL;
4384 LPWSTR advertise = NULL;
4385 LPWSTR output = NULL;
4386 HKEY hkey;
4387 UINT rc = ERROR_SUCCESS;
4388 MSICOMPONENT *comp;
4389 DWORD sz = 0;
4390 MSIRECORD *uirow;
4392 component = MSI_RecordGetString(rec,3);
4393 comp = get_loaded_component(package,component);
4395 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4396 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4397 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4399 TRACE("Skipping: Component %s not scheduled for install\n",
4400 debugstr_w(component));
4402 return ERROR_SUCCESS;
4405 compgroupid = MSI_RecordGetString(rec,1);
4406 qualifier = MSI_RecordGetString(rec,2);
4408 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4409 if (rc != ERROR_SUCCESS)
4410 goto end;
4412 text = MSI_RecordGetString(rec,4);
4413 feature = MSI_RecordGetString(rec,5);
4415 advertise = create_component_advertise_string(package, comp, feature);
4417 sz = strlenW(advertise);
4419 if (text)
4420 sz += lstrlenW(text);
4422 sz+=3;
4423 sz *= sizeof(WCHAR);
4425 output = msi_alloc_zero(sz);
4426 strcpyW(output,advertise);
4427 msi_free(advertise);
4429 if (text)
4430 strcatW(output,text);
4432 msi_reg_set_val_multi_str( hkey, qualifier, output );
4434 end:
4435 RegCloseKey(hkey);
4436 msi_free(output);
4438 /* the UI chunk */
4439 uirow = MSI_CreateRecord( 2 );
4440 MSI_RecordSetStringW( uirow, 1, compgroupid );
4441 MSI_RecordSetStringW( uirow, 2, qualifier);
4442 ui_actiondata( package, szPublishComponents, uirow);
4443 msiobj_release( &uirow->hdr );
4444 /* FIXME: call ui_progress? */
4446 return rc;
4450 * At present I am ignorning the advertised components part of this and only
4451 * focusing on the qualified component sets
4453 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4455 UINT rc;
4456 MSIQUERY * view;
4457 static const WCHAR ExecSeqQuery[] =
4458 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4459 '`','P','u','b','l','i','s','h',
4460 'C','o','m','p','o','n','e','n','t','`',0};
4462 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4463 if (rc != ERROR_SUCCESS)
4464 return ERROR_SUCCESS;
4466 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4467 msiobj_release(&view->hdr);
4469 return rc;
4472 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4474 MSIPACKAGE *package = (MSIPACKAGE*)param;
4475 MSIRECORD *row;
4476 MSIFILE *file;
4477 SC_HANDLE hscm, service = NULL;
4478 LPCWSTR comp, depends, pass;
4479 LPWSTR name = NULL, disp = NULL;
4480 LPCWSTR load_order, serv_name, key;
4481 DWORD serv_type, start_type;
4482 DWORD err_control;
4484 static const WCHAR query[] =
4485 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4486 '`','C','o','m','p','o','n','e','n','t','`',' ',
4487 'W','H','E','R','E',' ',
4488 '`','C','o','m','p','o','n','e','n','t','`',' ',
4489 '=','\'','%','s','\'',0};
4491 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4492 if (!hscm)
4494 ERR("Failed to open the SC Manager!\n");
4495 goto done;
4498 start_type = MSI_RecordGetInteger(rec, 5);
4499 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4500 goto done;
4502 depends = MSI_RecordGetString(rec, 8);
4503 if (depends && *depends)
4504 FIXME("Dependency list unhandled!\n");
4506 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4507 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4508 serv_type = MSI_RecordGetInteger(rec, 4);
4509 err_control = MSI_RecordGetInteger(rec, 6);
4510 load_order = MSI_RecordGetString(rec, 7);
4511 serv_name = MSI_RecordGetString(rec, 9);
4512 pass = MSI_RecordGetString(rec, 10);
4513 comp = MSI_RecordGetString(rec, 12);
4515 /* fetch the service path */
4516 row = MSI_QueryGetRecord(package->db, query, comp);
4517 if (!row)
4519 ERR("Control query failed!\n");
4520 goto done;
4523 key = MSI_RecordGetString(row, 6);
4525 file = get_loaded_file(package, key);
4526 msiobj_release(&row->hdr);
4527 if (!file)
4529 ERR("Failed to load the service file\n");
4530 goto done;
4533 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4534 start_type, err_control, file->TargetPath,
4535 load_order, NULL, NULL, serv_name, pass);
4536 if (!service)
4538 if (GetLastError() != ERROR_SERVICE_EXISTS)
4539 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4542 done:
4543 CloseServiceHandle(service);
4544 CloseServiceHandle(hscm);
4545 msi_free(name);
4546 msi_free(disp);
4548 return ERROR_SUCCESS;
4551 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4553 UINT rc;
4554 MSIQUERY * view;
4555 static const WCHAR ExecSeqQuery[] =
4556 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4557 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4559 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4560 if (rc != ERROR_SUCCESS)
4561 return ERROR_SUCCESS;
4563 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4564 msiobj_release(&view->hdr);
4566 return rc;
4569 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4570 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4572 LPCWSTR *vector, *temp_vector;
4573 LPWSTR p, q;
4574 DWORD sep_len;
4576 static const WCHAR separator[] = {'[','~',']',0};
4578 *numargs = 0;
4579 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4581 if (!args)
4582 return NULL;
4584 vector = msi_alloc(sizeof(LPWSTR));
4585 if (!vector)
4586 return NULL;
4588 p = args;
4591 (*numargs)++;
4592 vector[*numargs - 1] = p;
4594 if ((q = strstrW(p, separator)))
4596 *q = '\0';
4598 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4599 if (!temp_vector)
4601 msi_free(vector);
4602 return NULL;
4604 vector = temp_vector;
4606 p = q + sep_len;
4608 } while (q);
4610 return vector;
4613 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4615 MSIPACKAGE *package = (MSIPACKAGE *)param;
4616 MSICOMPONENT *comp;
4617 SC_HANDLE scm, service = NULL;
4618 LPCWSTR name, *vector = NULL;
4619 LPWSTR args;
4620 DWORD event, numargs;
4621 UINT r = ERROR_FUNCTION_FAILED;
4623 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4624 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4625 return ERROR_SUCCESS;
4627 name = MSI_RecordGetString(rec, 2);
4628 event = MSI_RecordGetInteger(rec, 3);
4629 args = strdupW(MSI_RecordGetString(rec, 4));
4631 if (!(event & msidbServiceControlEventStart))
4632 return ERROR_SUCCESS;
4634 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4635 if (!scm)
4637 ERR("Failed to open the service control manager\n");
4638 goto done;
4641 service = OpenServiceW(scm, name, SERVICE_START);
4642 if (!service)
4644 ERR("Failed to open service %s\n", debugstr_w(name));
4645 goto done;
4648 vector = msi_service_args_to_vector(args, &numargs);
4650 if (!StartServiceW(service, numargs, vector))
4652 ERR("Failed to start service %s\n", debugstr_w(name));
4653 goto done;
4656 r = ERROR_SUCCESS;
4658 done:
4659 CloseServiceHandle(service);
4660 CloseServiceHandle(scm);
4662 msi_free(args);
4663 msi_free(vector);
4664 return r;
4667 static UINT ACTION_StartServices( MSIPACKAGE *package )
4669 UINT rc;
4670 MSIQUERY *view;
4672 static const WCHAR query[] = {
4673 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4674 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4676 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4677 if (rc != ERROR_SUCCESS)
4678 return ERROR_SUCCESS;
4680 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4681 msiobj_release(&view->hdr);
4683 return rc;
4686 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4688 DWORD i, needed, count;
4689 ENUM_SERVICE_STATUSW *dependencies;
4690 SERVICE_STATUS ss;
4691 SC_HANDLE depserv;
4693 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4694 0, &needed, &count))
4695 return TRUE;
4697 if (GetLastError() != ERROR_MORE_DATA)
4698 return FALSE;
4700 dependencies = msi_alloc(needed);
4701 if (!dependencies)
4702 return FALSE;
4704 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4705 needed, &needed, &count))
4706 goto error;
4708 for (i = 0; i < count; i++)
4710 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4711 SERVICE_STOP | SERVICE_QUERY_STATUS);
4712 if (!depserv)
4713 goto error;
4715 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4716 goto error;
4719 return TRUE;
4721 error:
4722 msi_free(dependencies);
4723 return FALSE;
4726 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4728 MSIPACKAGE *package = (MSIPACKAGE *)param;
4729 MSICOMPONENT *comp;
4730 SERVICE_STATUS status;
4731 SERVICE_STATUS_PROCESS ssp;
4732 SC_HANDLE scm = NULL, service = NULL;
4733 LPWSTR name, args;
4734 DWORD event, needed;
4736 event = MSI_RecordGetInteger(rec, 3);
4737 if (!(event & msidbServiceControlEventStop))
4738 return ERROR_SUCCESS;
4740 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4741 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4742 return ERROR_SUCCESS;
4744 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4745 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4746 args = strdupW(MSI_RecordGetString(rec, 4));
4748 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4749 if (!scm)
4751 WARN("Failed to open the SCM: %d\n", GetLastError());
4752 goto done;
4755 service = OpenServiceW(scm, name,
4756 SERVICE_STOP |
4757 SERVICE_QUERY_STATUS |
4758 SERVICE_ENUMERATE_DEPENDENTS);
4759 if (!service)
4761 WARN("Failed to open service (%s): %d\n",
4762 debugstr_w(name), GetLastError());
4763 goto done;
4766 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4767 sizeof(SERVICE_STATUS_PROCESS), &needed))
4769 WARN("Failed to query service status (%s): %d\n",
4770 debugstr_w(name), GetLastError());
4771 goto done;
4774 if (ssp.dwCurrentState == SERVICE_STOPPED)
4775 goto done;
4777 stop_service_dependents(scm, service);
4779 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4780 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4782 done:
4783 CloseServiceHandle(service);
4784 CloseServiceHandle(scm);
4785 msi_free(name);
4786 msi_free(args);
4788 return ERROR_SUCCESS;
4791 static UINT ACTION_StopServices( MSIPACKAGE *package )
4793 UINT rc;
4794 MSIQUERY *view;
4796 static const WCHAR query[] = {
4797 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4798 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4800 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4801 if (rc != ERROR_SUCCESS)
4802 return ERROR_SUCCESS;
4804 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4805 msiobj_release(&view->hdr);
4807 return rc;
4810 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4812 MSIFILE *file;
4814 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4816 if (!lstrcmpW(file->File, filename))
4817 return file;
4820 return NULL;
4823 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4825 MSIPACKAGE *package = (MSIPACKAGE*)param;
4826 LPWSTR driver, driver_path, ptr;
4827 WCHAR outpath[MAX_PATH];
4828 MSIFILE *driver_file, *setup_file;
4829 LPCWSTR desc;
4830 DWORD len, usage;
4831 UINT r = ERROR_SUCCESS;
4833 static const WCHAR driver_fmt[] = {
4834 'D','r','i','v','e','r','=','%','s',0};
4835 static const WCHAR setup_fmt[] = {
4836 'S','e','t','u','p','=','%','s',0};
4837 static const WCHAR usage_fmt[] = {
4838 'F','i','l','e','U','s','a','g','e','=','1',0};
4840 desc = MSI_RecordGetString(rec, 3);
4842 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4843 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4845 if (!driver_file || !setup_file)
4847 ERR("ODBC Driver entry not found!\n");
4848 return ERROR_FUNCTION_FAILED;
4851 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4852 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4853 lstrlenW(usage_fmt) + 1;
4854 driver = msi_alloc(len * sizeof(WCHAR));
4855 if (!driver)
4856 return ERROR_OUTOFMEMORY;
4858 ptr = driver;
4859 lstrcpyW(ptr, desc);
4860 ptr += lstrlenW(ptr) + 1;
4862 sprintfW(ptr, driver_fmt, driver_file->FileName);
4863 ptr += lstrlenW(ptr) + 1;
4865 sprintfW(ptr, setup_fmt, setup_file->FileName);
4866 ptr += lstrlenW(ptr) + 1;
4868 lstrcpyW(ptr, usage_fmt);
4869 ptr += lstrlenW(ptr) + 1;
4870 *ptr = '\0';
4872 driver_path = strdupW(driver_file->TargetPath);
4873 ptr = strrchrW(driver_path, '\\');
4874 if (ptr) *ptr = '\0';
4876 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4877 NULL, ODBC_INSTALL_COMPLETE, &usage))
4879 ERR("Failed to install SQL driver!\n");
4880 r = ERROR_FUNCTION_FAILED;
4883 msi_free(driver);
4884 msi_free(driver_path);
4886 return r;
4889 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4891 MSIPACKAGE *package = (MSIPACKAGE*)param;
4892 LPWSTR translator, translator_path, ptr;
4893 WCHAR outpath[MAX_PATH];
4894 MSIFILE *translator_file, *setup_file;
4895 LPCWSTR desc;
4896 DWORD len, usage;
4897 UINT r = ERROR_SUCCESS;
4899 static const WCHAR translator_fmt[] = {
4900 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4901 static const WCHAR setup_fmt[] = {
4902 'S','e','t','u','p','=','%','s',0};
4904 desc = MSI_RecordGetString(rec, 3);
4906 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4907 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4909 if (!translator_file || !setup_file)
4911 ERR("ODBC Translator entry not found!\n");
4912 return ERROR_FUNCTION_FAILED;
4915 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4916 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4917 translator = msi_alloc(len * sizeof(WCHAR));
4918 if (!translator)
4919 return ERROR_OUTOFMEMORY;
4921 ptr = translator;
4922 lstrcpyW(ptr, desc);
4923 ptr += lstrlenW(ptr) + 1;
4925 sprintfW(ptr, translator_fmt, translator_file->FileName);
4926 ptr += lstrlenW(ptr) + 1;
4928 sprintfW(ptr, setup_fmt, setup_file->FileName);
4929 ptr += lstrlenW(ptr) + 1;
4930 *ptr = '\0';
4932 translator_path = strdupW(translator_file->TargetPath);
4933 ptr = strrchrW(translator_path, '\\');
4934 if (ptr) *ptr = '\0';
4936 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4937 NULL, ODBC_INSTALL_COMPLETE, &usage))
4939 ERR("Failed to install SQL translator!\n");
4940 r = ERROR_FUNCTION_FAILED;
4943 msi_free(translator);
4944 msi_free(translator_path);
4946 return r;
4949 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4951 LPWSTR attrs;
4952 LPCWSTR desc, driver;
4953 WORD request = ODBC_ADD_SYS_DSN;
4954 INT registration;
4955 DWORD len;
4956 UINT r = ERROR_SUCCESS;
4958 static const WCHAR attrs_fmt[] = {
4959 'D','S','N','=','%','s',0 };
4961 desc = MSI_RecordGetString(rec, 3);
4962 driver = MSI_RecordGetString(rec, 4);
4963 registration = MSI_RecordGetInteger(rec, 5);
4965 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4966 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4968 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4969 attrs = msi_alloc(len * sizeof(WCHAR));
4970 if (!attrs)
4971 return ERROR_OUTOFMEMORY;
4973 sprintfW(attrs, attrs_fmt, desc);
4974 attrs[len - 1] = '\0';
4976 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4978 ERR("Failed to install SQL data source!\n");
4979 r = ERROR_FUNCTION_FAILED;
4982 msi_free(attrs);
4984 return r;
4987 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4989 UINT rc;
4990 MSIQUERY *view;
4992 static const WCHAR driver_query[] = {
4993 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4994 'O','D','B','C','D','r','i','v','e','r',0 };
4996 static const WCHAR translator_query[] = {
4997 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4998 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5000 static const WCHAR source_query[] = {
5001 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5002 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5004 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5005 if (rc != ERROR_SUCCESS)
5006 return ERROR_SUCCESS;
5008 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5009 msiobj_release(&view->hdr);
5011 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5012 if (rc != ERROR_SUCCESS)
5013 return ERROR_SUCCESS;
5015 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5016 msiobj_release(&view->hdr);
5018 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5019 if (rc != ERROR_SUCCESS)
5020 return ERROR_SUCCESS;
5022 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5023 msiobj_release(&view->hdr);
5025 return rc;
5028 #define ENV_ACT_SETALWAYS 0x1
5029 #define ENV_ACT_SETABSENT 0x2
5030 #define ENV_ACT_REMOVE 0x4
5031 #define ENV_ACT_REMOVEMATCH 0x8
5033 #define ENV_MOD_MACHINE 0x20000000
5034 #define ENV_MOD_APPEND 0x40000000
5035 #define ENV_MOD_PREFIX 0x80000000
5036 #define ENV_MOD_MASK 0xC0000000
5038 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5040 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5042 LPCWSTR cptr = *name;
5043 LPCWSTR ptr = *value;
5045 static const WCHAR prefix[] = {'[','~',']',0};
5046 static const int prefix_len = 3;
5048 *flags = 0;
5049 while (*cptr)
5051 if (*cptr == '=')
5052 *flags |= ENV_ACT_SETALWAYS;
5053 else if (*cptr == '+')
5054 *flags |= ENV_ACT_SETABSENT;
5055 else if (*cptr == '-')
5056 *flags |= ENV_ACT_REMOVE;
5057 else if (*cptr == '!')
5058 *flags |= ENV_ACT_REMOVEMATCH;
5059 else if (*cptr == '*')
5060 *flags |= ENV_MOD_MACHINE;
5061 else
5062 break;
5064 cptr++;
5065 (*name)++;
5068 if (!*cptr)
5070 ERR("Missing environment variable\n");
5071 return ERROR_FUNCTION_FAILED;
5074 if (!strncmpW(ptr, prefix, prefix_len))
5076 *flags |= ENV_MOD_APPEND;
5077 *value += lstrlenW(prefix);
5079 else if (lstrlenW(*value) >= prefix_len)
5081 ptr += lstrlenW(ptr) - prefix_len;
5082 if (!lstrcmpW(ptr, prefix))
5084 *flags |= ENV_MOD_PREFIX;
5085 /* the "[~]" will be removed by deformat_string */;
5089 if (!*flags ||
5090 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5091 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5092 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5093 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5095 ERR("Invalid flags: %08x\n", *flags);
5096 return ERROR_FUNCTION_FAILED;
5099 return ERROR_SUCCESS;
5102 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5104 MSIPACKAGE *package = param;
5105 LPCWSTR name, value;
5106 LPWSTR data = NULL, newval = NULL;
5107 LPWSTR deformatted = NULL, ptr;
5108 DWORD flags, type, size;
5109 LONG res;
5110 HKEY env = NULL, root;
5111 LPCWSTR environment;
5113 static const WCHAR user_env[] =
5114 {'E','n','v','i','r','o','n','m','e','n','t',0};
5115 static const WCHAR machine_env[] =
5116 {'S','y','s','t','e','m','\\',
5117 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5118 'C','o','n','t','r','o','l','\\',
5119 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5120 'E','n','v','i','r','o','n','m','e','n','t',0};
5121 static const WCHAR semicolon[] = {';',0};
5123 name = MSI_RecordGetString(rec, 2);
5124 value = MSI_RecordGetString(rec, 3);
5126 res = env_set_flags(&name, &value, &flags);
5127 if (res != ERROR_SUCCESS)
5128 goto done;
5130 deformat_string(package, value, &deformatted);
5131 if (!deformatted)
5133 res = ERROR_OUTOFMEMORY;
5134 goto done;
5137 value = deformatted;
5139 if (flags & ENV_MOD_MACHINE)
5141 environment = machine_env;
5142 root = HKEY_LOCAL_MACHINE;
5144 else
5146 environment = user_env;
5147 root = HKEY_CURRENT_USER;
5150 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5151 KEY_ALL_ACCESS, NULL, &env, NULL);
5152 if (res != ERROR_SUCCESS)
5153 goto done;
5155 if (flags & ENV_ACT_REMOVE)
5156 FIXME("Not removing environment variable on uninstall!\n");
5158 size = 0;
5159 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5160 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5161 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5162 goto done;
5164 if (res != ERROR_FILE_NOT_FOUND)
5166 if (flags & ENV_ACT_SETABSENT)
5168 res = ERROR_SUCCESS;
5169 goto done;
5172 data = msi_alloc(size);
5173 if (!data)
5175 RegCloseKey(env);
5176 return ERROR_OUTOFMEMORY;
5179 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5180 if (res != ERROR_SUCCESS)
5181 goto done;
5183 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5185 res = RegDeleteKeyW(env, name);
5186 goto done;
5189 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5190 newval = msi_alloc(size);
5191 ptr = newval;
5192 if (!newval)
5194 res = ERROR_OUTOFMEMORY;
5195 goto done;
5198 if (!(flags & ENV_MOD_MASK))
5199 lstrcpyW(newval, value);
5200 else
5202 if (flags & ENV_MOD_PREFIX)
5204 lstrcpyW(newval, value);
5205 lstrcatW(newval, semicolon);
5206 ptr = newval + lstrlenW(value) + 1;
5209 lstrcpyW(ptr, data);
5211 if (flags & ENV_MOD_APPEND)
5213 lstrcatW(newval, semicolon);
5214 lstrcatW(newval, value);
5218 else
5220 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5221 newval = msi_alloc(size);
5222 if (!newval)
5224 res = ERROR_OUTOFMEMORY;
5225 goto done;
5228 lstrcpyW(newval, value);
5231 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5232 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5234 done:
5235 if (env) RegCloseKey(env);
5236 msi_free(deformatted);
5237 msi_free(data);
5238 msi_free(newval);
5239 return res;
5242 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5244 UINT rc;
5245 MSIQUERY * view;
5246 static const WCHAR ExecSeqQuery[] =
5247 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5248 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5249 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5250 if (rc != ERROR_SUCCESS)
5251 return ERROR_SUCCESS;
5253 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5254 msiobj_release(&view->hdr);
5256 return rc;
5259 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5261 typedef struct
5263 struct list entry;
5264 LPWSTR sourcename;
5265 LPWSTR destname;
5266 LPWSTR source;
5267 LPWSTR dest;
5268 } FILE_LIST;
5270 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5272 BOOL ret;
5274 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5275 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5277 WARN("Source or dest is directory, not moving\n");
5278 return FALSE;
5281 if (options == msidbMoveFileOptionsMove)
5283 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5284 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5285 if (!ret)
5287 WARN("MoveFile failed: %d\n", GetLastError());
5288 return FALSE;
5291 else
5293 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5294 ret = CopyFileW(source, dest, FALSE);
5295 if (!ret)
5297 WARN("CopyFile failed: %d\n", GetLastError());
5298 return FALSE;
5302 return TRUE;
5305 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5307 LPWSTR path, ptr;
5308 DWORD dirlen, pathlen;
5310 ptr = strrchrW(wildcard, '\\');
5311 dirlen = ptr - wildcard + 1;
5313 pathlen = dirlen + lstrlenW(filename) + 1;
5314 path = msi_alloc(pathlen * sizeof(WCHAR));
5316 lstrcpynW(path, wildcard, dirlen + 1);
5317 lstrcatW(path, filename);
5319 return path;
5322 static void free_file_entry(FILE_LIST *file)
5324 msi_free(file->source);
5325 msi_free(file->dest);
5326 msi_free(file);
5329 static void free_list(FILE_LIST *list)
5331 while (!list_empty(&list->entry))
5333 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5335 list_remove(&file->entry);
5336 free_file_entry(file);
5340 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5342 FILE_LIST *new, *file;
5343 LPWSTR ptr, filename;
5344 DWORD size;
5346 new = msi_alloc_zero(sizeof(FILE_LIST));
5347 if (!new)
5348 return FALSE;
5350 new->source = strdupW(source);
5351 ptr = strrchrW(dest, '\\') + 1;
5352 filename = strrchrW(new->source, '\\') + 1;
5354 new->sourcename = filename;
5356 if (*ptr)
5357 new->destname = ptr;
5358 else
5359 new->destname = new->sourcename;
5361 size = (ptr - dest) + lstrlenW(filename) + 1;
5362 new->dest = msi_alloc(size * sizeof(WCHAR));
5363 if (!new->dest)
5365 free_file_entry(new);
5366 return FALSE;
5369 lstrcpynW(new->dest, dest, ptr - dest + 1);
5370 lstrcatW(new->dest, filename);
5372 if (list_empty(&files->entry))
5374 list_add_head(&files->entry, &new->entry);
5375 return TRUE;
5378 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5380 if (lstrcmpW(source, file->source) < 0)
5382 list_add_before(&file->entry, &new->entry);
5383 return TRUE;
5387 list_add_after(&file->entry, &new->entry);
5388 return TRUE;
5391 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5393 WIN32_FIND_DATAW wfd;
5394 HANDLE hfile;
5395 LPWSTR path;
5396 BOOL res;
5397 FILE_LIST files, *file;
5398 DWORD size;
5400 hfile = FindFirstFileW(source, &wfd);
5401 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5403 list_init(&files.entry);
5405 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5407 if (is_dot_dir(wfd.cFileName)) continue;
5409 path = wildcard_to_file(source, wfd.cFileName);
5410 if (!path)
5412 res = FALSE;
5413 goto done;
5416 add_wildcard(&files, path, dest);
5417 msi_free(path);
5420 /* no files match the wildcard */
5421 if (list_empty(&files.entry))
5422 goto done;
5424 /* only the first wildcard match gets renamed to dest */
5425 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5426 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5427 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5428 if (!file->dest)
5430 res = FALSE;
5431 goto done;
5434 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5436 while (!list_empty(&files.entry))
5438 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5440 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5442 list_remove(&file->entry);
5443 free_file_entry(file);
5446 res = TRUE;
5448 done:
5449 free_list(&files);
5450 FindClose(hfile);
5451 return res;
5454 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5456 MSIPACKAGE *package = param;
5457 MSICOMPONENT *comp;
5458 LPCWSTR sourcename, destname;
5459 LPWSTR sourcedir = NULL, destdir = NULL;
5460 LPWSTR source = NULL, dest = NULL;
5461 int options;
5462 DWORD size;
5463 BOOL ret, wildcards;
5465 static const WCHAR backslash[] = {'\\',0};
5467 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5468 if (!comp || !comp->Enabled ||
5469 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5471 TRACE("Component not set for install, not moving file\n");
5472 return ERROR_SUCCESS;
5475 sourcename = MSI_RecordGetString(rec, 3);
5476 destname = MSI_RecordGetString(rec, 4);
5477 options = MSI_RecordGetInteger(rec, 7);
5479 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5480 if (!sourcedir)
5481 goto done;
5483 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5484 if (!destdir)
5485 goto done;
5487 if (!sourcename)
5489 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5490 goto done;
5492 source = strdupW(sourcedir);
5493 if (!source)
5494 goto done;
5496 else
5498 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5499 source = msi_alloc(size * sizeof(WCHAR));
5500 if (!source)
5501 goto done;
5503 lstrcpyW(source, sourcedir);
5504 if (source[lstrlenW(source) - 1] != '\\')
5505 lstrcatW(source, backslash);
5506 lstrcatW(source, sourcename);
5509 wildcards = strchrW(source, '*') || strchrW(source, '?');
5511 if (!destname && !wildcards)
5513 destname = strdupW(sourcename);
5514 if (!destname)
5515 goto done;
5518 size = 0;
5519 if (destname)
5520 size = lstrlenW(destname);
5522 size += lstrlenW(destdir) + 2;
5523 dest = msi_alloc(size * sizeof(WCHAR));
5524 if (!dest)
5525 goto done;
5527 lstrcpyW(dest, destdir);
5528 if (dest[lstrlenW(dest) - 1] != '\\')
5529 lstrcatW(dest, backslash);
5531 if (destname)
5532 lstrcatW(dest, destname);
5534 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5536 ret = CreateDirectoryW(destdir, NULL);
5537 if (!ret)
5539 WARN("CreateDirectory failed: %d\n", GetLastError());
5540 return ERROR_SUCCESS;
5544 if (!wildcards)
5545 msi_move_file(source, dest, options);
5546 else
5547 move_files_wildcard(source, dest, options);
5549 done:
5550 msi_free(sourcedir);
5551 msi_free(destdir);
5552 msi_free(source);
5553 msi_free(dest);
5555 return ERROR_SUCCESS;
5558 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5560 UINT rc;
5561 MSIQUERY *view;
5563 static const WCHAR ExecSeqQuery[] =
5564 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5565 '`','M','o','v','e','F','i','l','e','`',0};
5567 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5568 if (rc != ERROR_SUCCESS)
5569 return ERROR_SUCCESS;
5571 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5572 msiobj_release(&view->hdr);
5574 return rc;
5577 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5578 DWORD dwReserved);
5579 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5580 LPVOID pvReserved, HMODULE *phModDll);
5582 static BOOL init_functionpointers(void)
5584 HRESULT hr;
5585 HMODULE hfusion;
5586 HMODULE hmscoree;
5588 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5590 hmscoree = LoadLibraryA("mscoree.dll");
5591 if (!hmscoree)
5593 WARN("mscoree.dll not available\n");
5594 return FALSE;
5597 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5598 if (!pLoadLibraryShim)
5600 WARN("LoadLibraryShim not available\n");
5601 FreeLibrary(hmscoree);
5602 return FALSE;
5605 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5606 if (FAILED(hr))
5608 WARN("fusion.dll not available\n");
5609 FreeLibrary(hmscoree);
5610 return FALSE;
5613 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5615 FreeLibrary(hmscoree);
5616 return TRUE;
5619 static UINT install_assembly(LPWSTR path)
5621 IAssemblyCache *cache;
5622 HRESULT hr;
5623 UINT r = ERROR_FUNCTION_FAILED;
5625 if (!init_functionpointers() || !pCreateAssemblyCache)
5626 return ERROR_FUNCTION_FAILED;
5628 hr = pCreateAssemblyCache(&cache, 0);
5629 if (FAILED(hr))
5630 goto done;
5632 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5633 if (FAILED(hr))
5634 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5636 r = ERROR_SUCCESS;
5638 done:
5639 IAssemblyCache_Release(cache);
5640 return r;
5643 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5645 MSIPACKAGE *package = param;
5646 MSICOMPONENT *comp;
5647 MSIFEATURE *feature;
5648 MSIFILE *file;
5649 WCHAR path[MAX_PATH];
5650 LPCWSTR app;
5651 DWORD attr;
5652 UINT r;
5654 comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5655 if (!comp || !comp->Enabled ||
5656 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5658 ERR("Component not set for install, not publishing assembly\n");
5659 return ERROR_SUCCESS;
5662 feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5663 if (feature)
5664 msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5666 if (MSI_RecordGetString(rec, 3))
5667 FIXME("Manifest unhandled\n");
5669 app = MSI_RecordGetString(rec, 4);
5670 if (app)
5672 FIXME("Assembly should be privately installed\n");
5673 return ERROR_SUCCESS;
5676 attr = MSI_RecordGetInteger(rec, 5);
5677 if (attr == msidbAssemblyAttributesWin32)
5679 FIXME("Win32 assemblies not handled\n");
5680 return ERROR_SUCCESS;
5683 /* FIXME: extract all files belonging to this component */
5684 file = msi_find_file(package, comp->KeyPath);
5685 if (!file)
5687 ERR("File %s not found\n", debugstr_w(comp->KeyPath));
5688 return ERROR_FUNCTION_FAILED;
5691 GetTempPathW(MAX_PATH, path);
5693 if (file->IsCompressed)
5695 r = msi_extract_file(package, file, path);
5696 if (r != ERROR_SUCCESS)
5698 ERR("Failed to extract temporary assembly\n");
5699 return r;
5702 PathAddBackslashW(path);
5703 lstrcatW(path, file->FileName);
5705 else
5707 PathAddBackslashW(path);
5708 lstrcatW(path, file->FileName);
5710 if (!CopyFileW(file->SourcePath, path, FALSE))
5712 ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5713 return ERROR_FUNCTION_FAILED;
5717 r = install_assembly(path);
5718 if (r != ERROR_SUCCESS)
5719 ERR("Failed to install assembly\n");
5721 /* FIXME: write Installer assembly reg values */
5723 return r;
5726 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5728 UINT rc;
5729 MSIQUERY *view;
5731 static const WCHAR ExecSeqQuery[] =
5732 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5733 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5735 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5736 if (rc != ERROR_SUCCESS)
5737 return ERROR_SUCCESS;
5739 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5740 msiobj_release(&view->hdr);
5742 return rc;
5745 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5746 LPCSTR action, LPCWSTR table )
5748 static const WCHAR query[] = {
5749 'S','E','L','E','C','T',' ','*',' ',
5750 'F','R','O','M',' ','`','%','s','`',0 };
5751 MSIQUERY *view = NULL;
5752 DWORD count = 0;
5753 UINT r;
5755 r = MSI_OpenQuery( package->db, &view, query, table );
5756 if (r == ERROR_SUCCESS)
5758 r = MSI_IterateRecords(view, &count, NULL, package);
5759 msiobj_release(&view->hdr);
5762 if (count)
5763 FIXME("%s -> %u ignored %s table values\n",
5764 action, count, debugstr_w(table));
5766 return ERROR_SUCCESS;
5769 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5771 TRACE("%p\n", package);
5772 return ERROR_SUCCESS;
5775 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5777 static const WCHAR table[] =
5778 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5779 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5782 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5784 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5785 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5788 static UINT ACTION_BindImage( MSIPACKAGE *package )
5790 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5791 return msi_unimplemented_action_stub( package, "BindImage", table );
5794 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5796 static const WCHAR table[] = {
5797 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5798 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5801 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5803 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5804 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5807 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5809 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5810 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5813 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5815 static const WCHAR table[] = {
5816 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5817 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5819 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5821 static const WCHAR table[] = {
5822 'P','r','o','d','u','c','t','I','D',0 };
5823 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5826 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5828 static const WCHAR table[] = {
5829 'E','n','v','i','r','o','n','m','e','n','t',0 };
5830 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5833 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5835 static const WCHAR table[] = {
5836 'M','s','i','A','s','s','e','m','b','l','y',0 };
5837 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5840 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5842 static const WCHAR table[] = { 'F','o','n','t',0 };
5843 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5846 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5848 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5849 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5852 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5854 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5855 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5858 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5860 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5861 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5864 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5866 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5867 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5870 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5872 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5873 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5876 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5878 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5879 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5882 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5884 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5885 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5888 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5890 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5891 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5894 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5896 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5897 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5900 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5902 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5903 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5906 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5908 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5909 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5912 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5914 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5915 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5918 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5920 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5921 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5924 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5926 static const WCHAR table[] = { 'M','I','M','E',0 };
5927 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5930 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5932 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5933 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5936 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5938 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5939 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5942 static const struct _actions StandardActions[] = {
5943 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5944 { szAppSearch, ACTION_AppSearch },
5945 { szBindImage, ACTION_BindImage },
5946 { szCCPSearch, ACTION_CCPSearch },
5947 { szCostFinalize, ACTION_CostFinalize },
5948 { szCostInitialize, ACTION_CostInitialize },
5949 { szCreateFolders, ACTION_CreateFolders },
5950 { szCreateShortcuts, ACTION_CreateShortcuts },
5951 { szDeleteServices, ACTION_DeleteServices },
5952 { szDisableRollback, NULL },
5953 { szDuplicateFiles, ACTION_DuplicateFiles },
5954 { szExecuteAction, ACTION_ExecuteAction },
5955 { szFileCost, ACTION_FileCost },
5956 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5957 { szForceReboot, ACTION_ForceReboot },
5958 { szInstallAdminPackage, NULL },
5959 { szInstallExecute, ACTION_InstallExecute },
5960 { szInstallExecuteAgain, ACTION_InstallExecute },
5961 { szInstallFiles, ACTION_InstallFiles},
5962 { szInstallFinalize, ACTION_InstallFinalize },
5963 { szInstallInitialize, ACTION_InstallInitialize },
5964 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5965 { szInstallValidate, ACTION_InstallValidate },
5966 { szIsolateComponents, ACTION_IsolateComponents },
5967 { szLaunchConditions, ACTION_LaunchConditions },
5968 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5969 { szMoveFiles, ACTION_MoveFiles },
5970 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5971 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5972 { szInstallODBC, ACTION_InstallODBC },
5973 { szInstallServices, ACTION_InstallServices },
5974 { szPatchFiles, ACTION_PatchFiles },
5975 { szProcessComponents, ACTION_ProcessComponents },
5976 { szPublishComponents, ACTION_PublishComponents },
5977 { szPublishFeatures, ACTION_PublishFeatures },
5978 { szPublishProduct, ACTION_PublishProduct },
5979 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5980 { szRegisterComPlus, ACTION_RegisterComPlus},
5981 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5982 { szRegisterFonts, ACTION_RegisterFonts },
5983 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5984 { szRegisterProduct, ACTION_RegisterProduct },
5985 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5986 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5987 { szRegisterUser, ACTION_RegisterUser },
5988 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5989 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5990 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5991 { szRemoveFiles, ACTION_RemoveFiles },
5992 { szRemoveFolders, ACTION_RemoveFolders },
5993 { szRemoveIniValues, ACTION_RemoveIniValues },
5994 { szRemoveODBC, ACTION_RemoveODBC },
5995 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5996 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5997 { szResolveSource, ACTION_ResolveSource },
5998 { szRMCCPSearch, ACTION_RMCCPSearch },
5999 { szScheduleReboot, NULL },
6000 { szSelfRegModules, ACTION_SelfRegModules },
6001 { szSelfUnregModules, ACTION_SelfUnregModules },
6002 { szSetODBCFolders, NULL },
6003 { szStartServices, ACTION_StartServices },
6004 { szStopServices, ACTION_StopServices },
6005 { szUnpublishComponents, ACTION_UnpublishComponents },
6006 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6007 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6008 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6009 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6010 { szUnregisterFonts, ACTION_UnregisterFonts },
6011 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6012 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6013 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6014 { szValidateProductID, ACTION_ValidateProductID },
6015 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6016 { szWriteIniValues, ACTION_WriteIniValues },
6017 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6018 { NULL, NULL },
6021 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6022 UINT* rc, BOOL force )
6024 BOOL ret = FALSE;
6025 BOOL run = force;
6026 int i;
6028 if (!run && !package->script->CurrentlyScripting)
6029 run = TRUE;
6031 if (!run)
6033 if (strcmpW(action,szInstallFinalize) == 0 ||
6034 strcmpW(action,szInstallExecute) == 0 ||
6035 strcmpW(action,szInstallExecuteAgain) == 0)
6036 run = TRUE;
6039 i = 0;
6040 while (StandardActions[i].action != NULL)
6042 if (strcmpW(StandardActions[i].action, action)==0)
6044 if (!run)
6046 ui_actioninfo(package, action, TRUE, 0);
6047 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6048 ui_actioninfo(package, action, FALSE, *rc);
6050 else
6052 ui_actionstart(package, action);
6053 if (StandardActions[i].handler)
6055 *rc = StandardActions[i].handler(package);
6057 else
6059 FIXME("unhandled standard action %s\n",debugstr_w(action));
6060 *rc = ERROR_SUCCESS;
6063 ret = TRUE;
6064 break;
6066 i++;
6068 return ret;