push a4aeee26091b4888c4962ca8601c7905b237df2d
[wine/hacks.git] / dlls / msi / action.c
blob8cd5923bdbe38e2ae5d5284a7e34942de8616eb1
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 & msidbFileAttributesCompressed)
1486 file->IsCompressed = TRUE;
1488 else if (file->Attributes & msidbFileAttributesNoncompressed)
1490 file->IsCompressed = FALSE;
1492 else
1494 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1497 if (!file->IsCompressed)
1499 LPWSTR p, path;
1501 p = resolve_folder(package, file->Component->Directory,
1502 TRUE, FALSE, TRUE, NULL);
1503 path = build_directory_name(2, p, file->ShortName);
1505 if (file->LongName &&
1506 GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
1508 msi_free(path);
1509 path = build_directory_name(2, p, file->LongName);
1512 file->SourcePath = path;
1513 msi_free(p);
1516 load_file_hash(package, file);
1518 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1520 list_add_tail( &package->files, &file->entry );
1522 return ERROR_SUCCESS;
1525 static UINT load_all_files(MSIPACKAGE *package)
1527 MSIQUERY * view;
1528 UINT rc;
1529 static const WCHAR Query[] =
1530 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1531 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1532 '`','S','e','q','u','e','n','c','e','`', 0};
1534 if (!list_empty(&package->files))
1535 return ERROR_SUCCESS;
1537 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1538 if (rc != ERROR_SUCCESS)
1539 return ERROR_SUCCESS;
1541 rc = MSI_IterateRecords(view, NULL, load_file, package);
1542 msiobj_release(&view->hdr);
1544 return ERROR_SUCCESS;
1547 static UINT load_folder( MSIRECORD *row, LPVOID param )
1549 MSIPACKAGE *package = param;
1550 static const WCHAR szDot[] = { '.',0 };
1551 static WCHAR szEmpty[] = { 0 };
1552 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1553 MSIFOLDER *folder;
1555 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1556 if (!folder)
1557 return ERROR_NOT_ENOUGH_MEMORY;
1559 folder->Directory = msi_dup_record_field( row, 1 );
1561 TRACE("%s\n", debugstr_w(folder->Directory));
1563 p = msi_dup_record_field(row, 3);
1565 /* split src and target dir */
1566 tgt_short = p;
1567 src_short = folder_split_path( p, ':' );
1569 /* split the long and short paths */
1570 tgt_long = folder_split_path( tgt_short, '|' );
1571 src_long = folder_split_path( src_short, '|' );
1573 /* check for no-op dirs */
1574 if (!lstrcmpW(szDot, tgt_short))
1575 tgt_short = szEmpty;
1576 if (!lstrcmpW(szDot, src_short))
1577 src_short = szEmpty;
1579 if (!tgt_long)
1580 tgt_long = tgt_short;
1582 if (!src_short) {
1583 src_short = tgt_short;
1584 src_long = tgt_long;
1587 if (!src_long)
1588 src_long = src_short;
1590 /* FIXME: use the target short path too */
1591 folder->TargetDefault = strdupW(tgt_long);
1592 folder->SourceShortPath = strdupW(src_short);
1593 folder->SourceLongPath = strdupW(src_long);
1594 msi_free(p);
1596 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1597 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1598 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1600 folder->Parent = msi_dup_record_field( row, 2 );
1602 folder->Property = msi_dup_property( package, folder->Directory );
1604 list_add_tail( &package->folders, &folder->entry );
1606 TRACE("returning %p\n", folder);
1608 return ERROR_SUCCESS;
1611 static UINT load_all_folders( MSIPACKAGE *package )
1613 static const WCHAR query[] = {
1614 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1615 '`','D','i','r','e','c','t','o','r','y','`',0 };
1616 MSIQUERY *view;
1617 UINT r;
1619 if (!list_empty(&package->folders))
1620 return ERROR_SUCCESS;
1622 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1623 if (r != ERROR_SUCCESS)
1624 return r;
1626 r = MSI_IterateRecords(view, NULL, load_folder, package);
1627 msiobj_release(&view->hdr);
1628 return r;
1632 * I am not doing any of the costing functionality yet.
1633 * Mostly looking at doing the Component and Feature loading
1635 * The native MSI does A LOT of modification to tables here. Mostly adding
1636 * a lot of temporary columns to the Feature and Component tables.
1638 * note: Native msi also tracks the short filename. But I am only going to
1639 * track the long ones. Also looking at this directory table
1640 * it appears that the directory table does not get the parents
1641 * resolved base on property only based on their entries in the
1642 * directory table.
1644 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1646 static const WCHAR szCosting[] =
1647 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1648 static const WCHAR szZero[] = { '0', 0 };
1650 MSI_SetPropertyW(package, szCosting, szZero);
1651 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1653 load_all_folders( package );
1654 load_all_components( package );
1655 load_all_features( package );
1656 load_all_files( package );
1658 return ERROR_SUCCESS;
1661 static UINT execute_script(MSIPACKAGE *package, UINT script )
1663 UINT i;
1664 UINT rc = ERROR_SUCCESS;
1666 TRACE("Executing Script %i\n",script);
1668 if (!package->script)
1670 ERR("no script!\n");
1671 return ERROR_FUNCTION_FAILED;
1674 for (i = 0; i < package->script->ActionCount[script]; i++)
1676 LPWSTR action;
1677 action = package->script->Actions[script][i];
1678 ui_actionstart(package, action);
1679 TRACE("Executing Action (%s)\n",debugstr_w(action));
1680 rc = ACTION_PerformAction(package, action, script, TRUE);
1681 if (rc != ERROR_SUCCESS)
1682 break;
1684 msi_free_action_script(package, script);
1685 return rc;
1688 static UINT ACTION_FileCost(MSIPACKAGE *package)
1690 return ERROR_SUCCESS;
1693 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1695 MSICOMPONENT *comp;
1696 INSTALLSTATE state;
1697 UINT r;
1699 state = MsiQueryProductStateW(package->ProductCode);
1701 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1703 if (!comp->ComponentId)
1704 continue;
1706 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1707 comp->Installed = INSTALLSTATE_ABSENT;
1708 else
1710 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1711 package->Context, comp->ComponentId,
1712 &comp->Installed);
1713 if (r != ERROR_SUCCESS)
1714 comp->Installed = INSTALLSTATE_ABSENT;
1719 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1721 MSIFEATURE *feature;
1722 INSTALLSTATE state;
1724 state = MsiQueryProductStateW(package->ProductCode);
1726 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1728 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1729 feature->Installed = INSTALLSTATE_ABSENT;
1730 else
1732 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1733 feature->Feature);
1738 static BOOL process_state_property(MSIPACKAGE* package, int level,
1739 LPCWSTR property, INSTALLSTATE state)
1741 static const WCHAR all[]={'A','L','L',0};
1742 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1743 LPWSTR override;
1744 MSIFEATURE *feature;
1746 override = msi_dup_property( package, property );
1747 if (!override)
1748 return FALSE;
1750 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1752 if (lstrcmpW(property, remove) &&
1753 (feature->Level <= 0 || feature->Level > level))
1754 continue;
1756 if (strcmpiW(override,all)==0)
1757 msi_feature_set_state(package, feature, state);
1758 else
1760 LPWSTR ptr = override;
1761 LPWSTR ptr2 = strchrW(override,',');
1763 while (ptr)
1765 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1766 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1768 msi_feature_set_state(package, feature, state);
1769 break;
1771 if (ptr2)
1773 ptr=ptr2+1;
1774 ptr2 = strchrW(ptr,',');
1776 else
1777 break;
1781 msi_free(override);
1783 return TRUE;
1786 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1788 int level;
1789 static const WCHAR szlevel[] =
1790 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1791 static const WCHAR szAddLocal[] =
1792 {'A','D','D','L','O','C','A','L',0};
1793 static const WCHAR szAddSource[] =
1794 {'A','D','D','S','O','U','R','C','E',0};
1795 static const WCHAR szRemove[] =
1796 {'R','E','M','O','V','E',0};
1797 static const WCHAR szReinstall[] =
1798 {'R','E','I','N','S','T','A','L','L',0};
1799 BOOL override = FALSE;
1800 MSICOMPONENT* component;
1801 MSIFEATURE *feature;
1804 /* I do not know if this is where it should happen.. but */
1806 TRACE("Checking Install Level\n");
1808 level = msi_get_property_int(package, szlevel, 1);
1810 /* ok here is the _real_ rub
1811 * all these activation/deactivation things happen in order and things
1812 * later on the list override things earlier on the list.
1813 * 1) INSTALLLEVEL processing
1814 * 2) ADDLOCAL
1815 * 3) REMOVE
1816 * 4) ADDSOURCE
1817 * 5) ADDDEFAULT
1818 * 6) REINSTALL
1819 * 7) COMPADDLOCAL
1820 * 8) COMPADDSOURCE
1821 * 9) FILEADDLOCAL
1822 * 10) FILEADDSOURCE
1823 * 11) FILEADDDEFAULT
1825 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1826 * REMOVE are the big ones, since we don't handle administrative installs
1827 * yet anyway.
1829 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1830 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1831 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1832 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1834 if (!override)
1836 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1838 BOOL feature_state = ((feature->Level > 0) &&
1839 (feature->Level <= level));
1841 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1843 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1844 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1845 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1846 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1847 else
1848 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1852 /* disable child features of unselected parent features */
1853 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1855 FeatureList *fl;
1857 if (feature->Level > 0 && feature->Level <= level)
1858 continue;
1860 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1861 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1864 else
1866 /* set the Preselected Property */
1867 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1868 static const WCHAR szOne[] = { '1', 0 };
1870 MSI_SetPropertyW(package,szPreselected,szOne);
1874 * now we want to enable or disable components base on feature
1877 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1879 ComponentList *cl;
1881 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1882 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1884 if (!feature->Level)
1885 continue;
1887 /* features with components that have compressed files are made local */
1888 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1890 if (cl->component->Enabled &&
1891 cl->component->ForceLocalState &&
1892 feature->Action == INSTALLSTATE_SOURCE)
1894 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1895 break;
1899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1901 component = cl->component;
1903 if (!component->Enabled)
1904 continue;
1906 switch (feature->Action)
1908 case INSTALLSTATE_ABSENT:
1909 component->anyAbsent = 1;
1910 break;
1911 case INSTALLSTATE_ADVERTISED:
1912 component->hasAdvertiseFeature = 1;
1913 break;
1914 case INSTALLSTATE_SOURCE:
1915 component->hasSourceFeature = 1;
1916 break;
1917 case INSTALLSTATE_LOCAL:
1918 component->hasLocalFeature = 1;
1919 break;
1920 case INSTALLSTATE_DEFAULT:
1921 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1922 component->hasAdvertiseFeature = 1;
1923 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1924 component->hasSourceFeature = 1;
1925 else
1926 component->hasLocalFeature = 1;
1927 break;
1928 default:
1929 break;
1934 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1936 /* if the component isn't enabled, leave it alone */
1937 if (!component->Enabled)
1938 continue;
1940 /* check if it's local or source */
1941 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1942 (component->hasLocalFeature || component->hasSourceFeature))
1944 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1945 !component->ForceLocalState)
1946 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1947 else
1948 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1949 continue;
1952 /* if any feature is local, the component must be local too */
1953 if (component->hasLocalFeature)
1955 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1956 continue;
1959 if (component->hasSourceFeature)
1961 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1962 continue;
1965 if (component->hasAdvertiseFeature)
1967 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1968 continue;
1971 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1972 if (component->anyAbsent)
1973 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1976 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1978 if (component->Action == INSTALLSTATE_DEFAULT)
1980 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1981 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1984 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1985 debugstr_w(component->Component), component->Installed, component->Action);
1989 return ERROR_SUCCESS;
1992 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1994 MSIPACKAGE *package = (MSIPACKAGE*)param;
1995 LPCWSTR name;
1996 LPWSTR path;
1997 MSIFOLDER *f;
1999 name = MSI_RecordGetString(row,1);
2001 f = get_loaded_folder(package, name);
2002 if (!f) return ERROR_SUCCESS;
2004 /* reset the ResolvedTarget */
2005 msi_free(f->ResolvedTarget);
2006 f->ResolvedTarget = NULL;
2008 /* This helper function now does ALL the work */
2009 TRACE("Dir %s ...\n",debugstr_w(name));
2010 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2011 TRACE("resolves to %s\n",debugstr_w(path));
2012 msi_free(path);
2014 return ERROR_SUCCESS;
2017 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2019 MSIPACKAGE *package = (MSIPACKAGE*)param;
2020 LPCWSTR name;
2021 MSIFEATURE *feature;
2023 name = MSI_RecordGetString( row, 1 );
2025 feature = get_loaded_feature( package, name );
2026 if (!feature)
2027 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2028 else
2030 LPCWSTR Condition;
2031 Condition = MSI_RecordGetString(row,3);
2033 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2035 int level = MSI_RecordGetInteger(row,2);
2036 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2037 feature->Level = level;
2040 return ERROR_SUCCESS;
2043 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2045 static const WCHAR name_fmt[] =
2046 {'%','u','.','%','u','.','%','u','.','%','u',0};
2047 static const WCHAR name[] = {'\\',0};
2048 VS_FIXEDFILEINFO *lpVer;
2049 WCHAR filever[0x100];
2050 LPVOID version;
2051 DWORD versize;
2052 DWORD handle;
2053 UINT sz;
2055 TRACE("%s\n", debugstr_w(filename));
2057 versize = GetFileVersionInfoSizeW( filename, &handle );
2058 if (!versize)
2059 return NULL;
2061 version = msi_alloc( versize );
2062 GetFileVersionInfoW( filename, 0, versize, version );
2064 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2066 msi_free( version );
2067 return NULL;
2070 sprintfW( filever, name_fmt,
2071 HIWORD(lpVer->dwFileVersionMS),
2072 LOWORD(lpVer->dwFileVersionMS),
2073 HIWORD(lpVer->dwFileVersionLS),
2074 LOWORD(lpVer->dwFileVersionLS));
2076 msi_free( version );
2078 return strdupW( filever );
2081 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2083 LPWSTR file_version;
2084 MSIFILE *file;
2086 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2088 MSICOMPONENT* comp = file->Component;
2089 LPWSTR p;
2091 if (!comp)
2092 continue;
2094 if (file->IsCompressed)
2095 comp->ForceLocalState = TRUE;
2097 /* calculate target */
2098 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2100 msi_free(file->TargetPath);
2102 TRACE("file %s is named %s\n",
2103 debugstr_w(file->File), debugstr_w(file->FileName));
2105 file->TargetPath = build_directory_name(2, p, file->FileName);
2107 msi_free(p);
2109 TRACE("file %s resolves to %s\n",
2110 debugstr_w(file->File), debugstr_w(file->TargetPath));
2112 /* don't check files of components that aren't installed */
2113 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2114 comp->Installed == INSTALLSTATE_ABSENT)
2116 file->state = msifs_missing; /* assume files are missing */
2117 continue;
2120 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2122 file->state = msifs_missing;
2123 comp->Cost += file->FileSize;
2124 continue;
2127 if (file->Version &&
2128 (file_version = msi_get_disk_file_version( file->TargetPath )))
2130 TRACE("new %s old %s\n", debugstr_w(file->Version),
2131 debugstr_w(file_version));
2132 /* FIXME: seems like a bad way to compare version numbers */
2133 if (lstrcmpiW(file_version, file->Version)<0)
2135 file->state = msifs_overwrite;
2136 comp->Cost += file->FileSize;
2138 else
2139 file->state = msifs_present;
2140 msi_free( file_version );
2142 else
2143 file->state = msifs_present;
2146 return ERROR_SUCCESS;
2150 * A lot is done in this function aside from just the costing.
2151 * The costing needs to be implemented at some point but for now I am going
2152 * to focus on the directory building
2155 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2157 static const WCHAR ExecSeqQuery[] =
2158 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2159 '`','D','i','r','e','c','t','o','r','y','`',0};
2160 static const WCHAR ConditionQuery[] =
2161 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2162 '`','C','o','n','d','i','t','i','o','n','`',0};
2163 static const WCHAR szCosting[] =
2164 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2165 static const WCHAR szlevel[] =
2166 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2167 static const WCHAR szOutOfDiskSpace[] =
2168 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2169 static const WCHAR szOne[] = { '1', 0 };
2170 static const WCHAR szZero[] = { '0', 0 };
2171 MSICOMPONENT *comp;
2172 UINT rc;
2173 MSIQUERY * view;
2174 LPWSTR level;
2176 TRACE("Building Directory properties\n");
2178 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2179 if (rc == ERROR_SUCCESS)
2181 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2182 package);
2183 msiobj_release(&view->hdr);
2186 /* read components states from the registry */
2187 ACTION_GetComponentInstallStates(package);
2188 ACTION_GetFeatureInstallStates(package);
2190 TRACE("File calculations\n");
2191 msi_check_file_install_states( package );
2193 TRACE("Evaluating Condition Table\n");
2195 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2196 if (rc == ERROR_SUCCESS)
2198 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2199 package);
2200 msiobj_release(&view->hdr);
2203 TRACE("Enabling or Disabling Components\n");
2204 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2206 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2208 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2209 comp->Enabled = FALSE;
2211 else
2212 comp->Enabled = TRUE;
2215 MSI_SetPropertyW(package,szCosting,szOne);
2216 /* set default run level if not set */
2217 level = msi_dup_property( package, szlevel );
2218 if (!level)
2219 MSI_SetPropertyW(package,szlevel, szOne);
2220 msi_free(level);
2222 /* FIXME: check volume disk space */
2223 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2225 return MSI_SetFeatureStates(package);
2228 /* OK this value is "interpreted" and then formatted based on the
2229 first few characters */
2230 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2231 DWORD *size)
2233 LPSTR data = NULL;
2235 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2237 if (value[1]=='x')
2239 LPWSTR ptr;
2240 CHAR byte[5];
2241 LPWSTR deformated = NULL;
2242 int count;
2244 deformat_string(package, &value[2], &deformated);
2246 /* binary value type */
2247 ptr = deformated;
2248 *type = REG_BINARY;
2249 if (strlenW(ptr)%2)
2250 *size = (strlenW(ptr)/2)+1;
2251 else
2252 *size = strlenW(ptr)/2;
2254 data = msi_alloc(*size);
2256 byte[0] = '0';
2257 byte[1] = 'x';
2258 byte[4] = 0;
2259 count = 0;
2260 /* if uneven pad with a zero in front */
2261 if (strlenW(ptr)%2)
2263 byte[2]= '0';
2264 byte[3]= *ptr;
2265 ptr++;
2266 data[count] = (BYTE)strtol(byte,NULL,0);
2267 count ++;
2268 TRACE("Uneven byte count\n");
2270 while (*ptr)
2272 byte[2]= *ptr;
2273 ptr++;
2274 byte[3]= *ptr;
2275 ptr++;
2276 data[count] = (BYTE)strtol(byte,NULL,0);
2277 count ++;
2279 msi_free(deformated);
2281 TRACE("Data %i bytes(%i)\n",*size,count);
2283 else
2285 LPWSTR deformated;
2286 LPWSTR p;
2287 DWORD d = 0;
2288 deformat_string(package, &value[1], &deformated);
2290 *type=REG_DWORD;
2291 *size = sizeof(DWORD);
2292 data = msi_alloc(*size);
2293 p = deformated;
2294 if (*p == '-')
2295 p++;
2296 while (*p)
2298 if ( (*p < '0') || (*p > '9') )
2299 break;
2300 d *= 10;
2301 d += (*p - '0');
2302 p++;
2304 if (deformated[0] == '-')
2305 d = -d;
2306 *(LPDWORD)data = d;
2307 TRACE("DWORD %i\n",*(LPDWORD)data);
2309 msi_free(deformated);
2312 else
2314 static const WCHAR szMulti[] = {'[','~',']',0};
2315 LPCWSTR ptr;
2316 *type=REG_SZ;
2318 if (value[0]=='#')
2320 if (value[1]=='%')
2322 ptr = &value[2];
2323 *type=REG_EXPAND_SZ;
2325 else
2326 ptr = &value[1];
2328 else
2329 ptr=value;
2331 if (strstrW(value,szMulti))
2332 *type = REG_MULTI_SZ;
2334 /* remove initial delimiter */
2335 if (!strncmpW(value, szMulti, 3))
2336 ptr = value + 3;
2338 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2340 /* add double NULL terminator */
2341 if (*type == REG_MULTI_SZ)
2343 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2344 data = msi_realloc_zero(data, *size);
2347 return data;
2350 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2352 MSIPACKAGE *package = (MSIPACKAGE*)param;
2353 static const WCHAR szHCR[] =
2354 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2355 'R','O','O','T','\\',0};
2356 static const WCHAR szHCU[] =
2357 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2358 'U','S','E','R','\\',0};
2359 static const WCHAR szHLM[] =
2360 {'H','K','E','Y','_','L','O','C','A','L','_',
2361 'M','A','C','H','I','N','E','\\',0};
2362 static const WCHAR szHU[] =
2363 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2365 LPSTR value_data = NULL;
2366 HKEY root_key, hkey;
2367 DWORD type,size;
2368 LPWSTR deformated;
2369 LPCWSTR szRoot, component, name, key, value;
2370 MSICOMPONENT *comp;
2371 MSIRECORD * uirow;
2372 LPWSTR uikey;
2373 INT root;
2374 BOOL check_first = FALSE;
2375 UINT rc;
2377 ui_progress(package,2,0,0,0);
2379 value = NULL;
2380 key = NULL;
2381 uikey = NULL;
2382 name = NULL;
2384 component = MSI_RecordGetString(row, 6);
2385 comp = get_loaded_component(package,component);
2386 if (!comp)
2387 return ERROR_SUCCESS;
2389 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2391 TRACE("Skipping write due to disabled component %s\n",
2392 debugstr_w(component));
2394 comp->Action = comp->Installed;
2396 return ERROR_SUCCESS;
2399 comp->Action = INSTALLSTATE_LOCAL;
2401 name = MSI_RecordGetString(row, 4);
2402 if( MSI_RecordIsNull(row,5) && name )
2404 /* null values can have special meanings */
2405 if (name[0]=='-' && name[1] == 0)
2406 return ERROR_SUCCESS;
2407 else if ((name[0]=='+' && name[1] == 0) ||
2408 (name[0] == '*' && name[1] == 0))
2409 name = NULL;
2410 check_first = TRUE;
2413 root = MSI_RecordGetInteger(row,2);
2414 key = MSI_RecordGetString(row, 3);
2416 /* get the root key */
2417 switch (root)
2419 case -1:
2421 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2422 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2423 if (all_users && all_users[0] == '1')
2425 root_key = HKEY_LOCAL_MACHINE;
2426 szRoot = szHLM;
2428 else
2430 root_key = HKEY_CURRENT_USER;
2431 szRoot = szHCU;
2433 msi_free(all_users);
2435 break;
2436 case 0: root_key = HKEY_CLASSES_ROOT;
2437 szRoot = szHCR;
2438 break;
2439 case 1: root_key = HKEY_CURRENT_USER;
2440 szRoot = szHCU;
2441 break;
2442 case 2: root_key = HKEY_LOCAL_MACHINE;
2443 szRoot = szHLM;
2444 break;
2445 case 3: root_key = HKEY_USERS;
2446 szRoot = szHU;
2447 break;
2448 default:
2449 ERR("Unknown root %i\n",root);
2450 root_key=NULL;
2451 szRoot = NULL;
2452 break;
2454 if (!root_key)
2455 return ERROR_SUCCESS;
2457 deformat_string(package, key , &deformated);
2458 size = strlenW(deformated) + strlenW(szRoot) + 1;
2459 uikey = msi_alloc(size*sizeof(WCHAR));
2460 strcpyW(uikey,szRoot);
2461 strcatW(uikey,deformated);
2463 if (RegCreateKeyW( root_key, deformated, &hkey))
2465 ERR("Could not create key %s\n",debugstr_w(deformated));
2466 msi_free(deformated);
2467 msi_free(uikey);
2468 return ERROR_SUCCESS;
2470 msi_free(deformated);
2472 value = MSI_RecordGetString(row,5);
2473 if (value)
2474 value_data = parse_value(package, value, &type, &size);
2475 else
2477 static const WCHAR szEmpty[] = {0};
2478 value_data = (LPSTR)strdupW(szEmpty);
2479 size = sizeof(szEmpty);
2480 type = REG_SZ;
2483 deformat_string(package, name, &deformated);
2485 if (!check_first)
2487 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2488 debugstr_w(uikey));
2489 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2491 else
2493 DWORD sz = 0;
2494 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2495 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2497 TRACE("value %s of %s checked already exists\n",
2498 debugstr_w(deformated), debugstr_w(uikey));
2500 else
2502 TRACE("Checked and setting value %s of %s\n",
2503 debugstr_w(deformated), debugstr_w(uikey));
2504 if (deformated || size)
2505 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2508 RegCloseKey(hkey);
2510 uirow = MSI_CreateRecord(3);
2511 MSI_RecordSetStringW(uirow,2,deformated);
2512 MSI_RecordSetStringW(uirow,1,uikey);
2514 if (type == REG_SZ)
2515 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2516 else
2517 MSI_RecordSetStringW(uirow,3,value);
2519 ui_actiondata(package,szWriteRegistryValues,uirow);
2520 msiobj_release( &uirow->hdr );
2522 msi_free(value_data);
2523 msi_free(deformated);
2524 msi_free(uikey);
2526 return ERROR_SUCCESS;
2529 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2531 UINT rc;
2532 MSIQUERY * view;
2533 static const WCHAR ExecSeqQuery[] =
2534 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2535 '`','R','e','g','i','s','t','r','y','`',0 };
2537 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2538 if (rc != ERROR_SUCCESS)
2539 return ERROR_SUCCESS;
2541 /* increment progress bar each time action data is sent */
2542 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2544 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2546 msiobj_release(&view->hdr);
2547 return rc;
2550 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2552 package->script->CurrentlyScripting = TRUE;
2554 return ERROR_SUCCESS;
2558 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2560 MSICOMPONENT *comp;
2561 DWORD progress = 0;
2562 DWORD total = 0;
2563 static const WCHAR q1[]=
2564 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2565 '`','R','e','g','i','s','t','r','y','`',0};
2566 UINT rc;
2567 MSIQUERY * view;
2568 MSIFEATURE *feature;
2569 MSIFILE *file;
2571 TRACE("InstallValidate\n");
2573 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2574 if (rc == ERROR_SUCCESS)
2576 MSI_IterateRecords( view, &progress, NULL, package );
2577 msiobj_release( &view->hdr );
2578 total += progress * REG_PROGRESS_VALUE;
2581 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2582 total += COMPONENT_PROGRESS_VALUE;
2584 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2585 total += file->FileSize;
2587 ui_progress(package,0,total,0,0);
2589 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2591 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2592 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2593 feature->ActionRequest);
2596 return ERROR_SUCCESS;
2599 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2601 MSIPACKAGE* package = (MSIPACKAGE*)param;
2602 LPCWSTR cond = NULL;
2603 LPCWSTR message = NULL;
2604 UINT r;
2606 static const WCHAR title[]=
2607 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2609 cond = MSI_RecordGetString(row,1);
2611 r = MSI_EvaluateConditionW(package,cond);
2612 if (r == MSICONDITION_FALSE)
2614 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2616 LPWSTR deformated;
2617 message = MSI_RecordGetString(row,2);
2618 deformat_string(package,message,&deformated);
2619 MessageBoxW(NULL,deformated,title,MB_OK);
2620 msi_free(deformated);
2623 return ERROR_INSTALL_FAILURE;
2626 return ERROR_SUCCESS;
2629 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2631 UINT rc;
2632 MSIQUERY * view = NULL;
2633 static const WCHAR ExecSeqQuery[] =
2634 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2635 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2637 TRACE("Checking launch conditions\n");
2639 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2640 if (rc != ERROR_SUCCESS)
2641 return ERROR_SUCCESS;
2643 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2644 msiobj_release(&view->hdr);
2646 return rc;
2649 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2652 if (!cmp->KeyPath)
2653 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2655 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2657 MSIRECORD * row = 0;
2658 UINT root,len;
2659 LPWSTR deformated,buffer,deformated_name;
2660 LPCWSTR key,name;
2661 static const WCHAR ExecSeqQuery[] =
2662 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2663 '`','R','e','g','i','s','t','r','y','`',' ',
2664 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2665 ' ','=',' ' ,'\'','%','s','\'',0 };
2666 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2667 static const WCHAR fmt2[]=
2668 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2670 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2671 if (!row)
2672 return NULL;
2674 root = MSI_RecordGetInteger(row,2);
2675 key = MSI_RecordGetString(row, 3);
2676 name = MSI_RecordGetString(row, 4);
2677 deformat_string(package, key , &deformated);
2678 deformat_string(package, name, &deformated_name);
2680 len = strlenW(deformated) + 6;
2681 if (deformated_name)
2682 len+=strlenW(deformated_name);
2684 buffer = msi_alloc( len *sizeof(WCHAR));
2686 if (deformated_name)
2687 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2688 else
2689 sprintfW(buffer,fmt,root,deformated);
2691 msi_free(deformated);
2692 msi_free(deformated_name);
2693 msiobj_release(&row->hdr);
2695 return buffer;
2697 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2699 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2700 return NULL;
2702 else
2704 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2706 if (file)
2707 return strdupW( file->TargetPath );
2709 return NULL;
2712 static HKEY openSharedDLLsKey(void)
2714 HKEY hkey=0;
2715 static const WCHAR path[] =
2716 {'S','o','f','t','w','a','r','e','\\',
2717 'M','i','c','r','o','s','o','f','t','\\',
2718 'W','i','n','d','o','w','s','\\',
2719 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2720 'S','h','a','r','e','d','D','L','L','s',0};
2722 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2723 return hkey;
2726 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2728 HKEY hkey;
2729 DWORD count=0;
2730 DWORD type;
2731 DWORD sz = sizeof(count);
2732 DWORD rc;
2734 hkey = openSharedDLLsKey();
2735 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2736 if (rc != ERROR_SUCCESS)
2737 count = 0;
2738 RegCloseKey(hkey);
2739 return count;
2742 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2744 HKEY hkey;
2746 hkey = openSharedDLLsKey();
2747 if (count > 0)
2748 msi_reg_set_val_dword( hkey, path, count );
2749 else
2750 RegDeleteValueW(hkey,path);
2751 RegCloseKey(hkey);
2752 return count;
2756 * Return TRUE if the count should be written out and FALSE if not
2758 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2760 MSIFEATURE *feature;
2761 INT count = 0;
2762 BOOL write = FALSE;
2764 /* only refcount DLLs */
2765 if (comp->KeyPath == NULL ||
2766 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2767 comp->Attributes & msidbComponentAttributesODBCDataSource)
2768 write = FALSE;
2769 else
2771 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2772 write = (count > 0);
2774 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2775 write = TRUE;
2778 /* increment counts */
2779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2781 ComponentList *cl;
2783 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2784 continue;
2786 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2788 if ( cl->component == comp )
2789 count++;
2793 /* decrement counts */
2794 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2796 ComponentList *cl;
2798 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2799 continue;
2801 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2803 if ( cl->component == comp )
2804 count--;
2808 /* ref count all the files in the component */
2809 if (write)
2811 MSIFILE *file;
2813 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2815 if (file->Component == comp)
2816 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2820 /* add a count for permanent */
2821 if (comp->Attributes & msidbComponentAttributesPermanent)
2822 count ++;
2824 comp->RefCount = count;
2826 if (write)
2827 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2830 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2832 WCHAR squished_pc[GUID_SIZE];
2833 WCHAR squished_cc[GUID_SIZE];
2834 UINT rc;
2835 MSICOMPONENT *comp;
2836 HKEY hkey;
2838 TRACE("\n");
2840 squash_guid(package->ProductCode,squished_pc);
2841 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2843 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2845 MSIRECORD * uirow;
2847 ui_progress(package,2,0,0,0);
2848 if (!comp->ComponentId)
2849 continue;
2851 squash_guid(comp->ComponentId,squished_cc);
2853 msi_free(comp->FullKeypath);
2854 comp->FullKeypath = resolve_keypath( package, comp );
2856 ACTION_RefCountComponent( package, comp );
2858 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2859 debugstr_w(comp->Component),
2860 debugstr_w(squished_cc),
2861 debugstr_w(comp->FullKeypath),
2862 comp->RefCount);
2864 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2865 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2867 if (!comp->FullKeypath)
2868 continue;
2870 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2871 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2872 else
2873 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2875 if (rc != ERROR_SUCCESS)
2876 continue;
2878 if (comp->Attributes & msidbComponentAttributesPermanent)
2880 static const WCHAR szPermKey[] =
2881 { '0','0','0','0','0','0','0','0','0','0','0','0',
2882 '0','0','0','0','0','0','0','0','0','0','0','0',
2883 '0','0','0','0','0','0','0','0',0 };
2885 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2888 if (comp->Action == INSTALLSTATE_LOCAL)
2889 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2890 else
2892 MSIFILE *file;
2893 MSIRECORD *row;
2894 LPWSTR ptr, ptr2;
2895 WCHAR source[MAX_PATH];
2896 WCHAR base[MAX_PATH];
2898 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2899 static const WCHAR query[] = {
2900 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2901 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2902 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2903 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2904 '`','D','i','s','k','I','d','`',0};
2906 file = get_loaded_file(package, comp->KeyPath);
2907 if (!file)
2908 continue;
2910 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2911 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2912 ptr2 = strrchrW(source, '\\') + 1;
2913 msiobj_release(&row->hdr);
2915 lstrcpyW(base, package->PackagePath);
2916 ptr = strrchrW(base, '\\');
2917 *(ptr + 1) = '\0';
2919 ptr = file->SourcePath + lstrlenW(base);
2920 lstrcpyW(ptr2, ptr);
2922 msi_reg_set_val_str(hkey, squished_pc, source);
2924 RegCloseKey(hkey);
2926 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2928 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2929 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2930 else
2931 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2934 /* UI stuff */
2935 uirow = MSI_CreateRecord(3);
2936 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2937 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2938 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2939 ui_actiondata(package,szProcessComponents,uirow);
2940 msiobj_release( &uirow->hdr );
2943 return ERROR_SUCCESS;
2946 typedef struct {
2947 CLSID clsid;
2948 LPWSTR source;
2950 LPWSTR path;
2951 ITypeLib *ptLib;
2952 } typelib_struct;
2954 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2955 LPWSTR lpszName, LONG_PTR lParam)
2957 TLIBATTR *attr;
2958 typelib_struct *tl_struct = (typelib_struct*) lParam;
2959 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2960 int sz;
2961 HRESULT res;
2963 if (!IS_INTRESOURCE(lpszName))
2965 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2966 return TRUE;
2969 sz = strlenW(tl_struct->source)+4;
2970 sz *= sizeof(WCHAR);
2972 if ((INT_PTR)lpszName == 1)
2973 tl_struct->path = strdupW(tl_struct->source);
2974 else
2976 tl_struct->path = msi_alloc(sz);
2977 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2980 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2981 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2982 if (!SUCCEEDED(res))
2984 msi_free(tl_struct->path);
2985 tl_struct->path = NULL;
2987 return TRUE;
2990 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2991 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2993 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2994 return FALSE;
2997 msi_free(tl_struct->path);
2998 tl_struct->path = NULL;
3000 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3001 ITypeLib_Release(tl_struct->ptLib);
3003 return TRUE;
3006 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3008 MSIPACKAGE* package = (MSIPACKAGE*)param;
3009 LPCWSTR component;
3010 MSICOMPONENT *comp;
3011 MSIFILE *file;
3012 typelib_struct tl_struct;
3013 ITypeLib *tlib;
3014 HMODULE module;
3015 HRESULT hr;
3017 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3019 component = MSI_RecordGetString(row,3);
3020 comp = get_loaded_component(package,component);
3021 if (!comp)
3022 return ERROR_SUCCESS;
3024 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3026 TRACE("Skipping typelib reg due to disabled component\n");
3028 comp->Action = comp->Installed;
3030 return ERROR_SUCCESS;
3033 comp->Action = INSTALLSTATE_LOCAL;
3035 file = get_loaded_file( package, comp->KeyPath );
3036 if (!file)
3037 return ERROR_SUCCESS;
3039 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3040 if (module)
3042 LPCWSTR guid;
3043 guid = MSI_RecordGetString(row,1);
3044 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3045 tl_struct.source = strdupW( file->TargetPath );
3046 tl_struct.path = NULL;
3048 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3049 (LONG_PTR)&tl_struct);
3051 if (tl_struct.path)
3053 LPWSTR help = NULL;
3054 LPCWSTR helpid;
3055 HRESULT res;
3057 helpid = MSI_RecordGetString(row,6);
3059 if (helpid)
3060 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3061 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3062 msi_free(help);
3064 if (!SUCCEEDED(res))
3065 ERR("Failed to register type library %s\n",
3066 debugstr_w(tl_struct.path));
3067 else
3069 ui_actiondata(package,szRegisterTypeLibraries,row);
3071 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3074 ITypeLib_Release(tl_struct.ptLib);
3075 msi_free(tl_struct.path);
3077 else
3078 ERR("Failed to load type library %s\n",
3079 debugstr_w(tl_struct.source));
3081 FreeLibrary(module);
3082 msi_free(tl_struct.source);
3084 else
3086 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3087 if (FAILED(hr))
3089 ERR("Failed to load type library: %08x\n", hr);
3090 return ERROR_FUNCTION_FAILED;
3093 ITypeLib_Release(tlib);
3096 return ERROR_SUCCESS;
3099 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3102 * OK this is a bit confusing.. I am given a _Component key and I believe
3103 * that the file that is being registered as a type library is the "key file
3104 * of that component" which I interpret to mean "The file in the KeyPath of
3105 * that component".
3107 UINT rc;
3108 MSIQUERY * view;
3109 static const WCHAR Query[] =
3110 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3111 '`','T','y','p','e','L','i','b','`',0};
3113 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3114 if (rc != ERROR_SUCCESS)
3115 return ERROR_SUCCESS;
3117 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3118 msiobj_release(&view->hdr);
3119 return rc;
3122 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3124 MSIPACKAGE *package = (MSIPACKAGE*)param;
3125 LPWSTR target_file, target_folder, filename;
3126 LPCWSTR buffer, extension;
3127 MSICOMPONENT *comp;
3128 static const WCHAR szlnk[]={'.','l','n','k',0};
3129 IShellLinkW *sl = NULL;
3130 IPersistFile *pf = NULL;
3131 HRESULT res;
3133 buffer = MSI_RecordGetString(row,4);
3134 comp = get_loaded_component(package,buffer);
3135 if (!comp)
3136 return ERROR_SUCCESS;
3138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3140 TRACE("Skipping shortcut creation due to disabled component\n");
3142 comp->Action = comp->Installed;
3144 return ERROR_SUCCESS;
3147 comp->Action = INSTALLSTATE_LOCAL;
3149 ui_actiondata(package,szCreateShortcuts,row);
3151 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3152 &IID_IShellLinkW, (LPVOID *) &sl );
3154 if (FAILED( res ))
3156 ERR("CLSID_ShellLink not available\n");
3157 goto err;
3160 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3161 if (FAILED( res ))
3163 ERR("QueryInterface(IID_IPersistFile) failed\n");
3164 goto err;
3167 buffer = MSI_RecordGetString(row,2);
3168 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3170 /* may be needed because of a bug somewhere else */
3171 create_full_pathW(target_folder);
3173 filename = msi_dup_record_field( row, 3 );
3174 reduce_to_longfilename(filename);
3176 extension = strchrW(filename,'.');
3177 if (!extension || strcmpiW(extension,szlnk))
3179 int len = strlenW(filename);
3180 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3181 memcpy(filename + len, szlnk, sizeof(szlnk));
3183 target_file = build_directory_name(2, target_folder, filename);
3184 msi_free(target_folder);
3185 msi_free(filename);
3187 buffer = MSI_RecordGetString(row,5);
3188 if (strchrW(buffer,'['))
3190 LPWSTR deformated;
3191 deformat_string(package,buffer,&deformated);
3192 IShellLinkW_SetPath(sl,deformated);
3193 msi_free(deformated);
3195 else
3197 FIXME("poorly handled shortcut format, advertised shortcut\n");
3198 IShellLinkW_SetPath(sl,comp->FullKeypath);
3201 if (!MSI_RecordIsNull(row,6))
3203 LPWSTR deformated;
3204 buffer = MSI_RecordGetString(row,6);
3205 deformat_string(package,buffer,&deformated);
3206 IShellLinkW_SetArguments(sl,deformated);
3207 msi_free(deformated);
3210 if (!MSI_RecordIsNull(row,7))
3212 buffer = MSI_RecordGetString(row,7);
3213 IShellLinkW_SetDescription(sl,buffer);
3216 if (!MSI_RecordIsNull(row,8))
3217 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3219 if (!MSI_RecordIsNull(row,9))
3221 LPWSTR Path;
3222 INT index;
3224 buffer = MSI_RecordGetString(row,9);
3226 Path = build_icon_path(package,buffer);
3227 index = MSI_RecordGetInteger(row,10);
3229 /* no value means 0 */
3230 if (index == MSI_NULL_INTEGER)
3231 index = 0;
3233 IShellLinkW_SetIconLocation(sl,Path,index);
3234 msi_free(Path);
3237 if (!MSI_RecordIsNull(row,11))
3238 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3240 if (!MSI_RecordIsNull(row,12))
3242 LPWSTR Path;
3243 buffer = MSI_RecordGetString(row,12);
3244 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3245 if (Path)
3246 IShellLinkW_SetWorkingDirectory(sl,Path);
3247 msi_free(Path);
3250 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3251 IPersistFile_Save(pf,target_file,FALSE);
3253 msi_free(target_file);
3255 err:
3256 if (pf)
3257 IPersistFile_Release( pf );
3258 if (sl)
3259 IShellLinkW_Release( sl );
3261 return ERROR_SUCCESS;
3264 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3266 UINT rc;
3267 HRESULT res;
3268 MSIQUERY * view;
3269 static const WCHAR Query[] =
3270 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3271 '`','S','h','o','r','t','c','u','t','`',0};
3273 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3274 if (rc != ERROR_SUCCESS)
3275 return ERROR_SUCCESS;
3277 res = CoInitialize( NULL );
3278 if (FAILED (res))
3280 ERR("CoInitialize failed\n");
3281 return ERROR_FUNCTION_FAILED;
3284 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3285 msiobj_release(&view->hdr);
3287 CoUninitialize();
3289 return rc;
3292 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3294 MSIPACKAGE* package = (MSIPACKAGE*)param;
3295 HANDLE the_file;
3296 LPWSTR FilePath;
3297 LPCWSTR FileName;
3298 CHAR buffer[1024];
3299 DWORD sz;
3300 UINT rc;
3301 MSIRECORD *uirow;
3303 FileName = MSI_RecordGetString(row,1);
3304 if (!FileName)
3306 ERR("Unable to get FileName\n");
3307 return ERROR_SUCCESS;
3310 FilePath = build_icon_path(package,FileName);
3312 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3314 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3315 FILE_ATTRIBUTE_NORMAL, NULL);
3317 if (the_file == INVALID_HANDLE_VALUE)
3319 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3320 msi_free(FilePath);
3321 return ERROR_SUCCESS;
3326 DWORD write;
3327 sz = 1024;
3328 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3329 if (rc != ERROR_SUCCESS)
3331 ERR("Failed to get stream\n");
3332 CloseHandle(the_file);
3333 DeleteFileW(FilePath);
3334 break;
3336 WriteFile(the_file,buffer,sz,&write,NULL);
3337 } while (sz == 1024);
3339 msi_free(FilePath);
3341 CloseHandle(the_file);
3343 uirow = MSI_CreateRecord(1);
3344 MSI_RecordSetStringW(uirow,1,FileName);
3345 ui_actiondata(package,szPublishProduct,uirow);
3346 msiobj_release( &uirow->hdr );
3348 return ERROR_SUCCESS;
3351 static UINT msi_publish_icons(MSIPACKAGE *package)
3353 UINT r;
3354 MSIQUERY *view;
3356 static const WCHAR query[]= {
3357 'S','E','L','E','C','T',' ','*',' ',
3358 'F','R','O','M',' ','`','I','c','o','n','`',0};
3360 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3361 if (r == ERROR_SUCCESS)
3363 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3364 msiobj_release(&view->hdr);
3367 return ERROR_SUCCESS;
3370 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3372 UINT r;
3373 HKEY source;
3374 LPWSTR buffer;
3375 MSIMEDIADISK *disk;
3376 MSISOURCELISTINFO *info;
3378 static const WCHAR szEmpty[] = {0};
3379 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3381 r = RegCreateKeyW(hkey, szSourceList, &source);
3382 if (r != ERROR_SUCCESS)
3383 return r;
3385 RegCloseKey(source);
3387 buffer = strrchrW(package->PackagePath, '\\') + 1;
3388 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3389 package->Context, MSICODE_PRODUCT,
3390 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3391 if (r != ERROR_SUCCESS)
3392 return r;
3394 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3395 package->Context, MSICODE_PRODUCT,
3396 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3397 if (r != ERROR_SUCCESS)
3398 return r;
3400 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3401 package->Context, MSICODE_PRODUCT,
3402 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3403 if (r != ERROR_SUCCESS)
3404 return r;
3406 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3408 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3409 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3410 info->options, info->value);
3411 else
3412 MsiSourceListSetInfoW(package->ProductCode, NULL,
3413 info->context, info->options,
3414 info->property, info->value);
3417 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3419 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3420 disk->context, disk->options,
3421 disk->disk_id, disk->volume_label, disk->disk_prompt);
3424 return ERROR_SUCCESS;
3427 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3429 MSIHANDLE hdb, suminfo;
3430 WCHAR guids[MAX_PATH];
3431 WCHAR packcode[SQUISH_GUID_SIZE];
3432 LPWSTR buffer;
3433 LPWSTR ptr;
3434 DWORD langid;
3435 DWORD size;
3436 UINT r;
3438 static const WCHAR szProductLanguage[] =
3439 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3440 static const WCHAR szARPProductIcon[] =
3441 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3442 static const WCHAR szProductVersion[] =
3443 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3444 static const WCHAR szAssignment[] =
3445 {'A','s','s','i','g','n','m','e','n','t',0};
3446 static const WCHAR szAdvertiseFlags[] =
3447 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3448 static const WCHAR szClients[] =
3449 {'C','l','i','e','n','t','s',0};
3450 static const WCHAR szColon[] = {':',0};
3452 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3453 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3454 msi_free(buffer);
3456 langid = msi_get_property_int(package, szProductLanguage, 0);
3457 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3459 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3460 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3462 /* FIXME */
3463 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3465 buffer = msi_dup_property(package, szARPProductIcon);
3466 if (buffer)
3468 LPWSTR path = build_icon_path(package,buffer);
3469 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3470 msi_free(path);
3471 msi_free(buffer);
3474 buffer = msi_dup_property(package, szProductVersion);
3475 if (buffer)
3477 DWORD verdword = msi_version_str_to_dword(buffer);
3478 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3479 msi_free(buffer);
3482 msi_reg_set_val_dword(hkey, szAssignment, 0);
3483 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3484 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3485 msi_reg_set_val_str(hkey, szClients, szColon);
3487 hdb = alloc_msihandle(&package->db->hdr);
3488 if (!hdb)
3489 return ERROR_NOT_ENOUGH_MEMORY;
3491 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3492 MsiCloseHandle(hdb);
3493 if (r != ERROR_SUCCESS)
3494 goto done;
3496 size = MAX_PATH;
3497 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3498 NULL, guids, &size);
3499 if (r != ERROR_SUCCESS)
3500 goto done;
3502 ptr = strchrW(guids, ';');
3503 if (ptr) *ptr = 0;
3504 squash_guid(guids, packcode);
3505 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3507 done:
3508 MsiCloseHandle(suminfo);
3509 return ERROR_SUCCESS;
3512 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3514 UINT r;
3515 HKEY hkey;
3516 LPWSTR upgrade;
3517 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3519 static const WCHAR szUpgradeCode[] =
3520 {'U','p','g','r','a','d','e','C','o','d','e',0};
3522 upgrade = msi_dup_property(package, szUpgradeCode);
3523 if (!upgrade)
3524 return ERROR_SUCCESS;
3526 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3528 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3529 if (r != ERROR_SUCCESS)
3530 goto done;
3532 else
3534 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3535 if (r != ERROR_SUCCESS)
3536 goto done;
3539 squash_guid(package->ProductCode, squashed_pc);
3540 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3542 RegCloseKey(hkey);
3544 done:
3545 msi_free(upgrade);
3546 return r;
3549 static BOOL msi_check_publish(MSIPACKAGE *package)
3551 MSIFEATURE *feature;
3553 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3555 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3556 return TRUE;
3559 return FALSE;
3562 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3564 MSIFEATURE *feature;
3566 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3568 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3569 return FALSE;
3572 return TRUE;
3576 * 99% of the work done here is only done for
3577 * advertised installs. However this is where the
3578 * Icon table is processed and written out
3579 * so that is what I am going to do here.
3581 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3583 UINT rc;
3584 HKEY hukey=0;
3585 HKEY hudkey=0;
3587 /* FIXME: also need to publish if the product is in advertise mode */
3588 if (!msi_check_publish(package))
3589 return ERROR_SUCCESS;
3591 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3593 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3594 if (rc != ERROR_SUCCESS)
3595 goto end;
3597 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3598 if (rc != ERROR_SUCCESS)
3599 goto end;
3601 else
3603 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3604 if (rc != ERROR_SUCCESS)
3605 goto end;
3607 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3608 if (rc != ERROR_SUCCESS)
3609 goto end;
3612 rc = msi_publish_upgrade_code(package);
3613 if (rc != ERROR_SUCCESS)
3614 goto end;
3616 rc = msi_publish_product_properties(package, hukey);
3617 if (rc != ERROR_SUCCESS)
3618 goto end;
3620 rc = msi_publish_sourcelist(package, hukey);
3621 if (rc != ERROR_SUCCESS)
3622 goto end;
3624 rc = msi_publish_icons(package);
3626 end:
3627 RegCloseKey(hukey);
3628 RegCloseKey(hudkey);
3630 return rc;
3633 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3635 MSIPACKAGE *package = (MSIPACKAGE*)param;
3636 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3637 LPWSTR deformated_section, deformated_key, deformated_value;
3638 LPWSTR folder, fullname = NULL;
3639 MSIRECORD * uirow;
3640 INT action;
3641 MSICOMPONENT *comp;
3642 static const WCHAR szWindowsFolder[] =
3643 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3645 component = MSI_RecordGetString(row, 8);
3646 comp = get_loaded_component(package,component);
3648 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3650 TRACE("Skipping ini file due to disabled component %s\n",
3651 debugstr_w(component));
3653 comp->Action = comp->Installed;
3655 return ERROR_SUCCESS;
3658 comp->Action = INSTALLSTATE_LOCAL;
3660 identifier = MSI_RecordGetString(row,1);
3661 filename = MSI_RecordGetString(row,2);
3662 dirproperty = MSI_RecordGetString(row,3);
3663 section = MSI_RecordGetString(row,4);
3664 key = MSI_RecordGetString(row,5);
3665 value = MSI_RecordGetString(row,6);
3666 action = MSI_RecordGetInteger(row,7);
3668 deformat_string(package,section,&deformated_section);
3669 deformat_string(package,key,&deformated_key);
3670 deformat_string(package,value,&deformated_value);
3672 if (dirproperty)
3674 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3675 if (!folder)
3676 folder = msi_dup_property( package, dirproperty );
3678 else
3679 folder = msi_dup_property( package, szWindowsFolder );
3681 if (!folder)
3683 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3684 goto cleanup;
3687 fullname = build_directory_name(2, folder, filename);
3689 if (action == 0)
3691 TRACE("Adding value %s to section %s in %s\n",
3692 debugstr_w(deformated_key), debugstr_w(deformated_section),
3693 debugstr_w(fullname));
3694 WritePrivateProfileStringW(deformated_section, deformated_key,
3695 deformated_value, fullname);
3697 else if (action == 1)
3699 WCHAR returned[10];
3700 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3701 returned, 10, fullname);
3702 if (returned[0] == 0)
3704 TRACE("Adding value %s to section %s in %s\n",
3705 debugstr_w(deformated_key), debugstr_w(deformated_section),
3706 debugstr_w(fullname));
3708 WritePrivateProfileStringW(deformated_section, deformated_key,
3709 deformated_value, fullname);
3712 else if (action == 3)
3713 FIXME("Append to existing section not yet implemented\n");
3715 uirow = MSI_CreateRecord(4);
3716 MSI_RecordSetStringW(uirow,1,identifier);
3717 MSI_RecordSetStringW(uirow,2,deformated_section);
3718 MSI_RecordSetStringW(uirow,3,deformated_key);
3719 MSI_RecordSetStringW(uirow,4,deformated_value);
3720 ui_actiondata(package,szWriteIniValues,uirow);
3721 msiobj_release( &uirow->hdr );
3722 cleanup:
3723 msi_free(fullname);
3724 msi_free(folder);
3725 msi_free(deformated_key);
3726 msi_free(deformated_value);
3727 msi_free(deformated_section);
3728 return ERROR_SUCCESS;
3731 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3733 UINT rc;
3734 MSIQUERY * view;
3735 static const WCHAR ExecSeqQuery[] =
3736 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3737 '`','I','n','i','F','i','l','e','`',0};
3739 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3740 if (rc != ERROR_SUCCESS)
3742 TRACE("no IniFile table\n");
3743 return ERROR_SUCCESS;
3746 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3747 msiobj_release(&view->hdr);
3748 return rc;
3751 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3753 MSIPACKAGE *package = (MSIPACKAGE*)param;
3754 LPCWSTR filename;
3755 LPWSTR FullName;
3756 MSIFILE *file;
3757 DWORD len;
3758 static const WCHAR ExeStr[] =
3759 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3760 static const WCHAR close[] = {'\"',0};
3761 STARTUPINFOW si;
3762 PROCESS_INFORMATION info;
3763 BOOL brc;
3764 MSIRECORD *uirow;
3765 LPWSTR uipath, p;
3767 memset(&si,0,sizeof(STARTUPINFOW));
3769 filename = MSI_RecordGetString(row,1);
3770 file = get_loaded_file( package, filename );
3772 if (!file)
3774 ERR("Unable to find file id %s\n",debugstr_w(filename));
3775 return ERROR_SUCCESS;
3778 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3780 FullName = msi_alloc(len*sizeof(WCHAR));
3781 strcpyW(FullName,ExeStr);
3782 strcatW( FullName, file->TargetPath );
3783 strcatW(FullName,close);
3785 TRACE("Registering %s\n",debugstr_w(FullName));
3786 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3787 &si, &info);
3789 if (brc)
3791 CloseHandle(info.hThread);
3792 msi_dialog_check_messages(info.hProcess);
3793 CloseHandle(info.hProcess);
3796 msi_free(FullName);
3798 /* the UI chunk */
3799 uirow = MSI_CreateRecord( 2 );
3800 uipath = strdupW( file->TargetPath );
3801 p = strrchrW(uipath,'\\');
3802 if (p)
3803 p[0]=0;
3804 MSI_RecordSetStringW( uirow, 1, &p[1] );
3805 MSI_RecordSetStringW( uirow, 2, uipath);
3806 ui_actiondata( package, szSelfRegModules, uirow);
3807 msiobj_release( &uirow->hdr );
3808 msi_free( uipath );
3809 /* FIXME: call ui_progress? */
3811 return ERROR_SUCCESS;
3814 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3816 UINT rc;
3817 MSIQUERY * view;
3818 static const WCHAR ExecSeqQuery[] =
3819 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3820 '`','S','e','l','f','R','e','g','`',0};
3822 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3823 if (rc != ERROR_SUCCESS)
3825 TRACE("no SelfReg table\n");
3826 return ERROR_SUCCESS;
3829 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3830 msiobj_release(&view->hdr);
3832 return ERROR_SUCCESS;
3835 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3837 MSIFEATURE *feature;
3838 UINT rc;
3839 HKEY hkey;
3840 HKEY userdata;
3842 if (!msi_check_publish(package))
3843 return ERROR_SUCCESS;
3845 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3847 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3848 &hkey, TRUE);
3849 if (rc != ERROR_SUCCESS)
3850 goto end;
3852 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3853 &userdata, TRUE);
3854 if (rc != ERROR_SUCCESS)
3855 goto end;
3857 else
3859 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3860 if (rc != ERROR_SUCCESS)
3861 goto end;
3863 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3864 &userdata, TRUE);
3865 if (rc != ERROR_SUCCESS)
3866 goto end;
3869 /* here the guids are base 85 encoded */
3870 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3872 ComponentList *cl;
3873 LPWSTR data = NULL;
3874 GUID clsid;
3875 INT size;
3876 BOOL absent = FALSE;
3877 MSIRECORD *uirow;
3879 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3880 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3881 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3882 absent = TRUE;
3884 size = 1;
3885 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3887 size += 21;
3889 if (feature->Feature_Parent)
3890 size += strlenW( feature->Feature_Parent )+2;
3892 data = msi_alloc(size * sizeof(WCHAR));
3894 data[0] = 0;
3895 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3897 MSICOMPONENT* component = cl->component;
3898 WCHAR buf[21];
3900 buf[0] = 0;
3901 if (component->ComponentId)
3903 TRACE("From %s\n",debugstr_w(component->ComponentId));
3904 CLSIDFromString(component->ComponentId, &clsid);
3905 encode_base85_guid(&clsid,buf);
3906 TRACE("to %s\n",debugstr_w(buf));
3907 strcatW(data,buf);
3911 if (feature->Feature_Parent)
3913 static const WCHAR sep[] = {'\2',0};
3914 strcatW(data,sep);
3915 strcatW(data,feature->Feature_Parent);
3918 msi_reg_set_val_str( userdata, feature->Feature, data );
3919 msi_free(data);
3921 size = 0;
3922 if (feature->Feature_Parent)
3923 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3924 if (!absent)
3926 static const WCHAR emptyW[] = {0};
3927 size += sizeof(WCHAR);
3928 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3929 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3931 else
3933 size += 2*sizeof(WCHAR);
3934 data = msi_alloc(size);
3935 data[0] = 0x6;
3936 data[1] = 0;
3937 if (feature->Feature_Parent)
3938 strcpyW( &data[1], feature->Feature_Parent );
3939 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3940 (LPBYTE)data,size);
3941 msi_free(data);
3944 /* the UI chunk */
3945 uirow = MSI_CreateRecord( 1 );
3946 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3947 ui_actiondata( package, szPublishFeatures, uirow);
3948 msiobj_release( &uirow->hdr );
3949 /* FIXME: call ui_progress? */
3952 end:
3953 RegCloseKey(hkey);
3954 RegCloseKey(userdata);
3955 return rc;
3958 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3960 UINT r;
3961 HKEY hkey;
3963 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3965 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3966 if (r == ERROR_SUCCESS)
3968 RegDeleteValueW(hkey, feature->Feature);
3969 RegCloseKey(hkey);
3972 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3973 if (r == ERROR_SUCCESS)
3975 RegDeleteValueW(hkey, feature->Feature);
3976 RegCloseKey(hkey);
3979 return ERROR_SUCCESS;
3982 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3984 MSIFEATURE *feature;
3986 if (!msi_check_unpublish(package))
3987 return ERROR_SUCCESS;
3989 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3991 msi_unpublish_feature(package, feature);
3994 return ERROR_SUCCESS;
3997 static UINT msi_get_local_package_name( LPWSTR path )
3999 static const WCHAR szInstaller[] = {
4000 '\\','I','n','s','t','a','l','l','e','r','\\',0};
4001 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4002 DWORD time, len, i;
4003 HANDLE handle;
4005 time = GetTickCount();
4006 GetWindowsDirectoryW( path, MAX_PATH );
4007 lstrcatW( path, szInstaller );
4008 CreateDirectoryW( path, NULL );
4010 len = lstrlenW(path);
4011 for (i=0; i<0x10000; i++)
4013 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4014 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4015 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4016 if (handle != INVALID_HANDLE_VALUE)
4018 CloseHandle(handle);
4019 break;
4021 if (GetLastError() != ERROR_FILE_EXISTS &&
4022 GetLastError() != ERROR_SHARING_VIOLATION)
4023 return ERROR_FUNCTION_FAILED;
4026 return ERROR_SUCCESS;
4029 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4031 WCHAR packagefile[MAX_PATH];
4032 UINT r;
4034 r = msi_get_local_package_name( packagefile );
4035 if (r != ERROR_SUCCESS)
4036 return r;
4038 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4040 r = CopyFileW( package->db->path, packagefile, FALSE);
4042 if (!r)
4044 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4045 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4046 return ERROR_FUNCTION_FAILED;
4049 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4051 return ERROR_SUCCESS;
4054 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4056 LPWSTR prop, val, key;
4057 SYSTEMTIME systime;
4058 DWORD size, langid;
4059 WCHAR date[9];
4060 LPWSTR buffer;
4062 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4063 static const WCHAR szWindowsInstaller[] =
4064 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4065 static const WCHAR modpath_fmt[] =
4066 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4067 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4068 static const WCHAR szModifyPath[] =
4069 {'M','o','d','i','f','y','P','a','t','h',0};
4070 static const WCHAR szUninstallString[] =
4071 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4072 static const WCHAR szEstimatedSize[] =
4073 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4074 static const WCHAR szProductLanguage[] =
4075 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4076 static const WCHAR szProductVersion[] =
4077 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4078 static const WCHAR szProductName[] =
4079 {'P','r','o','d','u','c','t','N','a','m','e',0};
4080 static const WCHAR szDisplayName[] =
4081 {'D','i','s','p','l','a','y','N','a','m','e',0};
4082 static const WCHAR szDisplayVersion[] =
4083 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4084 static const WCHAR szManufacturer[] =
4085 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4087 static const LPCSTR propval[] = {
4088 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4089 "ARPCONTACT", "Contact",
4090 "ARPCOMMENTS", "Comments",
4091 "ProductName", "DisplayName",
4092 "ProductVersion", "DisplayVersion",
4093 "ARPHELPLINK", "HelpLink",
4094 "ARPHELPTELEPHONE", "HelpTelephone",
4095 "ARPINSTALLLOCATION", "InstallLocation",
4096 "SourceDir", "InstallSource",
4097 "Manufacturer", "Publisher",
4098 "ARPREADME", "Readme",
4099 "ARPSIZE", "Size",
4100 "ARPURLINFOABOUT", "URLInfoAbout",
4101 "ARPURLUPDATEINFO", "URLUpdateInfo",
4102 NULL,
4104 const LPCSTR *p = propval;
4106 while (*p)
4108 prop = strdupAtoW(*p++);
4109 key = strdupAtoW(*p++);
4110 val = msi_dup_property(package, prop);
4111 msi_reg_set_val_str(hkey, key, val);
4112 msi_free(val);
4113 msi_free(key);
4114 msi_free(prop);
4117 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4119 size = deformat_string(package, modpath_fmt, &buffer);
4120 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4121 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4122 msi_free(buffer);
4124 /* FIXME: Write real Estimated Size when we have it */
4125 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4127 buffer = msi_dup_property(package, szProductName);
4128 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4129 msi_free(buffer);
4131 buffer = msi_dup_property(package, cszSourceDir);
4132 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4133 msi_free(buffer);
4135 buffer = msi_dup_property(package, szManufacturer);
4136 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4137 msi_free(buffer);
4139 GetLocalTime(&systime);
4140 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4141 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4143 langid = msi_get_property_int(package, szProductLanguage, 0);
4144 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4146 buffer = msi_dup_property(package, szProductVersion);
4147 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4148 if (buffer)
4150 DWORD verdword = msi_version_str_to_dword(buffer);
4152 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4153 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4154 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4155 msi_free(buffer);
4158 return ERROR_SUCCESS;
4161 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4163 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4164 LPWSTR upgrade_code;
4165 HKEY hkey, props;
4166 HKEY upgrade;
4167 UINT rc;
4169 static const WCHAR szUpgradeCode[] = {
4170 'U','p','g','r','a','d','e','C','o','d','e',0};
4172 /* FIXME: also need to publish if the product is in advertise mode */
4173 if (!msi_check_publish(package))
4174 return ERROR_SUCCESS;
4176 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4177 if (rc != ERROR_SUCCESS)
4178 return rc;
4180 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4182 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4183 if (rc != ERROR_SUCCESS)
4184 goto done;
4186 else
4188 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4189 if (rc != ERROR_SUCCESS)
4190 goto done;
4193 msi_make_package_local(package, props);
4195 rc = msi_publish_install_properties(package, hkey);
4196 if (rc != ERROR_SUCCESS)
4197 goto done;
4199 rc = msi_publish_install_properties(package, props);
4200 if (rc != ERROR_SUCCESS)
4201 goto done;
4203 upgrade_code = msi_dup_property(package, szUpgradeCode);
4204 if (upgrade_code)
4206 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4207 squash_guid(package->ProductCode, squashed_pc);
4208 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4209 RegCloseKey(upgrade);
4210 msi_free(upgrade_code);
4213 done:
4214 RegCloseKey(hkey);
4216 return ERROR_SUCCESS;
4219 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4221 return execute_script(package,INSTALL_SCRIPT);
4224 static UINT msi_unpublish_product(MSIPACKAGE *package)
4226 LPWSTR upgrade;
4227 LPWSTR remove = NULL;
4228 LPWSTR *features = NULL;
4229 BOOL full_uninstall = TRUE;
4230 MSIFEATURE *feature;
4232 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4233 static const WCHAR szAll[] = {'A','L','L',0};
4234 static const WCHAR szUpgradeCode[] =
4235 {'U','p','g','r','a','d','e','C','o','d','e',0};
4237 remove = msi_dup_property(package, szRemove);
4238 if (!remove)
4239 return ERROR_SUCCESS;
4241 features = msi_split_string(remove, ',');
4242 if (!features)
4244 msi_free(remove);
4245 ERR("REMOVE feature list is empty!\n");
4246 return ERROR_FUNCTION_FAILED;
4249 if (!lstrcmpW(features[0], szAll))
4250 full_uninstall = TRUE;
4251 else
4253 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4255 if (feature->Action != INSTALLSTATE_ABSENT)
4256 full_uninstall = FALSE;
4260 if (!full_uninstall)
4261 goto done;
4263 MSIREG_DeleteProductKey(package->ProductCode);
4264 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4265 MSIREG_DeleteUninstallKey(package->ProductCode);
4267 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4269 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4270 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4272 else
4274 MSIREG_DeleteUserProductKey(package->ProductCode);
4275 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4278 upgrade = msi_dup_property(package, szUpgradeCode);
4279 if (upgrade)
4281 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4282 msi_free(upgrade);
4285 done:
4286 msi_free(remove);
4287 msi_free(features);
4288 return ERROR_SUCCESS;
4291 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4293 UINT rc;
4295 rc = msi_unpublish_product(package);
4296 if (rc != ERROR_SUCCESS)
4297 return rc;
4299 /* turn off scheduling */
4300 package->script->CurrentlyScripting= FALSE;
4302 /* first do the same as an InstallExecute */
4303 rc = ACTION_InstallExecute(package);
4304 if (rc != ERROR_SUCCESS)
4305 return rc;
4307 /* then handle Commit Actions */
4308 rc = execute_script(package,COMMIT_SCRIPT);
4310 return rc;
4313 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4315 static const WCHAR RunOnce[] = {
4316 'S','o','f','t','w','a','r','e','\\',
4317 'M','i','c','r','o','s','o','f','t','\\',
4318 'W','i','n','d','o','w','s','\\',
4319 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4320 'R','u','n','O','n','c','e',0};
4321 static const WCHAR InstallRunOnce[] = {
4322 'S','o','f','t','w','a','r','e','\\',
4323 'M','i','c','r','o','s','o','f','t','\\',
4324 'W','i','n','d','o','w','s','\\',
4325 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4326 'I','n','s','t','a','l','l','e','r','\\',
4327 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4329 static const WCHAR msiexec_fmt[] = {
4330 '%','s',
4331 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4332 '\"','%','s','\"',0};
4333 static const WCHAR install_fmt[] = {
4334 '/','I',' ','\"','%','s','\"',' ',
4335 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4336 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4337 WCHAR buffer[256], sysdir[MAX_PATH];
4338 HKEY hkey;
4339 WCHAR squished_pc[100];
4341 squash_guid(package->ProductCode,squished_pc);
4343 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4344 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4345 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4346 squished_pc);
4348 msi_reg_set_val_str( hkey, squished_pc, buffer );
4349 RegCloseKey(hkey);
4351 TRACE("Reboot command %s\n",debugstr_w(buffer));
4353 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4354 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4356 msi_reg_set_val_str( hkey, squished_pc, buffer );
4357 RegCloseKey(hkey);
4359 return ERROR_INSTALL_SUSPEND;
4362 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4364 DWORD attrib;
4365 UINT rc;
4368 * We are currently doing what should be done here in the top level Install
4369 * however for Administrative and uninstalls this step will be needed
4371 if (!package->PackagePath)
4372 return ERROR_SUCCESS;
4374 msi_set_sourcedir_props(package, TRUE);
4376 attrib = GetFileAttributesW(package->db->path);
4377 if (attrib == INVALID_FILE_ATTRIBUTES)
4379 LPWSTR prompt;
4380 LPWSTR msg;
4381 DWORD size = 0;
4383 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4384 package->Context, MSICODE_PRODUCT,
4385 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4386 if (rc == ERROR_MORE_DATA)
4388 prompt = msi_alloc(size * sizeof(WCHAR));
4389 MsiSourceListGetInfoW(package->ProductCode, NULL,
4390 package->Context, MSICODE_PRODUCT,
4391 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4393 else
4394 prompt = strdupW(package->db->path);
4396 msg = generate_error_string(package,1302,1,prompt);
4397 while(attrib == INVALID_FILE_ATTRIBUTES)
4399 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4400 if (rc == IDCANCEL)
4402 rc = ERROR_INSTALL_USEREXIT;
4403 break;
4405 attrib = GetFileAttributesW(package->db->path);
4407 msi_free(prompt);
4408 rc = ERROR_SUCCESS;
4410 else
4411 return ERROR_SUCCESS;
4413 return rc;
4416 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4418 HKEY hkey=0;
4419 LPWSTR buffer;
4420 LPWSTR productid;
4421 UINT rc,i;
4423 static const WCHAR szPropKeys[][80] =
4425 {'P','r','o','d','u','c','t','I','D',0},
4426 {'U','S','E','R','N','A','M','E',0},
4427 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4428 {0},
4431 static const WCHAR szRegKeys[][80] =
4433 {'P','r','o','d','u','c','t','I','D',0},
4434 {'R','e','g','O','w','n','e','r',0},
4435 {'R','e','g','C','o','m','p','a','n','y',0},
4436 {0},
4439 if (msi_check_unpublish(package))
4441 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4442 return ERROR_SUCCESS;
4445 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4446 if (!productid)
4447 return ERROR_SUCCESS;
4449 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4450 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4451 else
4452 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4454 if (rc != ERROR_SUCCESS)
4455 goto end;
4457 for( i = 0; szPropKeys[i][0]; i++ )
4459 buffer = msi_dup_property( package, szPropKeys[i] );
4460 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4461 msi_free( buffer );
4464 end:
4465 msi_free(productid);
4466 RegCloseKey(hkey);
4468 /* FIXME: call ui_actiondata */
4470 return rc;
4474 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4476 UINT rc;
4478 package->script->InWhatSequence |= SEQUENCE_EXEC;
4479 rc = ACTION_ProcessExecSequence(package,FALSE);
4480 return rc;
4484 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4486 MSIPACKAGE *package = (MSIPACKAGE*)param;
4487 LPCWSTR compgroupid=NULL;
4488 LPCWSTR feature=NULL;
4489 LPCWSTR text = NULL;
4490 LPCWSTR qualifier = NULL;
4491 LPCWSTR component = NULL;
4492 LPWSTR advertise = NULL;
4493 LPWSTR output = NULL;
4494 HKEY hkey;
4495 UINT rc = ERROR_SUCCESS;
4496 MSICOMPONENT *comp;
4497 DWORD sz = 0;
4498 MSIRECORD *uirow;
4500 component = MSI_RecordGetString(rec,3);
4501 comp = get_loaded_component(package,component);
4503 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4504 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4505 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4507 TRACE("Skipping: Component %s not scheduled for install\n",
4508 debugstr_w(component));
4510 return ERROR_SUCCESS;
4513 compgroupid = MSI_RecordGetString(rec,1);
4514 qualifier = MSI_RecordGetString(rec,2);
4516 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4517 if (rc != ERROR_SUCCESS)
4518 goto end;
4520 text = MSI_RecordGetString(rec,4);
4521 feature = MSI_RecordGetString(rec,5);
4523 advertise = create_component_advertise_string(package, comp, feature);
4525 sz = strlenW(advertise);
4527 if (text)
4528 sz += lstrlenW(text);
4530 sz+=3;
4531 sz *= sizeof(WCHAR);
4533 output = msi_alloc_zero(sz);
4534 strcpyW(output,advertise);
4535 msi_free(advertise);
4537 if (text)
4538 strcatW(output,text);
4540 msi_reg_set_val_multi_str( hkey, qualifier, output );
4542 end:
4543 RegCloseKey(hkey);
4544 msi_free(output);
4546 /* the UI chunk */
4547 uirow = MSI_CreateRecord( 2 );
4548 MSI_RecordSetStringW( uirow, 1, compgroupid );
4549 MSI_RecordSetStringW( uirow, 2, qualifier);
4550 ui_actiondata( package, szPublishComponents, uirow);
4551 msiobj_release( &uirow->hdr );
4552 /* FIXME: call ui_progress? */
4554 return rc;
4558 * At present I am ignorning the advertised components part of this and only
4559 * focusing on the qualified component sets
4561 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4563 UINT rc;
4564 MSIQUERY * view;
4565 static const WCHAR ExecSeqQuery[] =
4566 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4567 '`','P','u','b','l','i','s','h',
4568 'C','o','m','p','o','n','e','n','t','`',0};
4570 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4571 if (rc != ERROR_SUCCESS)
4572 return ERROR_SUCCESS;
4574 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4575 msiobj_release(&view->hdr);
4577 return rc;
4580 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4582 MSIPACKAGE *package = (MSIPACKAGE*)param;
4583 MSIRECORD *row;
4584 MSIFILE *file;
4585 SC_HANDLE hscm, service = NULL;
4586 LPCWSTR comp, depends, pass;
4587 LPWSTR name = NULL, disp = NULL;
4588 LPCWSTR load_order, serv_name, key;
4589 DWORD serv_type, start_type;
4590 DWORD err_control;
4592 static const WCHAR query[] =
4593 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4594 '`','C','o','m','p','o','n','e','n','t','`',' ',
4595 'W','H','E','R','E',' ',
4596 '`','C','o','m','p','o','n','e','n','t','`',' ',
4597 '=','\'','%','s','\'',0};
4599 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4600 if (!hscm)
4602 ERR("Failed to open the SC Manager!\n");
4603 goto done;
4606 start_type = MSI_RecordGetInteger(rec, 5);
4607 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4608 goto done;
4610 depends = MSI_RecordGetString(rec, 8);
4611 if (depends && *depends)
4612 FIXME("Dependency list unhandled!\n");
4614 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4615 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4616 serv_type = MSI_RecordGetInteger(rec, 4);
4617 err_control = MSI_RecordGetInteger(rec, 6);
4618 load_order = MSI_RecordGetString(rec, 7);
4619 serv_name = MSI_RecordGetString(rec, 9);
4620 pass = MSI_RecordGetString(rec, 10);
4621 comp = MSI_RecordGetString(rec, 12);
4623 /* fetch the service path */
4624 row = MSI_QueryGetRecord(package->db, query, comp);
4625 if (!row)
4627 ERR("Control query failed!\n");
4628 goto done;
4631 key = MSI_RecordGetString(row, 6);
4633 file = get_loaded_file(package, key);
4634 msiobj_release(&row->hdr);
4635 if (!file)
4637 ERR("Failed to load the service file\n");
4638 goto done;
4641 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4642 start_type, err_control, file->TargetPath,
4643 load_order, NULL, NULL, serv_name, pass);
4644 if (!service)
4646 if (GetLastError() != ERROR_SERVICE_EXISTS)
4647 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4650 done:
4651 CloseServiceHandle(service);
4652 CloseServiceHandle(hscm);
4653 msi_free(name);
4654 msi_free(disp);
4656 return ERROR_SUCCESS;
4659 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4661 UINT rc;
4662 MSIQUERY * view;
4663 static const WCHAR ExecSeqQuery[] =
4664 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4665 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4667 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4668 if (rc != ERROR_SUCCESS)
4669 return ERROR_SUCCESS;
4671 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4672 msiobj_release(&view->hdr);
4674 return rc;
4677 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4678 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4680 LPCWSTR *vector, *temp_vector;
4681 LPWSTR p, q;
4682 DWORD sep_len;
4684 static const WCHAR separator[] = {'[','~',']',0};
4686 *numargs = 0;
4687 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4689 if (!args)
4690 return NULL;
4692 vector = msi_alloc(sizeof(LPWSTR));
4693 if (!vector)
4694 return NULL;
4696 p = args;
4699 (*numargs)++;
4700 vector[*numargs - 1] = p;
4702 if ((q = strstrW(p, separator)))
4704 *q = '\0';
4706 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4707 if (!temp_vector)
4709 msi_free(vector);
4710 return NULL;
4712 vector = temp_vector;
4714 p = q + sep_len;
4716 } while (q);
4718 return vector;
4721 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4723 MSIPACKAGE *package = (MSIPACKAGE *)param;
4724 MSICOMPONENT *comp;
4725 SC_HANDLE scm, service = NULL;
4726 LPCWSTR name, *vector = NULL;
4727 LPWSTR args;
4728 DWORD event, numargs;
4729 UINT r = ERROR_FUNCTION_FAILED;
4731 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4732 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4733 return ERROR_SUCCESS;
4735 name = MSI_RecordGetString(rec, 2);
4736 event = MSI_RecordGetInteger(rec, 3);
4737 args = strdupW(MSI_RecordGetString(rec, 4));
4739 if (!(event & msidbServiceControlEventStart))
4740 return ERROR_SUCCESS;
4742 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4743 if (!scm)
4745 ERR("Failed to open the service control manager\n");
4746 goto done;
4749 service = OpenServiceW(scm, name, SERVICE_START);
4750 if (!service)
4752 ERR("Failed to open service %s\n", debugstr_w(name));
4753 goto done;
4756 vector = msi_service_args_to_vector(args, &numargs);
4758 if (!StartServiceW(service, numargs, vector))
4760 ERR("Failed to start service %s\n", debugstr_w(name));
4761 goto done;
4764 r = ERROR_SUCCESS;
4766 done:
4767 CloseServiceHandle(service);
4768 CloseServiceHandle(scm);
4770 msi_free(args);
4771 msi_free(vector);
4772 return r;
4775 static UINT ACTION_StartServices( MSIPACKAGE *package )
4777 UINT rc;
4778 MSIQUERY *view;
4780 static const WCHAR query[] = {
4781 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4782 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4784 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4785 if (rc != ERROR_SUCCESS)
4786 return ERROR_SUCCESS;
4788 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4789 msiobj_release(&view->hdr);
4791 return rc;
4794 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4796 DWORD i, needed, count;
4797 ENUM_SERVICE_STATUSW *dependencies;
4798 SERVICE_STATUS ss;
4799 SC_HANDLE depserv;
4801 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4802 0, &needed, &count))
4803 return TRUE;
4805 if (GetLastError() != ERROR_MORE_DATA)
4806 return FALSE;
4808 dependencies = msi_alloc(needed);
4809 if (!dependencies)
4810 return FALSE;
4812 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4813 needed, &needed, &count))
4814 goto error;
4816 for (i = 0; i < count; i++)
4818 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4819 SERVICE_STOP | SERVICE_QUERY_STATUS);
4820 if (!depserv)
4821 goto error;
4823 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4824 goto error;
4827 return TRUE;
4829 error:
4830 msi_free(dependencies);
4831 return FALSE;
4834 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4836 MSIPACKAGE *package = (MSIPACKAGE *)param;
4837 MSICOMPONENT *comp;
4838 SERVICE_STATUS status;
4839 SERVICE_STATUS_PROCESS ssp;
4840 SC_HANDLE scm = NULL, service = NULL;
4841 LPWSTR name, args;
4842 DWORD event, needed;
4844 event = MSI_RecordGetInteger(rec, 3);
4845 if (!(event & msidbServiceControlEventStop))
4846 return ERROR_SUCCESS;
4848 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4849 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4850 return ERROR_SUCCESS;
4852 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4853 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4854 args = strdupW(MSI_RecordGetString(rec, 4));
4856 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4857 if (!scm)
4859 WARN("Failed to open the SCM: %d\n", GetLastError());
4860 goto done;
4863 service = OpenServiceW(scm, name,
4864 SERVICE_STOP |
4865 SERVICE_QUERY_STATUS |
4866 SERVICE_ENUMERATE_DEPENDENTS);
4867 if (!service)
4869 WARN("Failed to open service (%s): %d\n",
4870 debugstr_w(name), GetLastError());
4871 goto done;
4874 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4875 sizeof(SERVICE_STATUS_PROCESS), &needed))
4877 WARN("Failed to query service status (%s): %d\n",
4878 debugstr_w(name), GetLastError());
4879 goto done;
4882 if (ssp.dwCurrentState == SERVICE_STOPPED)
4883 goto done;
4885 stop_service_dependents(scm, service);
4887 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4888 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4890 done:
4891 CloseServiceHandle(service);
4892 CloseServiceHandle(scm);
4893 msi_free(name);
4894 msi_free(args);
4896 return ERROR_SUCCESS;
4899 static UINT ACTION_StopServices( MSIPACKAGE *package )
4901 UINT rc;
4902 MSIQUERY *view;
4904 static const WCHAR query[] = {
4905 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4906 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4908 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4909 if (rc != ERROR_SUCCESS)
4910 return ERROR_SUCCESS;
4912 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4913 msiobj_release(&view->hdr);
4915 return rc;
4918 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4920 MSIFILE *file;
4922 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4924 if (!lstrcmpW(file->File, filename))
4925 return file;
4928 return NULL;
4931 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4933 MSIPACKAGE *package = (MSIPACKAGE*)param;
4934 LPWSTR driver, driver_path, ptr;
4935 WCHAR outpath[MAX_PATH];
4936 MSIFILE *driver_file, *setup_file;
4937 LPCWSTR desc;
4938 DWORD len, usage;
4939 UINT r = ERROR_SUCCESS;
4941 static const WCHAR driver_fmt[] = {
4942 'D','r','i','v','e','r','=','%','s',0};
4943 static const WCHAR setup_fmt[] = {
4944 'S','e','t','u','p','=','%','s',0};
4945 static const WCHAR usage_fmt[] = {
4946 'F','i','l','e','U','s','a','g','e','=','1',0};
4948 desc = MSI_RecordGetString(rec, 3);
4950 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4951 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4953 if (!driver_file || !setup_file)
4955 ERR("ODBC Driver entry not found!\n");
4956 return ERROR_FUNCTION_FAILED;
4959 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4960 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4961 lstrlenW(usage_fmt) + 1;
4962 driver = msi_alloc(len * sizeof(WCHAR));
4963 if (!driver)
4964 return ERROR_OUTOFMEMORY;
4966 ptr = driver;
4967 lstrcpyW(ptr, desc);
4968 ptr += lstrlenW(ptr) + 1;
4970 sprintfW(ptr, driver_fmt, driver_file->FileName);
4971 ptr += lstrlenW(ptr) + 1;
4973 sprintfW(ptr, setup_fmt, setup_file->FileName);
4974 ptr += lstrlenW(ptr) + 1;
4976 lstrcpyW(ptr, usage_fmt);
4977 ptr += lstrlenW(ptr) + 1;
4978 *ptr = '\0';
4980 driver_path = strdupW(driver_file->TargetPath);
4981 ptr = strrchrW(driver_path, '\\');
4982 if (ptr) *ptr = '\0';
4984 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4985 NULL, ODBC_INSTALL_COMPLETE, &usage))
4987 ERR("Failed to install SQL driver!\n");
4988 r = ERROR_FUNCTION_FAILED;
4991 msi_free(driver);
4992 msi_free(driver_path);
4994 return r;
4997 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4999 MSIPACKAGE *package = (MSIPACKAGE*)param;
5000 LPWSTR translator, translator_path, ptr;
5001 WCHAR outpath[MAX_PATH];
5002 MSIFILE *translator_file, *setup_file;
5003 LPCWSTR desc;
5004 DWORD len, usage;
5005 UINT r = ERROR_SUCCESS;
5007 static const WCHAR translator_fmt[] = {
5008 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5009 static const WCHAR setup_fmt[] = {
5010 'S','e','t','u','p','=','%','s',0};
5012 desc = MSI_RecordGetString(rec, 3);
5014 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5015 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5017 if (!translator_file || !setup_file)
5019 ERR("ODBC Translator entry not found!\n");
5020 return ERROR_FUNCTION_FAILED;
5023 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5024 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5025 translator = msi_alloc(len * sizeof(WCHAR));
5026 if (!translator)
5027 return ERROR_OUTOFMEMORY;
5029 ptr = translator;
5030 lstrcpyW(ptr, desc);
5031 ptr += lstrlenW(ptr) + 1;
5033 sprintfW(ptr, translator_fmt, translator_file->FileName);
5034 ptr += lstrlenW(ptr) + 1;
5036 sprintfW(ptr, setup_fmt, setup_file->FileName);
5037 ptr += lstrlenW(ptr) + 1;
5038 *ptr = '\0';
5040 translator_path = strdupW(translator_file->TargetPath);
5041 ptr = strrchrW(translator_path, '\\');
5042 if (ptr) *ptr = '\0';
5044 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5045 NULL, ODBC_INSTALL_COMPLETE, &usage))
5047 ERR("Failed to install SQL translator!\n");
5048 r = ERROR_FUNCTION_FAILED;
5051 msi_free(translator);
5052 msi_free(translator_path);
5054 return r;
5057 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5059 LPWSTR attrs;
5060 LPCWSTR desc, driver;
5061 WORD request = ODBC_ADD_SYS_DSN;
5062 INT registration;
5063 DWORD len;
5064 UINT r = ERROR_SUCCESS;
5066 static const WCHAR attrs_fmt[] = {
5067 'D','S','N','=','%','s',0 };
5069 desc = MSI_RecordGetString(rec, 3);
5070 driver = MSI_RecordGetString(rec, 4);
5071 registration = MSI_RecordGetInteger(rec, 5);
5073 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5074 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5076 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5077 attrs = msi_alloc(len * sizeof(WCHAR));
5078 if (!attrs)
5079 return ERROR_OUTOFMEMORY;
5081 sprintfW(attrs, attrs_fmt, desc);
5082 attrs[len - 1] = '\0';
5084 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5086 ERR("Failed to install SQL data source!\n");
5087 r = ERROR_FUNCTION_FAILED;
5090 msi_free(attrs);
5092 return r;
5095 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5097 UINT rc;
5098 MSIQUERY *view;
5100 static const WCHAR driver_query[] = {
5101 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5102 'O','D','B','C','D','r','i','v','e','r',0 };
5104 static const WCHAR translator_query[] = {
5105 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5106 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5108 static const WCHAR source_query[] = {
5109 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5110 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5112 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5113 if (rc != ERROR_SUCCESS)
5114 return ERROR_SUCCESS;
5116 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5117 msiobj_release(&view->hdr);
5119 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5120 if (rc != ERROR_SUCCESS)
5121 return ERROR_SUCCESS;
5123 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5124 msiobj_release(&view->hdr);
5126 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5127 if (rc != ERROR_SUCCESS)
5128 return ERROR_SUCCESS;
5130 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5131 msiobj_release(&view->hdr);
5133 return rc;
5136 #define ENV_ACT_SETALWAYS 0x1
5137 #define ENV_ACT_SETABSENT 0x2
5138 #define ENV_ACT_REMOVE 0x4
5139 #define ENV_ACT_REMOVEMATCH 0x8
5141 #define ENV_MOD_MACHINE 0x20000000
5142 #define ENV_MOD_APPEND 0x40000000
5143 #define ENV_MOD_PREFIX 0x80000000
5144 #define ENV_MOD_MASK 0xC0000000
5146 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5148 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5150 LPCWSTR cptr = *name;
5151 LPCWSTR ptr = *value;
5153 static const WCHAR prefix[] = {'[','~',']',0};
5154 static const int prefix_len = 3;
5156 *flags = 0;
5157 while (*cptr)
5159 if (*cptr == '=')
5160 *flags |= ENV_ACT_SETALWAYS;
5161 else if (*cptr == '+')
5162 *flags |= ENV_ACT_SETABSENT;
5163 else if (*cptr == '-')
5164 *flags |= ENV_ACT_REMOVE;
5165 else if (*cptr == '!')
5166 *flags |= ENV_ACT_REMOVEMATCH;
5167 else if (*cptr == '*')
5168 *flags |= ENV_MOD_MACHINE;
5169 else
5170 break;
5172 cptr++;
5173 (*name)++;
5176 if (!*cptr)
5178 ERR("Missing environment variable\n");
5179 return ERROR_FUNCTION_FAILED;
5182 if (!strncmpW(ptr, prefix, prefix_len))
5184 *flags |= ENV_MOD_APPEND;
5185 *value += lstrlenW(prefix);
5187 else if (lstrlenW(*value) >= prefix_len)
5189 ptr += lstrlenW(ptr) - prefix_len;
5190 if (!lstrcmpW(ptr, prefix))
5192 *flags |= ENV_MOD_PREFIX;
5193 /* the "[~]" will be removed by deformat_string */;
5197 if (!*flags ||
5198 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5199 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5200 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5201 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5203 ERR("Invalid flags: %08x\n", *flags);
5204 return ERROR_FUNCTION_FAILED;
5207 return ERROR_SUCCESS;
5210 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5212 MSIPACKAGE *package = param;
5213 LPCWSTR name, value;
5214 LPWSTR data = NULL, newval = NULL;
5215 LPWSTR deformatted = NULL, ptr;
5216 DWORD flags, type, size;
5217 LONG res;
5218 HKEY env = NULL, root;
5219 LPCWSTR environment;
5221 static const WCHAR user_env[] =
5222 {'E','n','v','i','r','o','n','m','e','n','t',0};
5223 static const WCHAR machine_env[] =
5224 {'S','y','s','t','e','m','\\',
5225 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5226 'C','o','n','t','r','o','l','\\',
5227 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5228 'E','n','v','i','r','o','n','m','e','n','t',0};
5229 static const WCHAR semicolon[] = {';',0};
5231 name = MSI_RecordGetString(rec, 2);
5232 value = MSI_RecordGetString(rec, 3);
5234 res = env_set_flags(&name, &value, &flags);
5235 if (res != ERROR_SUCCESS)
5236 goto done;
5238 deformat_string(package, value, &deformatted);
5239 if (!deformatted)
5241 res = ERROR_OUTOFMEMORY;
5242 goto done;
5245 value = deformatted;
5247 if (flags & ENV_MOD_MACHINE)
5249 environment = machine_env;
5250 root = HKEY_LOCAL_MACHINE;
5252 else
5254 environment = user_env;
5255 root = HKEY_CURRENT_USER;
5258 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5259 KEY_ALL_ACCESS, NULL, &env, NULL);
5260 if (res != ERROR_SUCCESS)
5261 goto done;
5263 if (flags & ENV_ACT_REMOVE)
5264 FIXME("Not removing environment variable on uninstall!\n");
5266 size = 0;
5267 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5268 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5269 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5270 goto done;
5272 if (res != ERROR_FILE_NOT_FOUND)
5274 if (flags & ENV_ACT_SETABSENT)
5276 res = ERROR_SUCCESS;
5277 goto done;
5280 data = msi_alloc(size);
5281 if (!data)
5283 RegCloseKey(env);
5284 return ERROR_OUTOFMEMORY;
5287 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5288 if (res != ERROR_SUCCESS)
5289 goto done;
5291 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5293 res = RegDeleteKeyW(env, name);
5294 goto done;
5297 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5298 newval = msi_alloc(size);
5299 ptr = newval;
5300 if (!newval)
5302 res = ERROR_OUTOFMEMORY;
5303 goto done;
5306 if (!(flags & ENV_MOD_MASK))
5307 lstrcpyW(newval, value);
5308 else
5310 if (flags & ENV_MOD_PREFIX)
5312 lstrcpyW(newval, value);
5313 lstrcatW(newval, semicolon);
5314 ptr = newval + lstrlenW(value) + 1;
5317 lstrcpyW(ptr, data);
5319 if (flags & ENV_MOD_APPEND)
5321 lstrcatW(newval, semicolon);
5322 lstrcatW(newval, value);
5326 else
5328 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5329 newval = msi_alloc(size);
5330 if (!newval)
5332 res = ERROR_OUTOFMEMORY;
5333 goto done;
5336 lstrcpyW(newval, value);
5339 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5340 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5342 done:
5343 if (env) RegCloseKey(env);
5344 msi_free(deformatted);
5345 msi_free(data);
5346 msi_free(newval);
5347 return res;
5350 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5352 UINT rc;
5353 MSIQUERY * view;
5354 static const WCHAR ExecSeqQuery[] =
5355 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5356 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5357 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5358 if (rc != ERROR_SUCCESS)
5359 return ERROR_SUCCESS;
5361 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5362 msiobj_release(&view->hdr);
5364 return rc;
5367 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5369 typedef struct
5371 struct list entry;
5372 LPWSTR sourcename;
5373 LPWSTR destname;
5374 LPWSTR source;
5375 LPWSTR dest;
5376 } FILE_LIST;
5378 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5380 BOOL ret;
5382 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5383 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5385 WARN("Source or dest is directory, not moving\n");
5386 return FALSE;
5389 if (options == msidbMoveFileOptionsMove)
5391 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5392 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5393 if (!ret)
5395 WARN("MoveFile failed: %d\n", GetLastError());
5396 return FALSE;
5399 else
5401 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5402 ret = CopyFileW(source, dest, FALSE);
5403 if (!ret)
5405 WARN("CopyFile failed: %d\n", GetLastError());
5406 return FALSE;
5410 return TRUE;
5413 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5415 LPWSTR path, ptr;
5416 DWORD dirlen, pathlen;
5418 ptr = strrchrW(wildcard, '\\');
5419 dirlen = ptr - wildcard + 1;
5421 pathlen = dirlen + lstrlenW(filename) + 1;
5422 path = msi_alloc(pathlen * sizeof(WCHAR));
5424 lstrcpynW(path, wildcard, dirlen + 1);
5425 lstrcatW(path, filename);
5427 return path;
5430 static void free_file_entry(FILE_LIST *file)
5432 msi_free(file->source);
5433 msi_free(file->dest);
5434 msi_free(file);
5437 static void free_list(FILE_LIST *list)
5439 while (!list_empty(&list->entry))
5441 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5443 list_remove(&file->entry);
5444 free_file_entry(file);
5448 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5450 FILE_LIST *new, *file;
5451 LPWSTR ptr, filename;
5452 DWORD size;
5454 new = msi_alloc_zero(sizeof(FILE_LIST));
5455 if (!new)
5456 return FALSE;
5458 new->source = strdupW(source);
5459 ptr = strrchrW(dest, '\\') + 1;
5460 filename = strrchrW(new->source, '\\') + 1;
5462 new->sourcename = filename;
5464 if (*ptr)
5465 new->destname = ptr;
5466 else
5467 new->destname = new->sourcename;
5469 size = (ptr - dest) + lstrlenW(filename) + 1;
5470 new->dest = msi_alloc(size * sizeof(WCHAR));
5471 if (!new->dest)
5473 free_file_entry(new);
5474 return FALSE;
5477 lstrcpynW(new->dest, dest, ptr - dest + 1);
5478 lstrcatW(new->dest, filename);
5480 if (list_empty(&files->entry))
5482 list_add_head(&files->entry, &new->entry);
5483 return TRUE;
5486 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5488 if (lstrcmpW(source, file->source) < 0)
5490 list_add_before(&file->entry, &new->entry);
5491 return TRUE;
5495 list_add_after(&file->entry, &new->entry);
5496 return TRUE;
5499 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5501 WIN32_FIND_DATAW wfd;
5502 HANDLE hfile;
5503 LPWSTR path;
5504 BOOL res;
5505 FILE_LIST files, *file;
5506 DWORD size;
5508 hfile = FindFirstFileW(source, &wfd);
5509 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5511 list_init(&files.entry);
5513 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5515 if (is_dot_dir(wfd.cFileName)) continue;
5517 path = wildcard_to_file(source, wfd.cFileName);
5518 if (!path)
5520 res = FALSE;
5521 goto done;
5524 add_wildcard(&files, path, dest);
5525 msi_free(path);
5528 /* no files match the wildcard */
5529 if (list_empty(&files.entry))
5530 goto done;
5532 /* only the first wildcard match gets renamed to dest */
5533 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5534 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5535 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5536 if (!file->dest)
5538 res = FALSE;
5539 goto done;
5542 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5544 while (!list_empty(&files.entry))
5546 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5548 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5550 list_remove(&file->entry);
5551 free_file_entry(file);
5554 res = TRUE;
5556 done:
5557 free_list(&files);
5558 FindClose(hfile);
5559 return res;
5562 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5564 MSIPACKAGE *package = param;
5565 MSICOMPONENT *comp;
5566 LPCWSTR sourcename, destname;
5567 LPWSTR sourcedir = NULL, destdir = NULL;
5568 LPWSTR source = NULL, dest = NULL;
5569 int options;
5570 DWORD size;
5571 BOOL ret, wildcards;
5573 static const WCHAR backslash[] = {'\\',0};
5575 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5576 if (!comp || !comp->Enabled ||
5577 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5579 TRACE("Component not set for install, not moving file\n");
5580 return ERROR_SUCCESS;
5583 sourcename = MSI_RecordGetString(rec, 3);
5584 destname = MSI_RecordGetString(rec, 4);
5585 options = MSI_RecordGetInteger(rec, 7);
5587 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5588 if (!sourcedir)
5589 goto done;
5591 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5592 if (!destdir)
5593 goto done;
5595 if (!sourcename)
5597 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5598 goto done;
5600 source = strdupW(sourcedir);
5601 if (!source)
5602 goto done;
5604 else
5606 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5607 source = msi_alloc(size * sizeof(WCHAR));
5608 if (!source)
5609 goto done;
5611 lstrcpyW(source, sourcedir);
5612 if (source[lstrlenW(source) - 1] != '\\')
5613 lstrcatW(source, backslash);
5614 lstrcatW(source, sourcename);
5617 wildcards = strchrW(source, '*') || strchrW(source, '?');
5619 if (!destname && !wildcards)
5621 destname = strdupW(sourcename);
5622 if (!destname)
5623 goto done;
5626 size = 0;
5627 if (destname)
5628 size = lstrlenW(destname);
5630 size += lstrlenW(destdir) + 2;
5631 dest = msi_alloc(size * sizeof(WCHAR));
5632 if (!dest)
5633 goto done;
5635 lstrcpyW(dest, destdir);
5636 if (dest[lstrlenW(dest) - 1] != '\\')
5637 lstrcatW(dest, backslash);
5639 if (destname)
5640 lstrcatW(dest, destname);
5642 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5644 ret = CreateDirectoryW(destdir, NULL);
5645 if (!ret)
5647 WARN("CreateDirectory failed: %d\n", GetLastError());
5648 return ERROR_SUCCESS;
5652 if (!wildcards)
5653 msi_move_file(source, dest, options);
5654 else
5655 move_files_wildcard(source, dest, options);
5657 done:
5658 msi_free(sourcedir);
5659 msi_free(destdir);
5660 msi_free(source);
5661 msi_free(dest);
5663 return ERROR_SUCCESS;
5666 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5668 UINT rc;
5669 MSIQUERY *view;
5671 static const WCHAR ExecSeqQuery[] =
5672 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5673 '`','M','o','v','e','F','i','l','e','`',0};
5675 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5676 if (rc != ERROR_SUCCESS)
5677 return ERROR_SUCCESS;
5679 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5680 msiobj_release(&view->hdr);
5682 return rc;
5685 typedef struct tagMSIASSEMBLY
5687 struct list entry;
5688 MSICOMPONENT *component;
5689 MSIFEATURE *feature;
5690 MSIFILE *file;
5691 LPWSTR manifest;
5692 LPWSTR application;
5693 DWORD attributes;
5694 BOOL installed;
5695 } MSIASSEMBLY;
5697 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5698 DWORD dwReserved);
5699 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5700 LPVOID pvReserved, HMODULE *phModDll);
5702 static BOOL init_functionpointers(void)
5704 HRESULT hr;
5705 HMODULE hfusion;
5706 HMODULE hmscoree;
5708 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5710 hmscoree = LoadLibraryA("mscoree.dll");
5711 if (!hmscoree)
5713 WARN("mscoree.dll not available\n");
5714 return FALSE;
5717 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5718 if (!pLoadLibraryShim)
5720 WARN("LoadLibraryShim not available\n");
5721 FreeLibrary(hmscoree);
5722 return FALSE;
5725 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5726 if (FAILED(hr))
5728 WARN("fusion.dll not available\n");
5729 FreeLibrary(hmscoree);
5730 return FALSE;
5733 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5735 FreeLibrary(hmscoree);
5736 return TRUE;
5739 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5740 LPWSTR path)
5742 IAssemblyCache *cache;
5743 HRESULT hr;
5744 UINT r = ERROR_FUNCTION_FAILED;
5746 TRACE("installing assembly: %s\n", debugstr_w(path));
5748 if (assembly->feature)
5749 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5751 if (assembly->manifest)
5752 FIXME("Manifest unhandled\n");
5754 if (assembly->application)
5756 FIXME("Assembly should be privately installed\n");
5757 return ERROR_SUCCESS;
5760 if (assembly->attributes == msidbAssemblyAttributesWin32)
5762 FIXME("Win32 assemblies not handled\n");
5763 return ERROR_SUCCESS;
5766 if (!init_functionpointers() || !pCreateAssemblyCache)
5767 return ERROR_FUNCTION_FAILED;
5769 hr = pCreateAssemblyCache(&cache, 0);
5770 if (FAILED(hr))
5771 goto done;
5773 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5774 if (FAILED(hr))
5775 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5777 r = ERROR_SUCCESS;
5779 done:
5780 IAssemblyCache_Release(cache);
5781 return r;
5784 typedef struct tagASSEMBLY_LIST
5786 MSIPACKAGE *package;
5787 struct list *assemblies;
5788 } ASSEMBLY_LIST;
5790 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5792 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5793 MSIASSEMBLY *assembly;
5795 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5796 if (!assembly)
5797 return ERROR_OUTOFMEMORY;
5799 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5801 if (!assembly->component || !assembly->component->Enabled ||
5802 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5804 TRACE("Component not set for install, not publishing assembly\n");
5805 msi_free(assembly);
5806 return ERROR_SUCCESS;
5809 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5810 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5812 if (!assembly->file)
5814 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5815 return ERROR_FUNCTION_FAILED;
5818 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5819 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5820 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5821 assembly->installed = FALSE;
5823 list_add_head(list->assemblies, &assembly->entry);
5825 return ERROR_SUCCESS;
5828 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5830 MSIQUERY *view;
5831 ASSEMBLY_LIST list;
5832 UINT r;
5834 static const WCHAR query[] =
5835 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5836 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5838 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5839 if (r != ERROR_SUCCESS)
5840 return ERROR_SUCCESS;
5842 list.package = package;
5843 list.assemblies = assemblies;
5845 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5846 msiobj_release(&view->hdr);
5848 return r;
5851 static void free_assemblies(struct list *assemblies)
5853 struct list *item, *cursor;
5855 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5857 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5859 list_remove(&assembly->entry);
5860 msi_free(assembly->application);
5861 msi_free(assembly->manifest);
5862 msi_free(assembly);
5866 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5868 MSIASSEMBLY *assembly;
5870 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5872 if (!lstrcmpW(assembly->file->File, file))
5874 *out = assembly;
5875 return TRUE;
5879 return FALSE;
5882 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5883 LPWSTR *path, DWORD *attrs, PVOID user)
5885 MSIASSEMBLY *assembly;
5886 WCHAR temppath[MAX_PATH];
5887 struct list *assemblies = (struct list *)user;
5888 UINT r;
5890 if (!find_assembly(assemblies, file, &assembly))
5891 return FALSE;
5893 GetTempPathW(MAX_PATH, temppath);
5894 PathAddBackslashW(temppath);
5895 lstrcatW(temppath, assembly->file->FileName);
5897 if (action == MSICABEXTRACT_BEGINEXTRACT)
5899 if (assembly->installed)
5900 return FALSE;
5902 *path = strdupW(temppath);
5903 *attrs = assembly->file->Attributes;
5905 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5907 assembly->installed = TRUE;
5909 r = install_assembly(package, assembly, temppath);
5910 if (r != ERROR_SUCCESS)
5911 ERR("Failed to install assembly\n");
5914 return TRUE;
5917 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5919 UINT r;
5920 struct list assemblies = LIST_INIT(assemblies);
5921 MSIASSEMBLY *assembly;
5922 MSIMEDIAINFO *mi;
5923 WCHAR path[MAX_PATH];
5925 r = load_assemblies(package, &assemblies);
5926 if (r != ERROR_SUCCESS)
5927 goto done;
5929 if (list_empty(&assemblies))
5930 goto done;
5932 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5933 if (!mi)
5935 r = ERROR_OUTOFMEMORY;
5936 goto done;
5939 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5941 if (assembly->installed && !mi->is_continuous)
5942 continue;
5944 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5945 (assembly->file->IsCompressed && !mi->is_extracted))
5947 MSICABDATA data;
5949 r = ready_media(package, assembly->file, mi);
5950 if (r != ERROR_SUCCESS)
5952 ERR("Failed to ready media\n");
5953 break;
5956 data.mi = mi;
5957 data.package = package;
5958 data.cb = installassembly_cb;
5959 data.user = &assemblies;
5961 if (assembly->file->IsCompressed &&
5962 !msi_cabextract(package, mi, &data))
5964 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5965 r = ERROR_FUNCTION_FAILED;
5966 break;
5970 if (!assembly->file->IsCompressed)
5972 lstrcpyW(path, assembly->file->SourcePath);
5974 r = install_assembly(package, assembly, path);
5975 if (r != ERROR_SUCCESS)
5976 ERR("Failed to install assembly\n");
5979 /* FIXME: write Installer assembly reg values */
5982 done:
5983 free_assemblies(&assemblies);
5984 return r;
5987 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5988 LPCSTR action, LPCWSTR table )
5990 static const WCHAR query[] = {
5991 'S','E','L','E','C','T',' ','*',' ',
5992 'F','R','O','M',' ','`','%','s','`',0 };
5993 MSIQUERY *view = NULL;
5994 DWORD count = 0;
5995 UINT r;
5997 r = MSI_OpenQuery( package->db, &view, query, table );
5998 if (r == ERROR_SUCCESS)
6000 r = MSI_IterateRecords(view, &count, NULL, package);
6001 msiobj_release(&view->hdr);
6004 if (count)
6005 FIXME("%s -> %u ignored %s table values\n",
6006 action, count, debugstr_w(table));
6008 return ERROR_SUCCESS;
6011 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6013 TRACE("%p\n", package);
6014 return ERROR_SUCCESS;
6017 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6019 static const WCHAR table[] =
6020 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6021 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6024 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6026 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6027 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6030 static UINT ACTION_BindImage( MSIPACKAGE *package )
6032 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6033 return msi_unimplemented_action_stub( package, "BindImage", table );
6036 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6038 static const WCHAR table[] = {
6039 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6040 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6043 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6045 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6046 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6049 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6051 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6052 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6055 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6057 static const WCHAR table[] = {
6058 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6059 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6061 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6063 static const WCHAR table[] = {
6064 'P','r','o','d','u','c','t','I','D',0 };
6065 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6068 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6070 static const WCHAR table[] = {
6071 'E','n','v','i','r','o','n','m','e','n','t',0 };
6072 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6075 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6077 static const WCHAR table[] = {
6078 'M','s','i','A','s','s','e','m','b','l','y',0 };
6079 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6082 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6084 static const WCHAR table[] = { 'F','o','n','t',0 };
6085 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6088 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6090 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6091 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6094 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6096 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6097 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6100 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6102 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6103 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6106 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6108 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6109 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6112 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6114 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6115 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6118 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6120 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6121 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6124 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6126 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6127 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6130 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6132 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6133 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6136 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6138 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6139 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6142 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6144 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6145 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6148 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6150 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6151 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6154 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6156 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6157 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6160 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6162 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6163 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6166 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6168 static const WCHAR table[] = { 'M','I','M','E',0 };
6169 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6172 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6174 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6175 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6178 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6180 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6181 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6184 static const struct _actions StandardActions[] = {
6185 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6186 { szAppSearch, ACTION_AppSearch },
6187 { szBindImage, ACTION_BindImage },
6188 { szCCPSearch, ACTION_CCPSearch },
6189 { szCostFinalize, ACTION_CostFinalize },
6190 { szCostInitialize, ACTION_CostInitialize },
6191 { szCreateFolders, ACTION_CreateFolders },
6192 { szCreateShortcuts, ACTION_CreateShortcuts },
6193 { szDeleteServices, ACTION_DeleteServices },
6194 { szDisableRollback, NULL },
6195 { szDuplicateFiles, ACTION_DuplicateFiles },
6196 { szExecuteAction, ACTION_ExecuteAction },
6197 { szFileCost, ACTION_FileCost },
6198 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6199 { szForceReboot, ACTION_ForceReboot },
6200 { szInstallAdminPackage, NULL },
6201 { szInstallExecute, ACTION_InstallExecute },
6202 { szInstallExecuteAgain, ACTION_InstallExecute },
6203 { szInstallFiles, ACTION_InstallFiles},
6204 { szInstallFinalize, ACTION_InstallFinalize },
6205 { szInstallInitialize, ACTION_InstallInitialize },
6206 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6207 { szInstallValidate, ACTION_InstallValidate },
6208 { szIsolateComponents, ACTION_IsolateComponents },
6209 { szLaunchConditions, ACTION_LaunchConditions },
6210 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6211 { szMoveFiles, ACTION_MoveFiles },
6212 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6213 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6214 { szInstallODBC, ACTION_InstallODBC },
6215 { szInstallServices, ACTION_InstallServices },
6216 { szPatchFiles, ACTION_PatchFiles },
6217 { szProcessComponents, ACTION_ProcessComponents },
6218 { szPublishComponents, ACTION_PublishComponents },
6219 { szPublishFeatures, ACTION_PublishFeatures },
6220 { szPublishProduct, ACTION_PublishProduct },
6221 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6222 { szRegisterComPlus, ACTION_RegisterComPlus},
6223 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6224 { szRegisterFonts, ACTION_RegisterFonts },
6225 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6226 { szRegisterProduct, ACTION_RegisterProduct },
6227 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6228 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6229 { szRegisterUser, ACTION_RegisterUser },
6230 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6231 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6232 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6233 { szRemoveFiles, ACTION_RemoveFiles },
6234 { szRemoveFolders, ACTION_RemoveFolders },
6235 { szRemoveIniValues, ACTION_RemoveIniValues },
6236 { szRemoveODBC, ACTION_RemoveODBC },
6237 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6238 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6239 { szResolveSource, ACTION_ResolveSource },
6240 { szRMCCPSearch, ACTION_RMCCPSearch },
6241 { szScheduleReboot, NULL },
6242 { szSelfRegModules, ACTION_SelfRegModules },
6243 { szSelfUnregModules, ACTION_SelfUnregModules },
6244 { szSetODBCFolders, NULL },
6245 { szStartServices, ACTION_StartServices },
6246 { szStopServices, ACTION_StopServices },
6247 { szUnpublishComponents, ACTION_UnpublishComponents },
6248 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6249 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6250 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6251 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6252 { szUnregisterFonts, ACTION_UnregisterFonts },
6253 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6254 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6255 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6256 { szValidateProductID, ACTION_ValidateProductID },
6257 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6258 { szWriteIniValues, ACTION_WriteIniValues },
6259 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6260 { NULL, NULL },
6263 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6264 UINT* rc, BOOL force )
6266 BOOL ret = FALSE;
6267 BOOL run = force;
6268 int i;
6270 if (!run && !package->script->CurrentlyScripting)
6271 run = TRUE;
6273 if (!run)
6275 if (strcmpW(action,szInstallFinalize) == 0 ||
6276 strcmpW(action,szInstallExecute) == 0 ||
6277 strcmpW(action,szInstallExecuteAgain) == 0)
6278 run = TRUE;
6281 i = 0;
6282 while (StandardActions[i].action != NULL)
6284 if (strcmpW(StandardActions[i].action, action)==0)
6286 if (!run)
6288 ui_actioninfo(package, action, TRUE, 0);
6289 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6290 ui_actioninfo(package, action, FALSE, *rc);
6292 else
6294 ui_actionstart(package, action);
6295 if (StandardActions[i].handler)
6297 *rc = StandardActions[i].handler(package);
6299 else
6301 FIXME("unhandled standard action %s\n",debugstr_w(action));
6302 *rc = ERROR_SUCCESS;
6305 ret = TRUE;
6306 break;
6308 i++;
6310 return ret;