push 8742e928a3d078c1d2cecb5ceee0ffde3118cbb7
[wine/hacks.git] / dlls / msi / action.c
blob0c5741789140ab046ac5c0d23ec6391abbf95712
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 (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1486 file->IsCompressed = FALSE;
1488 else if (file->Attributes &
1489 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1491 file->IsCompressed = TRUE;
1493 else if (file->Attributes & msidbFileAttributesNoncompressed)
1495 file->IsCompressed = FALSE;
1497 else
1499 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1502 load_file_hash(package, file);
1504 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1506 list_add_tail( &package->files, &file->entry );
1508 return ERROR_SUCCESS;
1511 static UINT load_all_files(MSIPACKAGE *package)
1513 MSIQUERY * view;
1514 UINT rc;
1515 static const WCHAR Query[] =
1516 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1517 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1518 '`','S','e','q','u','e','n','c','e','`', 0};
1520 if (!list_empty(&package->files))
1521 return ERROR_SUCCESS;
1523 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1524 if (rc != ERROR_SUCCESS)
1525 return ERROR_SUCCESS;
1527 rc = MSI_IterateRecords(view, NULL, load_file, package);
1528 msiobj_release(&view->hdr);
1530 return ERROR_SUCCESS;
1533 static UINT load_folder( MSIRECORD *row, LPVOID param )
1535 MSIPACKAGE *package = param;
1536 static const WCHAR szDot[] = { '.',0 };
1537 static WCHAR szEmpty[] = { 0 };
1538 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1539 MSIFOLDER *folder;
1541 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1542 if (!folder)
1543 return ERROR_NOT_ENOUGH_MEMORY;
1545 folder->Directory = msi_dup_record_field( row, 1 );
1547 TRACE("%s\n", debugstr_w(folder->Directory));
1549 p = msi_dup_record_field(row, 3);
1551 /* split src and target dir */
1552 tgt_short = p;
1553 src_short = folder_split_path( p, ':' );
1555 /* split the long and short paths */
1556 tgt_long = folder_split_path( tgt_short, '|' );
1557 src_long = folder_split_path( src_short, '|' );
1559 /* check for no-op dirs */
1560 if (!lstrcmpW(szDot, tgt_short))
1561 tgt_short = szEmpty;
1562 if (!lstrcmpW(szDot, src_short))
1563 src_short = szEmpty;
1565 if (!tgt_long)
1566 tgt_long = tgt_short;
1568 if (!src_short) {
1569 src_short = tgt_short;
1570 src_long = tgt_long;
1573 if (!src_long)
1574 src_long = src_short;
1576 /* FIXME: use the target short path too */
1577 folder->TargetDefault = strdupW(tgt_long);
1578 folder->SourceShortPath = strdupW(src_short);
1579 folder->SourceLongPath = strdupW(src_long);
1580 msi_free(p);
1582 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1583 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1584 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1586 folder->Parent = msi_dup_record_field( row, 2 );
1588 folder->Property = msi_dup_property( package, folder->Directory );
1590 list_add_tail( &package->folders, &folder->entry );
1592 TRACE("returning %p\n", folder);
1594 return ERROR_SUCCESS;
1597 static UINT load_all_folders( MSIPACKAGE *package )
1599 static const WCHAR query[] = {
1600 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1601 '`','D','i','r','e','c','t','o','r','y','`',0 };
1602 MSIQUERY *view;
1603 UINT r;
1605 if (!list_empty(&package->folders))
1606 return ERROR_SUCCESS;
1608 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1609 if (r != ERROR_SUCCESS)
1610 return r;
1612 r = MSI_IterateRecords(view, NULL, load_folder, package);
1613 msiobj_release(&view->hdr);
1614 return r;
1618 * I am not doing any of the costing functionality yet.
1619 * Mostly looking at doing the Component and Feature loading
1621 * The native MSI does A LOT of modification to tables here. Mostly adding
1622 * a lot of temporary columns to the Feature and Component tables.
1624 * note: Native msi also tracks the short filename. But I am only going to
1625 * track the long ones. Also looking at this directory table
1626 * it appears that the directory table does not get the parents
1627 * resolved base on property only based on their entries in the
1628 * directory table.
1630 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1632 static const WCHAR szCosting[] =
1633 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1634 static const WCHAR szZero[] = { '0', 0 };
1636 MSI_SetPropertyW(package, szCosting, szZero);
1637 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1639 load_all_folders( package );
1640 load_all_components( package );
1641 load_all_features( package );
1642 load_all_files( package );
1644 return ERROR_SUCCESS;
1647 static UINT execute_script(MSIPACKAGE *package, UINT script )
1649 UINT i;
1650 UINT rc = ERROR_SUCCESS;
1652 TRACE("Executing Script %i\n",script);
1654 if (!package->script)
1656 ERR("no script!\n");
1657 return ERROR_FUNCTION_FAILED;
1660 for (i = 0; i < package->script->ActionCount[script]; i++)
1662 LPWSTR action;
1663 action = package->script->Actions[script][i];
1664 ui_actionstart(package, action);
1665 TRACE("Executing Action (%s)\n",debugstr_w(action));
1666 rc = ACTION_PerformAction(package, action, script, TRUE);
1667 if (rc != ERROR_SUCCESS)
1668 break;
1670 msi_free_action_script(package, script);
1671 return rc;
1674 static UINT ACTION_FileCost(MSIPACKAGE *package)
1676 return ERROR_SUCCESS;
1679 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1681 MSICOMPONENT *comp;
1682 INSTALLSTATE state;
1683 UINT r;
1685 state = MsiQueryProductStateW(package->ProductCode);
1687 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1689 if (!comp->ComponentId)
1690 continue;
1692 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1693 comp->Installed = INSTALLSTATE_ABSENT;
1694 else
1696 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1697 package->Context, comp->ComponentId,
1698 &comp->Installed);
1699 if (r != ERROR_SUCCESS)
1700 comp->Installed = INSTALLSTATE_ABSENT;
1705 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1707 MSIFEATURE *feature;
1708 INSTALLSTATE state;
1710 state = MsiQueryProductStateW(package->ProductCode);
1712 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1714 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1715 feature->Installed = INSTALLSTATE_ABSENT;
1716 else
1718 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1719 feature->Feature);
1724 static BOOL process_state_property(MSIPACKAGE* package, int level,
1725 LPCWSTR property, INSTALLSTATE state)
1727 static const WCHAR all[]={'A','L','L',0};
1728 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1729 LPWSTR override;
1730 MSIFEATURE *feature;
1732 override = msi_dup_property( package, property );
1733 if (!override)
1734 return FALSE;
1736 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1738 if (lstrcmpW(property, remove) &&
1739 (feature->Level <= 0 || feature->Level > level))
1740 continue;
1742 if (strcmpiW(override,all)==0)
1743 msi_feature_set_state(package, feature, state);
1744 else
1746 LPWSTR ptr = override;
1747 LPWSTR ptr2 = strchrW(override,',');
1749 while (ptr)
1751 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1752 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1754 msi_feature_set_state(package, feature, state);
1755 break;
1757 if (ptr2)
1759 ptr=ptr2+1;
1760 ptr2 = strchrW(ptr,',');
1762 else
1763 break;
1767 msi_free(override);
1769 return TRUE;
1772 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1774 int level;
1775 static const WCHAR szlevel[] =
1776 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1777 static const WCHAR szAddLocal[] =
1778 {'A','D','D','L','O','C','A','L',0};
1779 static const WCHAR szAddSource[] =
1780 {'A','D','D','S','O','U','R','C','E',0};
1781 static const WCHAR szRemove[] =
1782 {'R','E','M','O','V','E',0};
1783 static const WCHAR szReinstall[] =
1784 {'R','E','I','N','S','T','A','L','L',0};
1785 BOOL override = FALSE;
1786 MSICOMPONENT* component;
1787 MSIFEATURE *feature;
1790 /* I do not know if this is where it should happen.. but */
1792 TRACE("Checking Install Level\n");
1794 level = msi_get_property_int(package, szlevel, 1);
1796 /* ok here is the _real_ rub
1797 * all these activation/deactivation things happen in order and things
1798 * later on the list override things earlier on the list.
1799 * 1) INSTALLLEVEL processing
1800 * 2) ADDLOCAL
1801 * 3) REMOVE
1802 * 4) ADDSOURCE
1803 * 5) ADDDEFAULT
1804 * 6) REINSTALL
1805 * 7) COMPADDLOCAL
1806 * 8) COMPADDSOURCE
1807 * 9) FILEADDLOCAL
1808 * 10) FILEADDSOURCE
1809 * 11) FILEADDDEFAULT
1811 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1812 * REMOVE are the big ones, since we don't handle administrative installs
1813 * yet anyway.
1815 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1816 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1817 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1818 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1820 if (!override)
1822 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1824 BOOL feature_state = ((feature->Level > 0) &&
1825 (feature->Level <= level));
1827 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1829 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1830 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1831 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1832 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1833 else
1834 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1838 /* disable child features of unselected parent features */
1839 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1841 FeatureList *fl;
1843 if (feature->Level > 0 && feature->Level <= level)
1844 continue;
1846 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1847 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1850 else
1852 /* set the Preselected Property */
1853 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1854 static const WCHAR szOne[] = { '1', 0 };
1856 MSI_SetPropertyW(package,szPreselected,szOne);
1860 * now we want to enable or disable components base on feature
1863 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1865 ComponentList *cl;
1867 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1868 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1870 if (!feature->Level)
1871 continue;
1873 /* features with components that have compressed files are made local */
1874 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1876 if (cl->component->Enabled &&
1877 cl->component->ForceLocalState &&
1878 feature->Action == INSTALLSTATE_SOURCE)
1880 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1881 break;
1885 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1887 component = cl->component;
1889 if (!component->Enabled)
1890 continue;
1892 switch (feature->Action)
1894 case INSTALLSTATE_ABSENT:
1895 component->anyAbsent = 1;
1896 break;
1897 case INSTALLSTATE_ADVERTISED:
1898 component->hasAdvertiseFeature = 1;
1899 break;
1900 case INSTALLSTATE_SOURCE:
1901 component->hasSourceFeature = 1;
1902 break;
1903 case INSTALLSTATE_LOCAL:
1904 component->hasLocalFeature = 1;
1905 break;
1906 case INSTALLSTATE_DEFAULT:
1907 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1908 component->hasAdvertiseFeature = 1;
1909 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1910 component->hasSourceFeature = 1;
1911 else
1912 component->hasLocalFeature = 1;
1913 break;
1914 default:
1915 break;
1920 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1922 /* if the component isn't enabled, leave it alone */
1923 if (!component->Enabled)
1924 continue;
1926 /* check if it's local or source */
1927 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1928 (component->hasLocalFeature || component->hasSourceFeature))
1930 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1931 !component->ForceLocalState)
1932 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1933 else
1934 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1935 continue;
1938 /* if any feature is local, the component must be local too */
1939 if (component->hasLocalFeature)
1941 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1942 continue;
1945 if (component->hasSourceFeature)
1947 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1948 continue;
1951 if (component->hasAdvertiseFeature)
1953 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1954 continue;
1957 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1958 if (component->anyAbsent)
1959 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1962 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1964 if (component->Action == INSTALLSTATE_DEFAULT)
1966 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1967 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1970 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1971 debugstr_w(component->Component), component->Installed, component->Action);
1975 return ERROR_SUCCESS;
1978 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1980 MSIPACKAGE *package = (MSIPACKAGE*)param;
1981 LPCWSTR name;
1982 LPWSTR path;
1983 MSIFOLDER *f;
1985 name = MSI_RecordGetString(row,1);
1987 f = get_loaded_folder(package, name);
1988 if (!f) return ERROR_SUCCESS;
1990 /* reset the ResolvedTarget */
1991 msi_free(f->ResolvedTarget);
1992 f->ResolvedTarget = NULL;
1994 /* This helper function now does ALL the work */
1995 TRACE("Dir %s ...\n",debugstr_w(name));
1996 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1997 TRACE("resolves to %s\n",debugstr_w(path));
1998 msi_free(path);
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = (MSIPACKAGE*)param;
2006 LPCWSTR name;
2007 MSIFEATURE *feature;
2009 name = MSI_RecordGetString( row, 1 );
2011 feature = get_loaded_feature( package, name );
2012 if (!feature)
2013 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2014 else
2016 LPCWSTR Condition;
2017 Condition = MSI_RecordGetString(row,3);
2019 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2021 int level = MSI_RecordGetInteger(row,2);
2022 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2023 feature->Level = level;
2026 return ERROR_SUCCESS;
2029 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2031 static const WCHAR name_fmt[] =
2032 {'%','u','.','%','u','.','%','u','.','%','u',0};
2033 static const WCHAR name[] = {'\\',0};
2034 VS_FIXEDFILEINFO *lpVer;
2035 WCHAR filever[0x100];
2036 LPVOID version;
2037 DWORD versize;
2038 DWORD handle;
2039 UINT sz;
2041 TRACE("%s\n", debugstr_w(filename));
2043 versize = GetFileVersionInfoSizeW( filename, &handle );
2044 if (!versize)
2045 return NULL;
2047 version = msi_alloc( versize );
2048 GetFileVersionInfoW( filename, 0, versize, version );
2050 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2052 msi_free( version );
2053 return NULL;
2056 sprintfW( filever, name_fmt,
2057 HIWORD(lpVer->dwFileVersionMS),
2058 LOWORD(lpVer->dwFileVersionMS),
2059 HIWORD(lpVer->dwFileVersionLS),
2060 LOWORD(lpVer->dwFileVersionLS));
2062 msi_free( version );
2064 return strdupW( filever );
2067 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2069 LPWSTR file_version;
2070 MSIFILE *file;
2072 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2074 MSICOMPONENT* comp = file->Component;
2075 LPWSTR p;
2077 if (!comp)
2078 continue;
2080 if (file->IsCompressed)
2081 comp->ForceLocalState = TRUE;
2083 /* calculate target */
2084 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2086 msi_free(file->TargetPath);
2088 TRACE("file %s is named %s\n",
2089 debugstr_w(file->File), debugstr_w(file->FileName));
2091 file->TargetPath = build_directory_name(2, p, file->FileName);
2093 msi_free(p);
2095 TRACE("file %s resolves to %s\n",
2096 debugstr_w(file->File), debugstr_w(file->TargetPath));
2098 /* don't check files of components that aren't installed */
2099 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2100 comp->Installed == INSTALLSTATE_ABSENT)
2102 file->state = msifs_missing; /* assume files are missing */
2103 continue;
2106 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2108 file->state = msifs_missing;
2109 comp->Cost += file->FileSize;
2110 continue;
2113 if (file->Version &&
2114 (file_version = msi_get_disk_file_version( file->TargetPath )))
2116 TRACE("new %s old %s\n", debugstr_w(file->Version),
2117 debugstr_w(file_version));
2118 /* FIXME: seems like a bad way to compare version numbers */
2119 if (lstrcmpiW(file_version, file->Version)<0)
2121 file->state = msifs_overwrite;
2122 comp->Cost += file->FileSize;
2124 else
2125 file->state = msifs_present;
2126 msi_free( file_version );
2128 else
2129 file->state = msifs_present;
2132 return ERROR_SUCCESS;
2136 * A lot is done in this function aside from just the costing.
2137 * The costing needs to be implemented at some point but for now I am going
2138 * to focus on the directory building
2141 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2143 static const WCHAR ExecSeqQuery[] =
2144 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2145 '`','D','i','r','e','c','t','o','r','y','`',0};
2146 static const WCHAR ConditionQuery[] =
2147 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2148 '`','C','o','n','d','i','t','i','o','n','`',0};
2149 static const WCHAR szCosting[] =
2150 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2151 static const WCHAR szlevel[] =
2152 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2153 static const WCHAR szOutOfDiskSpace[] =
2154 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2155 static const WCHAR szOne[] = { '1', 0 };
2156 static const WCHAR szZero[] = { '0', 0 };
2157 MSICOMPONENT *comp;
2158 UINT rc;
2159 MSIQUERY * view;
2160 LPWSTR level;
2162 TRACE("Building Directory properties\n");
2164 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2165 if (rc == ERROR_SUCCESS)
2167 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2168 package);
2169 msiobj_release(&view->hdr);
2172 /* read components states from the registry */
2173 ACTION_GetComponentInstallStates(package);
2174 ACTION_GetFeatureInstallStates(package);
2176 TRACE("File calculations\n");
2177 msi_check_file_install_states( package );
2179 TRACE("Evaluating Condition Table\n");
2181 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2182 if (rc == ERROR_SUCCESS)
2184 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2185 package);
2186 msiobj_release(&view->hdr);
2189 TRACE("Enabling or Disabling Components\n");
2190 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2192 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2194 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2195 comp->Enabled = FALSE;
2197 else
2198 comp->Enabled = TRUE;
2201 MSI_SetPropertyW(package,szCosting,szOne);
2202 /* set default run level if not set */
2203 level = msi_dup_property( package, szlevel );
2204 if (!level)
2205 MSI_SetPropertyW(package,szlevel, szOne);
2206 msi_free(level);
2208 /* FIXME: check volume disk space */
2209 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2211 return MSI_SetFeatureStates(package);
2214 /* OK this value is "interpreted" and then formatted based on the
2215 first few characters */
2216 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2217 DWORD *size)
2219 LPSTR data = NULL;
2221 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2223 if (value[1]=='x')
2225 LPWSTR ptr;
2226 CHAR byte[5];
2227 LPWSTR deformated = NULL;
2228 int count;
2230 deformat_string(package, &value[2], &deformated);
2232 /* binary value type */
2233 ptr = deformated;
2234 *type = REG_BINARY;
2235 if (strlenW(ptr)%2)
2236 *size = (strlenW(ptr)/2)+1;
2237 else
2238 *size = strlenW(ptr)/2;
2240 data = msi_alloc(*size);
2242 byte[0] = '0';
2243 byte[1] = 'x';
2244 byte[4] = 0;
2245 count = 0;
2246 /* if uneven pad with a zero in front */
2247 if (strlenW(ptr)%2)
2249 byte[2]= '0';
2250 byte[3]= *ptr;
2251 ptr++;
2252 data[count] = (BYTE)strtol(byte,NULL,0);
2253 count ++;
2254 TRACE("Uneven byte count\n");
2256 while (*ptr)
2258 byte[2]= *ptr;
2259 ptr++;
2260 byte[3]= *ptr;
2261 ptr++;
2262 data[count] = (BYTE)strtol(byte,NULL,0);
2263 count ++;
2265 msi_free(deformated);
2267 TRACE("Data %i bytes(%i)\n",*size,count);
2269 else
2271 LPWSTR deformated;
2272 LPWSTR p;
2273 DWORD d = 0;
2274 deformat_string(package, &value[1], &deformated);
2276 *type=REG_DWORD;
2277 *size = sizeof(DWORD);
2278 data = msi_alloc(*size);
2279 p = deformated;
2280 if (*p == '-')
2281 p++;
2282 while (*p)
2284 if ( (*p < '0') || (*p > '9') )
2285 break;
2286 d *= 10;
2287 d += (*p - '0');
2288 p++;
2290 if (deformated[0] == '-')
2291 d = -d;
2292 *(LPDWORD)data = d;
2293 TRACE("DWORD %i\n",*(LPDWORD)data);
2295 msi_free(deformated);
2298 else
2300 static const WCHAR szMulti[] = {'[','~',']',0};
2301 LPCWSTR ptr;
2302 *type=REG_SZ;
2304 if (value[0]=='#')
2306 if (value[1]=='%')
2308 ptr = &value[2];
2309 *type=REG_EXPAND_SZ;
2311 else
2312 ptr = &value[1];
2314 else
2315 ptr=value;
2317 if (strstrW(value,szMulti))
2318 *type = REG_MULTI_SZ;
2320 /* remove initial delimiter */
2321 if (!strncmpW(value, szMulti, 3))
2322 ptr = value + 3;
2324 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2326 /* add double NULL terminator */
2327 if (*type == REG_MULTI_SZ)
2329 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2330 data = msi_realloc_zero(data, *size);
2333 return data;
2336 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2338 MSIPACKAGE *package = (MSIPACKAGE*)param;
2339 static const WCHAR szHCR[] =
2340 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2341 'R','O','O','T','\\',0};
2342 static const WCHAR szHCU[] =
2343 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2344 'U','S','E','R','\\',0};
2345 static const WCHAR szHLM[] =
2346 {'H','K','E','Y','_','L','O','C','A','L','_',
2347 'M','A','C','H','I','N','E','\\',0};
2348 static const WCHAR szHU[] =
2349 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2351 LPSTR value_data = NULL;
2352 HKEY root_key, hkey;
2353 DWORD type,size;
2354 LPWSTR deformated;
2355 LPCWSTR szRoot, component, name, key, value;
2356 MSICOMPONENT *comp;
2357 MSIRECORD * uirow;
2358 LPWSTR uikey;
2359 INT root;
2360 BOOL check_first = FALSE;
2361 UINT rc;
2363 ui_progress(package,2,0,0,0);
2365 value = NULL;
2366 key = NULL;
2367 uikey = NULL;
2368 name = NULL;
2370 component = MSI_RecordGetString(row, 6);
2371 comp = get_loaded_component(package,component);
2372 if (!comp)
2373 return ERROR_SUCCESS;
2375 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2377 TRACE("Skipping write due to disabled component %s\n",
2378 debugstr_w(component));
2380 comp->Action = comp->Installed;
2382 return ERROR_SUCCESS;
2385 comp->Action = INSTALLSTATE_LOCAL;
2387 name = MSI_RecordGetString(row, 4);
2388 if( MSI_RecordIsNull(row,5) && name )
2390 /* null values can have special meanings */
2391 if (name[0]=='-' && name[1] == 0)
2392 return ERROR_SUCCESS;
2393 else if ((name[0]=='+' && name[1] == 0) ||
2394 (name[0] == '*' && name[1] == 0))
2395 name = NULL;
2396 check_first = TRUE;
2399 root = MSI_RecordGetInteger(row,2);
2400 key = MSI_RecordGetString(row, 3);
2402 /* get the root key */
2403 switch (root)
2405 case -1:
2407 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2408 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2409 if (all_users && all_users[0] == '1')
2411 root_key = HKEY_LOCAL_MACHINE;
2412 szRoot = szHLM;
2414 else
2416 root_key = HKEY_CURRENT_USER;
2417 szRoot = szHCU;
2419 msi_free(all_users);
2421 break;
2422 case 0: root_key = HKEY_CLASSES_ROOT;
2423 szRoot = szHCR;
2424 break;
2425 case 1: root_key = HKEY_CURRENT_USER;
2426 szRoot = szHCU;
2427 break;
2428 case 2: root_key = HKEY_LOCAL_MACHINE;
2429 szRoot = szHLM;
2430 break;
2431 case 3: root_key = HKEY_USERS;
2432 szRoot = szHU;
2433 break;
2434 default:
2435 ERR("Unknown root %i\n",root);
2436 root_key=NULL;
2437 szRoot = NULL;
2438 break;
2440 if (!root_key)
2441 return ERROR_SUCCESS;
2443 deformat_string(package, key , &deformated);
2444 size = strlenW(deformated) + strlenW(szRoot) + 1;
2445 uikey = msi_alloc(size*sizeof(WCHAR));
2446 strcpyW(uikey,szRoot);
2447 strcatW(uikey,deformated);
2449 if (RegCreateKeyW( root_key, deformated, &hkey))
2451 ERR("Could not create key %s\n",debugstr_w(deformated));
2452 msi_free(deformated);
2453 msi_free(uikey);
2454 return ERROR_SUCCESS;
2456 msi_free(deformated);
2458 value = MSI_RecordGetString(row,5);
2459 if (value)
2460 value_data = parse_value(package, value, &type, &size);
2461 else
2463 static const WCHAR szEmpty[] = {0};
2464 value_data = (LPSTR)strdupW(szEmpty);
2465 size = sizeof(szEmpty);
2466 type = REG_SZ;
2469 deformat_string(package, name, &deformated);
2471 if (!check_first)
2473 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2474 debugstr_w(uikey));
2475 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2477 else
2479 DWORD sz = 0;
2480 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2481 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2483 TRACE("value %s of %s checked already exists\n",
2484 debugstr_w(deformated), debugstr_w(uikey));
2486 else
2488 TRACE("Checked and setting value %s of %s\n",
2489 debugstr_w(deformated), debugstr_w(uikey));
2490 if (deformated || size)
2491 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2494 RegCloseKey(hkey);
2496 uirow = MSI_CreateRecord(3);
2497 MSI_RecordSetStringW(uirow,2,deformated);
2498 MSI_RecordSetStringW(uirow,1,uikey);
2500 if (type == REG_SZ)
2501 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2502 else
2503 MSI_RecordSetStringW(uirow,3,value);
2505 ui_actiondata(package,szWriteRegistryValues,uirow);
2506 msiobj_release( &uirow->hdr );
2508 msi_free(value_data);
2509 msi_free(deformated);
2510 msi_free(uikey);
2512 return ERROR_SUCCESS;
2515 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2517 UINT rc;
2518 MSIQUERY * view;
2519 static const WCHAR ExecSeqQuery[] =
2520 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2521 '`','R','e','g','i','s','t','r','y','`',0 };
2523 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2524 if (rc != ERROR_SUCCESS)
2525 return ERROR_SUCCESS;
2527 /* increment progress bar each time action data is sent */
2528 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2530 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2532 msiobj_release(&view->hdr);
2533 return rc;
2536 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2538 package->script->CurrentlyScripting = TRUE;
2540 return ERROR_SUCCESS;
2544 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2546 MSICOMPONENT *comp;
2547 DWORD progress = 0;
2548 DWORD total = 0;
2549 static const WCHAR q1[]=
2550 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2551 '`','R','e','g','i','s','t','r','y','`',0};
2552 UINT rc;
2553 MSIQUERY * view;
2554 MSIFEATURE *feature;
2555 MSIFILE *file;
2557 TRACE("InstallValidate\n");
2559 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2560 if (rc == ERROR_SUCCESS)
2562 MSI_IterateRecords( view, &progress, NULL, package );
2563 msiobj_release( &view->hdr );
2564 total += progress * REG_PROGRESS_VALUE;
2567 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2568 total += COMPONENT_PROGRESS_VALUE;
2570 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2571 total += file->FileSize;
2573 ui_progress(package,0,total,0,0);
2575 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2577 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2578 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2579 feature->ActionRequest);
2582 return ERROR_SUCCESS;
2585 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2587 MSIPACKAGE* package = (MSIPACKAGE*)param;
2588 LPCWSTR cond = NULL;
2589 LPCWSTR message = NULL;
2590 UINT r;
2592 static const WCHAR title[]=
2593 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2595 cond = MSI_RecordGetString(row,1);
2597 r = MSI_EvaluateConditionW(package,cond);
2598 if (r == MSICONDITION_FALSE)
2600 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2602 LPWSTR deformated;
2603 message = MSI_RecordGetString(row,2);
2604 deformat_string(package,message,&deformated);
2605 MessageBoxW(NULL,deformated,title,MB_OK);
2606 msi_free(deformated);
2609 return ERROR_INSTALL_FAILURE;
2612 return ERROR_SUCCESS;
2615 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2617 UINT rc;
2618 MSIQUERY * view = NULL;
2619 static const WCHAR ExecSeqQuery[] =
2620 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2621 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2623 TRACE("Checking launch conditions\n");
2625 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2626 if (rc != ERROR_SUCCESS)
2627 return ERROR_SUCCESS;
2629 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2630 msiobj_release(&view->hdr);
2632 return rc;
2635 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2638 if (!cmp->KeyPath)
2639 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2641 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2643 MSIRECORD * row = 0;
2644 UINT root,len;
2645 LPWSTR deformated,buffer,deformated_name;
2646 LPCWSTR key,name;
2647 static const WCHAR ExecSeqQuery[] =
2648 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2649 '`','R','e','g','i','s','t','r','y','`',' ',
2650 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2651 ' ','=',' ' ,'\'','%','s','\'',0 };
2652 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2653 static const WCHAR fmt2[]=
2654 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2656 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2657 if (!row)
2658 return NULL;
2660 root = MSI_RecordGetInteger(row,2);
2661 key = MSI_RecordGetString(row, 3);
2662 name = MSI_RecordGetString(row, 4);
2663 deformat_string(package, key , &deformated);
2664 deformat_string(package, name, &deformated_name);
2666 len = strlenW(deformated) + 6;
2667 if (deformated_name)
2668 len+=strlenW(deformated_name);
2670 buffer = msi_alloc( len *sizeof(WCHAR));
2672 if (deformated_name)
2673 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2674 else
2675 sprintfW(buffer,fmt,root,deformated);
2677 msi_free(deformated);
2678 msi_free(deformated_name);
2679 msiobj_release(&row->hdr);
2681 return buffer;
2683 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2685 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2686 return NULL;
2688 else
2690 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2692 if (file)
2693 return strdupW( file->TargetPath );
2695 return NULL;
2698 static HKEY openSharedDLLsKey(void)
2700 HKEY hkey=0;
2701 static const WCHAR path[] =
2702 {'S','o','f','t','w','a','r','e','\\',
2703 'M','i','c','r','o','s','o','f','t','\\',
2704 'W','i','n','d','o','w','s','\\',
2705 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2706 'S','h','a','r','e','d','D','L','L','s',0};
2708 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2709 return hkey;
2712 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2714 HKEY hkey;
2715 DWORD count=0;
2716 DWORD type;
2717 DWORD sz = sizeof(count);
2718 DWORD rc;
2720 hkey = openSharedDLLsKey();
2721 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2722 if (rc != ERROR_SUCCESS)
2723 count = 0;
2724 RegCloseKey(hkey);
2725 return count;
2728 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2730 HKEY hkey;
2732 hkey = openSharedDLLsKey();
2733 if (count > 0)
2734 msi_reg_set_val_dword( hkey, path, count );
2735 else
2736 RegDeleteValueW(hkey,path);
2737 RegCloseKey(hkey);
2738 return count;
2742 * Return TRUE if the count should be written out and FALSE if not
2744 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2746 MSIFEATURE *feature;
2747 INT count = 0;
2748 BOOL write = FALSE;
2750 /* only refcount DLLs */
2751 if (comp->KeyPath == NULL ||
2752 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2753 comp->Attributes & msidbComponentAttributesODBCDataSource)
2754 write = FALSE;
2755 else
2757 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2758 write = (count > 0);
2760 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2761 write = TRUE;
2764 /* increment counts */
2765 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2767 ComponentList *cl;
2769 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2770 continue;
2772 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2774 if ( cl->component == comp )
2775 count++;
2779 /* decrement counts */
2780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2782 ComponentList *cl;
2784 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2785 continue;
2787 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2789 if ( cl->component == comp )
2790 count--;
2794 /* ref count all the files in the component */
2795 if (write)
2797 MSIFILE *file;
2799 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2801 if (file->Component == comp)
2802 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2806 /* add a count for permanent */
2807 if (comp->Attributes & msidbComponentAttributesPermanent)
2808 count ++;
2810 comp->RefCount = count;
2812 if (write)
2813 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2816 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2818 WCHAR squished_pc[GUID_SIZE];
2819 WCHAR squished_cc[GUID_SIZE];
2820 UINT rc;
2821 MSICOMPONENT *comp;
2822 HKEY hkey;
2824 TRACE("\n");
2826 squash_guid(package->ProductCode,squished_pc);
2827 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2829 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2831 MSIRECORD * uirow;
2833 ui_progress(package,2,0,0,0);
2834 if (!comp->ComponentId)
2835 continue;
2837 squash_guid(comp->ComponentId,squished_cc);
2839 msi_free(comp->FullKeypath);
2840 comp->FullKeypath = resolve_keypath( package, comp );
2842 ACTION_RefCountComponent( package, comp );
2844 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2845 debugstr_w(comp->Component),
2846 debugstr_w(squished_cc),
2847 debugstr_w(comp->FullKeypath),
2848 comp->RefCount);
2850 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2851 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2853 if (!comp->FullKeypath)
2854 continue;
2856 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2857 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2858 else
2859 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2861 if (rc != ERROR_SUCCESS)
2862 continue;
2864 if (comp->Attributes & msidbComponentAttributesPermanent)
2866 static const WCHAR szPermKey[] =
2867 { '0','0','0','0','0','0','0','0','0','0','0','0',
2868 '0','0','0','0','0','0','0','0','0','0','0','0',
2869 '0','0','0','0','0','0','0','0',0 };
2871 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2874 if (comp->Action == INSTALLSTATE_LOCAL)
2875 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2876 else
2878 MSIFILE *file;
2879 MSIRECORD *row;
2880 LPWSTR ptr, ptr2;
2881 WCHAR source[MAX_PATH];
2882 WCHAR base[MAX_PATH];
2883 LPWSTR sourcepath;
2885 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2886 static const WCHAR query[] = {
2887 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2888 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2889 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2890 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2891 '`','D','i','s','k','I','d','`',0};
2893 file = get_loaded_file(package, comp->KeyPath);
2894 if (!file)
2895 continue;
2897 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2898 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2899 ptr2 = strrchrW(source, '\\') + 1;
2900 msiobj_release(&row->hdr);
2902 lstrcpyW(base, package->PackagePath);
2903 ptr = strrchrW(base, '\\');
2904 *(ptr + 1) = '\0';
2906 sourcepath = resolve_file_source(package, file);
2907 ptr = sourcepath + lstrlenW(base);
2908 lstrcpyW(ptr2, ptr);
2909 msi_free(sourcepath);
2911 msi_reg_set_val_str(hkey, squished_pc, source);
2913 RegCloseKey(hkey);
2915 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2917 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2918 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2919 else
2920 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2923 /* UI stuff */
2924 uirow = MSI_CreateRecord(3);
2925 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2926 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2927 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2928 ui_actiondata(package,szProcessComponents,uirow);
2929 msiobj_release( &uirow->hdr );
2932 return ERROR_SUCCESS;
2935 typedef struct {
2936 CLSID clsid;
2937 LPWSTR source;
2939 LPWSTR path;
2940 ITypeLib *ptLib;
2941 } typelib_struct;
2943 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2944 LPWSTR lpszName, LONG_PTR lParam)
2946 TLIBATTR *attr;
2947 typelib_struct *tl_struct = (typelib_struct*) lParam;
2948 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2949 int sz;
2950 HRESULT res;
2952 if (!IS_INTRESOURCE(lpszName))
2954 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2955 return TRUE;
2958 sz = strlenW(tl_struct->source)+4;
2959 sz *= sizeof(WCHAR);
2961 if ((INT_PTR)lpszName == 1)
2962 tl_struct->path = strdupW(tl_struct->source);
2963 else
2965 tl_struct->path = msi_alloc(sz);
2966 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2969 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2970 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2971 if (FAILED(res))
2973 msi_free(tl_struct->path);
2974 tl_struct->path = NULL;
2976 return TRUE;
2979 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2980 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2982 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2983 return FALSE;
2986 msi_free(tl_struct->path);
2987 tl_struct->path = NULL;
2989 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2990 ITypeLib_Release(tl_struct->ptLib);
2992 return TRUE;
2995 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2997 MSIPACKAGE* package = (MSIPACKAGE*)param;
2998 LPCWSTR component;
2999 MSICOMPONENT *comp;
3000 MSIFILE *file;
3001 typelib_struct tl_struct;
3002 ITypeLib *tlib;
3003 HMODULE module;
3004 HRESULT hr;
3006 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3008 component = MSI_RecordGetString(row,3);
3009 comp = get_loaded_component(package,component);
3010 if (!comp)
3011 return ERROR_SUCCESS;
3013 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3015 TRACE("Skipping typelib reg due to disabled component\n");
3017 comp->Action = comp->Installed;
3019 return ERROR_SUCCESS;
3022 comp->Action = INSTALLSTATE_LOCAL;
3024 file = get_loaded_file( package, comp->KeyPath );
3025 if (!file)
3026 return ERROR_SUCCESS;
3028 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3029 if (module)
3031 LPCWSTR guid;
3032 guid = MSI_RecordGetString(row,1);
3033 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3034 tl_struct.source = strdupW( file->TargetPath );
3035 tl_struct.path = NULL;
3037 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3038 (LONG_PTR)&tl_struct);
3040 if (tl_struct.path)
3042 LPWSTR help = NULL;
3043 LPCWSTR helpid;
3044 HRESULT res;
3046 helpid = MSI_RecordGetString(row,6);
3048 if (helpid)
3049 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3050 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3051 msi_free(help);
3053 if (FAILED(res))
3054 ERR("Failed to register type library %s\n",
3055 debugstr_w(tl_struct.path));
3056 else
3058 ui_actiondata(package,szRegisterTypeLibraries,row);
3060 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3063 ITypeLib_Release(tl_struct.ptLib);
3064 msi_free(tl_struct.path);
3066 else
3067 ERR("Failed to load type library %s\n",
3068 debugstr_w(tl_struct.source));
3070 FreeLibrary(module);
3071 msi_free(tl_struct.source);
3073 else
3075 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3076 if (FAILED(hr))
3078 ERR("Failed to load type library: %08x\n", hr);
3079 return ERROR_FUNCTION_FAILED;
3082 ITypeLib_Release(tlib);
3085 return ERROR_SUCCESS;
3088 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3091 * OK this is a bit confusing.. I am given a _Component key and I believe
3092 * that the file that is being registered as a type library is the "key file
3093 * of that component" which I interpret to mean "The file in the KeyPath of
3094 * that component".
3096 UINT rc;
3097 MSIQUERY * view;
3098 static const WCHAR Query[] =
3099 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3100 '`','T','y','p','e','L','i','b','`',0};
3102 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3103 if (rc != ERROR_SUCCESS)
3104 return ERROR_SUCCESS;
3106 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3107 msiobj_release(&view->hdr);
3108 return rc;
3111 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3113 MSIPACKAGE *package = (MSIPACKAGE*)param;
3114 LPWSTR target_file, target_folder, filename;
3115 LPCWSTR buffer, extension;
3116 MSICOMPONENT *comp;
3117 static const WCHAR szlnk[]={'.','l','n','k',0};
3118 IShellLinkW *sl = NULL;
3119 IPersistFile *pf = NULL;
3120 HRESULT res;
3122 buffer = MSI_RecordGetString(row,4);
3123 comp = get_loaded_component(package,buffer);
3124 if (!comp)
3125 return ERROR_SUCCESS;
3127 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3129 TRACE("Skipping shortcut creation due to disabled component\n");
3131 comp->Action = comp->Installed;
3133 return ERROR_SUCCESS;
3136 comp->Action = INSTALLSTATE_LOCAL;
3138 ui_actiondata(package,szCreateShortcuts,row);
3140 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3141 &IID_IShellLinkW, (LPVOID *) &sl );
3143 if (FAILED( res ))
3145 ERR("CLSID_ShellLink not available\n");
3146 goto err;
3149 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3150 if (FAILED( res ))
3152 ERR("QueryInterface(IID_IPersistFile) failed\n");
3153 goto err;
3156 buffer = MSI_RecordGetString(row,2);
3157 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3159 /* may be needed because of a bug somewhere else */
3160 create_full_pathW(target_folder);
3162 filename = msi_dup_record_field( row, 3 );
3163 reduce_to_longfilename(filename);
3165 extension = strchrW(filename,'.');
3166 if (!extension || strcmpiW(extension,szlnk))
3168 int len = strlenW(filename);
3169 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3170 memcpy(filename + len, szlnk, sizeof(szlnk));
3172 target_file = build_directory_name(2, target_folder, filename);
3173 msi_free(target_folder);
3174 msi_free(filename);
3176 buffer = MSI_RecordGetString(row,5);
3177 if (strchrW(buffer,'['))
3179 LPWSTR deformated;
3180 deformat_string(package,buffer,&deformated);
3181 IShellLinkW_SetPath(sl,deformated);
3182 msi_free(deformated);
3184 else
3186 FIXME("poorly handled shortcut format, advertised shortcut\n");
3187 IShellLinkW_SetPath(sl,comp->FullKeypath);
3190 if (!MSI_RecordIsNull(row,6))
3192 LPWSTR deformated;
3193 buffer = MSI_RecordGetString(row,6);
3194 deformat_string(package,buffer,&deformated);
3195 IShellLinkW_SetArguments(sl,deformated);
3196 msi_free(deformated);
3199 if (!MSI_RecordIsNull(row,7))
3201 buffer = MSI_RecordGetString(row,7);
3202 IShellLinkW_SetDescription(sl,buffer);
3205 if (!MSI_RecordIsNull(row,8))
3206 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3208 if (!MSI_RecordIsNull(row,9))
3210 LPWSTR Path;
3211 INT index;
3213 buffer = MSI_RecordGetString(row,9);
3215 Path = build_icon_path(package,buffer);
3216 index = MSI_RecordGetInteger(row,10);
3218 /* no value means 0 */
3219 if (index == MSI_NULL_INTEGER)
3220 index = 0;
3222 IShellLinkW_SetIconLocation(sl,Path,index);
3223 msi_free(Path);
3226 if (!MSI_RecordIsNull(row,11))
3227 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3229 if (!MSI_RecordIsNull(row,12))
3231 LPWSTR Path;
3232 buffer = MSI_RecordGetString(row,12);
3233 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3234 if (Path)
3235 IShellLinkW_SetWorkingDirectory(sl,Path);
3236 msi_free(Path);
3239 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3240 IPersistFile_Save(pf,target_file,FALSE);
3242 msi_free(target_file);
3244 err:
3245 if (pf)
3246 IPersistFile_Release( pf );
3247 if (sl)
3248 IShellLinkW_Release( sl );
3250 return ERROR_SUCCESS;
3253 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3255 UINT rc;
3256 HRESULT res;
3257 MSIQUERY * view;
3258 static const WCHAR Query[] =
3259 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3260 '`','S','h','o','r','t','c','u','t','`',0};
3262 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3263 if (rc != ERROR_SUCCESS)
3264 return ERROR_SUCCESS;
3266 res = CoInitialize( NULL );
3267 if (FAILED (res))
3269 ERR("CoInitialize failed\n");
3270 return ERROR_FUNCTION_FAILED;
3273 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3274 msiobj_release(&view->hdr);
3276 CoUninitialize();
3278 return rc;
3281 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3283 MSIPACKAGE* package = (MSIPACKAGE*)param;
3284 HANDLE the_file;
3285 LPWSTR FilePath;
3286 LPCWSTR FileName;
3287 CHAR buffer[1024];
3288 DWORD sz;
3289 UINT rc;
3290 MSIRECORD *uirow;
3292 FileName = MSI_RecordGetString(row,1);
3293 if (!FileName)
3295 ERR("Unable to get FileName\n");
3296 return ERROR_SUCCESS;
3299 FilePath = build_icon_path(package,FileName);
3301 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3303 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3304 FILE_ATTRIBUTE_NORMAL, NULL);
3306 if (the_file == INVALID_HANDLE_VALUE)
3308 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3309 msi_free(FilePath);
3310 return ERROR_SUCCESS;
3315 DWORD write;
3316 sz = 1024;
3317 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3318 if (rc != ERROR_SUCCESS)
3320 ERR("Failed to get stream\n");
3321 CloseHandle(the_file);
3322 DeleteFileW(FilePath);
3323 break;
3325 WriteFile(the_file,buffer,sz,&write,NULL);
3326 } while (sz == 1024);
3328 msi_free(FilePath);
3330 CloseHandle(the_file);
3332 uirow = MSI_CreateRecord(1);
3333 MSI_RecordSetStringW(uirow,1,FileName);
3334 ui_actiondata(package,szPublishProduct,uirow);
3335 msiobj_release( &uirow->hdr );
3337 return ERROR_SUCCESS;
3340 static UINT msi_publish_icons(MSIPACKAGE *package)
3342 UINT r;
3343 MSIQUERY *view;
3345 static const WCHAR query[]= {
3346 'S','E','L','E','C','T',' ','*',' ',
3347 'F','R','O','M',' ','`','I','c','o','n','`',0};
3349 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3350 if (r == ERROR_SUCCESS)
3352 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3353 msiobj_release(&view->hdr);
3356 return ERROR_SUCCESS;
3359 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3361 UINT r;
3362 HKEY source;
3363 LPWSTR buffer;
3364 MSIMEDIADISK *disk;
3365 MSISOURCELISTINFO *info;
3367 static const WCHAR szEmpty[] = {0};
3368 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3370 r = RegCreateKeyW(hkey, szSourceList, &source);
3371 if (r != ERROR_SUCCESS)
3372 return r;
3374 RegCloseKey(source);
3376 buffer = strrchrW(package->PackagePath, '\\') + 1;
3377 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3378 package->Context, MSICODE_PRODUCT,
3379 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3380 if (r != ERROR_SUCCESS)
3381 return r;
3383 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3384 package->Context, MSICODE_PRODUCT,
3385 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3386 if (r != ERROR_SUCCESS)
3387 return r;
3389 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3390 package->Context, MSICODE_PRODUCT,
3391 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3392 if (r != ERROR_SUCCESS)
3393 return r;
3395 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3397 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3398 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3399 info->options, info->value);
3400 else
3401 MsiSourceListSetInfoW(package->ProductCode, NULL,
3402 info->context, info->options,
3403 info->property, info->value);
3406 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3408 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3409 disk->context, disk->options,
3410 disk->disk_id, disk->volume_label, disk->disk_prompt);
3413 return ERROR_SUCCESS;
3416 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3418 MSIHANDLE hdb, suminfo;
3419 WCHAR guids[MAX_PATH];
3420 WCHAR packcode[SQUISH_GUID_SIZE];
3421 LPWSTR buffer;
3422 LPWSTR ptr;
3423 DWORD langid;
3424 DWORD size;
3425 UINT r;
3427 static const WCHAR szProductLanguage[] =
3428 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3429 static const WCHAR szARPProductIcon[] =
3430 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3431 static const WCHAR szProductVersion[] =
3432 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3433 static const WCHAR szAssignment[] =
3434 {'A','s','s','i','g','n','m','e','n','t',0};
3435 static const WCHAR szAdvertiseFlags[] =
3436 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3437 static const WCHAR szClients[] =
3438 {'C','l','i','e','n','t','s',0};
3439 static const WCHAR szColon[] = {':',0};
3441 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3442 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3443 msi_free(buffer);
3445 langid = msi_get_property_int(package, szProductLanguage, 0);
3446 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3448 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3449 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3451 /* FIXME */
3452 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3454 buffer = msi_dup_property(package, szARPProductIcon);
3455 if (buffer)
3457 LPWSTR path = build_icon_path(package,buffer);
3458 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3459 msi_free(path);
3460 msi_free(buffer);
3463 buffer = msi_dup_property(package, szProductVersion);
3464 if (buffer)
3466 DWORD verdword = msi_version_str_to_dword(buffer);
3467 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3468 msi_free(buffer);
3471 msi_reg_set_val_dword(hkey, szAssignment, 0);
3472 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3473 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3474 msi_reg_set_val_str(hkey, szClients, szColon);
3476 hdb = alloc_msihandle(&package->db->hdr);
3477 if (!hdb)
3478 return ERROR_NOT_ENOUGH_MEMORY;
3480 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3481 MsiCloseHandle(hdb);
3482 if (r != ERROR_SUCCESS)
3483 goto done;
3485 size = MAX_PATH;
3486 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3487 NULL, guids, &size);
3488 if (r != ERROR_SUCCESS)
3489 goto done;
3491 ptr = strchrW(guids, ';');
3492 if (ptr) *ptr = 0;
3493 squash_guid(guids, packcode);
3494 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3496 done:
3497 MsiCloseHandle(suminfo);
3498 return ERROR_SUCCESS;
3501 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3503 UINT r;
3504 HKEY hkey;
3505 LPWSTR upgrade;
3506 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3508 static const WCHAR szUpgradeCode[] =
3509 {'U','p','g','r','a','d','e','C','o','d','e',0};
3511 upgrade = msi_dup_property(package, szUpgradeCode);
3512 if (!upgrade)
3513 return ERROR_SUCCESS;
3515 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3517 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3518 if (r != ERROR_SUCCESS)
3519 goto done;
3521 else
3523 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3524 if (r != ERROR_SUCCESS)
3525 goto done;
3528 squash_guid(package->ProductCode, squashed_pc);
3529 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3531 RegCloseKey(hkey);
3533 done:
3534 msi_free(upgrade);
3535 return r;
3538 static BOOL msi_check_publish(MSIPACKAGE *package)
3540 MSIFEATURE *feature;
3542 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3544 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3545 return TRUE;
3548 return FALSE;
3551 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3553 MSIFEATURE *feature;
3555 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3557 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3558 return FALSE;
3561 return TRUE;
3565 * 99% of the work done here is only done for
3566 * advertised installs. However this is where the
3567 * Icon table is processed and written out
3568 * so that is what I am going to do here.
3570 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3572 UINT rc;
3573 HKEY hukey=0;
3574 HKEY hudkey=0;
3576 /* FIXME: also need to publish if the product is in advertise mode */
3577 if (!msi_check_publish(package))
3578 return ERROR_SUCCESS;
3580 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3582 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3583 if (rc != ERROR_SUCCESS)
3584 goto end;
3586 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3587 if (rc != ERROR_SUCCESS)
3588 goto end;
3590 else
3592 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3593 if (rc != ERROR_SUCCESS)
3594 goto end;
3596 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3597 if (rc != ERROR_SUCCESS)
3598 goto end;
3601 rc = msi_publish_upgrade_code(package);
3602 if (rc != ERROR_SUCCESS)
3603 goto end;
3605 rc = msi_publish_product_properties(package, hukey);
3606 if (rc != ERROR_SUCCESS)
3607 goto end;
3609 rc = msi_publish_sourcelist(package, hukey);
3610 if (rc != ERROR_SUCCESS)
3611 goto end;
3613 rc = msi_publish_icons(package);
3615 end:
3616 RegCloseKey(hukey);
3617 RegCloseKey(hudkey);
3619 return rc;
3622 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3624 MSIPACKAGE *package = (MSIPACKAGE*)param;
3625 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3626 LPWSTR deformated_section, deformated_key, deformated_value;
3627 LPWSTR folder, fullname = NULL;
3628 MSIRECORD * uirow;
3629 INT action;
3630 MSICOMPONENT *comp;
3631 static const WCHAR szWindowsFolder[] =
3632 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3634 component = MSI_RecordGetString(row, 8);
3635 comp = get_loaded_component(package,component);
3637 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3639 TRACE("Skipping ini file due to disabled component %s\n",
3640 debugstr_w(component));
3642 comp->Action = comp->Installed;
3644 return ERROR_SUCCESS;
3647 comp->Action = INSTALLSTATE_LOCAL;
3649 identifier = MSI_RecordGetString(row,1);
3650 filename = MSI_RecordGetString(row,2);
3651 dirproperty = MSI_RecordGetString(row,3);
3652 section = MSI_RecordGetString(row,4);
3653 key = MSI_RecordGetString(row,5);
3654 value = MSI_RecordGetString(row,6);
3655 action = MSI_RecordGetInteger(row,7);
3657 deformat_string(package,section,&deformated_section);
3658 deformat_string(package,key,&deformated_key);
3659 deformat_string(package,value,&deformated_value);
3661 if (dirproperty)
3663 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3664 if (!folder)
3665 folder = msi_dup_property( package, dirproperty );
3667 else
3668 folder = msi_dup_property( package, szWindowsFolder );
3670 if (!folder)
3672 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3673 goto cleanup;
3676 fullname = build_directory_name(2, folder, filename);
3678 if (action == 0)
3680 TRACE("Adding value %s to section %s in %s\n",
3681 debugstr_w(deformated_key), debugstr_w(deformated_section),
3682 debugstr_w(fullname));
3683 WritePrivateProfileStringW(deformated_section, deformated_key,
3684 deformated_value, fullname);
3686 else if (action == 1)
3688 WCHAR returned[10];
3689 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3690 returned, 10, fullname);
3691 if (returned[0] == 0)
3693 TRACE("Adding value %s to section %s in %s\n",
3694 debugstr_w(deformated_key), debugstr_w(deformated_section),
3695 debugstr_w(fullname));
3697 WritePrivateProfileStringW(deformated_section, deformated_key,
3698 deformated_value, fullname);
3701 else if (action == 3)
3702 FIXME("Append to existing section not yet implemented\n");
3704 uirow = MSI_CreateRecord(4);
3705 MSI_RecordSetStringW(uirow,1,identifier);
3706 MSI_RecordSetStringW(uirow,2,deformated_section);
3707 MSI_RecordSetStringW(uirow,3,deformated_key);
3708 MSI_RecordSetStringW(uirow,4,deformated_value);
3709 ui_actiondata(package,szWriteIniValues,uirow);
3710 msiobj_release( &uirow->hdr );
3711 cleanup:
3712 msi_free(fullname);
3713 msi_free(folder);
3714 msi_free(deformated_key);
3715 msi_free(deformated_value);
3716 msi_free(deformated_section);
3717 return ERROR_SUCCESS;
3720 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3722 UINT rc;
3723 MSIQUERY * view;
3724 static const WCHAR ExecSeqQuery[] =
3725 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3726 '`','I','n','i','F','i','l','e','`',0};
3728 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3729 if (rc != ERROR_SUCCESS)
3731 TRACE("no IniFile table\n");
3732 return ERROR_SUCCESS;
3735 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3736 msiobj_release(&view->hdr);
3737 return rc;
3740 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3742 MSIPACKAGE *package = (MSIPACKAGE*)param;
3743 LPCWSTR filename;
3744 LPWSTR FullName;
3745 MSIFILE *file;
3746 DWORD len;
3747 static const WCHAR ExeStr[] =
3748 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3749 static const WCHAR close[] = {'\"',0};
3750 STARTUPINFOW si;
3751 PROCESS_INFORMATION info;
3752 BOOL brc;
3753 MSIRECORD *uirow;
3754 LPWSTR uipath, p;
3756 memset(&si,0,sizeof(STARTUPINFOW));
3758 filename = MSI_RecordGetString(row,1);
3759 file = get_loaded_file( package, filename );
3761 if (!file)
3763 ERR("Unable to find file id %s\n",debugstr_w(filename));
3764 return ERROR_SUCCESS;
3767 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3769 FullName = msi_alloc(len*sizeof(WCHAR));
3770 strcpyW(FullName,ExeStr);
3771 strcatW( FullName, file->TargetPath );
3772 strcatW(FullName,close);
3774 TRACE("Registering %s\n",debugstr_w(FullName));
3775 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3776 &si, &info);
3778 if (brc)
3780 CloseHandle(info.hThread);
3781 msi_dialog_check_messages(info.hProcess);
3782 CloseHandle(info.hProcess);
3785 msi_free(FullName);
3787 /* the UI chunk */
3788 uirow = MSI_CreateRecord( 2 );
3789 uipath = strdupW( file->TargetPath );
3790 p = strrchrW(uipath,'\\');
3791 if (p)
3792 p[0]=0;
3793 MSI_RecordSetStringW( uirow, 1, &p[1] );
3794 MSI_RecordSetStringW( uirow, 2, uipath);
3795 ui_actiondata( package, szSelfRegModules, uirow);
3796 msiobj_release( &uirow->hdr );
3797 msi_free( uipath );
3798 /* FIXME: call ui_progress? */
3800 return ERROR_SUCCESS;
3803 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3805 UINT rc;
3806 MSIQUERY * view;
3807 static const WCHAR ExecSeqQuery[] =
3808 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3809 '`','S','e','l','f','R','e','g','`',0};
3811 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3812 if (rc != ERROR_SUCCESS)
3814 TRACE("no SelfReg table\n");
3815 return ERROR_SUCCESS;
3818 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3819 msiobj_release(&view->hdr);
3821 return ERROR_SUCCESS;
3824 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3826 MSIFEATURE *feature;
3827 UINT rc;
3828 HKEY hkey;
3829 HKEY userdata = NULL;
3831 if (!msi_check_publish(package))
3832 return ERROR_SUCCESS;
3834 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3836 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3837 &hkey, TRUE);
3838 if (rc != ERROR_SUCCESS)
3839 goto end;
3841 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3842 &userdata, TRUE);
3843 if (rc != ERROR_SUCCESS)
3844 goto end;
3846 else
3848 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3849 if (rc != ERROR_SUCCESS)
3850 goto end;
3852 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3853 &userdata, TRUE);
3854 if (rc != ERROR_SUCCESS)
3855 goto end;
3858 /* here the guids are base 85 encoded */
3859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3861 ComponentList *cl;
3862 LPWSTR data = NULL;
3863 GUID clsid;
3864 INT size;
3865 BOOL absent = FALSE;
3866 MSIRECORD *uirow;
3868 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3869 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3870 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3871 absent = TRUE;
3873 size = 1;
3874 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3876 size += 21;
3878 if (feature->Feature_Parent)
3879 size += strlenW( feature->Feature_Parent )+2;
3881 data = msi_alloc(size * sizeof(WCHAR));
3883 data[0] = 0;
3884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3886 MSICOMPONENT* component = cl->component;
3887 WCHAR buf[21];
3889 buf[0] = 0;
3890 if (component->ComponentId)
3892 TRACE("From %s\n",debugstr_w(component->ComponentId));
3893 CLSIDFromString(component->ComponentId, &clsid);
3894 encode_base85_guid(&clsid,buf);
3895 TRACE("to %s\n",debugstr_w(buf));
3896 strcatW(data,buf);
3900 if (feature->Feature_Parent)
3902 static const WCHAR sep[] = {'\2',0};
3903 strcatW(data,sep);
3904 strcatW(data,feature->Feature_Parent);
3907 msi_reg_set_val_str( userdata, feature->Feature, data );
3908 msi_free(data);
3910 size = 0;
3911 if (feature->Feature_Parent)
3912 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3913 if (!absent)
3915 static const WCHAR emptyW[] = {0};
3916 size += sizeof(WCHAR);
3917 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3918 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3920 else
3922 size += 2*sizeof(WCHAR);
3923 data = msi_alloc(size);
3924 data[0] = 0x6;
3925 data[1] = 0;
3926 if (feature->Feature_Parent)
3927 strcpyW( &data[1], feature->Feature_Parent );
3928 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3929 (LPBYTE)data,size);
3930 msi_free(data);
3933 /* the UI chunk */
3934 uirow = MSI_CreateRecord( 1 );
3935 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3936 ui_actiondata( package, szPublishFeatures, uirow);
3937 msiobj_release( &uirow->hdr );
3938 /* FIXME: call ui_progress? */
3941 end:
3942 RegCloseKey(hkey);
3943 RegCloseKey(userdata);
3944 return rc;
3947 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3949 UINT r;
3950 HKEY hkey;
3952 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3954 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3955 if (r == ERROR_SUCCESS)
3957 RegDeleteValueW(hkey, feature->Feature);
3958 RegCloseKey(hkey);
3961 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3962 if (r == ERROR_SUCCESS)
3964 RegDeleteValueW(hkey, feature->Feature);
3965 RegCloseKey(hkey);
3968 return ERROR_SUCCESS;
3971 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3973 MSIFEATURE *feature;
3975 if (!msi_check_unpublish(package))
3976 return ERROR_SUCCESS;
3978 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3980 msi_unpublish_feature(package, feature);
3983 return ERROR_SUCCESS;
3986 static UINT msi_get_local_package_name( LPWSTR path )
3988 static const WCHAR szInstaller[] = {
3989 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3990 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3991 DWORD time, len, i;
3992 HANDLE handle;
3994 time = GetTickCount();
3995 GetWindowsDirectoryW( path, MAX_PATH );
3996 lstrcatW( path, szInstaller );
3997 CreateDirectoryW( path, NULL );
3999 len = lstrlenW(path);
4000 for (i=0; i<0x10000; i++)
4002 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4003 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4004 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4005 if (handle != INVALID_HANDLE_VALUE)
4007 CloseHandle(handle);
4008 break;
4010 if (GetLastError() != ERROR_FILE_EXISTS &&
4011 GetLastError() != ERROR_SHARING_VIOLATION)
4012 return ERROR_FUNCTION_FAILED;
4015 return ERROR_SUCCESS;
4018 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4020 WCHAR packagefile[MAX_PATH];
4021 UINT r;
4023 r = msi_get_local_package_name( packagefile );
4024 if (r != ERROR_SUCCESS)
4025 return r;
4027 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4029 r = CopyFileW( package->db->path, packagefile, FALSE);
4031 if (!r)
4033 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4034 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4035 return ERROR_FUNCTION_FAILED;
4038 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4040 return ERROR_SUCCESS;
4043 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4045 LPWSTR prop, val, key;
4046 SYSTEMTIME systime;
4047 DWORD size, langid;
4048 WCHAR date[9];
4049 LPWSTR buffer;
4051 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4052 static const WCHAR szWindowsInstaller[] =
4053 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4054 static const WCHAR modpath_fmt[] =
4055 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4056 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4057 static const WCHAR szModifyPath[] =
4058 {'M','o','d','i','f','y','P','a','t','h',0};
4059 static const WCHAR szUninstallString[] =
4060 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4061 static const WCHAR szEstimatedSize[] =
4062 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4063 static const WCHAR szProductLanguage[] =
4064 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4065 static const WCHAR szProductVersion[] =
4066 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4067 static const WCHAR szProductName[] =
4068 {'P','r','o','d','u','c','t','N','a','m','e',0};
4069 static const WCHAR szDisplayName[] =
4070 {'D','i','s','p','l','a','y','N','a','m','e',0};
4071 static const WCHAR szDisplayVersion[] =
4072 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4073 static const WCHAR szManufacturer[] =
4074 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4076 static const LPCSTR propval[] = {
4077 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4078 "ARPCONTACT", "Contact",
4079 "ARPCOMMENTS", "Comments",
4080 "ProductName", "DisplayName",
4081 "ProductVersion", "DisplayVersion",
4082 "ARPHELPLINK", "HelpLink",
4083 "ARPHELPTELEPHONE", "HelpTelephone",
4084 "ARPINSTALLLOCATION", "InstallLocation",
4085 "SourceDir", "InstallSource",
4086 "Manufacturer", "Publisher",
4087 "ARPREADME", "Readme",
4088 "ARPSIZE", "Size",
4089 "ARPURLINFOABOUT", "URLInfoAbout",
4090 "ARPURLUPDATEINFO", "URLUpdateInfo",
4091 NULL,
4093 const LPCSTR *p = propval;
4095 while (*p)
4097 prop = strdupAtoW(*p++);
4098 key = strdupAtoW(*p++);
4099 val = msi_dup_property(package, prop);
4100 msi_reg_set_val_str(hkey, key, val);
4101 msi_free(val);
4102 msi_free(key);
4103 msi_free(prop);
4106 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4108 size = deformat_string(package, modpath_fmt, &buffer);
4109 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4110 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4111 msi_free(buffer);
4113 /* FIXME: Write real Estimated Size when we have it */
4114 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4116 buffer = msi_dup_property(package, szProductName);
4117 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4118 msi_free(buffer);
4120 buffer = msi_dup_property(package, cszSourceDir);
4121 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4122 msi_free(buffer);
4124 buffer = msi_dup_property(package, szManufacturer);
4125 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4126 msi_free(buffer);
4128 GetLocalTime(&systime);
4129 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4130 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4132 langid = msi_get_property_int(package, szProductLanguage, 0);
4133 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4135 buffer = msi_dup_property(package, szProductVersion);
4136 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4137 if (buffer)
4139 DWORD verdword = msi_version_str_to_dword(buffer);
4141 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4142 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4143 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4144 msi_free(buffer);
4147 return ERROR_SUCCESS;
4150 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4152 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4153 LPWSTR upgrade_code;
4154 HKEY hkey, props;
4155 HKEY upgrade;
4156 UINT rc;
4158 static const WCHAR szUpgradeCode[] = {
4159 'U','p','g','r','a','d','e','C','o','d','e',0};
4161 /* FIXME: also need to publish if the product is in advertise mode */
4162 if (!msi_check_publish(package))
4163 return ERROR_SUCCESS;
4165 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4166 if (rc != ERROR_SUCCESS)
4167 return rc;
4169 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4171 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4172 if (rc != ERROR_SUCCESS)
4173 goto done;
4175 else
4177 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4178 if (rc != ERROR_SUCCESS)
4179 goto done;
4182 msi_make_package_local(package, props);
4184 rc = msi_publish_install_properties(package, hkey);
4185 if (rc != ERROR_SUCCESS)
4186 goto done;
4188 rc = msi_publish_install_properties(package, props);
4189 if (rc != ERROR_SUCCESS)
4190 goto done;
4192 upgrade_code = msi_dup_property(package, szUpgradeCode);
4193 if (upgrade_code)
4195 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4196 squash_guid(package->ProductCode, squashed_pc);
4197 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4198 RegCloseKey(upgrade);
4199 msi_free(upgrade_code);
4202 done:
4203 RegCloseKey(hkey);
4205 return ERROR_SUCCESS;
4208 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4210 return execute_script(package,INSTALL_SCRIPT);
4213 static UINT msi_unpublish_product(MSIPACKAGE *package)
4215 LPWSTR upgrade;
4216 LPWSTR remove = NULL;
4217 LPWSTR *features = NULL;
4218 BOOL full_uninstall = TRUE;
4219 MSIFEATURE *feature;
4221 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4222 static const WCHAR szAll[] = {'A','L','L',0};
4223 static const WCHAR szUpgradeCode[] =
4224 {'U','p','g','r','a','d','e','C','o','d','e',0};
4226 remove = msi_dup_property(package, szRemove);
4227 if (!remove)
4228 return ERROR_SUCCESS;
4230 features = msi_split_string(remove, ',');
4231 if (!features)
4233 msi_free(remove);
4234 ERR("REMOVE feature list is empty!\n");
4235 return ERROR_FUNCTION_FAILED;
4238 if (!lstrcmpW(features[0], szAll))
4239 full_uninstall = TRUE;
4240 else
4242 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4244 if (feature->Action != INSTALLSTATE_ABSENT)
4245 full_uninstall = FALSE;
4249 if (!full_uninstall)
4250 goto done;
4252 MSIREG_DeleteProductKey(package->ProductCode);
4253 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4254 MSIREG_DeleteUninstallKey(package->ProductCode);
4256 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4258 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4259 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4261 else
4263 MSIREG_DeleteUserProductKey(package->ProductCode);
4264 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4267 upgrade = msi_dup_property(package, szUpgradeCode);
4268 if (upgrade)
4270 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4271 msi_free(upgrade);
4274 done:
4275 msi_free(remove);
4276 msi_free(features);
4277 return ERROR_SUCCESS;
4280 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4282 UINT rc;
4284 rc = msi_unpublish_product(package);
4285 if (rc != ERROR_SUCCESS)
4286 return rc;
4288 /* turn off scheduling */
4289 package->script->CurrentlyScripting= FALSE;
4291 /* first do the same as an InstallExecute */
4292 rc = ACTION_InstallExecute(package);
4293 if (rc != ERROR_SUCCESS)
4294 return rc;
4296 /* then handle Commit Actions */
4297 rc = execute_script(package,COMMIT_SCRIPT);
4299 return rc;
4302 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4304 static const WCHAR RunOnce[] = {
4305 'S','o','f','t','w','a','r','e','\\',
4306 'M','i','c','r','o','s','o','f','t','\\',
4307 'W','i','n','d','o','w','s','\\',
4308 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4309 'R','u','n','O','n','c','e',0};
4310 static const WCHAR InstallRunOnce[] = {
4311 'S','o','f','t','w','a','r','e','\\',
4312 'M','i','c','r','o','s','o','f','t','\\',
4313 'W','i','n','d','o','w','s','\\',
4314 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4315 'I','n','s','t','a','l','l','e','r','\\',
4316 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4318 static const WCHAR msiexec_fmt[] = {
4319 '%','s',
4320 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4321 '\"','%','s','\"',0};
4322 static const WCHAR install_fmt[] = {
4323 '/','I',' ','\"','%','s','\"',' ',
4324 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4325 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4326 WCHAR buffer[256], sysdir[MAX_PATH];
4327 HKEY hkey;
4328 WCHAR squished_pc[100];
4330 squash_guid(package->ProductCode,squished_pc);
4332 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4333 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4334 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4335 squished_pc);
4337 msi_reg_set_val_str( hkey, squished_pc, buffer );
4338 RegCloseKey(hkey);
4340 TRACE("Reboot command %s\n",debugstr_w(buffer));
4342 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4343 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4345 msi_reg_set_val_str( hkey, squished_pc, buffer );
4346 RegCloseKey(hkey);
4348 return ERROR_INSTALL_SUSPEND;
4351 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4353 DWORD attrib;
4354 UINT rc;
4357 * We are currently doing what should be done here in the top level Install
4358 * however for Administrative and uninstalls this step will be needed
4360 if (!package->PackagePath)
4361 return ERROR_SUCCESS;
4363 msi_set_sourcedir_props(package, TRUE);
4365 attrib = GetFileAttributesW(package->db->path);
4366 if (attrib == INVALID_FILE_ATTRIBUTES)
4368 LPWSTR prompt;
4369 LPWSTR msg;
4370 DWORD size = 0;
4372 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4373 package->Context, MSICODE_PRODUCT,
4374 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4375 if (rc == ERROR_MORE_DATA)
4377 prompt = msi_alloc(size * sizeof(WCHAR));
4378 MsiSourceListGetInfoW(package->ProductCode, NULL,
4379 package->Context, MSICODE_PRODUCT,
4380 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4382 else
4383 prompt = strdupW(package->db->path);
4385 msg = generate_error_string(package,1302,1,prompt);
4386 while(attrib == INVALID_FILE_ATTRIBUTES)
4388 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4389 if (rc == IDCANCEL)
4391 rc = ERROR_INSTALL_USEREXIT;
4392 break;
4394 attrib = GetFileAttributesW(package->db->path);
4396 msi_free(prompt);
4397 rc = ERROR_SUCCESS;
4399 else
4400 return ERROR_SUCCESS;
4402 return rc;
4405 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4407 HKEY hkey=0;
4408 LPWSTR buffer;
4409 LPWSTR productid;
4410 UINT rc,i;
4412 static const WCHAR szPropKeys[][80] =
4414 {'P','r','o','d','u','c','t','I','D',0},
4415 {'U','S','E','R','N','A','M','E',0},
4416 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4417 {0},
4420 static const WCHAR szRegKeys[][80] =
4422 {'P','r','o','d','u','c','t','I','D',0},
4423 {'R','e','g','O','w','n','e','r',0},
4424 {'R','e','g','C','o','m','p','a','n','y',0},
4425 {0},
4428 if (msi_check_unpublish(package))
4430 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4431 return ERROR_SUCCESS;
4434 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4435 if (!productid)
4436 return ERROR_SUCCESS;
4438 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4439 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4440 else
4441 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4443 if (rc != ERROR_SUCCESS)
4444 goto end;
4446 for( i = 0; szPropKeys[i][0]; i++ )
4448 buffer = msi_dup_property( package, szPropKeys[i] );
4449 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4450 msi_free( buffer );
4453 end:
4454 msi_free(productid);
4455 RegCloseKey(hkey);
4457 /* FIXME: call ui_actiondata */
4459 return rc;
4463 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4465 UINT rc;
4467 package->script->InWhatSequence |= SEQUENCE_EXEC;
4468 rc = ACTION_ProcessExecSequence(package,FALSE);
4469 return rc;
4473 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4475 MSIPACKAGE *package = (MSIPACKAGE*)param;
4476 LPCWSTR compgroupid=NULL;
4477 LPCWSTR feature=NULL;
4478 LPCWSTR text = NULL;
4479 LPCWSTR qualifier = NULL;
4480 LPCWSTR component = NULL;
4481 LPWSTR advertise = NULL;
4482 LPWSTR output = NULL;
4483 HKEY hkey;
4484 UINT rc = ERROR_SUCCESS;
4485 MSICOMPONENT *comp;
4486 DWORD sz = 0;
4487 MSIRECORD *uirow;
4489 component = MSI_RecordGetString(rec,3);
4490 comp = get_loaded_component(package,component);
4492 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4493 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4494 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4496 TRACE("Skipping: Component %s not scheduled for install\n",
4497 debugstr_w(component));
4499 return ERROR_SUCCESS;
4502 compgroupid = MSI_RecordGetString(rec,1);
4503 qualifier = MSI_RecordGetString(rec,2);
4505 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4506 if (rc != ERROR_SUCCESS)
4507 goto end;
4509 text = MSI_RecordGetString(rec,4);
4510 feature = MSI_RecordGetString(rec,5);
4512 advertise = create_component_advertise_string(package, comp, feature);
4514 sz = strlenW(advertise);
4516 if (text)
4517 sz += lstrlenW(text);
4519 sz+=3;
4520 sz *= sizeof(WCHAR);
4522 output = msi_alloc_zero(sz);
4523 strcpyW(output,advertise);
4524 msi_free(advertise);
4526 if (text)
4527 strcatW(output,text);
4529 msi_reg_set_val_multi_str( hkey, qualifier, output );
4531 end:
4532 RegCloseKey(hkey);
4533 msi_free(output);
4535 /* the UI chunk */
4536 uirow = MSI_CreateRecord( 2 );
4537 MSI_RecordSetStringW( uirow, 1, compgroupid );
4538 MSI_RecordSetStringW( uirow, 2, qualifier);
4539 ui_actiondata( package, szPublishComponents, uirow);
4540 msiobj_release( &uirow->hdr );
4541 /* FIXME: call ui_progress? */
4543 return rc;
4547 * At present I am ignorning the advertised components part of this and only
4548 * focusing on the qualified component sets
4550 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4552 UINT rc;
4553 MSIQUERY * view;
4554 static const WCHAR ExecSeqQuery[] =
4555 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4556 '`','P','u','b','l','i','s','h',
4557 'C','o','m','p','o','n','e','n','t','`',0};
4559 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4560 if (rc != ERROR_SUCCESS)
4561 return ERROR_SUCCESS;
4563 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4564 msiobj_release(&view->hdr);
4566 return rc;
4569 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4571 MSIPACKAGE *package = (MSIPACKAGE*)param;
4572 MSIRECORD *row;
4573 MSIFILE *file;
4574 SC_HANDLE hscm, service = NULL;
4575 LPCWSTR comp, depends, pass;
4576 LPWSTR name = NULL, disp = NULL;
4577 LPCWSTR load_order, serv_name, key;
4578 DWORD serv_type, start_type;
4579 DWORD err_control;
4581 static const WCHAR query[] =
4582 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4583 '`','C','o','m','p','o','n','e','n','t','`',' ',
4584 'W','H','E','R','E',' ',
4585 '`','C','o','m','p','o','n','e','n','t','`',' ',
4586 '=','\'','%','s','\'',0};
4588 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4589 if (!hscm)
4591 ERR("Failed to open the SC Manager!\n");
4592 goto done;
4595 start_type = MSI_RecordGetInteger(rec, 5);
4596 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4597 goto done;
4599 depends = MSI_RecordGetString(rec, 8);
4600 if (depends && *depends)
4601 FIXME("Dependency list unhandled!\n");
4603 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4604 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4605 serv_type = MSI_RecordGetInteger(rec, 4);
4606 err_control = MSI_RecordGetInteger(rec, 6);
4607 load_order = MSI_RecordGetString(rec, 7);
4608 serv_name = MSI_RecordGetString(rec, 9);
4609 pass = MSI_RecordGetString(rec, 10);
4610 comp = MSI_RecordGetString(rec, 12);
4612 /* fetch the service path */
4613 row = MSI_QueryGetRecord(package->db, query, comp);
4614 if (!row)
4616 ERR("Control query failed!\n");
4617 goto done;
4620 key = MSI_RecordGetString(row, 6);
4622 file = get_loaded_file(package, key);
4623 msiobj_release(&row->hdr);
4624 if (!file)
4626 ERR("Failed to load the service file\n");
4627 goto done;
4630 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4631 start_type, err_control, file->TargetPath,
4632 load_order, NULL, NULL, serv_name, pass);
4633 if (!service)
4635 if (GetLastError() != ERROR_SERVICE_EXISTS)
4636 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4639 done:
4640 CloseServiceHandle(service);
4641 CloseServiceHandle(hscm);
4642 msi_free(name);
4643 msi_free(disp);
4645 return ERROR_SUCCESS;
4648 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4650 UINT rc;
4651 MSIQUERY * view;
4652 static const WCHAR ExecSeqQuery[] =
4653 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4654 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4656 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4657 if (rc != ERROR_SUCCESS)
4658 return ERROR_SUCCESS;
4660 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4661 msiobj_release(&view->hdr);
4663 return rc;
4666 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4667 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4669 LPCWSTR *vector, *temp_vector;
4670 LPWSTR p, q;
4671 DWORD sep_len;
4673 static const WCHAR separator[] = {'[','~',']',0};
4675 *numargs = 0;
4676 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4678 if (!args)
4679 return NULL;
4681 vector = msi_alloc(sizeof(LPWSTR));
4682 if (!vector)
4683 return NULL;
4685 p = args;
4688 (*numargs)++;
4689 vector[*numargs - 1] = p;
4691 if ((q = strstrW(p, separator)))
4693 *q = '\0';
4695 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4696 if (!temp_vector)
4698 msi_free(vector);
4699 return NULL;
4701 vector = temp_vector;
4703 p = q + sep_len;
4705 } while (q);
4707 return vector;
4710 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4712 MSIPACKAGE *package = (MSIPACKAGE *)param;
4713 MSICOMPONENT *comp;
4714 SC_HANDLE scm, service = NULL;
4715 LPCWSTR name, *vector = NULL;
4716 LPWSTR args;
4717 DWORD event, numargs;
4718 UINT r = ERROR_FUNCTION_FAILED;
4720 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4721 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4722 return ERROR_SUCCESS;
4724 name = MSI_RecordGetString(rec, 2);
4725 event = MSI_RecordGetInteger(rec, 3);
4726 args = strdupW(MSI_RecordGetString(rec, 4));
4728 if (!(event & msidbServiceControlEventStart))
4729 return ERROR_SUCCESS;
4731 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4732 if (!scm)
4734 ERR("Failed to open the service control manager\n");
4735 goto done;
4738 service = OpenServiceW(scm, name, SERVICE_START);
4739 if (!service)
4741 ERR("Failed to open service %s\n", debugstr_w(name));
4742 goto done;
4745 vector = msi_service_args_to_vector(args, &numargs);
4747 if (!StartServiceW(service, numargs, vector))
4749 ERR("Failed to start service %s\n", debugstr_w(name));
4750 goto done;
4753 r = ERROR_SUCCESS;
4755 done:
4756 CloseServiceHandle(service);
4757 CloseServiceHandle(scm);
4759 msi_free(args);
4760 msi_free(vector);
4761 return r;
4764 static UINT ACTION_StartServices( MSIPACKAGE *package )
4766 UINT rc;
4767 MSIQUERY *view;
4769 static const WCHAR query[] = {
4770 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4771 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4773 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4774 if (rc != ERROR_SUCCESS)
4775 return ERROR_SUCCESS;
4777 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4778 msiobj_release(&view->hdr);
4780 return rc;
4783 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4785 DWORD i, needed, count;
4786 ENUM_SERVICE_STATUSW *dependencies;
4787 SERVICE_STATUS ss;
4788 SC_HANDLE depserv;
4790 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4791 0, &needed, &count))
4792 return TRUE;
4794 if (GetLastError() != ERROR_MORE_DATA)
4795 return FALSE;
4797 dependencies = msi_alloc(needed);
4798 if (!dependencies)
4799 return FALSE;
4801 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4802 needed, &needed, &count))
4803 goto error;
4805 for (i = 0; i < count; i++)
4807 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4808 SERVICE_STOP | SERVICE_QUERY_STATUS);
4809 if (!depserv)
4810 goto error;
4812 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4813 goto error;
4816 return TRUE;
4818 error:
4819 msi_free(dependencies);
4820 return FALSE;
4823 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4825 MSIPACKAGE *package = (MSIPACKAGE *)param;
4826 MSICOMPONENT *comp;
4827 SERVICE_STATUS status;
4828 SERVICE_STATUS_PROCESS ssp;
4829 SC_HANDLE scm = NULL, service = NULL;
4830 LPWSTR name, args;
4831 DWORD event, needed;
4833 event = MSI_RecordGetInteger(rec, 3);
4834 if (!(event & msidbServiceControlEventStop))
4835 return ERROR_SUCCESS;
4837 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4838 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4839 return ERROR_SUCCESS;
4841 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4842 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4843 args = strdupW(MSI_RecordGetString(rec, 4));
4845 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4846 if (!scm)
4848 WARN("Failed to open the SCM: %d\n", GetLastError());
4849 goto done;
4852 service = OpenServiceW(scm, name,
4853 SERVICE_STOP |
4854 SERVICE_QUERY_STATUS |
4855 SERVICE_ENUMERATE_DEPENDENTS);
4856 if (!service)
4858 WARN("Failed to open service (%s): %d\n",
4859 debugstr_w(name), GetLastError());
4860 goto done;
4863 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4864 sizeof(SERVICE_STATUS_PROCESS), &needed))
4866 WARN("Failed to query service status (%s): %d\n",
4867 debugstr_w(name), GetLastError());
4868 goto done;
4871 if (ssp.dwCurrentState == SERVICE_STOPPED)
4872 goto done;
4874 stop_service_dependents(scm, service);
4876 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4877 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4879 done:
4880 CloseServiceHandle(service);
4881 CloseServiceHandle(scm);
4882 msi_free(name);
4883 msi_free(args);
4885 return ERROR_SUCCESS;
4888 static UINT ACTION_StopServices( MSIPACKAGE *package )
4890 UINT rc;
4891 MSIQUERY *view;
4893 static const WCHAR query[] = {
4894 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4895 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4897 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4898 if (rc != ERROR_SUCCESS)
4899 return ERROR_SUCCESS;
4901 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4902 msiobj_release(&view->hdr);
4904 return rc;
4907 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4909 MSIFILE *file;
4911 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4913 if (!lstrcmpW(file->File, filename))
4914 return file;
4917 return NULL;
4920 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4922 MSIPACKAGE *package = (MSIPACKAGE*)param;
4923 LPWSTR driver, driver_path, ptr;
4924 WCHAR outpath[MAX_PATH];
4925 MSIFILE *driver_file, *setup_file;
4926 LPCWSTR desc;
4927 DWORD len, usage;
4928 UINT r = ERROR_SUCCESS;
4930 static const WCHAR driver_fmt[] = {
4931 'D','r','i','v','e','r','=','%','s',0};
4932 static const WCHAR setup_fmt[] = {
4933 'S','e','t','u','p','=','%','s',0};
4934 static const WCHAR usage_fmt[] = {
4935 'F','i','l','e','U','s','a','g','e','=','1',0};
4937 desc = MSI_RecordGetString(rec, 3);
4939 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4940 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4942 if (!driver_file || !setup_file)
4944 ERR("ODBC Driver entry not found!\n");
4945 return ERROR_FUNCTION_FAILED;
4948 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4949 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4950 lstrlenW(usage_fmt) + 1;
4951 driver = msi_alloc(len * sizeof(WCHAR));
4952 if (!driver)
4953 return ERROR_OUTOFMEMORY;
4955 ptr = driver;
4956 lstrcpyW(ptr, desc);
4957 ptr += lstrlenW(ptr) + 1;
4959 sprintfW(ptr, driver_fmt, driver_file->FileName);
4960 ptr += lstrlenW(ptr) + 1;
4962 sprintfW(ptr, setup_fmt, setup_file->FileName);
4963 ptr += lstrlenW(ptr) + 1;
4965 lstrcpyW(ptr, usage_fmt);
4966 ptr += lstrlenW(ptr) + 1;
4967 *ptr = '\0';
4969 driver_path = strdupW(driver_file->TargetPath);
4970 ptr = strrchrW(driver_path, '\\');
4971 if (ptr) *ptr = '\0';
4973 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4974 NULL, ODBC_INSTALL_COMPLETE, &usage))
4976 ERR("Failed to install SQL driver!\n");
4977 r = ERROR_FUNCTION_FAILED;
4980 msi_free(driver);
4981 msi_free(driver_path);
4983 return r;
4986 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4988 MSIPACKAGE *package = (MSIPACKAGE*)param;
4989 LPWSTR translator, translator_path, ptr;
4990 WCHAR outpath[MAX_PATH];
4991 MSIFILE *translator_file, *setup_file;
4992 LPCWSTR desc;
4993 DWORD len, usage;
4994 UINT r = ERROR_SUCCESS;
4996 static const WCHAR translator_fmt[] = {
4997 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4998 static const WCHAR setup_fmt[] = {
4999 'S','e','t','u','p','=','%','s',0};
5001 desc = MSI_RecordGetString(rec, 3);
5003 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5004 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5006 if (!translator_file || !setup_file)
5008 ERR("ODBC Translator entry not found!\n");
5009 return ERROR_FUNCTION_FAILED;
5012 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5013 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5014 translator = msi_alloc(len * sizeof(WCHAR));
5015 if (!translator)
5016 return ERROR_OUTOFMEMORY;
5018 ptr = translator;
5019 lstrcpyW(ptr, desc);
5020 ptr += lstrlenW(ptr) + 1;
5022 sprintfW(ptr, translator_fmt, translator_file->FileName);
5023 ptr += lstrlenW(ptr) + 1;
5025 sprintfW(ptr, setup_fmt, setup_file->FileName);
5026 ptr += lstrlenW(ptr) + 1;
5027 *ptr = '\0';
5029 translator_path = strdupW(translator_file->TargetPath);
5030 ptr = strrchrW(translator_path, '\\');
5031 if (ptr) *ptr = '\0';
5033 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5034 NULL, ODBC_INSTALL_COMPLETE, &usage))
5036 ERR("Failed to install SQL translator!\n");
5037 r = ERROR_FUNCTION_FAILED;
5040 msi_free(translator);
5041 msi_free(translator_path);
5043 return r;
5046 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5048 LPWSTR attrs;
5049 LPCWSTR desc, driver;
5050 WORD request = ODBC_ADD_SYS_DSN;
5051 INT registration;
5052 DWORD len;
5053 UINT r = ERROR_SUCCESS;
5055 static const WCHAR attrs_fmt[] = {
5056 'D','S','N','=','%','s',0 };
5058 desc = MSI_RecordGetString(rec, 3);
5059 driver = MSI_RecordGetString(rec, 4);
5060 registration = MSI_RecordGetInteger(rec, 5);
5062 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5063 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5065 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5066 attrs = msi_alloc(len * sizeof(WCHAR));
5067 if (!attrs)
5068 return ERROR_OUTOFMEMORY;
5070 sprintfW(attrs, attrs_fmt, desc);
5071 attrs[len - 1] = '\0';
5073 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5075 ERR("Failed to install SQL data source!\n");
5076 r = ERROR_FUNCTION_FAILED;
5079 msi_free(attrs);
5081 return r;
5084 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5086 UINT rc;
5087 MSIQUERY *view;
5089 static const WCHAR driver_query[] = {
5090 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5091 'O','D','B','C','D','r','i','v','e','r',0 };
5093 static const WCHAR translator_query[] = {
5094 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5095 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5097 static const WCHAR source_query[] = {
5098 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5099 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5101 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5102 if (rc != ERROR_SUCCESS)
5103 return ERROR_SUCCESS;
5105 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5106 msiobj_release(&view->hdr);
5108 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5109 if (rc != ERROR_SUCCESS)
5110 return ERROR_SUCCESS;
5112 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5113 msiobj_release(&view->hdr);
5115 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5116 if (rc != ERROR_SUCCESS)
5117 return ERROR_SUCCESS;
5119 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5120 msiobj_release(&view->hdr);
5122 return rc;
5125 #define ENV_ACT_SETALWAYS 0x1
5126 #define ENV_ACT_SETABSENT 0x2
5127 #define ENV_ACT_REMOVE 0x4
5128 #define ENV_ACT_REMOVEMATCH 0x8
5130 #define ENV_MOD_MACHINE 0x20000000
5131 #define ENV_MOD_APPEND 0x40000000
5132 #define ENV_MOD_PREFIX 0x80000000
5133 #define ENV_MOD_MASK 0xC0000000
5135 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5137 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5139 LPCWSTR cptr = *name;
5140 LPCWSTR ptr = *value;
5142 static const WCHAR prefix[] = {'[','~',']',0};
5143 static const int prefix_len = 3;
5145 *flags = 0;
5146 while (*cptr)
5148 if (*cptr == '=')
5149 *flags |= ENV_ACT_SETALWAYS;
5150 else if (*cptr == '+')
5151 *flags |= ENV_ACT_SETABSENT;
5152 else if (*cptr == '-')
5153 *flags |= ENV_ACT_REMOVE;
5154 else if (*cptr == '!')
5155 *flags |= ENV_ACT_REMOVEMATCH;
5156 else if (*cptr == '*')
5157 *flags |= ENV_MOD_MACHINE;
5158 else
5159 break;
5161 cptr++;
5162 (*name)++;
5165 if (!*cptr)
5167 ERR("Missing environment variable\n");
5168 return ERROR_FUNCTION_FAILED;
5171 if (!strncmpW(ptr, prefix, prefix_len))
5173 *flags |= ENV_MOD_APPEND;
5174 *value += lstrlenW(prefix);
5176 else if (lstrlenW(*value) >= prefix_len)
5178 ptr += lstrlenW(ptr) - prefix_len;
5179 if (!lstrcmpW(ptr, prefix))
5181 *flags |= ENV_MOD_PREFIX;
5182 /* the "[~]" will be removed by deformat_string */;
5186 if (!*flags ||
5187 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5188 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5189 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5190 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5192 ERR("Invalid flags: %08x\n", *flags);
5193 return ERROR_FUNCTION_FAILED;
5196 return ERROR_SUCCESS;
5199 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5201 MSIPACKAGE *package = param;
5202 LPCWSTR name, value;
5203 LPWSTR data = NULL, newval = NULL;
5204 LPWSTR deformatted = NULL, ptr;
5205 DWORD flags, type, size;
5206 LONG res;
5207 HKEY env = NULL, root;
5208 LPCWSTR environment;
5210 static const WCHAR user_env[] =
5211 {'E','n','v','i','r','o','n','m','e','n','t',0};
5212 static const WCHAR machine_env[] =
5213 {'S','y','s','t','e','m','\\',
5214 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5215 'C','o','n','t','r','o','l','\\',
5216 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5217 'E','n','v','i','r','o','n','m','e','n','t',0};
5218 static const WCHAR semicolon[] = {';',0};
5220 name = MSI_RecordGetString(rec, 2);
5221 value = MSI_RecordGetString(rec, 3);
5223 res = env_set_flags(&name, &value, &flags);
5224 if (res != ERROR_SUCCESS)
5225 goto done;
5227 deformat_string(package, value, &deformatted);
5228 if (!deformatted)
5230 res = ERROR_OUTOFMEMORY;
5231 goto done;
5234 value = deformatted;
5236 if (flags & ENV_MOD_MACHINE)
5238 environment = machine_env;
5239 root = HKEY_LOCAL_MACHINE;
5241 else
5243 environment = user_env;
5244 root = HKEY_CURRENT_USER;
5247 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5248 KEY_ALL_ACCESS, NULL, &env, NULL);
5249 if (res != ERROR_SUCCESS)
5250 goto done;
5252 if (flags & ENV_ACT_REMOVE)
5253 FIXME("Not removing environment variable on uninstall!\n");
5255 size = 0;
5256 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5257 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5258 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5259 goto done;
5261 if (res != ERROR_FILE_NOT_FOUND)
5263 if (flags & ENV_ACT_SETABSENT)
5265 res = ERROR_SUCCESS;
5266 goto done;
5269 data = msi_alloc(size);
5270 if (!data)
5272 RegCloseKey(env);
5273 return ERROR_OUTOFMEMORY;
5276 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5277 if (res != ERROR_SUCCESS)
5278 goto done;
5280 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5282 res = RegDeleteKeyW(env, name);
5283 goto done;
5286 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5287 newval = msi_alloc(size);
5288 ptr = newval;
5289 if (!newval)
5291 res = ERROR_OUTOFMEMORY;
5292 goto done;
5295 if (!(flags & ENV_MOD_MASK))
5296 lstrcpyW(newval, value);
5297 else
5299 if (flags & ENV_MOD_PREFIX)
5301 lstrcpyW(newval, value);
5302 lstrcatW(newval, semicolon);
5303 ptr = newval + lstrlenW(value) + 1;
5306 lstrcpyW(ptr, data);
5308 if (flags & ENV_MOD_APPEND)
5310 lstrcatW(newval, semicolon);
5311 lstrcatW(newval, value);
5315 else
5317 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5318 newval = msi_alloc(size);
5319 if (!newval)
5321 res = ERROR_OUTOFMEMORY;
5322 goto done;
5325 lstrcpyW(newval, value);
5328 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5329 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5331 done:
5332 if (env) RegCloseKey(env);
5333 msi_free(deformatted);
5334 msi_free(data);
5335 msi_free(newval);
5336 return res;
5339 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5341 UINT rc;
5342 MSIQUERY * view;
5343 static const WCHAR ExecSeqQuery[] =
5344 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5345 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5346 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5347 if (rc != ERROR_SUCCESS)
5348 return ERROR_SUCCESS;
5350 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5351 msiobj_release(&view->hdr);
5353 return rc;
5356 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5358 typedef struct
5360 struct list entry;
5361 LPWSTR sourcename;
5362 LPWSTR destname;
5363 LPWSTR source;
5364 LPWSTR dest;
5365 } FILE_LIST;
5367 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5369 BOOL ret;
5371 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5372 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5374 WARN("Source or dest is directory, not moving\n");
5375 return FALSE;
5378 if (options == msidbMoveFileOptionsMove)
5380 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5381 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5382 if (!ret)
5384 WARN("MoveFile failed: %d\n", GetLastError());
5385 return FALSE;
5388 else
5390 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5391 ret = CopyFileW(source, dest, FALSE);
5392 if (!ret)
5394 WARN("CopyFile failed: %d\n", GetLastError());
5395 return FALSE;
5399 return TRUE;
5402 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5404 LPWSTR path, ptr;
5405 DWORD dirlen, pathlen;
5407 ptr = strrchrW(wildcard, '\\');
5408 dirlen = ptr - wildcard + 1;
5410 pathlen = dirlen + lstrlenW(filename) + 1;
5411 path = msi_alloc(pathlen * sizeof(WCHAR));
5413 lstrcpynW(path, wildcard, dirlen + 1);
5414 lstrcatW(path, filename);
5416 return path;
5419 static void free_file_entry(FILE_LIST *file)
5421 msi_free(file->source);
5422 msi_free(file->dest);
5423 msi_free(file);
5426 static void free_list(FILE_LIST *list)
5428 while (!list_empty(&list->entry))
5430 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5432 list_remove(&file->entry);
5433 free_file_entry(file);
5437 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5439 FILE_LIST *new, *file;
5440 LPWSTR ptr, filename;
5441 DWORD size;
5443 new = msi_alloc_zero(sizeof(FILE_LIST));
5444 if (!new)
5445 return FALSE;
5447 new->source = strdupW(source);
5448 ptr = strrchrW(dest, '\\') + 1;
5449 filename = strrchrW(new->source, '\\') + 1;
5451 new->sourcename = filename;
5453 if (*ptr)
5454 new->destname = ptr;
5455 else
5456 new->destname = new->sourcename;
5458 size = (ptr - dest) + lstrlenW(filename) + 1;
5459 new->dest = msi_alloc(size * sizeof(WCHAR));
5460 if (!new->dest)
5462 free_file_entry(new);
5463 return FALSE;
5466 lstrcpynW(new->dest, dest, ptr - dest + 1);
5467 lstrcatW(new->dest, filename);
5469 if (list_empty(&files->entry))
5471 list_add_head(&files->entry, &new->entry);
5472 return TRUE;
5475 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5477 if (lstrcmpW(source, file->source) < 0)
5479 list_add_before(&file->entry, &new->entry);
5480 return TRUE;
5484 list_add_after(&file->entry, &new->entry);
5485 return TRUE;
5488 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5490 WIN32_FIND_DATAW wfd;
5491 HANDLE hfile;
5492 LPWSTR path;
5493 BOOL res;
5494 FILE_LIST files, *file;
5495 DWORD size;
5497 hfile = FindFirstFileW(source, &wfd);
5498 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5500 list_init(&files.entry);
5502 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5504 if (is_dot_dir(wfd.cFileName)) continue;
5506 path = wildcard_to_file(source, wfd.cFileName);
5507 if (!path)
5509 res = FALSE;
5510 goto done;
5513 add_wildcard(&files, path, dest);
5514 msi_free(path);
5517 /* no files match the wildcard */
5518 if (list_empty(&files.entry))
5519 goto done;
5521 /* only the first wildcard match gets renamed to dest */
5522 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5523 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5524 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5525 if (!file->dest)
5527 res = FALSE;
5528 goto done;
5531 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5533 while (!list_empty(&files.entry))
5535 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5537 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5539 list_remove(&file->entry);
5540 free_file_entry(file);
5543 res = TRUE;
5545 done:
5546 free_list(&files);
5547 FindClose(hfile);
5548 return res;
5551 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5553 MSIPACKAGE *package = param;
5554 MSICOMPONENT *comp;
5555 LPCWSTR sourcename;
5556 LPWSTR destname = NULL;
5557 LPWSTR sourcedir = NULL, destdir = NULL;
5558 LPWSTR source = NULL, dest = NULL;
5559 int options;
5560 DWORD size;
5561 BOOL ret, wildcards;
5563 static const WCHAR backslash[] = {'\\',0};
5565 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5566 if (!comp || !comp->Enabled ||
5567 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5569 TRACE("Component not set for install, not moving file\n");
5570 return ERROR_SUCCESS;
5573 sourcename = MSI_RecordGetString(rec, 3);
5574 options = MSI_RecordGetInteger(rec, 7);
5576 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5577 if (!sourcedir)
5578 goto done;
5580 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5581 if (!destdir)
5582 goto done;
5584 if (!sourcename)
5586 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5587 goto done;
5589 source = strdupW(sourcedir);
5590 if (!source)
5591 goto done;
5593 else
5595 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5596 source = msi_alloc(size * sizeof(WCHAR));
5597 if (!source)
5598 goto done;
5600 lstrcpyW(source, sourcedir);
5601 if (source[lstrlenW(source) - 1] != '\\')
5602 lstrcatW(source, backslash);
5603 lstrcatW(source, sourcename);
5606 wildcards = strchrW(source, '*') || strchrW(source, '?');
5608 if (MSI_RecordIsNull(rec, 4))
5610 if (!wildcards)
5612 destname = strdupW(sourcename);
5613 if (!destname)
5614 goto done;
5617 else
5619 destname = strdupW(MSI_RecordGetString(rec, 4));
5620 if (destname)
5621 reduce_to_longfilename(destname);
5624 size = 0;
5625 if (destname)
5626 size = lstrlenW(destname);
5628 size += lstrlenW(destdir) + 2;
5629 dest = msi_alloc(size * sizeof(WCHAR));
5630 if (!dest)
5631 goto done;
5633 lstrcpyW(dest, destdir);
5634 if (dest[lstrlenW(dest) - 1] != '\\')
5635 lstrcatW(dest, backslash);
5637 if (destname)
5638 lstrcatW(dest, destname);
5640 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5642 ret = CreateDirectoryW(destdir, NULL);
5643 if (!ret)
5645 WARN("CreateDirectory failed: %d\n", GetLastError());
5646 return ERROR_SUCCESS;
5650 if (!wildcards)
5651 msi_move_file(source, dest, options);
5652 else
5653 move_files_wildcard(source, dest, options);
5655 done:
5656 msi_free(sourcedir);
5657 msi_free(destdir);
5658 msi_free(destname);
5659 msi_free(source);
5660 msi_free(dest);
5662 return ERROR_SUCCESS;
5665 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5667 UINT rc;
5668 MSIQUERY *view;
5670 static const WCHAR ExecSeqQuery[] =
5671 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5672 '`','M','o','v','e','F','i','l','e','`',0};
5674 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5675 if (rc != ERROR_SUCCESS)
5676 return ERROR_SUCCESS;
5678 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5679 msiobj_release(&view->hdr);
5681 return rc;
5684 typedef struct tagMSIASSEMBLY
5686 struct list entry;
5687 MSICOMPONENT *component;
5688 MSIFEATURE *feature;
5689 MSIFILE *file;
5690 LPWSTR manifest;
5691 LPWSTR application;
5692 DWORD attributes;
5693 BOOL installed;
5694 } MSIASSEMBLY;
5696 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5697 DWORD dwReserved);
5698 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5699 LPVOID pvReserved, HMODULE *phModDll);
5701 static BOOL init_functionpointers(void)
5703 HRESULT hr;
5704 HMODULE hfusion;
5705 HMODULE hmscoree;
5707 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5709 hmscoree = LoadLibraryA("mscoree.dll");
5710 if (!hmscoree)
5712 WARN("mscoree.dll not available\n");
5713 return FALSE;
5716 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5717 if (!pLoadLibraryShim)
5719 WARN("LoadLibraryShim not available\n");
5720 FreeLibrary(hmscoree);
5721 return FALSE;
5724 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5725 if (FAILED(hr))
5727 WARN("fusion.dll not available\n");
5728 FreeLibrary(hmscoree);
5729 return FALSE;
5732 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5734 FreeLibrary(hmscoree);
5735 return TRUE;
5738 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5739 LPWSTR path)
5741 IAssemblyCache *cache;
5742 HRESULT hr;
5743 UINT r = ERROR_FUNCTION_FAILED;
5745 TRACE("installing assembly: %s\n", debugstr_w(path));
5747 if (assembly->feature)
5748 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5750 if (assembly->manifest)
5751 FIXME("Manifest unhandled\n");
5753 if (assembly->application)
5755 FIXME("Assembly should be privately installed\n");
5756 return ERROR_SUCCESS;
5759 if (assembly->attributes == msidbAssemblyAttributesWin32)
5761 FIXME("Win32 assemblies not handled\n");
5762 return ERROR_SUCCESS;
5765 if (!init_functionpointers() || !pCreateAssemblyCache)
5766 return ERROR_FUNCTION_FAILED;
5768 hr = pCreateAssemblyCache(&cache, 0);
5769 if (FAILED(hr))
5770 goto done;
5772 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5773 if (FAILED(hr))
5774 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5776 r = ERROR_SUCCESS;
5778 done:
5779 IAssemblyCache_Release(cache);
5780 return r;
5783 typedef struct tagASSEMBLY_LIST
5785 MSIPACKAGE *package;
5786 struct list *assemblies;
5787 } ASSEMBLY_LIST;
5789 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5791 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5792 MSIASSEMBLY *assembly;
5794 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5795 if (!assembly)
5796 return ERROR_OUTOFMEMORY;
5798 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5800 if (!assembly->component || !assembly->component->Enabled ||
5801 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5803 TRACE("Component not set for install, not publishing assembly\n");
5804 msi_free(assembly);
5805 return ERROR_SUCCESS;
5808 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5809 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5811 if (!assembly->file)
5813 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5814 return ERROR_FUNCTION_FAILED;
5817 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5818 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5819 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5820 assembly->installed = FALSE;
5822 list_add_head(list->assemblies, &assembly->entry);
5824 return ERROR_SUCCESS;
5827 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5829 MSIQUERY *view;
5830 ASSEMBLY_LIST list;
5831 UINT r;
5833 static const WCHAR query[] =
5834 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5835 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5837 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5838 if (r != ERROR_SUCCESS)
5839 return ERROR_SUCCESS;
5841 list.package = package;
5842 list.assemblies = assemblies;
5844 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5845 msiobj_release(&view->hdr);
5847 return r;
5850 static void free_assemblies(struct list *assemblies)
5852 struct list *item, *cursor;
5854 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5856 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5858 list_remove(&assembly->entry);
5859 msi_free(assembly->application);
5860 msi_free(assembly->manifest);
5861 msi_free(assembly);
5865 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5867 MSIASSEMBLY *assembly;
5869 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5871 if (!lstrcmpW(assembly->file->File, file))
5873 *out = assembly;
5874 return TRUE;
5878 return FALSE;
5881 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5882 LPWSTR *path, DWORD *attrs, PVOID user)
5884 MSIASSEMBLY *assembly;
5885 WCHAR temppath[MAX_PATH];
5886 struct list *assemblies = (struct list *)user;
5887 UINT r;
5889 if (!find_assembly(assemblies, file, &assembly))
5890 return FALSE;
5892 GetTempPathW(MAX_PATH, temppath);
5893 PathAddBackslashW(temppath);
5894 lstrcatW(temppath, assembly->file->FileName);
5896 if (action == MSICABEXTRACT_BEGINEXTRACT)
5898 if (assembly->installed)
5899 return FALSE;
5901 *path = strdupW(temppath);
5902 *attrs = assembly->file->Attributes;
5904 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5906 assembly->installed = TRUE;
5908 r = install_assembly(package, assembly, temppath);
5909 if (r != ERROR_SUCCESS)
5910 ERR("Failed to install assembly\n");
5913 return TRUE;
5916 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5918 UINT r;
5919 struct list assemblies = LIST_INIT(assemblies);
5920 MSIASSEMBLY *assembly;
5921 MSIMEDIAINFO *mi;
5923 r = load_assemblies(package, &assemblies);
5924 if (r != ERROR_SUCCESS)
5925 goto done;
5927 if (list_empty(&assemblies))
5928 goto done;
5930 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5931 if (!mi)
5933 r = ERROR_OUTOFMEMORY;
5934 goto done;
5937 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5939 if (assembly->installed && !mi->is_continuous)
5940 continue;
5942 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5943 (assembly->file->IsCompressed && !mi->is_extracted))
5945 MSICABDATA data;
5947 r = ready_media(package, assembly->file, mi);
5948 if (r != ERROR_SUCCESS)
5950 ERR("Failed to ready media\n");
5951 break;
5954 data.mi = mi;
5955 data.package = package;
5956 data.cb = installassembly_cb;
5957 data.user = &assemblies;
5959 if (assembly->file->IsCompressed &&
5960 !msi_cabextract(package, mi, &data))
5962 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5963 r = ERROR_FUNCTION_FAILED;
5964 break;
5968 if (!assembly->file->IsCompressed)
5970 LPWSTR source = resolve_file_source(package, assembly->file);
5972 r = install_assembly(package, assembly, source);
5973 if (r != ERROR_SUCCESS)
5974 ERR("Failed to install assembly\n");
5976 msi_free(source);
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;