push ab8f8bd6525a0f7af95fd0a2a63931cd3569f61f
[wine/hacks.git] / dlls / msi / action.c
blobcb43c5d4d50e1216f4fa13d4a7c68578444206f9
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * Prototypes
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137 'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163 'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211 'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223 'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
236 struct _actions {
237 LPCWSTR action;
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
243 * helper functions
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
253 MSIRECORD * row;
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
256 if (!row)
257 return;
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
263 UINT rc)
265 MSIRECORD * row;
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268 '%','s', '.',0};
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272 '%','i','.',0};
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 WCHAR message[1024];
276 WCHAR timet[0x100];
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279 if (start)
280 sprintfW(message,template_s,timet,action);
281 else
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
293 LPCWSTR ptr,ptr2;
294 BOOL quote;
295 DWORD len;
296 LPWSTR prop = NULL, val = NULL;
298 if (!szCommandLine)
299 return ERROR_SUCCESS;
301 ptr = szCommandLine;
303 while (*ptr)
305 if (*ptr==' ')
307 ptr++;
308 continue;
311 TRACE("Looking at %s\n",debugstr_w(ptr));
313 ptr2 = strchrW(ptr,'=');
314 if (!ptr2)
316 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
317 break;
320 quote = FALSE;
322 len = ptr2-ptr;
323 prop = msi_alloc((len+1)*sizeof(WCHAR));
324 memcpy(prop,ptr,len*sizeof(WCHAR));
325 prop[len]=0;
326 ptr2++;
328 len = 0;
329 ptr = ptr2;
330 while (*ptr && (quote || (!quote && *ptr!=' ')))
332 if (*ptr == '"')
333 quote = !quote;
334 ptr++;
335 len++;
338 if (*ptr2=='"')
340 ptr2++;
341 len -= 2;
343 val = msi_alloc((len+1)*sizeof(WCHAR));
344 memcpy(val,ptr2,len*sizeof(WCHAR));
345 val[len] = 0;
347 if (lstrlenW(prop) > 0)
349 TRACE("Found commandline property (%s) = (%s)\n",
350 debugstr_w(prop), debugstr_w(val));
351 MSI_SetPropertyW(package,prop,val);
353 msi_free(val);
354 msi_free(prop);
357 return ERROR_SUCCESS;
361 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
363 LPCWSTR pc;
364 LPWSTR p, *ret = NULL;
365 UINT count = 0;
367 if (!str)
368 return ret;
370 /* count the number of substrings */
371 for ( pc = str, count = 0; pc; count++ )
373 pc = strchrW( pc, sep );
374 if (pc)
375 pc++;
378 /* allocate space for an array of substring pointers and the substrings */
379 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
380 (lstrlenW(str)+1) * sizeof(WCHAR) );
381 if (!ret)
382 return ret;
384 /* copy the string and set the pointers */
385 p = (LPWSTR) &ret[count+1];
386 lstrcpyW( p, str );
387 for( count = 0; (ret[count] = p); count++ )
389 p = strchrW( p, sep );
390 if (p)
391 *p++ = 0;
394 return ret;
397 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
399 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
400 LPWSTR prod_code, patch_product;
401 UINT ret;
403 prod_code = msi_dup_property( package, szProductCode );
404 patch_product = msi_get_suminfo_product( patch );
406 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
408 if ( strstrW( patch_product, prod_code ) )
409 ret = ERROR_SUCCESS;
410 else
411 ret = ERROR_FUNCTION_FAILED;
413 msi_free( patch_product );
414 msi_free( prod_code );
416 return ret;
419 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
420 MSIDATABASE *patch_db, LPCWSTR name )
422 UINT ret = ERROR_FUNCTION_FAILED;
423 IStorage *stg = NULL;
424 HRESULT r;
426 TRACE("%p %s\n", package, debugstr_w(name) );
428 if (*name++ != ':')
430 ERR("expected a colon in %s\n", debugstr_w(name));
431 return ERROR_FUNCTION_FAILED;
434 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
435 if (SUCCEEDED(r))
437 ret = msi_check_transform_applicable( package, stg );
438 if (ret == ERROR_SUCCESS)
439 msi_table_apply_transform( package->db, stg );
440 else
441 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
442 IStorage_Release( stg );
444 else
445 ERR("failed to open substorage %s\n", debugstr_w(name));
447 return ERROR_SUCCESS;
450 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
452 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
453 LPWSTR guid_list, *guids, product_code;
454 UINT i, ret = ERROR_FUNCTION_FAILED;
456 product_code = msi_dup_property( package, szProdCode );
457 if (!product_code)
459 /* FIXME: the property ProductCode should be written into the DB somewhere */
460 ERR("no product code to check\n");
461 return ERROR_SUCCESS;
464 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
465 guids = msi_split_string( guid_list, ';' );
466 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
468 if (!lstrcmpW( guids[i], product_code ))
469 ret = ERROR_SUCCESS;
471 msi_free( guids );
472 msi_free( guid_list );
473 msi_free( product_code );
475 return ret;
478 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
480 MSISUMMARYINFO *si;
481 LPWSTR str, *substorage;
482 UINT i, r = ERROR_SUCCESS;
484 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
485 if (!si)
486 return ERROR_FUNCTION_FAILED;
488 msi_check_patch_applicable( package, si );
490 /* enumerate the substorage */
491 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
492 substorage = msi_split_string( str, ';' );
493 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
494 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
495 msi_free( substorage );
496 msi_free( str );
498 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
500 msiobj_release( &si->hdr );
502 return r;
505 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
507 MSIDATABASE *patch_db = NULL;
508 UINT r;
510 TRACE("%p %s\n", package, debugstr_w( file ) );
512 /* FIXME:
513 * We probably want to make sure we only open a patch collection here.
514 * Patch collections (.msp) and databases (.msi) have different GUIDs
515 * but currently MSI_OpenDatabaseW will accept both.
517 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
518 if ( r != ERROR_SUCCESS )
520 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
521 return r;
524 msi_parse_patch_summary( package, patch_db );
527 * There might be a CAB file in the patch package,
528 * so append it to the list of storage to search for streams.
530 append_storage_to_db( package->db, patch_db->storage );
532 msiobj_release( &patch_db->hdr );
534 return ERROR_SUCCESS;
537 /* get the PATCH property, and apply all the patches it specifies */
538 static UINT msi_apply_patches( MSIPACKAGE *package )
540 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
541 LPWSTR patch_list, *patches;
542 UINT i, r = ERROR_SUCCESS;
544 patch_list = msi_dup_property( package, szPatch );
546 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
548 patches = msi_split_string( patch_list, ';' );
549 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
550 r = msi_apply_patch_package( package, patches[i] );
552 msi_free( patches );
553 msi_free( patch_list );
555 return r;
558 static UINT msi_apply_transforms( MSIPACKAGE *package )
560 static const WCHAR szTransforms[] = {
561 'T','R','A','N','S','F','O','R','M','S',0 };
562 LPWSTR xform_list, *xforms;
563 UINT i, r = ERROR_SUCCESS;
565 xform_list = msi_dup_property( package, szTransforms );
566 xforms = msi_split_string( xform_list, ';' );
568 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
570 if (xforms[i][0] == ':')
571 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
572 else
573 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
576 msi_free( xforms );
577 msi_free( xform_list );
579 return r;
582 static BOOL ui_sequence_exists( MSIPACKAGE *package )
584 MSIQUERY *view;
585 UINT rc;
587 static const WCHAR ExecSeqQuery [] =
588 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l',
590 'U','I','S','e','q','u','e','n','c','e','`',
591 ' ','W','H','E','R','E',' ',
592 '`','S','e','q','u','e','n','c','e','`',' ',
593 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
594 '`','S','e','q','u','e','n','c','e','`',0};
596 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
597 if (rc == ERROR_SUCCESS)
599 msiobj_release(&view->hdr);
600 return TRUE;
603 return FALSE;
606 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
608 LPWSTR p, db;
609 LPWSTR source, check;
610 DWORD len;
612 static const WCHAR szOriginalDatabase[] =
613 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
615 db = msi_dup_property( package, szOriginalDatabase );
616 if (!db)
617 return ERROR_OUTOFMEMORY;
619 p = strrchrW( db, '\\' );
620 if (!p)
622 p = strrchrW( db, '/' );
623 if (!p)
625 msi_free(db);
626 return ERROR_SUCCESS;
630 len = p - db + 2;
631 source = msi_alloc( len * sizeof(WCHAR) );
632 lstrcpynW( source, db, len );
634 check = msi_dup_property( package, cszSourceDir );
635 if (!check || replace)
636 MSI_SetPropertyW( package, cszSourceDir, source );
638 msi_free( check );
640 check = msi_dup_property( package, cszSOURCEDIR );
641 if (!check || replace)
642 MSI_SetPropertyW( package, cszSOURCEDIR, source );
644 msi_free( check );
645 msi_free( source );
646 msi_free( db );
648 return ERROR_SUCCESS;
651 static UINT msi_set_context(MSIPACKAGE *package)
653 WCHAR val[10];
654 DWORD sz = 10;
655 DWORD num;
656 UINT r;
658 static const WCHAR szOne[] = {'1',0};
659 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
661 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
663 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
664 if (r == ERROR_SUCCESS)
666 num = atolW(val);
667 if (num == 1 || num == 2)
668 package->Context = MSIINSTALLCONTEXT_MACHINE;
671 MSI_SetPropertyW(package, szAllUsers, szOne);
672 return ERROR_SUCCESS;
675 /****************************************************
676 * TOP level entry points
677 *****************************************************/
679 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
680 LPCWSTR szCommandLine )
682 UINT rc;
683 BOOL ui = FALSE, ui_exists;
684 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
685 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
686 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
688 MSI_SetPropertyW(package, szAction, szInstall);
690 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
692 package->script->InWhatSequence = SEQUENCE_INSTALL;
694 if (szPackagePath)
696 LPWSTR p, dir;
697 LPCWSTR file;
699 dir = strdupW(szPackagePath);
700 p = strrchrW(dir, '\\');
701 if (p)
703 *(++p) = 0;
704 file = szPackagePath + (p - dir);
706 else
708 msi_free(dir);
709 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
710 GetCurrentDirectoryW(MAX_PATH, dir);
711 lstrcatW(dir, cszbs);
712 file = szPackagePath;
715 msi_free( package->PackagePath );
716 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
717 if (!package->PackagePath)
719 msi_free(dir);
720 return ERROR_OUTOFMEMORY;
723 lstrcpyW(package->PackagePath, dir);
724 lstrcatW(package->PackagePath, file);
725 msi_free(dir);
727 msi_set_sourcedir_props(package, FALSE);
730 msi_parse_command_line( package, szCommandLine );
732 msi_apply_transforms( package );
733 msi_apply_patches( package );
735 /* properties may have been added by a transform */
736 msi_clone_properties( package );
737 msi_set_context( package );
739 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
741 package->script->InWhatSequence |= SEQUENCE_UI;
742 rc = ACTION_ProcessUISequence(package);
743 ui = TRUE;
744 ui_exists = ui_sequence_exists(package);
745 if (rc == ERROR_SUCCESS || !ui_exists)
747 package->script->InWhatSequence |= SEQUENCE_EXEC;
748 rc = ACTION_ProcessExecSequence(package,ui_exists);
751 else
752 rc = ACTION_ProcessExecSequence(package,FALSE);
754 package->script->CurrentlyScripting= FALSE;
756 /* process the ending type action */
757 if (rc == ERROR_SUCCESS)
758 ACTION_PerformActionSequence(package,-1,ui);
759 else if (rc == ERROR_INSTALL_USEREXIT)
760 ACTION_PerformActionSequence(package,-2,ui);
761 else if (rc == ERROR_INSTALL_SUSPEND)
762 ACTION_PerformActionSequence(package,-4,ui);
763 else /* failed */
764 ACTION_PerformActionSequence(package,-3,ui);
766 /* finish up running custom actions */
767 ACTION_FinishCustomActions(package);
769 return rc;
772 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
774 UINT rc = ERROR_SUCCESS;
775 MSIRECORD * row = 0;
776 static const WCHAR ExecSeqQuery[] =
777 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
778 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
779 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
780 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
782 static const WCHAR UISeqQuery[] =
783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
784 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
785 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
786 ' ', '=',' ','%','i',0};
788 if (UI)
789 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
790 else
791 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
793 if (row)
795 LPCWSTR action, cond;
797 TRACE("Running the actions\n");
799 /* check conditions */
800 cond = MSI_RecordGetString(row,2);
802 /* this is a hack to skip errors in the condition code */
803 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
804 goto end;
806 action = MSI_RecordGetString(row,1);
807 if (!action)
809 ERR("failed to fetch action\n");
810 rc = ERROR_FUNCTION_FAILED;
811 goto end;
814 if (UI)
815 rc = ACTION_PerformUIAction(package,action,-1);
816 else
817 rc = ACTION_PerformAction(package,action,-1,FALSE);
818 end:
819 msiobj_release(&row->hdr);
821 else
822 rc = ERROR_SUCCESS;
824 return rc;
827 typedef struct {
828 MSIPACKAGE* package;
829 BOOL UI;
830 } iterate_action_param;
832 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
834 iterate_action_param *iap= (iterate_action_param*)param;
835 UINT rc;
836 LPCWSTR cond, action;
838 action = MSI_RecordGetString(row,1);
839 if (!action)
841 ERR("Error is retrieving action name\n");
842 return ERROR_FUNCTION_FAILED;
845 /* check conditions */
846 cond = MSI_RecordGetString(row,2);
848 /* this is a hack to skip errors in the condition code */
849 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
851 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
852 return ERROR_SUCCESS;
855 if (iap->UI)
856 rc = ACTION_PerformUIAction(iap->package,action,-1);
857 else
858 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
860 msi_dialog_check_messages( NULL );
862 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
863 rc = iap->package->CurrentInstallState;
865 if (rc == ERROR_FUNCTION_NOT_CALLED)
866 rc = ERROR_SUCCESS;
868 if (rc != ERROR_SUCCESS)
869 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
871 return rc;
874 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
876 MSIQUERY * view;
877 UINT r;
878 static const WCHAR query[] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','%','s','`',
881 ' ','W','H','E','R','E',' ',
882 '`','S','e','q','u','e','n','c','e','`',' ',
883 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
884 '`','S','e','q','u','e','n','c','e','`',0};
885 iterate_action_param iap;
888 * FIXME: probably should be checking UILevel in the
889 * ACTION_PerformUIAction/ACTION_PerformAction
890 * rather than saving the UI level here. Those
891 * two functions can be merged too.
893 iap.package = package;
894 iap.UI = TRUE;
896 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
898 r = MSI_OpenQuery( package->db, &view, query, szTable );
899 if (r == ERROR_SUCCESS)
901 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
902 msiobj_release(&view->hdr);
905 return r;
908 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
910 MSIQUERY * view;
911 UINT rc;
912 static const WCHAR ExecSeqQuery[] =
913 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
914 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
915 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
917 'O','R','D','E','R',' ', 'B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0 };
919 MSIRECORD * row = 0;
920 static const WCHAR IVQuery[] =
921 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
922 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
923 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
924 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
925 ' ','\'', 'I','n','s','t','a','l','l',
926 'V','a','l','i','d','a','t','e','\'', 0};
927 INT seq = 0;
928 iterate_action_param iap;
930 iap.package = package;
931 iap.UI = FALSE;
933 if (package->script->ExecuteSequenceRun)
935 TRACE("Execute Sequence already Run\n");
936 return ERROR_SUCCESS;
939 package->script->ExecuteSequenceRun = TRUE;
941 /* get the sequence number */
942 if (UIran)
944 row = MSI_QueryGetRecord(package->db, IVQuery);
945 if( !row )
946 return ERROR_FUNCTION_FAILED;
947 seq = MSI_RecordGetInteger(row,1);
948 msiobj_release(&row->hdr);
951 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
952 if (rc == ERROR_SUCCESS)
954 TRACE("Running the actions\n");
956 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
957 msiobj_release(&view->hdr);
960 return rc;
963 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
965 MSIQUERY * view;
966 UINT rc;
967 static const WCHAR ExecSeqQuery [] =
968 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
969 '`','I','n','s','t','a','l','l',
970 'U','I','S','e','q','u','e','n','c','e','`',
971 ' ','W','H','E','R','E',' ',
972 '`','S','e','q','u','e','n','c','e','`',' ',
973 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
974 '`','S','e','q','u','e','n','c','e','`',0};
975 iterate_action_param iap;
977 iap.package = package;
978 iap.UI = TRUE;
980 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
982 if (rc == ERROR_SUCCESS)
984 TRACE("Running the actions\n");
986 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
987 msiobj_release(&view->hdr);
990 return rc;
993 /********************************************************
994 * ACTION helper functions and functions that perform the actions
995 *******************************************************/
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 WARN("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 WARN("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 if ( !name )
1279 return NULL;
1281 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1283 if ( !lstrcmpW( feature->Feature, name ) )
1284 return feature;
1287 return NULL;
1290 static UINT load_feature(MSIRECORD * row, LPVOID param)
1292 MSIPACKAGE* package = (MSIPACKAGE*)param;
1293 MSIFEATURE* feature;
1294 static const WCHAR Query1[] =
1295 {'S','E','L','E','C','T',' ',
1296 '`','C','o','m','p','o','n','e','n','t','_','`',
1297 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1298 'C','o','m','p','o','n','e','n','t','s','`',' ',
1299 'W','H','E','R','E',' ',
1300 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1301 MSIQUERY * view;
1302 UINT rc;
1303 _ilfs ilfs;
1305 /* fill in the data */
1307 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1308 if (!feature)
1309 return ERROR_NOT_ENOUGH_MEMORY;
1311 list_init( &feature->Children );
1312 list_init( &feature->Components );
1314 feature->Feature = msi_dup_record_field( row, 1 );
1316 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1318 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1319 feature->Title = msi_dup_record_field( row, 3 );
1320 feature->Description = msi_dup_record_field( row, 4 );
1322 if (!MSI_RecordIsNull(row,5))
1323 feature->Display = MSI_RecordGetInteger(row,5);
1325 feature->Level= MSI_RecordGetInteger(row,6);
1326 feature->Directory = msi_dup_record_field( row, 7 );
1327 feature->Attributes = MSI_RecordGetInteger(row,8);
1329 feature->Installed = INSTALLSTATE_UNKNOWN;
1330 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1332 list_add_tail( &package->features, &feature->entry );
1334 /* load feature components */
1336 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1337 if (rc != ERROR_SUCCESS)
1338 return ERROR_SUCCESS;
1340 ilfs.package = package;
1341 ilfs.feature = feature;
1343 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1344 msiobj_release(&view->hdr);
1346 return ERROR_SUCCESS;
1349 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1351 MSIPACKAGE* package = (MSIPACKAGE*)param;
1352 MSIFEATURE *parent, *child;
1354 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1355 if (!child)
1356 return ERROR_FUNCTION_FAILED;
1358 if (!child->Feature_Parent)
1359 return ERROR_SUCCESS;
1361 parent = find_feature_by_name( package, child->Feature_Parent );
1362 if (!parent)
1363 return ERROR_FUNCTION_FAILED;
1365 add_feature_child( parent, child );
1366 return ERROR_SUCCESS;
1369 static UINT load_all_features( MSIPACKAGE *package )
1371 static const WCHAR query[] = {
1372 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1373 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1374 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1375 MSIQUERY *view;
1376 UINT r;
1378 if (!list_empty(&package->features))
1379 return ERROR_SUCCESS;
1381 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1382 if (r != ERROR_SUCCESS)
1383 return r;
1385 r = MSI_IterateRecords( view, NULL, load_feature, package );
1386 if (r != ERROR_SUCCESS)
1387 return r;
1389 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1390 msiobj_release( &view->hdr );
1392 return r;
1395 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1397 if (!p)
1398 return p;
1399 p = strchrW(p, ch);
1400 if (!p)
1401 return p;
1402 *p = 0;
1403 return p+1;
1406 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1408 static const WCHAR query[] = {
1409 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1410 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1411 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1412 MSIQUERY *view = NULL;
1413 MSIRECORD *row = NULL;
1414 UINT r;
1416 TRACE("%s\n", debugstr_w(file->File));
1418 r = MSI_OpenQuery(package->db, &view, query, file->File);
1419 if (r != ERROR_SUCCESS)
1420 goto done;
1422 r = MSI_ViewExecute(view, NULL);
1423 if (r != ERROR_SUCCESS)
1424 goto done;
1426 r = MSI_ViewFetch(view, &row);
1427 if (r != ERROR_SUCCESS)
1428 goto done;
1430 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1431 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1432 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1433 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1434 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1436 done:
1437 if (view) msiobj_release(&view->hdr);
1438 if (row) msiobj_release(&row->hdr);
1439 return r;
1442 static UINT load_file(MSIRECORD *row, LPVOID param)
1444 MSIPACKAGE* package = (MSIPACKAGE*)param;
1445 LPCWSTR component;
1446 MSIFILE *file;
1448 /* fill in the data */
1450 file = msi_alloc_zero( sizeof (MSIFILE) );
1451 if (!file)
1452 return ERROR_NOT_ENOUGH_MEMORY;
1454 file->File = msi_dup_record_field( row, 1 );
1456 component = MSI_RecordGetString( row, 2 );
1457 file->Component = get_loaded_component( package, component );
1459 if (!file->Component)
1461 WARN("Component not found: %s\n", debugstr_w(component));
1462 msi_free(file->File);
1463 msi_free(file);
1464 return ERROR_SUCCESS;
1467 file->FileName = msi_dup_record_field( row, 3 );
1468 reduce_to_longfilename( file->FileName );
1470 file->ShortName = msi_dup_record_field( row, 3 );
1471 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1473 file->FileSize = MSI_RecordGetInteger( row, 4 );
1474 file->Version = msi_dup_record_field( row, 5 );
1475 file->Language = msi_dup_record_field( row, 6 );
1476 file->Attributes = MSI_RecordGetInteger( row, 7 );
1477 file->Sequence = MSI_RecordGetInteger( row, 8 );
1479 file->state = msifs_invalid;
1481 /* if the compressed bits are not set in the file attributes,
1482 * then read the information from the package word count property
1484 if (file->Attributes &
1485 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1487 file->IsCompressed = TRUE;
1489 else if (file->Attributes & msidbFileAttributesNoncompressed)
1491 file->IsCompressed = FALSE;
1493 else
1495 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1498 if (!file->IsCompressed)
1500 LPWSTR p, path;
1502 p = resolve_folder(package, file->Component->Directory,
1503 TRUE, FALSE, TRUE, NULL);
1504 path = build_directory_name(2, p, file->ShortName);
1506 if (file->LongName &&
1507 GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
1509 msi_free(path);
1510 path = build_directory_name(2, p, file->LongName);
1513 file->SourcePath = path;
1514 msi_free(p);
1517 load_file_hash(package, file);
1519 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1521 list_add_tail( &package->files, &file->entry );
1523 return ERROR_SUCCESS;
1526 static UINT load_all_files(MSIPACKAGE *package)
1528 MSIQUERY * view;
1529 UINT rc;
1530 static const WCHAR Query[] =
1531 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1532 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1533 '`','S','e','q','u','e','n','c','e','`', 0};
1535 if (!list_empty(&package->files))
1536 return ERROR_SUCCESS;
1538 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1539 if (rc != ERROR_SUCCESS)
1540 return ERROR_SUCCESS;
1542 rc = MSI_IterateRecords(view, NULL, load_file, package);
1543 msiobj_release(&view->hdr);
1545 return ERROR_SUCCESS;
1548 static UINT load_folder( MSIRECORD *row, LPVOID param )
1550 MSIPACKAGE *package = param;
1551 static const WCHAR szDot[] = { '.',0 };
1552 static WCHAR szEmpty[] = { 0 };
1553 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1554 MSIFOLDER *folder;
1556 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1557 if (!folder)
1558 return ERROR_NOT_ENOUGH_MEMORY;
1560 folder->Directory = msi_dup_record_field( row, 1 );
1562 TRACE("%s\n", debugstr_w(folder->Directory));
1564 p = msi_dup_record_field(row, 3);
1566 /* split src and target dir */
1567 tgt_short = p;
1568 src_short = folder_split_path( p, ':' );
1570 /* split the long and short paths */
1571 tgt_long = folder_split_path( tgt_short, '|' );
1572 src_long = folder_split_path( src_short, '|' );
1574 /* check for no-op dirs */
1575 if (!lstrcmpW(szDot, tgt_short))
1576 tgt_short = szEmpty;
1577 if (!lstrcmpW(szDot, src_short))
1578 src_short = szEmpty;
1580 if (!tgt_long)
1581 tgt_long = tgt_short;
1583 if (!src_short) {
1584 src_short = tgt_short;
1585 src_long = tgt_long;
1588 if (!src_long)
1589 src_long = src_short;
1591 /* FIXME: use the target short path too */
1592 folder->TargetDefault = strdupW(tgt_long);
1593 folder->SourceShortPath = strdupW(src_short);
1594 folder->SourceLongPath = strdupW(src_long);
1595 msi_free(p);
1597 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1598 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1599 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1601 folder->Parent = msi_dup_record_field( row, 2 );
1603 folder->Property = msi_dup_property( package, folder->Directory );
1605 list_add_tail( &package->folders, &folder->entry );
1607 TRACE("returning %p\n", folder);
1609 return ERROR_SUCCESS;
1612 static UINT load_all_folders( MSIPACKAGE *package )
1614 static const WCHAR query[] = {
1615 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1616 '`','D','i','r','e','c','t','o','r','y','`',0 };
1617 MSIQUERY *view;
1618 UINT r;
1620 if (!list_empty(&package->folders))
1621 return ERROR_SUCCESS;
1623 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1624 if (r != ERROR_SUCCESS)
1625 return r;
1627 r = MSI_IterateRecords(view, NULL, load_folder, package);
1628 msiobj_release(&view->hdr);
1629 return r;
1633 * I am not doing any of the costing functionality yet.
1634 * Mostly looking at doing the Component and Feature loading
1636 * The native MSI does A LOT of modification to tables here. Mostly adding
1637 * a lot of temporary columns to the Feature and Component tables.
1639 * note: Native msi also tracks the short filename. But I am only going to
1640 * track the long ones. Also looking at this directory table
1641 * it appears that the directory table does not get the parents
1642 * resolved base on property only based on their entries in the
1643 * directory table.
1645 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1647 static const WCHAR szCosting[] =
1648 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1649 static const WCHAR szZero[] = { '0', 0 };
1651 MSI_SetPropertyW(package, szCosting, szZero);
1652 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1654 load_all_folders( package );
1655 load_all_components( package );
1656 load_all_features( package );
1657 load_all_files( package );
1659 return ERROR_SUCCESS;
1662 static UINT execute_script(MSIPACKAGE *package, UINT script )
1664 UINT i;
1665 UINT rc = ERROR_SUCCESS;
1667 TRACE("Executing Script %i\n",script);
1669 if (!package->script)
1671 ERR("no script!\n");
1672 return ERROR_FUNCTION_FAILED;
1675 for (i = 0; i < package->script->ActionCount[script]; i++)
1677 LPWSTR action;
1678 action = package->script->Actions[script][i];
1679 ui_actionstart(package, action);
1680 TRACE("Executing Action (%s)\n",debugstr_w(action));
1681 rc = ACTION_PerformAction(package, action, script, TRUE);
1682 if (rc != ERROR_SUCCESS)
1683 break;
1685 msi_free_action_script(package, script);
1686 return rc;
1689 static UINT ACTION_FileCost(MSIPACKAGE *package)
1691 return ERROR_SUCCESS;
1694 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1696 MSICOMPONENT *comp;
1697 INSTALLSTATE state;
1698 UINT r;
1700 state = MsiQueryProductStateW(package->ProductCode);
1702 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1704 if (!comp->ComponentId)
1705 continue;
1707 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1708 comp->Installed = INSTALLSTATE_ABSENT;
1709 else
1711 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1712 package->Context, comp->ComponentId,
1713 &comp->Installed);
1714 if (r != ERROR_SUCCESS)
1715 comp->Installed = INSTALLSTATE_ABSENT;
1720 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1722 MSIFEATURE *feature;
1723 INSTALLSTATE state;
1725 state = MsiQueryProductStateW(package->ProductCode);
1727 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1729 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1730 feature->Installed = INSTALLSTATE_ABSENT;
1731 else
1733 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1734 feature->Feature);
1739 static BOOL process_state_property(MSIPACKAGE* package, int level,
1740 LPCWSTR property, INSTALLSTATE state)
1742 static const WCHAR all[]={'A','L','L',0};
1743 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1744 LPWSTR override;
1745 MSIFEATURE *feature;
1747 override = msi_dup_property( package, property );
1748 if (!override)
1749 return FALSE;
1751 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1753 if (lstrcmpW(property, remove) &&
1754 (feature->Level <= 0 || feature->Level > level))
1755 continue;
1757 if (strcmpiW(override,all)==0)
1758 msi_feature_set_state(package, feature, state);
1759 else
1761 LPWSTR ptr = override;
1762 LPWSTR ptr2 = strchrW(override,',');
1764 while (ptr)
1766 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1767 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1769 msi_feature_set_state(package, feature, state);
1770 break;
1772 if (ptr2)
1774 ptr=ptr2+1;
1775 ptr2 = strchrW(ptr,',');
1777 else
1778 break;
1782 msi_free(override);
1784 return TRUE;
1787 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1789 int level;
1790 static const WCHAR szlevel[] =
1791 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1792 static const WCHAR szAddLocal[] =
1793 {'A','D','D','L','O','C','A','L',0};
1794 static const WCHAR szAddSource[] =
1795 {'A','D','D','S','O','U','R','C','E',0};
1796 static const WCHAR szRemove[] =
1797 {'R','E','M','O','V','E',0};
1798 static const WCHAR szReinstall[] =
1799 {'R','E','I','N','S','T','A','L','L',0};
1800 BOOL override = FALSE;
1801 MSICOMPONENT* component;
1802 MSIFEATURE *feature;
1805 /* I do not know if this is where it should happen.. but */
1807 TRACE("Checking Install Level\n");
1809 level = msi_get_property_int(package, szlevel, 1);
1811 /* ok here is the _real_ rub
1812 * all these activation/deactivation things happen in order and things
1813 * later on the list override things earlier on the list.
1814 * 1) INSTALLLEVEL processing
1815 * 2) ADDLOCAL
1816 * 3) REMOVE
1817 * 4) ADDSOURCE
1818 * 5) ADDDEFAULT
1819 * 6) REINSTALL
1820 * 7) COMPADDLOCAL
1821 * 8) COMPADDSOURCE
1822 * 9) FILEADDLOCAL
1823 * 10) FILEADDSOURCE
1824 * 11) FILEADDDEFAULT
1826 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1827 * REMOVE are the big ones, since we don't handle administrative installs
1828 * yet anyway.
1830 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1831 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1832 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1833 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1835 if (!override)
1837 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1839 BOOL feature_state = ((feature->Level > 0) &&
1840 (feature->Level <= level));
1842 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1844 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1845 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1846 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1847 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1848 else
1849 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1853 /* disable child features of unselected parent features */
1854 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1856 FeatureList *fl;
1858 if (feature->Level > 0 && feature->Level <= level)
1859 continue;
1861 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1862 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1865 else
1867 /* set the Preselected Property */
1868 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1869 static const WCHAR szOne[] = { '1', 0 };
1871 MSI_SetPropertyW(package,szPreselected,szOne);
1875 * now we want to enable or disable components base on feature
1878 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1880 ComponentList *cl;
1882 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1883 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1885 if (!feature->Level)
1886 continue;
1888 /* features with components that have compressed files are made local */
1889 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1891 if (cl->component->Enabled &&
1892 cl->component->ForceLocalState &&
1893 feature->Action == INSTALLSTATE_SOURCE)
1895 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1896 break;
1900 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1902 component = cl->component;
1904 if (!component->Enabled)
1905 continue;
1907 switch (feature->Action)
1909 case INSTALLSTATE_ABSENT:
1910 component->anyAbsent = 1;
1911 break;
1912 case INSTALLSTATE_ADVERTISED:
1913 component->hasAdvertiseFeature = 1;
1914 break;
1915 case INSTALLSTATE_SOURCE:
1916 component->hasSourceFeature = 1;
1917 break;
1918 case INSTALLSTATE_LOCAL:
1919 component->hasLocalFeature = 1;
1920 break;
1921 case INSTALLSTATE_DEFAULT:
1922 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1923 component->hasAdvertiseFeature = 1;
1924 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1925 component->hasSourceFeature = 1;
1926 else
1927 component->hasLocalFeature = 1;
1928 break;
1929 default:
1930 break;
1935 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1937 /* if the component isn't enabled, leave it alone */
1938 if (!component->Enabled)
1939 continue;
1941 /* check if it's local or source */
1942 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1943 (component->hasLocalFeature || component->hasSourceFeature))
1945 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1946 !component->ForceLocalState)
1947 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1948 else
1949 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1950 continue;
1953 /* if any feature is local, the component must be local too */
1954 if (component->hasLocalFeature)
1956 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1957 continue;
1960 if (component->hasSourceFeature)
1962 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1963 continue;
1966 if (component->hasAdvertiseFeature)
1968 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1969 continue;
1972 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1973 if (component->anyAbsent)
1974 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1977 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1979 if (component->Action == INSTALLSTATE_DEFAULT)
1981 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1982 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1985 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1986 debugstr_w(component->Component), component->Installed, component->Action);
1990 return ERROR_SUCCESS;
1993 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1995 MSIPACKAGE *package = (MSIPACKAGE*)param;
1996 LPCWSTR name;
1997 LPWSTR path;
1998 MSIFOLDER *f;
2000 name = MSI_RecordGetString(row,1);
2002 f = get_loaded_folder(package, name);
2003 if (!f) return ERROR_SUCCESS;
2005 /* reset the ResolvedTarget */
2006 msi_free(f->ResolvedTarget);
2007 f->ResolvedTarget = NULL;
2009 /* This helper function now does ALL the work */
2010 TRACE("Dir %s ...\n",debugstr_w(name));
2011 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2012 TRACE("resolves to %s\n",debugstr_w(path));
2013 msi_free(path);
2015 return ERROR_SUCCESS;
2018 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2020 MSIPACKAGE *package = (MSIPACKAGE*)param;
2021 LPCWSTR name;
2022 MSIFEATURE *feature;
2024 name = MSI_RecordGetString( row, 1 );
2026 feature = get_loaded_feature( package, name );
2027 if (!feature)
2028 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2029 else
2031 LPCWSTR Condition;
2032 Condition = MSI_RecordGetString(row,3);
2034 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2036 int level = MSI_RecordGetInteger(row,2);
2037 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2038 feature->Level = level;
2041 return ERROR_SUCCESS;
2044 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2046 static const WCHAR name_fmt[] =
2047 {'%','u','.','%','u','.','%','u','.','%','u',0};
2048 static const WCHAR name[] = {'\\',0};
2049 VS_FIXEDFILEINFO *lpVer;
2050 WCHAR filever[0x100];
2051 LPVOID version;
2052 DWORD versize;
2053 DWORD handle;
2054 UINT sz;
2056 TRACE("%s\n", debugstr_w(filename));
2058 versize = GetFileVersionInfoSizeW( filename, &handle );
2059 if (!versize)
2060 return NULL;
2062 version = msi_alloc( versize );
2063 GetFileVersionInfoW( filename, 0, versize, version );
2065 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2067 msi_free( version );
2068 return NULL;
2071 sprintfW( filever, name_fmt,
2072 HIWORD(lpVer->dwFileVersionMS),
2073 LOWORD(lpVer->dwFileVersionMS),
2074 HIWORD(lpVer->dwFileVersionLS),
2075 LOWORD(lpVer->dwFileVersionLS));
2077 msi_free( version );
2079 return strdupW( filever );
2082 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2084 LPWSTR file_version;
2085 MSIFILE *file;
2087 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2089 MSICOMPONENT* comp = file->Component;
2090 LPWSTR p;
2092 if (!comp)
2093 continue;
2095 if (file->IsCompressed)
2096 comp->ForceLocalState = TRUE;
2098 /* calculate target */
2099 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2101 msi_free(file->TargetPath);
2103 TRACE("file %s is named %s\n",
2104 debugstr_w(file->File), debugstr_w(file->FileName));
2106 file->TargetPath = build_directory_name(2, p, file->FileName);
2108 msi_free(p);
2110 TRACE("file %s resolves to %s\n",
2111 debugstr_w(file->File), debugstr_w(file->TargetPath));
2113 /* don't check files of components that aren't installed */
2114 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2115 comp->Installed == INSTALLSTATE_ABSENT)
2117 file->state = msifs_missing; /* assume files are missing */
2118 continue;
2121 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2123 file->state = msifs_missing;
2124 comp->Cost += file->FileSize;
2125 continue;
2128 if (file->Version &&
2129 (file_version = msi_get_disk_file_version( file->TargetPath )))
2131 TRACE("new %s old %s\n", debugstr_w(file->Version),
2132 debugstr_w(file_version));
2133 /* FIXME: seems like a bad way to compare version numbers */
2134 if (lstrcmpiW(file_version, file->Version)<0)
2136 file->state = msifs_overwrite;
2137 comp->Cost += file->FileSize;
2139 else
2140 file->state = msifs_present;
2141 msi_free( file_version );
2143 else
2144 file->state = msifs_present;
2147 return ERROR_SUCCESS;
2151 * A lot is done in this function aside from just the costing.
2152 * The costing needs to be implemented at some point but for now I am going
2153 * to focus on the directory building
2156 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2158 static const WCHAR ExecSeqQuery[] =
2159 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2160 '`','D','i','r','e','c','t','o','r','y','`',0};
2161 static const WCHAR ConditionQuery[] =
2162 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2163 '`','C','o','n','d','i','t','i','o','n','`',0};
2164 static const WCHAR szCosting[] =
2165 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2166 static const WCHAR szlevel[] =
2167 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2168 static const WCHAR szOutOfDiskSpace[] =
2169 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2170 static const WCHAR szOne[] = { '1', 0 };
2171 static const WCHAR szZero[] = { '0', 0 };
2172 MSICOMPONENT *comp;
2173 UINT rc;
2174 MSIQUERY * view;
2175 LPWSTR level;
2177 TRACE("Building Directory properties\n");
2179 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2180 if (rc == ERROR_SUCCESS)
2182 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2183 package);
2184 msiobj_release(&view->hdr);
2187 /* read components states from the registry */
2188 ACTION_GetComponentInstallStates(package);
2189 ACTION_GetFeatureInstallStates(package);
2191 TRACE("File calculations\n");
2192 msi_check_file_install_states( package );
2194 TRACE("Evaluating Condition Table\n");
2196 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2197 if (rc == ERROR_SUCCESS)
2199 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2200 package);
2201 msiobj_release(&view->hdr);
2204 TRACE("Enabling or Disabling Components\n");
2205 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2207 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2209 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2210 comp->Enabled = FALSE;
2212 else
2213 comp->Enabled = TRUE;
2216 MSI_SetPropertyW(package,szCosting,szOne);
2217 /* set default run level if not set */
2218 level = msi_dup_property( package, szlevel );
2219 if (!level)
2220 MSI_SetPropertyW(package,szlevel, szOne);
2221 msi_free(level);
2223 /* FIXME: check volume disk space */
2224 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2226 return MSI_SetFeatureStates(package);
2229 /* OK this value is "interpreted" and then formatted based on the
2230 first few characters */
2231 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2232 DWORD *size)
2234 LPSTR data = NULL;
2236 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2238 if (value[1]=='x')
2240 LPWSTR ptr;
2241 CHAR byte[5];
2242 LPWSTR deformated = NULL;
2243 int count;
2245 deformat_string(package, &value[2], &deformated);
2247 /* binary value type */
2248 ptr = deformated;
2249 *type = REG_BINARY;
2250 if (strlenW(ptr)%2)
2251 *size = (strlenW(ptr)/2)+1;
2252 else
2253 *size = strlenW(ptr)/2;
2255 data = msi_alloc(*size);
2257 byte[0] = '0';
2258 byte[1] = 'x';
2259 byte[4] = 0;
2260 count = 0;
2261 /* if uneven pad with a zero in front */
2262 if (strlenW(ptr)%2)
2264 byte[2]= '0';
2265 byte[3]= *ptr;
2266 ptr++;
2267 data[count] = (BYTE)strtol(byte,NULL,0);
2268 count ++;
2269 TRACE("Uneven byte count\n");
2271 while (*ptr)
2273 byte[2]= *ptr;
2274 ptr++;
2275 byte[3]= *ptr;
2276 ptr++;
2277 data[count] = (BYTE)strtol(byte,NULL,0);
2278 count ++;
2280 msi_free(deformated);
2282 TRACE("Data %i bytes(%i)\n",*size,count);
2284 else
2286 LPWSTR deformated;
2287 LPWSTR p;
2288 DWORD d = 0;
2289 deformat_string(package, &value[1], &deformated);
2291 *type=REG_DWORD;
2292 *size = sizeof(DWORD);
2293 data = msi_alloc(*size);
2294 p = deformated;
2295 if (*p == '-')
2296 p++;
2297 while (*p)
2299 if ( (*p < '0') || (*p > '9') )
2300 break;
2301 d *= 10;
2302 d += (*p - '0');
2303 p++;
2305 if (deformated[0] == '-')
2306 d = -d;
2307 *(LPDWORD)data = d;
2308 TRACE("DWORD %i\n",*(LPDWORD)data);
2310 msi_free(deformated);
2313 else
2315 static const WCHAR szMulti[] = {'[','~',']',0};
2316 LPCWSTR ptr;
2317 *type=REG_SZ;
2319 if (value[0]=='#')
2321 if (value[1]=='%')
2323 ptr = &value[2];
2324 *type=REG_EXPAND_SZ;
2326 else
2327 ptr = &value[1];
2329 else
2330 ptr=value;
2332 if (strstrW(value,szMulti))
2333 *type = REG_MULTI_SZ;
2335 /* remove initial delimiter */
2336 if (!strncmpW(value, szMulti, 3))
2337 ptr = value + 3;
2339 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2341 /* add double NULL terminator */
2342 if (*type == REG_MULTI_SZ)
2344 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2345 data = msi_realloc_zero(data, *size);
2348 return data;
2351 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2353 MSIPACKAGE *package = (MSIPACKAGE*)param;
2354 static const WCHAR szHCR[] =
2355 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2356 'R','O','O','T','\\',0};
2357 static const WCHAR szHCU[] =
2358 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2359 'U','S','E','R','\\',0};
2360 static const WCHAR szHLM[] =
2361 {'H','K','E','Y','_','L','O','C','A','L','_',
2362 'M','A','C','H','I','N','E','\\',0};
2363 static const WCHAR szHU[] =
2364 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2366 LPSTR value_data = NULL;
2367 HKEY root_key, hkey;
2368 DWORD type,size;
2369 LPWSTR deformated;
2370 LPCWSTR szRoot, component, name, key, value;
2371 MSICOMPONENT *comp;
2372 MSIRECORD * uirow;
2373 LPWSTR uikey;
2374 INT root;
2375 BOOL check_first = FALSE;
2376 UINT rc;
2378 ui_progress(package,2,0,0,0);
2380 value = NULL;
2381 key = NULL;
2382 uikey = NULL;
2383 name = NULL;
2385 component = MSI_RecordGetString(row, 6);
2386 comp = get_loaded_component(package,component);
2387 if (!comp)
2388 return ERROR_SUCCESS;
2390 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2392 TRACE("Skipping write due to disabled component %s\n",
2393 debugstr_w(component));
2395 comp->Action = comp->Installed;
2397 return ERROR_SUCCESS;
2400 comp->Action = INSTALLSTATE_LOCAL;
2402 name = MSI_RecordGetString(row, 4);
2403 if( MSI_RecordIsNull(row,5) && name )
2405 /* null values can have special meanings */
2406 if (name[0]=='-' && name[1] == 0)
2407 return ERROR_SUCCESS;
2408 else if ((name[0]=='+' && name[1] == 0) ||
2409 (name[0] == '*' && name[1] == 0))
2410 name = NULL;
2411 check_first = TRUE;
2414 root = MSI_RecordGetInteger(row,2);
2415 key = MSI_RecordGetString(row, 3);
2417 /* get the root key */
2418 switch (root)
2420 case -1:
2422 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2423 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2424 if (all_users && all_users[0] == '1')
2426 root_key = HKEY_LOCAL_MACHINE;
2427 szRoot = szHLM;
2429 else
2431 root_key = HKEY_CURRENT_USER;
2432 szRoot = szHCU;
2434 msi_free(all_users);
2436 break;
2437 case 0: root_key = HKEY_CLASSES_ROOT;
2438 szRoot = szHCR;
2439 break;
2440 case 1: root_key = HKEY_CURRENT_USER;
2441 szRoot = szHCU;
2442 break;
2443 case 2: root_key = HKEY_LOCAL_MACHINE;
2444 szRoot = szHLM;
2445 break;
2446 case 3: root_key = HKEY_USERS;
2447 szRoot = szHU;
2448 break;
2449 default:
2450 ERR("Unknown root %i\n",root);
2451 root_key=NULL;
2452 szRoot = NULL;
2453 break;
2455 if (!root_key)
2456 return ERROR_SUCCESS;
2458 deformat_string(package, key , &deformated);
2459 size = strlenW(deformated) + strlenW(szRoot) + 1;
2460 uikey = msi_alloc(size*sizeof(WCHAR));
2461 strcpyW(uikey,szRoot);
2462 strcatW(uikey,deformated);
2464 if (RegCreateKeyW( root_key, deformated, &hkey))
2466 ERR("Could not create key %s\n",debugstr_w(deformated));
2467 msi_free(deformated);
2468 msi_free(uikey);
2469 return ERROR_SUCCESS;
2471 msi_free(deformated);
2473 value = MSI_RecordGetString(row,5);
2474 if (value)
2475 value_data = parse_value(package, value, &type, &size);
2476 else
2478 static const WCHAR szEmpty[] = {0};
2479 value_data = (LPSTR)strdupW(szEmpty);
2480 size = sizeof(szEmpty);
2481 type = REG_SZ;
2484 deformat_string(package, name, &deformated);
2486 if (!check_first)
2488 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2489 debugstr_w(uikey));
2490 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2492 else
2494 DWORD sz = 0;
2495 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2496 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2498 TRACE("value %s of %s checked already exists\n",
2499 debugstr_w(deformated), debugstr_w(uikey));
2501 else
2503 TRACE("Checked and setting value %s of %s\n",
2504 debugstr_w(deformated), debugstr_w(uikey));
2505 if (deformated || size)
2506 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2509 RegCloseKey(hkey);
2511 uirow = MSI_CreateRecord(3);
2512 MSI_RecordSetStringW(uirow,2,deformated);
2513 MSI_RecordSetStringW(uirow,1,uikey);
2515 if (type == REG_SZ)
2516 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2517 else
2518 MSI_RecordSetStringW(uirow,3,value);
2520 ui_actiondata(package,szWriteRegistryValues,uirow);
2521 msiobj_release( &uirow->hdr );
2523 msi_free(value_data);
2524 msi_free(deformated);
2525 msi_free(uikey);
2527 return ERROR_SUCCESS;
2530 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2532 UINT rc;
2533 MSIQUERY * view;
2534 static const WCHAR ExecSeqQuery[] =
2535 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2536 '`','R','e','g','i','s','t','r','y','`',0 };
2538 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2539 if (rc != ERROR_SUCCESS)
2540 return ERROR_SUCCESS;
2542 /* increment progress bar each time action data is sent */
2543 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2545 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2547 msiobj_release(&view->hdr);
2548 return rc;
2551 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2553 package->script->CurrentlyScripting = TRUE;
2555 return ERROR_SUCCESS;
2559 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2561 MSICOMPONENT *comp;
2562 DWORD progress = 0;
2563 DWORD total = 0;
2564 static const WCHAR q1[]=
2565 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2566 '`','R','e','g','i','s','t','r','y','`',0};
2567 UINT rc;
2568 MSIQUERY * view;
2569 MSIFEATURE *feature;
2570 MSIFILE *file;
2572 TRACE("InstallValidate\n");
2574 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2575 if (rc == ERROR_SUCCESS)
2577 MSI_IterateRecords( view, &progress, NULL, package );
2578 msiobj_release( &view->hdr );
2579 total += progress * REG_PROGRESS_VALUE;
2582 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2583 total += COMPONENT_PROGRESS_VALUE;
2585 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2586 total += file->FileSize;
2588 ui_progress(package,0,total,0,0);
2590 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2592 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2593 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2594 feature->ActionRequest);
2597 return ERROR_SUCCESS;
2600 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2602 MSIPACKAGE* package = (MSIPACKAGE*)param;
2603 LPCWSTR cond = NULL;
2604 LPCWSTR message = NULL;
2605 UINT r;
2607 static const WCHAR title[]=
2608 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2610 cond = MSI_RecordGetString(row,1);
2612 r = MSI_EvaluateConditionW(package,cond);
2613 if (r == MSICONDITION_FALSE)
2615 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2617 LPWSTR deformated;
2618 message = MSI_RecordGetString(row,2);
2619 deformat_string(package,message,&deformated);
2620 MessageBoxW(NULL,deformated,title,MB_OK);
2621 msi_free(deformated);
2624 return ERROR_INSTALL_FAILURE;
2627 return ERROR_SUCCESS;
2630 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2632 UINT rc;
2633 MSIQUERY * view = NULL;
2634 static const WCHAR ExecSeqQuery[] =
2635 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2636 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2638 TRACE("Checking launch conditions\n");
2640 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2641 if (rc != ERROR_SUCCESS)
2642 return ERROR_SUCCESS;
2644 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2645 msiobj_release(&view->hdr);
2647 return rc;
2650 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2653 if (!cmp->KeyPath)
2654 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2656 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2658 MSIRECORD * row = 0;
2659 UINT root,len;
2660 LPWSTR deformated,buffer,deformated_name;
2661 LPCWSTR key,name;
2662 static const WCHAR ExecSeqQuery[] =
2663 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2664 '`','R','e','g','i','s','t','r','y','`',' ',
2665 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2666 ' ','=',' ' ,'\'','%','s','\'',0 };
2667 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2668 static const WCHAR fmt2[]=
2669 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2671 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2672 if (!row)
2673 return NULL;
2675 root = MSI_RecordGetInteger(row,2);
2676 key = MSI_RecordGetString(row, 3);
2677 name = MSI_RecordGetString(row, 4);
2678 deformat_string(package, key , &deformated);
2679 deformat_string(package, name, &deformated_name);
2681 len = strlenW(deformated) + 6;
2682 if (deformated_name)
2683 len+=strlenW(deformated_name);
2685 buffer = msi_alloc( len *sizeof(WCHAR));
2687 if (deformated_name)
2688 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2689 else
2690 sprintfW(buffer,fmt,root,deformated);
2692 msi_free(deformated);
2693 msi_free(deformated_name);
2694 msiobj_release(&row->hdr);
2696 return buffer;
2698 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2700 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2701 return NULL;
2703 else
2705 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2707 if (file)
2708 return strdupW( file->TargetPath );
2710 return NULL;
2713 static HKEY openSharedDLLsKey(void)
2715 HKEY hkey=0;
2716 static const WCHAR path[] =
2717 {'S','o','f','t','w','a','r','e','\\',
2718 'M','i','c','r','o','s','o','f','t','\\',
2719 'W','i','n','d','o','w','s','\\',
2720 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2721 'S','h','a','r','e','d','D','L','L','s',0};
2723 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2724 return hkey;
2727 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2729 HKEY hkey;
2730 DWORD count=0;
2731 DWORD type;
2732 DWORD sz = sizeof(count);
2733 DWORD rc;
2735 hkey = openSharedDLLsKey();
2736 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2737 if (rc != ERROR_SUCCESS)
2738 count = 0;
2739 RegCloseKey(hkey);
2740 return count;
2743 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2745 HKEY hkey;
2747 hkey = openSharedDLLsKey();
2748 if (count > 0)
2749 msi_reg_set_val_dword( hkey, path, count );
2750 else
2751 RegDeleteValueW(hkey,path);
2752 RegCloseKey(hkey);
2753 return count;
2757 * Return TRUE if the count should be written out and FALSE if not
2759 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2761 MSIFEATURE *feature;
2762 INT count = 0;
2763 BOOL write = FALSE;
2765 /* only refcount DLLs */
2766 if (comp->KeyPath == NULL ||
2767 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2768 comp->Attributes & msidbComponentAttributesODBCDataSource)
2769 write = FALSE;
2770 else
2772 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2773 write = (count > 0);
2775 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2776 write = TRUE;
2779 /* increment counts */
2780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2782 ComponentList *cl;
2784 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2785 continue;
2787 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2789 if ( cl->component == comp )
2790 count++;
2794 /* decrement counts */
2795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2797 ComponentList *cl;
2799 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2800 continue;
2802 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2804 if ( cl->component == comp )
2805 count--;
2809 /* ref count all the files in the component */
2810 if (write)
2812 MSIFILE *file;
2814 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2816 if (file->Component == comp)
2817 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2821 /* add a count for permanent */
2822 if (comp->Attributes & msidbComponentAttributesPermanent)
2823 count ++;
2825 comp->RefCount = count;
2827 if (write)
2828 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2831 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2833 WCHAR squished_pc[GUID_SIZE];
2834 WCHAR squished_cc[GUID_SIZE];
2835 UINT rc;
2836 MSICOMPONENT *comp;
2837 HKEY hkey;
2839 TRACE("\n");
2841 squash_guid(package->ProductCode,squished_pc);
2842 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2844 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2846 MSIRECORD * uirow;
2848 ui_progress(package,2,0,0,0);
2849 if (!comp->ComponentId)
2850 continue;
2852 squash_guid(comp->ComponentId,squished_cc);
2854 msi_free(comp->FullKeypath);
2855 comp->FullKeypath = resolve_keypath( package, comp );
2857 ACTION_RefCountComponent( package, comp );
2859 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2860 debugstr_w(comp->Component),
2861 debugstr_w(squished_cc),
2862 debugstr_w(comp->FullKeypath),
2863 comp->RefCount);
2865 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2866 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2868 if (!comp->FullKeypath)
2869 continue;
2871 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2872 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2873 else
2874 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2876 if (rc != ERROR_SUCCESS)
2877 continue;
2879 if (comp->Attributes & msidbComponentAttributesPermanent)
2881 static const WCHAR szPermKey[] =
2882 { '0','0','0','0','0','0','0','0','0','0','0','0',
2883 '0','0','0','0','0','0','0','0','0','0','0','0',
2884 '0','0','0','0','0','0','0','0',0 };
2886 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2889 if (comp->Action == INSTALLSTATE_LOCAL)
2890 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2891 else
2893 MSIFILE *file;
2894 MSIRECORD *row;
2895 LPWSTR ptr, ptr2;
2896 WCHAR source[MAX_PATH];
2897 WCHAR base[MAX_PATH];
2899 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2900 static const WCHAR query[] = {
2901 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2902 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2903 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2904 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2905 '`','D','i','s','k','I','d','`',0};
2907 file = get_loaded_file(package, comp->KeyPath);
2908 if (!file)
2909 continue;
2911 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2912 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2913 ptr2 = strrchrW(source, '\\') + 1;
2914 msiobj_release(&row->hdr);
2916 lstrcpyW(base, package->PackagePath);
2917 ptr = strrchrW(base, '\\');
2918 *(ptr + 1) = '\0';
2920 ptr = file->SourcePath + lstrlenW(base);
2921 lstrcpyW(ptr2, ptr);
2923 msi_reg_set_val_str(hkey, squished_pc, source);
2925 RegCloseKey(hkey);
2927 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2929 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2930 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2931 else
2932 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2935 /* UI stuff */
2936 uirow = MSI_CreateRecord(3);
2937 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2938 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2939 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2940 ui_actiondata(package,szProcessComponents,uirow);
2941 msiobj_release( &uirow->hdr );
2944 return ERROR_SUCCESS;
2947 typedef struct {
2948 CLSID clsid;
2949 LPWSTR source;
2951 LPWSTR path;
2952 ITypeLib *ptLib;
2953 } typelib_struct;
2955 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2956 LPWSTR lpszName, LONG_PTR lParam)
2958 TLIBATTR *attr;
2959 typelib_struct *tl_struct = (typelib_struct*) lParam;
2960 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2961 int sz;
2962 HRESULT res;
2964 if (!IS_INTRESOURCE(lpszName))
2966 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2967 return TRUE;
2970 sz = strlenW(tl_struct->source)+4;
2971 sz *= sizeof(WCHAR);
2973 if ((INT_PTR)lpszName == 1)
2974 tl_struct->path = strdupW(tl_struct->source);
2975 else
2977 tl_struct->path = msi_alloc(sz);
2978 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2981 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2982 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2983 if (!SUCCEEDED(res))
2985 msi_free(tl_struct->path);
2986 tl_struct->path = NULL;
2988 return TRUE;
2991 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2992 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2994 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2995 return FALSE;
2998 msi_free(tl_struct->path);
2999 tl_struct->path = NULL;
3001 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3002 ITypeLib_Release(tl_struct->ptLib);
3004 return TRUE;
3007 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3009 MSIPACKAGE* package = (MSIPACKAGE*)param;
3010 LPCWSTR component;
3011 MSICOMPONENT *comp;
3012 MSIFILE *file;
3013 typelib_struct tl_struct;
3014 ITypeLib *tlib;
3015 HMODULE module;
3016 HRESULT hr;
3018 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3020 component = MSI_RecordGetString(row,3);
3021 comp = get_loaded_component(package,component);
3022 if (!comp)
3023 return ERROR_SUCCESS;
3025 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3027 TRACE("Skipping typelib reg due to disabled component\n");
3029 comp->Action = comp->Installed;
3031 return ERROR_SUCCESS;
3034 comp->Action = INSTALLSTATE_LOCAL;
3036 file = get_loaded_file( package, comp->KeyPath );
3037 if (!file)
3038 return ERROR_SUCCESS;
3040 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3041 if (module)
3043 LPCWSTR guid;
3044 guid = MSI_RecordGetString(row,1);
3045 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3046 tl_struct.source = strdupW( file->TargetPath );
3047 tl_struct.path = NULL;
3049 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3050 (LONG_PTR)&tl_struct);
3052 if (tl_struct.path)
3054 LPWSTR help = NULL;
3055 LPCWSTR helpid;
3056 HRESULT res;
3058 helpid = MSI_RecordGetString(row,6);
3060 if (helpid)
3061 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3062 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3063 msi_free(help);
3065 if (!SUCCEEDED(res))
3066 ERR("Failed to register type library %s\n",
3067 debugstr_w(tl_struct.path));
3068 else
3070 ui_actiondata(package,szRegisterTypeLibraries,row);
3072 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3075 ITypeLib_Release(tl_struct.ptLib);
3076 msi_free(tl_struct.path);
3078 else
3079 ERR("Failed to load type library %s\n",
3080 debugstr_w(tl_struct.source));
3082 FreeLibrary(module);
3083 msi_free(tl_struct.source);
3085 else
3087 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3088 if (FAILED(hr))
3090 ERR("Failed to load type library: %08x\n", hr);
3091 return ERROR_FUNCTION_FAILED;
3094 ITypeLib_Release(tlib);
3097 return ERROR_SUCCESS;
3100 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3103 * OK this is a bit confusing.. I am given a _Component key and I believe
3104 * that the file that is being registered as a type library is the "key file
3105 * of that component" which I interpret to mean "The file in the KeyPath of
3106 * that component".
3108 UINT rc;
3109 MSIQUERY * view;
3110 static const WCHAR Query[] =
3111 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3112 '`','T','y','p','e','L','i','b','`',0};
3114 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3115 if (rc != ERROR_SUCCESS)
3116 return ERROR_SUCCESS;
3118 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3119 msiobj_release(&view->hdr);
3120 return rc;
3123 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3125 MSIPACKAGE *package = (MSIPACKAGE*)param;
3126 LPWSTR target_file, target_folder, filename;
3127 LPCWSTR buffer, extension;
3128 MSICOMPONENT *comp;
3129 static const WCHAR szlnk[]={'.','l','n','k',0};
3130 IShellLinkW *sl = NULL;
3131 IPersistFile *pf = NULL;
3132 HRESULT res;
3134 buffer = MSI_RecordGetString(row,4);
3135 comp = get_loaded_component(package,buffer);
3136 if (!comp)
3137 return ERROR_SUCCESS;
3139 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3141 TRACE("Skipping shortcut creation due to disabled component\n");
3143 comp->Action = comp->Installed;
3145 return ERROR_SUCCESS;
3148 comp->Action = INSTALLSTATE_LOCAL;
3150 ui_actiondata(package,szCreateShortcuts,row);
3152 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3153 &IID_IShellLinkW, (LPVOID *) &sl );
3155 if (FAILED( res ))
3157 ERR("CLSID_ShellLink not available\n");
3158 goto err;
3161 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3162 if (FAILED( res ))
3164 ERR("QueryInterface(IID_IPersistFile) failed\n");
3165 goto err;
3168 buffer = MSI_RecordGetString(row,2);
3169 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3171 /* may be needed because of a bug somewhere else */
3172 create_full_pathW(target_folder);
3174 filename = msi_dup_record_field( row, 3 );
3175 reduce_to_longfilename(filename);
3177 extension = strchrW(filename,'.');
3178 if (!extension || strcmpiW(extension,szlnk))
3180 int len = strlenW(filename);
3181 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3182 memcpy(filename + len, szlnk, sizeof(szlnk));
3184 target_file = build_directory_name(2, target_folder, filename);
3185 msi_free(target_folder);
3186 msi_free(filename);
3188 buffer = MSI_RecordGetString(row,5);
3189 if (strchrW(buffer,'['))
3191 LPWSTR deformated;
3192 deformat_string(package,buffer,&deformated);
3193 IShellLinkW_SetPath(sl,deformated);
3194 msi_free(deformated);
3196 else
3198 FIXME("poorly handled shortcut format, advertised shortcut\n");
3199 IShellLinkW_SetPath(sl,comp->FullKeypath);
3202 if (!MSI_RecordIsNull(row,6))
3204 LPWSTR deformated;
3205 buffer = MSI_RecordGetString(row,6);
3206 deformat_string(package,buffer,&deformated);
3207 IShellLinkW_SetArguments(sl,deformated);
3208 msi_free(deformated);
3211 if (!MSI_RecordIsNull(row,7))
3213 buffer = MSI_RecordGetString(row,7);
3214 IShellLinkW_SetDescription(sl,buffer);
3217 if (!MSI_RecordIsNull(row,8))
3218 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3220 if (!MSI_RecordIsNull(row,9))
3222 LPWSTR Path;
3223 INT index;
3225 buffer = MSI_RecordGetString(row,9);
3227 Path = build_icon_path(package,buffer);
3228 index = MSI_RecordGetInteger(row,10);
3230 /* no value means 0 */
3231 if (index == MSI_NULL_INTEGER)
3232 index = 0;
3234 IShellLinkW_SetIconLocation(sl,Path,index);
3235 msi_free(Path);
3238 if (!MSI_RecordIsNull(row,11))
3239 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3241 if (!MSI_RecordIsNull(row,12))
3243 LPWSTR Path;
3244 buffer = MSI_RecordGetString(row,12);
3245 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3246 if (Path)
3247 IShellLinkW_SetWorkingDirectory(sl,Path);
3248 msi_free(Path);
3251 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3252 IPersistFile_Save(pf,target_file,FALSE);
3254 msi_free(target_file);
3256 err:
3257 if (pf)
3258 IPersistFile_Release( pf );
3259 if (sl)
3260 IShellLinkW_Release( sl );
3262 return ERROR_SUCCESS;
3265 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3267 UINT rc;
3268 HRESULT res;
3269 MSIQUERY * view;
3270 static const WCHAR Query[] =
3271 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3272 '`','S','h','o','r','t','c','u','t','`',0};
3274 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3275 if (rc != ERROR_SUCCESS)
3276 return ERROR_SUCCESS;
3278 res = CoInitialize( NULL );
3279 if (FAILED (res))
3281 ERR("CoInitialize failed\n");
3282 return ERROR_FUNCTION_FAILED;
3285 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3286 msiobj_release(&view->hdr);
3288 CoUninitialize();
3290 return rc;
3293 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3295 MSIPACKAGE* package = (MSIPACKAGE*)param;
3296 HANDLE the_file;
3297 LPWSTR FilePath;
3298 LPCWSTR FileName;
3299 CHAR buffer[1024];
3300 DWORD sz;
3301 UINT rc;
3302 MSIRECORD *uirow;
3304 FileName = MSI_RecordGetString(row,1);
3305 if (!FileName)
3307 ERR("Unable to get FileName\n");
3308 return ERROR_SUCCESS;
3311 FilePath = build_icon_path(package,FileName);
3313 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3315 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3316 FILE_ATTRIBUTE_NORMAL, NULL);
3318 if (the_file == INVALID_HANDLE_VALUE)
3320 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3321 msi_free(FilePath);
3322 return ERROR_SUCCESS;
3327 DWORD write;
3328 sz = 1024;
3329 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3330 if (rc != ERROR_SUCCESS)
3332 ERR("Failed to get stream\n");
3333 CloseHandle(the_file);
3334 DeleteFileW(FilePath);
3335 break;
3337 WriteFile(the_file,buffer,sz,&write,NULL);
3338 } while (sz == 1024);
3340 msi_free(FilePath);
3342 CloseHandle(the_file);
3344 uirow = MSI_CreateRecord(1);
3345 MSI_RecordSetStringW(uirow,1,FileName);
3346 ui_actiondata(package,szPublishProduct,uirow);
3347 msiobj_release( &uirow->hdr );
3349 return ERROR_SUCCESS;
3352 static UINT msi_publish_icons(MSIPACKAGE *package)
3354 UINT r;
3355 MSIQUERY *view;
3357 static const WCHAR query[]= {
3358 'S','E','L','E','C','T',' ','*',' ',
3359 'F','R','O','M',' ','`','I','c','o','n','`',0};
3361 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3362 if (r == ERROR_SUCCESS)
3364 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3365 msiobj_release(&view->hdr);
3368 return ERROR_SUCCESS;
3371 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3373 UINT r;
3374 HKEY source;
3375 LPWSTR buffer;
3376 MSIMEDIADISK *disk;
3377 MSISOURCELISTINFO *info;
3379 static const WCHAR szEmpty[] = {0};
3380 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3382 r = RegCreateKeyW(hkey, szSourceList, &source);
3383 if (r != ERROR_SUCCESS)
3384 return r;
3386 RegCloseKey(source);
3388 buffer = strrchrW(package->PackagePath, '\\') + 1;
3389 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3390 package->Context, MSICODE_PRODUCT,
3391 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3392 if (r != ERROR_SUCCESS)
3393 return r;
3395 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3396 package->Context, MSICODE_PRODUCT,
3397 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3398 if (r != ERROR_SUCCESS)
3399 return r;
3401 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3402 package->Context, MSICODE_PRODUCT,
3403 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3404 if (r != ERROR_SUCCESS)
3405 return r;
3407 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3409 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3410 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3411 info->options, info->value);
3412 else
3413 MsiSourceListSetInfoW(package->ProductCode, NULL,
3414 info->context, info->options,
3415 info->property, info->value);
3418 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3420 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3421 disk->context, disk->options,
3422 disk->disk_id, disk->volume_label, disk->disk_prompt);
3425 return ERROR_SUCCESS;
3428 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3430 MSIHANDLE hdb, suminfo;
3431 WCHAR guids[MAX_PATH];
3432 WCHAR packcode[SQUISH_GUID_SIZE];
3433 LPWSTR buffer;
3434 LPWSTR ptr;
3435 DWORD langid;
3436 DWORD size;
3437 UINT r;
3439 static const WCHAR szProductLanguage[] =
3440 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3441 static const WCHAR szARPProductIcon[] =
3442 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3443 static const WCHAR szProductVersion[] =
3444 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3445 static const WCHAR szAssignment[] =
3446 {'A','s','s','i','g','n','m','e','n','t',0};
3447 static const WCHAR szAdvertiseFlags[] =
3448 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3449 static const WCHAR szClients[] =
3450 {'C','l','i','e','n','t','s',0};
3451 static const WCHAR szColon[] = {':',0};
3453 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3454 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3455 msi_free(buffer);
3457 langid = msi_get_property_int(package, szProductLanguage, 0);
3458 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3460 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3461 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3463 /* FIXME */
3464 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3466 buffer = msi_dup_property(package, szARPProductIcon);
3467 if (buffer)
3469 LPWSTR path = build_icon_path(package,buffer);
3470 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3471 msi_free(path);
3472 msi_free(buffer);
3475 buffer = msi_dup_property(package, szProductVersion);
3476 if (buffer)
3478 DWORD verdword = msi_version_str_to_dword(buffer);
3479 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3480 msi_free(buffer);
3483 msi_reg_set_val_dword(hkey, szAssignment, 0);
3484 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3485 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3486 msi_reg_set_val_str(hkey, szClients, szColon);
3488 hdb = alloc_msihandle(&package->db->hdr);
3489 if (!hdb)
3490 return ERROR_NOT_ENOUGH_MEMORY;
3492 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3493 MsiCloseHandle(hdb);
3494 if (r != ERROR_SUCCESS)
3495 goto done;
3497 size = MAX_PATH;
3498 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3499 NULL, guids, &size);
3500 if (r != ERROR_SUCCESS)
3501 goto done;
3503 ptr = strchrW(guids, ';');
3504 if (ptr) *ptr = 0;
3505 squash_guid(guids, packcode);
3506 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3508 done:
3509 MsiCloseHandle(suminfo);
3510 return ERROR_SUCCESS;
3513 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3515 UINT r;
3516 HKEY hkey;
3517 LPWSTR upgrade;
3518 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3520 static const WCHAR szUpgradeCode[] =
3521 {'U','p','g','r','a','d','e','C','o','d','e',0};
3523 upgrade = msi_dup_property(package, szUpgradeCode);
3524 if (!upgrade)
3525 return ERROR_SUCCESS;
3527 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3529 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3530 if (r != ERROR_SUCCESS)
3531 goto done;
3533 else
3535 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3536 if (r != ERROR_SUCCESS)
3537 goto done;
3540 squash_guid(package->ProductCode, squashed_pc);
3541 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3543 RegCloseKey(hkey);
3545 done:
3546 msi_free(upgrade);
3547 return r;
3550 static BOOL msi_check_publish(MSIPACKAGE *package)
3552 MSIFEATURE *feature;
3554 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3556 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3557 return TRUE;
3560 return FALSE;
3563 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3565 MSIFEATURE *feature;
3567 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3569 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3570 return FALSE;
3573 return TRUE;
3577 * 99% of the work done here is only done for
3578 * advertised installs. However this is where the
3579 * Icon table is processed and written out
3580 * so that is what I am going to do here.
3582 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3584 UINT rc;
3585 HKEY hukey=0;
3586 HKEY hudkey=0;
3588 /* FIXME: also need to publish if the product is in advertise mode */
3589 if (!msi_check_publish(package))
3590 return ERROR_SUCCESS;
3592 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3594 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3595 if (rc != ERROR_SUCCESS)
3596 goto end;
3598 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3599 if (rc != ERROR_SUCCESS)
3600 goto end;
3602 else
3604 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3605 if (rc != ERROR_SUCCESS)
3606 goto end;
3608 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3609 if (rc != ERROR_SUCCESS)
3610 goto end;
3613 rc = msi_publish_upgrade_code(package);
3614 if (rc != ERROR_SUCCESS)
3615 goto end;
3617 rc = msi_publish_product_properties(package, hukey);
3618 if (rc != ERROR_SUCCESS)
3619 goto end;
3621 rc = msi_publish_sourcelist(package, hukey);
3622 if (rc != ERROR_SUCCESS)
3623 goto end;
3625 rc = msi_publish_icons(package);
3627 end:
3628 RegCloseKey(hukey);
3629 RegCloseKey(hudkey);
3631 return rc;
3634 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3636 MSIPACKAGE *package = (MSIPACKAGE*)param;
3637 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3638 LPWSTR deformated_section, deformated_key, deformated_value;
3639 LPWSTR folder, fullname = NULL;
3640 MSIRECORD * uirow;
3641 INT action;
3642 MSICOMPONENT *comp;
3643 static const WCHAR szWindowsFolder[] =
3644 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3646 component = MSI_RecordGetString(row, 8);
3647 comp = get_loaded_component(package,component);
3649 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3651 TRACE("Skipping ini file due to disabled component %s\n",
3652 debugstr_w(component));
3654 comp->Action = comp->Installed;
3656 return ERROR_SUCCESS;
3659 comp->Action = INSTALLSTATE_LOCAL;
3661 identifier = MSI_RecordGetString(row,1);
3662 filename = MSI_RecordGetString(row,2);
3663 dirproperty = MSI_RecordGetString(row,3);
3664 section = MSI_RecordGetString(row,4);
3665 key = MSI_RecordGetString(row,5);
3666 value = MSI_RecordGetString(row,6);
3667 action = MSI_RecordGetInteger(row,7);
3669 deformat_string(package,section,&deformated_section);
3670 deformat_string(package,key,&deformated_key);
3671 deformat_string(package,value,&deformated_value);
3673 if (dirproperty)
3675 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3676 if (!folder)
3677 folder = msi_dup_property( package, dirproperty );
3679 else
3680 folder = msi_dup_property( package, szWindowsFolder );
3682 if (!folder)
3684 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3685 goto cleanup;
3688 fullname = build_directory_name(2, folder, filename);
3690 if (action == 0)
3692 TRACE("Adding value %s to section %s in %s\n",
3693 debugstr_w(deformated_key), debugstr_w(deformated_section),
3694 debugstr_w(fullname));
3695 WritePrivateProfileStringW(deformated_section, deformated_key,
3696 deformated_value, fullname);
3698 else if (action == 1)
3700 WCHAR returned[10];
3701 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3702 returned, 10, fullname);
3703 if (returned[0] == 0)
3705 TRACE("Adding value %s to section %s in %s\n",
3706 debugstr_w(deformated_key), debugstr_w(deformated_section),
3707 debugstr_w(fullname));
3709 WritePrivateProfileStringW(deformated_section, deformated_key,
3710 deformated_value, fullname);
3713 else if (action == 3)
3714 FIXME("Append to existing section not yet implemented\n");
3716 uirow = MSI_CreateRecord(4);
3717 MSI_RecordSetStringW(uirow,1,identifier);
3718 MSI_RecordSetStringW(uirow,2,deformated_section);
3719 MSI_RecordSetStringW(uirow,3,deformated_key);
3720 MSI_RecordSetStringW(uirow,4,deformated_value);
3721 ui_actiondata(package,szWriteIniValues,uirow);
3722 msiobj_release( &uirow->hdr );
3723 cleanup:
3724 msi_free(fullname);
3725 msi_free(folder);
3726 msi_free(deformated_key);
3727 msi_free(deformated_value);
3728 msi_free(deformated_section);
3729 return ERROR_SUCCESS;
3732 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3734 UINT rc;
3735 MSIQUERY * view;
3736 static const WCHAR ExecSeqQuery[] =
3737 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3738 '`','I','n','i','F','i','l','e','`',0};
3740 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3741 if (rc != ERROR_SUCCESS)
3743 TRACE("no IniFile table\n");
3744 return ERROR_SUCCESS;
3747 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3748 msiobj_release(&view->hdr);
3749 return rc;
3752 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3754 MSIPACKAGE *package = (MSIPACKAGE*)param;
3755 LPCWSTR filename;
3756 LPWSTR FullName;
3757 MSIFILE *file;
3758 DWORD len;
3759 static const WCHAR ExeStr[] =
3760 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3761 static const WCHAR close[] = {'\"',0};
3762 STARTUPINFOW si;
3763 PROCESS_INFORMATION info;
3764 BOOL brc;
3765 MSIRECORD *uirow;
3766 LPWSTR uipath, p;
3768 memset(&si,0,sizeof(STARTUPINFOW));
3770 filename = MSI_RecordGetString(row,1);
3771 file = get_loaded_file( package, filename );
3773 if (!file)
3775 ERR("Unable to find file id %s\n",debugstr_w(filename));
3776 return ERROR_SUCCESS;
3779 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3781 FullName = msi_alloc(len*sizeof(WCHAR));
3782 strcpyW(FullName,ExeStr);
3783 strcatW( FullName, file->TargetPath );
3784 strcatW(FullName,close);
3786 TRACE("Registering %s\n",debugstr_w(FullName));
3787 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3788 &si, &info);
3790 if (brc)
3792 CloseHandle(info.hThread);
3793 msi_dialog_check_messages(info.hProcess);
3794 CloseHandle(info.hProcess);
3797 msi_free(FullName);
3799 /* the UI chunk */
3800 uirow = MSI_CreateRecord( 2 );
3801 uipath = strdupW( file->TargetPath );
3802 p = strrchrW(uipath,'\\');
3803 if (p)
3804 p[0]=0;
3805 MSI_RecordSetStringW( uirow, 1, &p[1] );
3806 MSI_RecordSetStringW( uirow, 2, uipath);
3807 ui_actiondata( package, szSelfRegModules, uirow);
3808 msiobj_release( &uirow->hdr );
3809 msi_free( uipath );
3810 /* FIXME: call ui_progress? */
3812 return ERROR_SUCCESS;
3815 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3817 UINT rc;
3818 MSIQUERY * view;
3819 static const WCHAR ExecSeqQuery[] =
3820 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3821 '`','S','e','l','f','R','e','g','`',0};
3823 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3824 if (rc != ERROR_SUCCESS)
3826 TRACE("no SelfReg table\n");
3827 return ERROR_SUCCESS;
3830 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3831 msiobj_release(&view->hdr);
3833 return ERROR_SUCCESS;
3836 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3838 MSIFEATURE *feature;
3839 UINT rc;
3840 HKEY hkey;
3841 HKEY userdata = NULL;
3843 if (!msi_check_publish(package))
3844 return ERROR_SUCCESS;
3846 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3848 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3849 &hkey, TRUE);
3850 if (rc != ERROR_SUCCESS)
3851 goto end;
3853 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3854 &userdata, TRUE);
3855 if (rc != ERROR_SUCCESS)
3856 goto end;
3858 else
3860 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3861 if (rc != ERROR_SUCCESS)
3862 goto end;
3864 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3865 &userdata, TRUE);
3866 if (rc != ERROR_SUCCESS)
3867 goto end;
3870 /* here the guids are base 85 encoded */
3871 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3873 ComponentList *cl;
3874 LPWSTR data = NULL;
3875 GUID clsid;
3876 INT size;
3877 BOOL absent = FALSE;
3878 MSIRECORD *uirow;
3880 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3881 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3882 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3883 absent = TRUE;
3885 size = 1;
3886 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3888 size += 21;
3890 if (feature->Feature_Parent)
3891 size += strlenW( feature->Feature_Parent )+2;
3893 data = msi_alloc(size * sizeof(WCHAR));
3895 data[0] = 0;
3896 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3898 MSICOMPONENT* component = cl->component;
3899 WCHAR buf[21];
3901 buf[0] = 0;
3902 if (component->ComponentId)
3904 TRACE("From %s\n",debugstr_w(component->ComponentId));
3905 CLSIDFromString(component->ComponentId, &clsid);
3906 encode_base85_guid(&clsid,buf);
3907 TRACE("to %s\n",debugstr_w(buf));
3908 strcatW(data,buf);
3912 if (feature->Feature_Parent)
3914 static const WCHAR sep[] = {'\2',0};
3915 strcatW(data,sep);
3916 strcatW(data,feature->Feature_Parent);
3919 msi_reg_set_val_str( userdata, feature->Feature, data );
3920 msi_free(data);
3922 size = 0;
3923 if (feature->Feature_Parent)
3924 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3925 if (!absent)
3927 static const WCHAR emptyW[] = {0};
3928 size += sizeof(WCHAR);
3929 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3930 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3932 else
3934 size += 2*sizeof(WCHAR);
3935 data = msi_alloc(size);
3936 data[0] = 0x6;
3937 data[1] = 0;
3938 if (feature->Feature_Parent)
3939 strcpyW( &data[1], feature->Feature_Parent );
3940 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3941 (LPBYTE)data,size);
3942 msi_free(data);
3945 /* the UI chunk */
3946 uirow = MSI_CreateRecord( 1 );
3947 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3948 ui_actiondata( package, szPublishFeatures, uirow);
3949 msiobj_release( &uirow->hdr );
3950 /* FIXME: call ui_progress? */
3953 end:
3954 RegCloseKey(hkey);
3955 RegCloseKey(userdata);
3956 return rc;
3959 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3961 UINT r;
3962 HKEY hkey;
3964 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3966 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3967 if (r == ERROR_SUCCESS)
3969 RegDeleteValueW(hkey, feature->Feature);
3970 RegCloseKey(hkey);
3973 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3974 if (r == ERROR_SUCCESS)
3976 RegDeleteValueW(hkey, feature->Feature);
3977 RegCloseKey(hkey);
3980 return ERROR_SUCCESS;
3983 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3985 MSIFEATURE *feature;
3987 if (!msi_check_unpublish(package))
3988 return ERROR_SUCCESS;
3990 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3992 msi_unpublish_feature(package, feature);
3995 return ERROR_SUCCESS;
3998 static UINT msi_get_local_package_name( LPWSTR path )
4000 static const WCHAR szInstaller[] = {
4001 '\\','I','n','s','t','a','l','l','e','r','\\',0};
4002 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4003 DWORD time, len, i;
4004 HANDLE handle;
4006 time = GetTickCount();
4007 GetWindowsDirectoryW( path, MAX_PATH );
4008 lstrcatW( path, szInstaller );
4009 CreateDirectoryW( path, NULL );
4011 len = lstrlenW(path);
4012 for (i=0; i<0x10000; i++)
4014 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4015 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4016 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4017 if (handle != INVALID_HANDLE_VALUE)
4019 CloseHandle(handle);
4020 break;
4022 if (GetLastError() != ERROR_FILE_EXISTS &&
4023 GetLastError() != ERROR_SHARING_VIOLATION)
4024 return ERROR_FUNCTION_FAILED;
4027 return ERROR_SUCCESS;
4030 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4032 WCHAR packagefile[MAX_PATH];
4033 UINT r;
4035 r = msi_get_local_package_name( packagefile );
4036 if (r != ERROR_SUCCESS)
4037 return r;
4039 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4041 r = CopyFileW( package->db->path, packagefile, FALSE);
4043 if (!r)
4045 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4046 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4047 return ERROR_FUNCTION_FAILED;
4050 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4052 return ERROR_SUCCESS;
4055 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4057 LPWSTR prop, val, key;
4058 SYSTEMTIME systime;
4059 DWORD size, langid;
4060 WCHAR date[9];
4061 LPWSTR buffer;
4063 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4064 static const WCHAR szWindowsInstaller[] =
4065 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4066 static const WCHAR modpath_fmt[] =
4067 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4068 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4069 static const WCHAR szModifyPath[] =
4070 {'M','o','d','i','f','y','P','a','t','h',0};
4071 static const WCHAR szUninstallString[] =
4072 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4073 static const WCHAR szEstimatedSize[] =
4074 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4075 static const WCHAR szProductLanguage[] =
4076 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4077 static const WCHAR szProductVersion[] =
4078 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4079 static const WCHAR szProductName[] =
4080 {'P','r','o','d','u','c','t','N','a','m','e',0};
4081 static const WCHAR szDisplayName[] =
4082 {'D','i','s','p','l','a','y','N','a','m','e',0};
4083 static const WCHAR szDisplayVersion[] =
4084 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4085 static const WCHAR szManufacturer[] =
4086 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4088 static const LPCSTR propval[] = {
4089 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4090 "ARPCONTACT", "Contact",
4091 "ARPCOMMENTS", "Comments",
4092 "ProductName", "DisplayName",
4093 "ProductVersion", "DisplayVersion",
4094 "ARPHELPLINK", "HelpLink",
4095 "ARPHELPTELEPHONE", "HelpTelephone",
4096 "ARPINSTALLLOCATION", "InstallLocation",
4097 "SourceDir", "InstallSource",
4098 "Manufacturer", "Publisher",
4099 "ARPREADME", "Readme",
4100 "ARPSIZE", "Size",
4101 "ARPURLINFOABOUT", "URLInfoAbout",
4102 "ARPURLUPDATEINFO", "URLUpdateInfo",
4103 NULL,
4105 const LPCSTR *p = propval;
4107 while (*p)
4109 prop = strdupAtoW(*p++);
4110 key = strdupAtoW(*p++);
4111 val = msi_dup_property(package, prop);
4112 msi_reg_set_val_str(hkey, key, val);
4113 msi_free(val);
4114 msi_free(key);
4115 msi_free(prop);
4118 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4120 size = deformat_string(package, modpath_fmt, &buffer);
4121 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4122 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4123 msi_free(buffer);
4125 /* FIXME: Write real Estimated Size when we have it */
4126 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4128 buffer = msi_dup_property(package, szProductName);
4129 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4130 msi_free(buffer);
4132 buffer = msi_dup_property(package, cszSourceDir);
4133 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4134 msi_free(buffer);
4136 buffer = msi_dup_property(package, szManufacturer);
4137 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4138 msi_free(buffer);
4140 GetLocalTime(&systime);
4141 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4142 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4144 langid = msi_get_property_int(package, szProductLanguage, 0);
4145 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4147 buffer = msi_dup_property(package, szProductVersion);
4148 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4149 if (buffer)
4151 DWORD verdword = msi_version_str_to_dword(buffer);
4153 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4154 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4155 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4156 msi_free(buffer);
4159 return ERROR_SUCCESS;
4162 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4164 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4165 LPWSTR upgrade_code;
4166 HKEY hkey, props;
4167 HKEY upgrade;
4168 UINT rc;
4170 static const WCHAR szUpgradeCode[] = {
4171 'U','p','g','r','a','d','e','C','o','d','e',0};
4173 /* FIXME: also need to publish if the product is in advertise mode */
4174 if (!msi_check_publish(package))
4175 return ERROR_SUCCESS;
4177 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4178 if (rc != ERROR_SUCCESS)
4179 return rc;
4181 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4183 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4184 if (rc != ERROR_SUCCESS)
4185 goto done;
4187 else
4189 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4190 if (rc != ERROR_SUCCESS)
4191 goto done;
4194 msi_make_package_local(package, props);
4196 rc = msi_publish_install_properties(package, hkey);
4197 if (rc != ERROR_SUCCESS)
4198 goto done;
4200 rc = msi_publish_install_properties(package, props);
4201 if (rc != ERROR_SUCCESS)
4202 goto done;
4204 upgrade_code = msi_dup_property(package, szUpgradeCode);
4205 if (upgrade_code)
4207 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4208 squash_guid(package->ProductCode, squashed_pc);
4209 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4210 RegCloseKey(upgrade);
4211 msi_free(upgrade_code);
4214 done:
4215 RegCloseKey(hkey);
4217 return ERROR_SUCCESS;
4220 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4222 return execute_script(package,INSTALL_SCRIPT);
4225 static UINT msi_unpublish_product(MSIPACKAGE *package)
4227 LPWSTR upgrade;
4228 LPWSTR remove = NULL;
4229 LPWSTR *features = NULL;
4230 BOOL full_uninstall = TRUE;
4231 MSIFEATURE *feature;
4233 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4234 static const WCHAR szAll[] = {'A','L','L',0};
4235 static const WCHAR szUpgradeCode[] =
4236 {'U','p','g','r','a','d','e','C','o','d','e',0};
4238 remove = msi_dup_property(package, szRemove);
4239 if (!remove)
4240 return ERROR_SUCCESS;
4242 features = msi_split_string(remove, ',');
4243 if (!features)
4245 msi_free(remove);
4246 ERR("REMOVE feature list is empty!\n");
4247 return ERROR_FUNCTION_FAILED;
4250 if (!lstrcmpW(features[0], szAll))
4251 full_uninstall = TRUE;
4252 else
4254 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4256 if (feature->Action != INSTALLSTATE_ABSENT)
4257 full_uninstall = FALSE;
4261 if (!full_uninstall)
4262 goto done;
4264 MSIREG_DeleteProductKey(package->ProductCode);
4265 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4266 MSIREG_DeleteUninstallKey(package->ProductCode);
4268 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4270 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4271 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4273 else
4275 MSIREG_DeleteUserProductKey(package->ProductCode);
4276 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4279 upgrade = msi_dup_property(package, szUpgradeCode);
4280 if (upgrade)
4282 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4283 msi_free(upgrade);
4286 done:
4287 msi_free(remove);
4288 msi_free(features);
4289 return ERROR_SUCCESS;
4292 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4294 UINT rc;
4296 rc = msi_unpublish_product(package);
4297 if (rc != ERROR_SUCCESS)
4298 return rc;
4300 /* turn off scheduling */
4301 package->script->CurrentlyScripting= FALSE;
4303 /* first do the same as an InstallExecute */
4304 rc = ACTION_InstallExecute(package);
4305 if (rc != ERROR_SUCCESS)
4306 return rc;
4308 /* then handle Commit Actions */
4309 rc = execute_script(package,COMMIT_SCRIPT);
4311 return rc;
4314 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4316 static const WCHAR RunOnce[] = {
4317 'S','o','f','t','w','a','r','e','\\',
4318 'M','i','c','r','o','s','o','f','t','\\',
4319 'W','i','n','d','o','w','s','\\',
4320 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4321 'R','u','n','O','n','c','e',0};
4322 static const WCHAR InstallRunOnce[] = {
4323 'S','o','f','t','w','a','r','e','\\',
4324 'M','i','c','r','o','s','o','f','t','\\',
4325 'W','i','n','d','o','w','s','\\',
4326 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4327 'I','n','s','t','a','l','l','e','r','\\',
4328 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4330 static const WCHAR msiexec_fmt[] = {
4331 '%','s',
4332 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4333 '\"','%','s','\"',0};
4334 static const WCHAR install_fmt[] = {
4335 '/','I',' ','\"','%','s','\"',' ',
4336 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4337 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4338 WCHAR buffer[256], sysdir[MAX_PATH];
4339 HKEY hkey;
4340 WCHAR squished_pc[100];
4342 squash_guid(package->ProductCode,squished_pc);
4344 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4345 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4346 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4347 squished_pc);
4349 msi_reg_set_val_str( hkey, squished_pc, buffer );
4350 RegCloseKey(hkey);
4352 TRACE("Reboot command %s\n",debugstr_w(buffer));
4354 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4355 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4357 msi_reg_set_val_str( hkey, squished_pc, buffer );
4358 RegCloseKey(hkey);
4360 return ERROR_INSTALL_SUSPEND;
4363 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4365 DWORD attrib;
4366 UINT rc;
4369 * We are currently doing what should be done here in the top level Install
4370 * however for Administrative and uninstalls this step will be needed
4372 if (!package->PackagePath)
4373 return ERROR_SUCCESS;
4375 msi_set_sourcedir_props(package, TRUE);
4377 attrib = GetFileAttributesW(package->db->path);
4378 if (attrib == INVALID_FILE_ATTRIBUTES)
4380 LPWSTR prompt;
4381 LPWSTR msg;
4382 DWORD size = 0;
4384 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4385 package->Context, MSICODE_PRODUCT,
4386 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4387 if (rc == ERROR_MORE_DATA)
4389 prompt = msi_alloc(size * sizeof(WCHAR));
4390 MsiSourceListGetInfoW(package->ProductCode, NULL,
4391 package->Context, MSICODE_PRODUCT,
4392 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4394 else
4395 prompt = strdupW(package->db->path);
4397 msg = generate_error_string(package,1302,1,prompt);
4398 while(attrib == INVALID_FILE_ATTRIBUTES)
4400 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4401 if (rc == IDCANCEL)
4403 rc = ERROR_INSTALL_USEREXIT;
4404 break;
4406 attrib = GetFileAttributesW(package->db->path);
4408 msi_free(prompt);
4409 rc = ERROR_SUCCESS;
4411 else
4412 return ERROR_SUCCESS;
4414 return rc;
4417 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4419 HKEY hkey=0;
4420 LPWSTR buffer;
4421 LPWSTR productid;
4422 UINT rc,i;
4424 static const WCHAR szPropKeys[][80] =
4426 {'P','r','o','d','u','c','t','I','D',0},
4427 {'U','S','E','R','N','A','M','E',0},
4428 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4429 {0},
4432 static const WCHAR szRegKeys[][80] =
4434 {'P','r','o','d','u','c','t','I','D',0},
4435 {'R','e','g','O','w','n','e','r',0},
4436 {'R','e','g','C','o','m','p','a','n','y',0},
4437 {0},
4440 if (msi_check_unpublish(package))
4442 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4443 return ERROR_SUCCESS;
4446 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4447 if (!productid)
4448 return ERROR_SUCCESS;
4450 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4451 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4452 else
4453 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4455 if (rc != ERROR_SUCCESS)
4456 goto end;
4458 for( i = 0; szPropKeys[i][0]; i++ )
4460 buffer = msi_dup_property( package, szPropKeys[i] );
4461 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4462 msi_free( buffer );
4465 end:
4466 msi_free(productid);
4467 RegCloseKey(hkey);
4469 /* FIXME: call ui_actiondata */
4471 return rc;
4475 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4477 UINT rc;
4479 package->script->InWhatSequence |= SEQUENCE_EXEC;
4480 rc = ACTION_ProcessExecSequence(package,FALSE);
4481 return rc;
4485 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4487 MSIPACKAGE *package = (MSIPACKAGE*)param;
4488 LPCWSTR compgroupid=NULL;
4489 LPCWSTR feature=NULL;
4490 LPCWSTR text = NULL;
4491 LPCWSTR qualifier = NULL;
4492 LPCWSTR component = NULL;
4493 LPWSTR advertise = NULL;
4494 LPWSTR output = NULL;
4495 HKEY hkey;
4496 UINT rc = ERROR_SUCCESS;
4497 MSICOMPONENT *comp;
4498 DWORD sz = 0;
4499 MSIRECORD *uirow;
4501 component = MSI_RecordGetString(rec,3);
4502 comp = get_loaded_component(package,component);
4504 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4505 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4506 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4508 TRACE("Skipping: Component %s not scheduled for install\n",
4509 debugstr_w(component));
4511 return ERROR_SUCCESS;
4514 compgroupid = MSI_RecordGetString(rec,1);
4515 qualifier = MSI_RecordGetString(rec,2);
4517 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4518 if (rc != ERROR_SUCCESS)
4519 goto end;
4521 text = MSI_RecordGetString(rec,4);
4522 feature = MSI_RecordGetString(rec,5);
4524 advertise = create_component_advertise_string(package, comp, feature);
4526 sz = strlenW(advertise);
4528 if (text)
4529 sz += lstrlenW(text);
4531 sz+=3;
4532 sz *= sizeof(WCHAR);
4534 output = msi_alloc_zero(sz);
4535 strcpyW(output,advertise);
4536 msi_free(advertise);
4538 if (text)
4539 strcatW(output,text);
4541 msi_reg_set_val_multi_str( hkey, qualifier, output );
4543 end:
4544 RegCloseKey(hkey);
4545 msi_free(output);
4547 /* the UI chunk */
4548 uirow = MSI_CreateRecord( 2 );
4549 MSI_RecordSetStringW( uirow, 1, compgroupid );
4550 MSI_RecordSetStringW( uirow, 2, qualifier);
4551 ui_actiondata( package, szPublishComponents, uirow);
4552 msiobj_release( &uirow->hdr );
4553 /* FIXME: call ui_progress? */
4555 return rc;
4559 * At present I am ignorning the advertised components part of this and only
4560 * focusing on the qualified component sets
4562 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4564 UINT rc;
4565 MSIQUERY * view;
4566 static const WCHAR ExecSeqQuery[] =
4567 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4568 '`','P','u','b','l','i','s','h',
4569 'C','o','m','p','o','n','e','n','t','`',0};
4571 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4572 if (rc != ERROR_SUCCESS)
4573 return ERROR_SUCCESS;
4575 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4576 msiobj_release(&view->hdr);
4578 return rc;
4581 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4583 MSIPACKAGE *package = (MSIPACKAGE*)param;
4584 MSIRECORD *row;
4585 MSIFILE *file;
4586 SC_HANDLE hscm, service = NULL;
4587 LPCWSTR comp, depends, pass;
4588 LPWSTR name = NULL, disp = NULL;
4589 LPCWSTR load_order, serv_name, key;
4590 DWORD serv_type, start_type;
4591 DWORD err_control;
4593 static const WCHAR query[] =
4594 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4595 '`','C','o','m','p','o','n','e','n','t','`',' ',
4596 'W','H','E','R','E',' ',
4597 '`','C','o','m','p','o','n','e','n','t','`',' ',
4598 '=','\'','%','s','\'',0};
4600 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4601 if (!hscm)
4603 ERR("Failed to open the SC Manager!\n");
4604 goto done;
4607 start_type = MSI_RecordGetInteger(rec, 5);
4608 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4609 goto done;
4611 depends = MSI_RecordGetString(rec, 8);
4612 if (depends && *depends)
4613 FIXME("Dependency list unhandled!\n");
4615 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4616 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4617 serv_type = MSI_RecordGetInteger(rec, 4);
4618 err_control = MSI_RecordGetInteger(rec, 6);
4619 load_order = MSI_RecordGetString(rec, 7);
4620 serv_name = MSI_RecordGetString(rec, 9);
4621 pass = MSI_RecordGetString(rec, 10);
4622 comp = MSI_RecordGetString(rec, 12);
4624 /* fetch the service path */
4625 row = MSI_QueryGetRecord(package->db, query, comp);
4626 if (!row)
4628 ERR("Control query failed!\n");
4629 goto done;
4632 key = MSI_RecordGetString(row, 6);
4634 file = get_loaded_file(package, key);
4635 msiobj_release(&row->hdr);
4636 if (!file)
4638 ERR("Failed to load the service file\n");
4639 goto done;
4642 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4643 start_type, err_control, file->TargetPath,
4644 load_order, NULL, NULL, serv_name, pass);
4645 if (!service)
4647 if (GetLastError() != ERROR_SERVICE_EXISTS)
4648 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4651 done:
4652 CloseServiceHandle(service);
4653 CloseServiceHandle(hscm);
4654 msi_free(name);
4655 msi_free(disp);
4657 return ERROR_SUCCESS;
4660 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4662 UINT rc;
4663 MSIQUERY * view;
4664 static const WCHAR ExecSeqQuery[] =
4665 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4666 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4668 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4669 if (rc != ERROR_SUCCESS)
4670 return ERROR_SUCCESS;
4672 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4673 msiobj_release(&view->hdr);
4675 return rc;
4678 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4679 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4681 LPCWSTR *vector, *temp_vector;
4682 LPWSTR p, q;
4683 DWORD sep_len;
4685 static const WCHAR separator[] = {'[','~',']',0};
4687 *numargs = 0;
4688 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4690 if (!args)
4691 return NULL;
4693 vector = msi_alloc(sizeof(LPWSTR));
4694 if (!vector)
4695 return NULL;
4697 p = args;
4700 (*numargs)++;
4701 vector[*numargs - 1] = p;
4703 if ((q = strstrW(p, separator)))
4705 *q = '\0';
4707 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4708 if (!temp_vector)
4710 msi_free(vector);
4711 return NULL;
4713 vector = temp_vector;
4715 p = q + sep_len;
4717 } while (q);
4719 return vector;
4722 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4724 MSIPACKAGE *package = (MSIPACKAGE *)param;
4725 MSICOMPONENT *comp;
4726 SC_HANDLE scm, service = NULL;
4727 LPCWSTR name, *vector = NULL;
4728 LPWSTR args;
4729 DWORD event, numargs;
4730 UINT r = ERROR_FUNCTION_FAILED;
4732 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4733 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4734 return ERROR_SUCCESS;
4736 name = MSI_RecordGetString(rec, 2);
4737 event = MSI_RecordGetInteger(rec, 3);
4738 args = strdupW(MSI_RecordGetString(rec, 4));
4740 if (!(event & msidbServiceControlEventStart))
4741 return ERROR_SUCCESS;
4743 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4744 if (!scm)
4746 ERR("Failed to open the service control manager\n");
4747 goto done;
4750 service = OpenServiceW(scm, name, SERVICE_START);
4751 if (!service)
4753 ERR("Failed to open service %s\n", debugstr_w(name));
4754 goto done;
4757 vector = msi_service_args_to_vector(args, &numargs);
4759 if (!StartServiceW(service, numargs, vector))
4761 ERR("Failed to start service %s\n", debugstr_w(name));
4762 goto done;
4765 r = ERROR_SUCCESS;
4767 done:
4768 CloseServiceHandle(service);
4769 CloseServiceHandle(scm);
4771 msi_free(args);
4772 msi_free(vector);
4773 return r;
4776 static UINT ACTION_StartServices( MSIPACKAGE *package )
4778 UINT rc;
4779 MSIQUERY *view;
4781 static const WCHAR query[] = {
4782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4783 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4785 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4786 if (rc != ERROR_SUCCESS)
4787 return ERROR_SUCCESS;
4789 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4790 msiobj_release(&view->hdr);
4792 return rc;
4795 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4797 DWORD i, needed, count;
4798 ENUM_SERVICE_STATUSW *dependencies;
4799 SERVICE_STATUS ss;
4800 SC_HANDLE depserv;
4802 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4803 0, &needed, &count))
4804 return TRUE;
4806 if (GetLastError() != ERROR_MORE_DATA)
4807 return FALSE;
4809 dependencies = msi_alloc(needed);
4810 if (!dependencies)
4811 return FALSE;
4813 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4814 needed, &needed, &count))
4815 goto error;
4817 for (i = 0; i < count; i++)
4819 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4820 SERVICE_STOP | SERVICE_QUERY_STATUS);
4821 if (!depserv)
4822 goto error;
4824 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4825 goto error;
4828 return TRUE;
4830 error:
4831 msi_free(dependencies);
4832 return FALSE;
4835 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4837 MSIPACKAGE *package = (MSIPACKAGE *)param;
4838 MSICOMPONENT *comp;
4839 SERVICE_STATUS status;
4840 SERVICE_STATUS_PROCESS ssp;
4841 SC_HANDLE scm = NULL, service = NULL;
4842 LPWSTR name, args;
4843 DWORD event, needed;
4845 event = MSI_RecordGetInteger(rec, 3);
4846 if (!(event & msidbServiceControlEventStop))
4847 return ERROR_SUCCESS;
4849 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4850 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4851 return ERROR_SUCCESS;
4853 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4854 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4855 args = strdupW(MSI_RecordGetString(rec, 4));
4857 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4858 if (!scm)
4860 WARN("Failed to open the SCM: %d\n", GetLastError());
4861 goto done;
4864 service = OpenServiceW(scm, name,
4865 SERVICE_STOP |
4866 SERVICE_QUERY_STATUS |
4867 SERVICE_ENUMERATE_DEPENDENTS);
4868 if (!service)
4870 WARN("Failed to open service (%s): %d\n",
4871 debugstr_w(name), GetLastError());
4872 goto done;
4875 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4876 sizeof(SERVICE_STATUS_PROCESS), &needed))
4878 WARN("Failed to query service status (%s): %d\n",
4879 debugstr_w(name), GetLastError());
4880 goto done;
4883 if (ssp.dwCurrentState == SERVICE_STOPPED)
4884 goto done;
4886 stop_service_dependents(scm, service);
4888 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4889 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4891 done:
4892 CloseServiceHandle(service);
4893 CloseServiceHandle(scm);
4894 msi_free(name);
4895 msi_free(args);
4897 return ERROR_SUCCESS;
4900 static UINT ACTION_StopServices( MSIPACKAGE *package )
4902 UINT rc;
4903 MSIQUERY *view;
4905 static const WCHAR query[] = {
4906 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4907 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4909 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4910 if (rc != ERROR_SUCCESS)
4911 return ERROR_SUCCESS;
4913 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4914 msiobj_release(&view->hdr);
4916 return rc;
4919 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4921 MSIFILE *file;
4923 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4925 if (!lstrcmpW(file->File, filename))
4926 return file;
4929 return NULL;
4932 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4934 MSIPACKAGE *package = (MSIPACKAGE*)param;
4935 LPWSTR driver, driver_path, ptr;
4936 WCHAR outpath[MAX_PATH];
4937 MSIFILE *driver_file, *setup_file;
4938 LPCWSTR desc;
4939 DWORD len, usage;
4940 UINT r = ERROR_SUCCESS;
4942 static const WCHAR driver_fmt[] = {
4943 'D','r','i','v','e','r','=','%','s',0};
4944 static const WCHAR setup_fmt[] = {
4945 'S','e','t','u','p','=','%','s',0};
4946 static const WCHAR usage_fmt[] = {
4947 'F','i','l','e','U','s','a','g','e','=','1',0};
4949 desc = MSI_RecordGetString(rec, 3);
4951 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4952 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4954 if (!driver_file || !setup_file)
4956 ERR("ODBC Driver entry not found!\n");
4957 return ERROR_FUNCTION_FAILED;
4960 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4961 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4962 lstrlenW(usage_fmt) + 1;
4963 driver = msi_alloc(len * sizeof(WCHAR));
4964 if (!driver)
4965 return ERROR_OUTOFMEMORY;
4967 ptr = driver;
4968 lstrcpyW(ptr, desc);
4969 ptr += lstrlenW(ptr) + 1;
4971 sprintfW(ptr, driver_fmt, driver_file->FileName);
4972 ptr += lstrlenW(ptr) + 1;
4974 sprintfW(ptr, setup_fmt, setup_file->FileName);
4975 ptr += lstrlenW(ptr) + 1;
4977 lstrcpyW(ptr, usage_fmt);
4978 ptr += lstrlenW(ptr) + 1;
4979 *ptr = '\0';
4981 driver_path = strdupW(driver_file->TargetPath);
4982 ptr = strrchrW(driver_path, '\\');
4983 if (ptr) *ptr = '\0';
4985 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4986 NULL, ODBC_INSTALL_COMPLETE, &usage))
4988 ERR("Failed to install SQL driver!\n");
4989 r = ERROR_FUNCTION_FAILED;
4992 msi_free(driver);
4993 msi_free(driver_path);
4995 return r;
4998 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5000 MSIPACKAGE *package = (MSIPACKAGE*)param;
5001 LPWSTR translator, translator_path, ptr;
5002 WCHAR outpath[MAX_PATH];
5003 MSIFILE *translator_file, *setup_file;
5004 LPCWSTR desc;
5005 DWORD len, usage;
5006 UINT r = ERROR_SUCCESS;
5008 static const WCHAR translator_fmt[] = {
5009 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5010 static const WCHAR setup_fmt[] = {
5011 'S','e','t','u','p','=','%','s',0};
5013 desc = MSI_RecordGetString(rec, 3);
5015 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5016 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5018 if (!translator_file || !setup_file)
5020 ERR("ODBC Translator entry not found!\n");
5021 return ERROR_FUNCTION_FAILED;
5024 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5025 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5026 translator = msi_alloc(len * sizeof(WCHAR));
5027 if (!translator)
5028 return ERROR_OUTOFMEMORY;
5030 ptr = translator;
5031 lstrcpyW(ptr, desc);
5032 ptr += lstrlenW(ptr) + 1;
5034 sprintfW(ptr, translator_fmt, translator_file->FileName);
5035 ptr += lstrlenW(ptr) + 1;
5037 sprintfW(ptr, setup_fmt, setup_file->FileName);
5038 ptr += lstrlenW(ptr) + 1;
5039 *ptr = '\0';
5041 translator_path = strdupW(translator_file->TargetPath);
5042 ptr = strrchrW(translator_path, '\\');
5043 if (ptr) *ptr = '\0';
5045 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5046 NULL, ODBC_INSTALL_COMPLETE, &usage))
5048 ERR("Failed to install SQL translator!\n");
5049 r = ERROR_FUNCTION_FAILED;
5052 msi_free(translator);
5053 msi_free(translator_path);
5055 return r;
5058 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5060 LPWSTR attrs;
5061 LPCWSTR desc, driver;
5062 WORD request = ODBC_ADD_SYS_DSN;
5063 INT registration;
5064 DWORD len;
5065 UINT r = ERROR_SUCCESS;
5067 static const WCHAR attrs_fmt[] = {
5068 'D','S','N','=','%','s',0 };
5070 desc = MSI_RecordGetString(rec, 3);
5071 driver = MSI_RecordGetString(rec, 4);
5072 registration = MSI_RecordGetInteger(rec, 5);
5074 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5075 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5077 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5078 attrs = msi_alloc(len * sizeof(WCHAR));
5079 if (!attrs)
5080 return ERROR_OUTOFMEMORY;
5082 sprintfW(attrs, attrs_fmt, desc);
5083 attrs[len - 1] = '\0';
5085 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5087 ERR("Failed to install SQL data source!\n");
5088 r = ERROR_FUNCTION_FAILED;
5091 msi_free(attrs);
5093 return r;
5096 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5098 UINT rc;
5099 MSIQUERY *view;
5101 static const WCHAR driver_query[] = {
5102 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5103 'O','D','B','C','D','r','i','v','e','r',0 };
5105 static const WCHAR translator_query[] = {
5106 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5107 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5109 static const WCHAR source_query[] = {
5110 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5111 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5113 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5114 if (rc != ERROR_SUCCESS)
5115 return ERROR_SUCCESS;
5117 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5118 msiobj_release(&view->hdr);
5120 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5121 if (rc != ERROR_SUCCESS)
5122 return ERROR_SUCCESS;
5124 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5125 msiobj_release(&view->hdr);
5127 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5128 if (rc != ERROR_SUCCESS)
5129 return ERROR_SUCCESS;
5131 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5132 msiobj_release(&view->hdr);
5134 return rc;
5137 #define ENV_ACT_SETALWAYS 0x1
5138 #define ENV_ACT_SETABSENT 0x2
5139 #define ENV_ACT_REMOVE 0x4
5140 #define ENV_ACT_REMOVEMATCH 0x8
5142 #define ENV_MOD_MACHINE 0x20000000
5143 #define ENV_MOD_APPEND 0x40000000
5144 #define ENV_MOD_PREFIX 0x80000000
5145 #define ENV_MOD_MASK 0xC0000000
5147 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5149 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5151 LPCWSTR cptr = *name;
5152 LPCWSTR ptr = *value;
5154 static const WCHAR prefix[] = {'[','~',']',0};
5155 static const int prefix_len = 3;
5157 *flags = 0;
5158 while (*cptr)
5160 if (*cptr == '=')
5161 *flags |= ENV_ACT_SETALWAYS;
5162 else if (*cptr == '+')
5163 *flags |= ENV_ACT_SETABSENT;
5164 else if (*cptr == '-')
5165 *flags |= ENV_ACT_REMOVE;
5166 else if (*cptr == '!')
5167 *flags |= ENV_ACT_REMOVEMATCH;
5168 else if (*cptr == '*')
5169 *flags |= ENV_MOD_MACHINE;
5170 else
5171 break;
5173 cptr++;
5174 (*name)++;
5177 if (!*cptr)
5179 ERR("Missing environment variable\n");
5180 return ERROR_FUNCTION_FAILED;
5183 if (!strncmpW(ptr, prefix, prefix_len))
5185 *flags |= ENV_MOD_APPEND;
5186 *value += lstrlenW(prefix);
5188 else if (lstrlenW(*value) >= prefix_len)
5190 ptr += lstrlenW(ptr) - prefix_len;
5191 if (!lstrcmpW(ptr, prefix))
5193 *flags |= ENV_MOD_PREFIX;
5194 /* the "[~]" will be removed by deformat_string */;
5198 if (!*flags ||
5199 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5200 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5201 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5202 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5204 ERR("Invalid flags: %08x\n", *flags);
5205 return ERROR_FUNCTION_FAILED;
5208 return ERROR_SUCCESS;
5211 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5213 MSIPACKAGE *package = param;
5214 LPCWSTR name, value;
5215 LPWSTR data = NULL, newval = NULL;
5216 LPWSTR deformatted = NULL, ptr;
5217 DWORD flags, type, size;
5218 LONG res;
5219 HKEY env = NULL, root;
5220 LPCWSTR environment;
5222 static const WCHAR user_env[] =
5223 {'E','n','v','i','r','o','n','m','e','n','t',0};
5224 static const WCHAR machine_env[] =
5225 {'S','y','s','t','e','m','\\',
5226 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5227 'C','o','n','t','r','o','l','\\',
5228 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5229 'E','n','v','i','r','o','n','m','e','n','t',0};
5230 static const WCHAR semicolon[] = {';',0};
5232 name = MSI_RecordGetString(rec, 2);
5233 value = MSI_RecordGetString(rec, 3);
5235 res = env_set_flags(&name, &value, &flags);
5236 if (res != ERROR_SUCCESS)
5237 goto done;
5239 deformat_string(package, value, &deformatted);
5240 if (!deformatted)
5242 res = ERROR_OUTOFMEMORY;
5243 goto done;
5246 value = deformatted;
5248 if (flags & ENV_MOD_MACHINE)
5250 environment = machine_env;
5251 root = HKEY_LOCAL_MACHINE;
5253 else
5255 environment = user_env;
5256 root = HKEY_CURRENT_USER;
5259 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5260 KEY_ALL_ACCESS, NULL, &env, NULL);
5261 if (res != ERROR_SUCCESS)
5262 goto done;
5264 if (flags & ENV_ACT_REMOVE)
5265 FIXME("Not removing environment variable on uninstall!\n");
5267 size = 0;
5268 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5269 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5270 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5271 goto done;
5273 if (res != ERROR_FILE_NOT_FOUND)
5275 if (flags & ENV_ACT_SETABSENT)
5277 res = ERROR_SUCCESS;
5278 goto done;
5281 data = msi_alloc(size);
5282 if (!data)
5284 RegCloseKey(env);
5285 return ERROR_OUTOFMEMORY;
5288 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5289 if (res != ERROR_SUCCESS)
5290 goto done;
5292 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5294 res = RegDeleteKeyW(env, name);
5295 goto done;
5298 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5299 newval = msi_alloc(size);
5300 ptr = newval;
5301 if (!newval)
5303 res = ERROR_OUTOFMEMORY;
5304 goto done;
5307 if (!(flags & ENV_MOD_MASK))
5308 lstrcpyW(newval, value);
5309 else
5311 if (flags & ENV_MOD_PREFIX)
5313 lstrcpyW(newval, value);
5314 lstrcatW(newval, semicolon);
5315 ptr = newval + lstrlenW(value) + 1;
5318 lstrcpyW(ptr, data);
5320 if (flags & ENV_MOD_APPEND)
5322 lstrcatW(newval, semicolon);
5323 lstrcatW(newval, value);
5327 else
5329 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5330 newval = msi_alloc(size);
5331 if (!newval)
5333 res = ERROR_OUTOFMEMORY;
5334 goto done;
5337 lstrcpyW(newval, value);
5340 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5341 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5343 done:
5344 if (env) RegCloseKey(env);
5345 msi_free(deformatted);
5346 msi_free(data);
5347 msi_free(newval);
5348 return res;
5351 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5353 UINT rc;
5354 MSIQUERY * view;
5355 static const WCHAR ExecSeqQuery[] =
5356 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5357 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5358 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5359 if (rc != ERROR_SUCCESS)
5360 return ERROR_SUCCESS;
5362 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5363 msiobj_release(&view->hdr);
5365 return rc;
5368 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5370 typedef struct
5372 struct list entry;
5373 LPWSTR sourcename;
5374 LPWSTR destname;
5375 LPWSTR source;
5376 LPWSTR dest;
5377 } FILE_LIST;
5379 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5381 BOOL ret;
5383 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5384 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5386 WARN("Source or dest is directory, not moving\n");
5387 return FALSE;
5390 if (options == msidbMoveFileOptionsMove)
5392 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5393 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5394 if (!ret)
5396 WARN("MoveFile failed: %d\n", GetLastError());
5397 return FALSE;
5400 else
5402 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5403 ret = CopyFileW(source, dest, FALSE);
5404 if (!ret)
5406 WARN("CopyFile failed: %d\n", GetLastError());
5407 return FALSE;
5411 return TRUE;
5414 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5416 LPWSTR path, ptr;
5417 DWORD dirlen, pathlen;
5419 ptr = strrchrW(wildcard, '\\');
5420 dirlen = ptr - wildcard + 1;
5422 pathlen = dirlen + lstrlenW(filename) + 1;
5423 path = msi_alloc(pathlen * sizeof(WCHAR));
5425 lstrcpynW(path, wildcard, dirlen + 1);
5426 lstrcatW(path, filename);
5428 return path;
5431 static void free_file_entry(FILE_LIST *file)
5433 msi_free(file->source);
5434 msi_free(file->dest);
5435 msi_free(file);
5438 static void free_list(FILE_LIST *list)
5440 while (!list_empty(&list->entry))
5442 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5444 list_remove(&file->entry);
5445 free_file_entry(file);
5449 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5451 FILE_LIST *new, *file;
5452 LPWSTR ptr, filename;
5453 DWORD size;
5455 new = msi_alloc_zero(sizeof(FILE_LIST));
5456 if (!new)
5457 return FALSE;
5459 new->source = strdupW(source);
5460 ptr = strrchrW(dest, '\\') + 1;
5461 filename = strrchrW(new->source, '\\') + 1;
5463 new->sourcename = filename;
5465 if (*ptr)
5466 new->destname = ptr;
5467 else
5468 new->destname = new->sourcename;
5470 size = (ptr - dest) + lstrlenW(filename) + 1;
5471 new->dest = msi_alloc(size * sizeof(WCHAR));
5472 if (!new->dest)
5474 free_file_entry(new);
5475 return FALSE;
5478 lstrcpynW(new->dest, dest, ptr - dest + 1);
5479 lstrcatW(new->dest, filename);
5481 if (list_empty(&files->entry))
5483 list_add_head(&files->entry, &new->entry);
5484 return TRUE;
5487 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5489 if (lstrcmpW(source, file->source) < 0)
5491 list_add_before(&file->entry, &new->entry);
5492 return TRUE;
5496 list_add_after(&file->entry, &new->entry);
5497 return TRUE;
5500 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5502 WIN32_FIND_DATAW wfd;
5503 HANDLE hfile;
5504 LPWSTR path;
5505 BOOL res;
5506 FILE_LIST files, *file;
5507 DWORD size;
5509 hfile = FindFirstFileW(source, &wfd);
5510 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5512 list_init(&files.entry);
5514 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5516 if (is_dot_dir(wfd.cFileName)) continue;
5518 path = wildcard_to_file(source, wfd.cFileName);
5519 if (!path)
5521 res = FALSE;
5522 goto done;
5525 add_wildcard(&files, path, dest);
5526 msi_free(path);
5529 /* no files match the wildcard */
5530 if (list_empty(&files.entry))
5531 goto done;
5533 /* only the first wildcard match gets renamed to dest */
5534 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5535 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5536 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5537 if (!file->dest)
5539 res = FALSE;
5540 goto done;
5543 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5545 while (!list_empty(&files.entry))
5547 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5549 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5551 list_remove(&file->entry);
5552 free_file_entry(file);
5555 res = TRUE;
5557 done:
5558 free_list(&files);
5559 FindClose(hfile);
5560 return res;
5563 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5565 MSIPACKAGE *package = param;
5566 MSICOMPONENT *comp;
5567 LPCWSTR sourcename;
5568 LPWSTR destname = NULL;
5569 LPWSTR sourcedir = NULL, destdir = NULL;
5570 LPWSTR source = NULL, dest = NULL;
5571 int options;
5572 DWORD size;
5573 BOOL ret, wildcards;
5575 static const WCHAR backslash[] = {'\\',0};
5577 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5578 if (!comp || !comp->Enabled ||
5579 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5581 TRACE("Component not set for install, not moving file\n");
5582 return ERROR_SUCCESS;
5585 sourcename = MSI_RecordGetString(rec, 3);
5586 options = MSI_RecordGetInteger(rec, 7);
5588 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5589 if (!sourcedir)
5590 goto done;
5592 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5593 if (!destdir)
5594 goto done;
5596 if (!sourcename)
5598 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5599 goto done;
5601 source = strdupW(sourcedir);
5602 if (!source)
5603 goto done;
5605 else
5607 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5608 source = msi_alloc(size * sizeof(WCHAR));
5609 if (!source)
5610 goto done;
5612 lstrcpyW(source, sourcedir);
5613 if (source[lstrlenW(source) - 1] != '\\')
5614 lstrcatW(source, backslash);
5615 lstrcatW(source, sourcename);
5618 wildcards = strchrW(source, '*') || strchrW(source, '?');
5620 if (MSI_RecordIsNull(rec, 4))
5622 if (!wildcards)
5624 destname = strdupW(sourcename);
5625 if (!destname)
5626 goto done;
5629 else
5631 destname = strdupW(MSI_RecordGetString(rec, 4));
5632 if (destname)
5633 reduce_to_longfilename(destname);
5636 size = 0;
5637 if (destname)
5638 size = lstrlenW(destname);
5640 size += lstrlenW(destdir) + 2;
5641 dest = msi_alloc(size * sizeof(WCHAR));
5642 if (!dest)
5643 goto done;
5645 lstrcpyW(dest, destdir);
5646 if (dest[lstrlenW(dest) - 1] != '\\')
5647 lstrcatW(dest, backslash);
5649 if (destname)
5650 lstrcatW(dest, destname);
5652 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5654 ret = CreateDirectoryW(destdir, NULL);
5655 if (!ret)
5657 WARN("CreateDirectory failed: %d\n", GetLastError());
5658 return ERROR_SUCCESS;
5662 if (!wildcards)
5663 msi_move_file(source, dest, options);
5664 else
5665 move_files_wildcard(source, dest, options);
5667 done:
5668 msi_free(sourcedir);
5669 msi_free(destdir);
5670 msi_free(destname);
5671 msi_free(source);
5672 msi_free(dest);
5674 return ERROR_SUCCESS;
5677 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5679 UINT rc;
5680 MSIQUERY *view;
5682 static const WCHAR ExecSeqQuery[] =
5683 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5684 '`','M','o','v','e','F','i','l','e','`',0};
5686 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5687 if (rc != ERROR_SUCCESS)
5688 return ERROR_SUCCESS;
5690 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5691 msiobj_release(&view->hdr);
5693 return rc;
5696 typedef struct tagMSIASSEMBLY
5698 struct list entry;
5699 MSICOMPONENT *component;
5700 MSIFEATURE *feature;
5701 MSIFILE *file;
5702 LPWSTR manifest;
5703 LPWSTR application;
5704 DWORD attributes;
5705 BOOL installed;
5706 } MSIASSEMBLY;
5708 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5709 DWORD dwReserved);
5710 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5711 LPVOID pvReserved, HMODULE *phModDll);
5713 static BOOL init_functionpointers(void)
5715 HRESULT hr;
5716 HMODULE hfusion;
5717 HMODULE hmscoree;
5719 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5721 hmscoree = LoadLibraryA("mscoree.dll");
5722 if (!hmscoree)
5724 WARN("mscoree.dll not available\n");
5725 return FALSE;
5728 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5729 if (!pLoadLibraryShim)
5731 WARN("LoadLibraryShim not available\n");
5732 FreeLibrary(hmscoree);
5733 return FALSE;
5736 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5737 if (FAILED(hr))
5739 WARN("fusion.dll not available\n");
5740 FreeLibrary(hmscoree);
5741 return FALSE;
5744 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5746 FreeLibrary(hmscoree);
5747 return TRUE;
5750 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5751 LPWSTR path)
5753 IAssemblyCache *cache;
5754 HRESULT hr;
5755 UINT r = ERROR_FUNCTION_FAILED;
5757 TRACE("installing assembly: %s\n", debugstr_w(path));
5759 if (assembly->feature)
5760 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5762 if (assembly->manifest)
5763 FIXME("Manifest unhandled\n");
5765 if (assembly->application)
5767 FIXME("Assembly should be privately installed\n");
5768 return ERROR_SUCCESS;
5771 if (assembly->attributes == msidbAssemblyAttributesWin32)
5773 FIXME("Win32 assemblies not handled\n");
5774 return ERROR_SUCCESS;
5777 if (!init_functionpointers() || !pCreateAssemblyCache)
5778 return ERROR_FUNCTION_FAILED;
5780 hr = pCreateAssemblyCache(&cache, 0);
5781 if (FAILED(hr))
5782 goto done;
5784 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5785 if (FAILED(hr))
5786 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5788 r = ERROR_SUCCESS;
5790 done:
5791 IAssemblyCache_Release(cache);
5792 return r;
5795 typedef struct tagASSEMBLY_LIST
5797 MSIPACKAGE *package;
5798 struct list *assemblies;
5799 } ASSEMBLY_LIST;
5801 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5803 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5804 MSIASSEMBLY *assembly;
5806 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5807 if (!assembly)
5808 return ERROR_OUTOFMEMORY;
5810 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5812 if (!assembly->component || !assembly->component->Enabled ||
5813 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5815 TRACE("Component not set for install, not publishing assembly\n");
5816 msi_free(assembly);
5817 return ERROR_SUCCESS;
5820 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5821 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5823 if (!assembly->file)
5825 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5826 return ERROR_FUNCTION_FAILED;
5829 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5830 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5831 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5832 assembly->installed = FALSE;
5834 list_add_head(list->assemblies, &assembly->entry);
5836 return ERROR_SUCCESS;
5839 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5841 MSIQUERY *view;
5842 ASSEMBLY_LIST list;
5843 UINT r;
5845 static const WCHAR query[] =
5846 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5847 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5849 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5850 if (r != ERROR_SUCCESS)
5851 return ERROR_SUCCESS;
5853 list.package = package;
5854 list.assemblies = assemblies;
5856 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5857 msiobj_release(&view->hdr);
5859 return r;
5862 static void free_assemblies(struct list *assemblies)
5864 struct list *item, *cursor;
5866 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5868 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5870 list_remove(&assembly->entry);
5871 msi_free(assembly->application);
5872 msi_free(assembly->manifest);
5873 msi_free(assembly);
5877 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5879 MSIASSEMBLY *assembly;
5881 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5883 if (!lstrcmpW(assembly->file->File, file))
5885 *out = assembly;
5886 return TRUE;
5890 return FALSE;
5893 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5894 LPWSTR *path, DWORD *attrs, PVOID user)
5896 MSIASSEMBLY *assembly;
5897 WCHAR temppath[MAX_PATH];
5898 struct list *assemblies = (struct list *)user;
5899 UINT r;
5901 if (!find_assembly(assemblies, file, &assembly))
5902 return FALSE;
5904 GetTempPathW(MAX_PATH, temppath);
5905 PathAddBackslashW(temppath);
5906 lstrcatW(temppath, assembly->file->FileName);
5908 if (action == MSICABEXTRACT_BEGINEXTRACT)
5910 if (assembly->installed)
5911 return FALSE;
5913 *path = strdupW(temppath);
5914 *attrs = assembly->file->Attributes;
5916 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5918 assembly->installed = TRUE;
5920 r = install_assembly(package, assembly, temppath);
5921 if (r != ERROR_SUCCESS)
5922 ERR("Failed to install assembly\n");
5925 return TRUE;
5928 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5930 UINT r;
5931 struct list assemblies = LIST_INIT(assemblies);
5932 MSIASSEMBLY *assembly;
5933 MSIMEDIAINFO *mi;
5934 WCHAR path[MAX_PATH];
5936 r = load_assemblies(package, &assemblies);
5937 if (r != ERROR_SUCCESS)
5938 goto done;
5940 if (list_empty(&assemblies))
5941 goto done;
5943 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5944 if (!mi)
5946 r = ERROR_OUTOFMEMORY;
5947 goto done;
5950 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5952 if (assembly->installed && !mi->is_continuous)
5953 continue;
5955 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5956 (assembly->file->IsCompressed && !mi->is_extracted))
5958 MSICABDATA data;
5960 r = ready_media(package, assembly->file, mi);
5961 if (r != ERROR_SUCCESS)
5963 ERR("Failed to ready media\n");
5964 break;
5967 data.mi = mi;
5968 data.package = package;
5969 data.cb = installassembly_cb;
5970 data.user = &assemblies;
5972 if (assembly->file->IsCompressed &&
5973 !msi_cabextract(package, mi, &data))
5975 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5976 r = ERROR_FUNCTION_FAILED;
5977 break;
5981 if (!assembly->file->IsCompressed)
5983 lstrcpyW(path, assembly->file->SourcePath);
5985 r = install_assembly(package, assembly, path);
5986 if (r != ERROR_SUCCESS)
5987 ERR("Failed to install assembly\n");
5990 /* FIXME: write Installer assembly reg values */
5993 done:
5994 free_assemblies(&assemblies);
5995 return r;
5998 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5999 LPCSTR action, LPCWSTR table )
6001 static const WCHAR query[] = {
6002 'S','E','L','E','C','T',' ','*',' ',
6003 'F','R','O','M',' ','`','%','s','`',0 };
6004 MSIQUERY *view = NULL;
6005 DWORD count = 0;
6006 UINT r;
6008 r = MSI_OpenQuery( package->db, &view, query, table );
6009 if (r == ERROR_SUCCESS)
6011 r = MSI_IterateRecords(view, &count, NULL, package);
6012 msiobj_release(&view->hdr);
6015 if (count)
6016 FIXME("%s -> %u ignored %s table values\n",
6017 action, count, debugstr_w(table));
6019 return ERROR_SUCCESS;
6022 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6024 TRACE("%p\n", package);
6025 return ERROR_SUCCESS;
6028 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6030 static const WCHAR table[] =
6031 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6032 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6035 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6037 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6038 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6041 static UINT ACTION_BindImage( MSIPACKAGE *package )
6043 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6044 return msi_unimplemented_action_stub( package, "BindImage", table );
6047 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6049 static const WCHAR table[] = {
6050 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6051 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6054 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6056 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6057 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6060 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6062 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6063 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6066 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6068 static const WCHAR table[] = {
6069 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6070 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6072 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6074 static const WCHAR table[] = {
6075 'P','r','o','d','u','c','t','I','D',0 };
6076 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6079 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6081 static const WCHAR table[] = {
6082 'E','n','v','i','r','o','n','m','e','n','t',0 };
6083 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6086 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6088 static const WCHAR table[] = {
6089 'M','s','i','A','s','s','e','m','b','l','y',0 };
6090 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6093 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6095 static const WCHAR table[] = { 'F','o','n','t',0 };
6096 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6099 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6101 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6102 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6105 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6107 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6108 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6111 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6113 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6114 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6117 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6119 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6120 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6123 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6125 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6126 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6129 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6131 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6132 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6135 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6137 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6138 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6141 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6143 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6144 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6147 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6149 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6150 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6153 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6155 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6156 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6159 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6161 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6162 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6165 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6167 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6168 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6171 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6173 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6174 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6177 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6179 static const WCHAR table[] = { 'M','I','M','E',0 };
6180 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6183 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6185 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6186 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6189 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6191 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6192 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6195 static const struct _actions StandardActions[] = {
6196 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6197 { szAppSearch, ACTION_AppSearch },
6198 { szBindImage, ACTION_BindImage },
6199 { szCCPSearch, ACTION_CCPSearch },
6200 { szCostFinalize, ACTION_CostFinalize },
6201 { szCostInitialize, ACTION_CostInitialize },
6202 { szCreateFolders, ACTION_CreateFolders },
6203 { szCreateShortcuts, ACTION_CreateShortcuts },
6204 { szDeleteServices, ACTION_DeleteServices },
6205 { szDisableRollback, NULL },
6206 { szDuplicateFiles, ACTION_DuplicateFiles },
6207 { szExecuteAction, ACTION_ExecuteAction },
6208 { szFileCost, ACTION_FileCost },
6209 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6210 { szForceReboot, ACTION_ForceReboot },
6211 { szInstallAdminPackage, NULL },
6212 { szInstallExecute, ACTION_InstallExecute },
6213 { szInstallExecuteAgain, ACTION_InstallExecute },
6214 { szInstallFiles, ACTION_InstallFiles},
6215 { szInstallFinalize, ACTION_InstallFinalize },
6216 { szInstallInitialize, ACTION_InstallInitialize },
6217 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6218 { szInstallValidate, ACTION_InstallValidate },
6219 { szIsolateComponents, ACTION_IsolateComponents },
6220 { szLaunchConditions, ACTION_LaunchConditions },
6221 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6222 { szMoveFiles, ACTION_MoveFiles },
6223 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6224 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6225 { szInstallODBC, ACTION_InstallODBC },
6226 { szInstallServices, ACTION_InstallServices },
6227 { szPatchFiles, ACTION_PatchFiles },
6228 { szProcessComponents, ACTION_ProcessComponents },
6229 { szPublishComponents, ACTION_PublishComponents },
6230 { szPublishFeatures, ACTION_PublishFeatures },
6231 { szPublishProduct, ACTION_PublishProduct },
6232 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6233 { szRegisterComPlus, ACTION_RegisterComPlus},
6234 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6235 { szRegisterFonts, ACTION_RegisterFonts },
6236 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6237 { szRegisterProduct, ACTION_RegisterProduct },
6238 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6239 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6240 { szRegisterUser, ACTION_RegisterUser },
6241 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6242 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6243 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6244 { szRemoveFiles, ACTION_RemoveFiles },
6245 { szRemoveFolders, ACTION_RemoveFolders },
6246 { szRemoveIniValues, ACTION_RemoveIniValues },
6247 { szRemoveODBC, ACTION_RemoveODBC },
6248 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6249 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6250 { szResolveSource, ACTION_ResolveSource },
6251 { szRMCCPSearch, ACTION_RMCCPSearch },
6252 { szScheduleReboot, NULL },
6253 { szSelfRegModules, ACTION_SelfRegModules },
6254 { szSelfUnregModules, ACTION_SelfUnregModules },
6255 { szSetODBCFolders, NULL },
6256 { szStartServices, ACTION_StartServices },
6257 { szStopServices, ACTION_StopServices },
6258 { szUnpublishComponents, ACTION_UnpublishComponents },
6259 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6260 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6261 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6262 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6263 { szUnregisterFonts, ACTION_UnregisterFonts },
6264 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6265 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6266 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6267 { szValidateProductID, ACTION_ValidateProductID },
6268 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6269 { szWriteIniValues, ACTION_WriteIniValues },
6270 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6271 { NULL, NULL },
6274 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6275 UINT* rc, BOOL force )
6277 BOOL ret = FALSE;
6278 BOOL run = force;
6279 int i;
6281 if (!run && !package->script->CurrentlyScripting)
6282 run = TRUE;
6284 if (!run)
6286 if (strcmpW(action,szInstallFinalize) == 0 ||
6287 strcmpW(action,szInstallExecute) == 0 ||
6288 strcmpW(action,szInstallExecuteAgain) == 0)
6289 run = TRUE;
6292 i = 0;
6293 while (StandardActions[i].action != NULL)
6295 if (strcmpW(StandardActions[i].action, action)==0)
6297 if (!run)
6299 ui_actioninfo(package, action, TRUE, 0);
6300 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6301 ui_actioninfo(package, action, FALSE, *rc);
6303 else
6305 ui_actionstart(package, action);
6306 if (StandardActions[i].handler)
6308 *rc = StandardActions[i].handler(package);
6310 else
6312 FIXME("unhandled standard action %s\n",debugstr_w(action));
6313 *rc = ERROR_SUCCESS;
6316 ret = TRUE;
6317 break;
6319 i++;
6321 return ret;