msi: Use the right FAILED/SUCCEEDED macro instead of negating the opposite.
[wine/multimedia.git] / dlls / msi / action.c
blob7f73eaa9cbc26829d8ce092b227a86e664da8bc2
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * Prototypes
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137 'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163 'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211 'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223 'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
236 struct _actions {
237 LPCWSTR action;
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
243 * helper functions
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
253 MSIRECORD * row;
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
256 if (!row)
257 return;
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
263 UINT rc)
265 MSIRECORD * row;
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268 '%','s', '.',0};
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272 '%','i','.',0};
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 WCHAR message[1024];
276 WCHAR timet[0x100];
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279 if (start)
280 sprintfW(message,template_s,timet,action);
281 else
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
293 LPCWSTR ptr,ptr2;
294 BOOL quote;
295 DWORD len;
296 LPWSTR prop = NULL, val = NULL;
298 if (!szCommandLine)
299 return ERROR_SUCCESS;
301 ptr = szCommandLine;
303 while (*ptr)
305 if (*ptr==' ')
307 ptr++;
308 continue;
311 TRACE("Looking at %s\n",debugstr_w(ptr));
313 ptr2 = strchrW(ptr,'=');
314 if (!ptr2)
316 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
317 break;
320 quote = FALSE;
322 len = ptr2-ptr;
323 prop = msi_alloc((len+1)*sizeof(WCHAR));
324 memcpy(prop,ptr,len*sizeof(WCHAR));
325 prop[len]=0;
326 ptr2++;
328 len = 0;
329 ptr = ptr2;
330 while (*ptr && (quote || (!quote && *ptr!=' ')))
332 if (*ptr == '"')
333 quote = !quote;
334 ptr++;
335 len++;
338 if (*ptr2=='"')
340 ptr2++;
341 len -= 2;
343 val = msi_alloc((len+1)*sizeof(WCHAR));
344 memcpy(val,ptr2,len*sizeof(WCHAR));
345 val[len] = 0;
347 if (lstrlenW(prop) > 0)
349 TRACE("Found commandline property (%s) = (%s)\n",
350 debugstr_w(prop), debugstr_w(val));
351 MSI_SetPropertyW(package,prop,val);
353 msi_free(val);
354 msi_free(prop);
357 return ERROR_SUCCESS;
361 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
363 LPCWSTR pc;
364 LPWSTR p, *ret = NULL;
365 UINT count = 0;
367 if (!str)
368 return ret;
370 /* count the number of substrings */
371 for ( pc = str, count = 0; pc; count++ )
373 pc = strchrW( pc, sep );
374 if (pc)
375 pc++;
378 /* allocate space for an array of substring pointers and the substrings */
379 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
380 (lstrlenW(str)+1) * sizeof(WCHAR) );
381 if (!ret)
382 return ret;
384 /* copy the string and set the pointers */
385 p = (LPWSTR) &ret[count+1];
386 lstrcpyW( p, str );
387 for( count = 0; (ret[count] = p); count++ )
389 p = strchrW( p, sep );
390 if (p)
391 *p++ = 0;
394 return ret;
397 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
399 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
400 LPWSTR prod_code, patch_product;
401 UINT ret;
403 prod_code = msi_dup_property( package, szProductCode );
404 patch_product = msi_get_suminfo_product( patch );
406 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
408 if ( strstrW( patch_product, prod_code ) )
409 ret = ERROR_SUCCESS;
410 else
411 ret = ERROR_FUNCTION_FAILED;
413 msi_free( patch_product );
414 msi_free( prod_code );
416 return ret;
419 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
420 MSIDATABASE *patch_db, LPCWSTR name )
422 UINT ret = ERROR_FUNCTION_FAILED;
423 IStorage *stg = NULL;
424 HRESULT r;
426 TRACE("%p %s\n", package, debugstr_w(name) );
428 if (*name++ != ':')
430 ERR("expected a colon in %s\n", debugstr_w(name));
431 return ERROR_FUNCTION_FAILED;
434 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
435 if (SUCCEEDED(r))
437 ret = msi_check_transform_applicable( package, stg );
438 if (ret == ERROR_SUCCESS)
439 msi_table_apply_transform( package->db, stg );
440 else
441 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
442 IStorage_Release( stg );
444 else
445 ERR("failed to open substorage %s\n", debugstr_w(name));
447 return ERROR_SUCCESS;
450 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
452 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
453 LPWSTR guid_list, *guids, product_code;
454 UINT i, ret = ERROR_FUNCTION_FAILED;
456 product_code = msi_dup_property( package, szProdCode );
457 if (!product_code)
459 /* FIXME: the property ProductCode should be written into the DB somewhere */
460 ERR("no product code to check\n");
461 return ERROR_SUCCESS;
464 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
465 guids = msi_split_string( guid_list, ';' );
466 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
468 if (!lstrcmpW( guids[i], product_code ))
469 ret = ERROR_SUCCESS;
471 msi_free( guids );
472 msi_free( guid_list );
473 msi_free( product_code );
475 return ret;
478 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
480 MSISUMMARYINFO *si;
481 LPWSTR str, *substorage;
482 UINT i, r = ERROR_SUCCESS;
484 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
485 if (!si)
486 return ERROR_FUNCTION_FAILED;
488 msi_check_patch_applicable( package, si );
490 /* enumerate the substorage */
491 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
492 substorage = msi_split_string( str, ';' );
493 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
494 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
495 msi_free( substorage );
496 msi_free( str );
498 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
500 msiobj_release( &si->hdr );
502 return r;
505 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
507 MSIDATABASE *patch_db = NULL;
508 UINT r;
510 TRACE("%p %s\n", package, debugstr_w( file ) );
512 /* FIXME:
513 * We probably want to make sure we only open a patch collection here.
514 * Patch collections (.msp) and databases (.msi) have different GUIDs
515 * but currently MSI_OpenDatabaseW will accept both.
517 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
518 if ( r != ERROR_SUCCESS )
520 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
521 return r;
524 msi_parse_patch_summary( package, patch_db );
527 * There might be a CAB file in the patch package,
528 * so append it to the list of storage to search for streams.
530 append_storage_to_db( package->db, patch_db->storage );
532 msiobj_release( &patch_db->hdr );
534 return ERROR_SUCCESS;
537 /* get the PATCH property, and apply all the patches it specifies */
538 static UINT msi_apply_patches( MSIPACKAGE *package )
540 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
541 LPWSTR patch_list, *patches;
542 UINT i, r = ERROR_SUCCESS;
544 patch_list = msi_dup_property( package, szPatch );
546 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
548 patches = msi_split_string( patch_list, ';' );
549 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
550 r = msi_apply_patch_package( package, patches[i] );
552 msi_free( patches );
553 msi_free( patch_list );
555 return r;
558 static UINT msi_apply_transforms( MSIPACKAGE *package )
560 static const WCHAR szTransforms[] = {
561 'T','R','A','N','S','F','O','R','M','S',0 };
562 LPWSTR xform_list, *xforms;
563 UINT i, r = ERROR_SUCCESS;
565 xform_list = msi_dup_property( package, szTransforms );
566 xforms = msi_split_string( xform_list, ';' );
568 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
570 if (xforms[i][0] == ':')
571 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
572 else
573 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
576 msi_free( xforms );
577 msi_free( xform_list );
579 return r;
582 static BOOL ui_sequence_exists( MSIPACKAGE *package )
584 MSIQUERY *view;
585 UINT rc;
587 static const WCHAR ExecSeqQuery [] =
588 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l',
590 'U','I','S','e','q','u','e','n','c','e','`',
591 ' ','W','H','E','R','E',' ',
592 '`','S','e','q','u','e','n','c','e','`',' ',
593 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
594 '`','S','e','q','u','e','n','c','e','`',0};
596 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
597 if (rc == ERROR_SUCCESS)
599 msiobj_release(&view->hdr);
600 return TRUE;
603 return FALSE;
606 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
608 LPWSTR p, db;
609 LPWSTR source, check;
610 DWORD len;
612 static const WCHAR szOriginalDatabase[] =
613 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
615 db = msi_dup_property( package, szOriginalDatabase );
616 if (!db)
617 return ERROR_OUTOFMEMORY;
619 p = strrchrW( db, '\\' );
620 if (!p)
622 p = strrchrW( db, '/' );
623 if (!p)
625 msi_free(db);
626 return ERROR_SUCCESS;
630 len = p - db + 2;
631 source = msi_alloc( len * sizeof(WCHAR) );
632 lstrcpynW( source, db, len );
634 check = msi_dup_property( package, cszSourceDir );
635 if (!check || replace)
636 MSI_SetPropertyW( package, cszSourceDir, source );
638 msi_free( check );
640 check = msi_dup_property( package, cszSOURCEDIR );
641 if (!check || replace)
642 MSI_SetPropertyW( package, cszSOURCEDIR, source );
644 msi_free( check );
645 msi_free( source );
646 msi_free( db );
648 return ERROR_SUCCESS;
651 static UINT msi_set_context(MSIPACKAGE *package)
653 WCHAR val[10];
654 DWORD sz = 10;
655 DWORD num;
656 UINT r;
658 static const WCHAR szOne[] = {'1',0};
659 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
661 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
663 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
664 if (r == ERROR_SUCCESS)
666 num = atolW(val);
667 if (num == 1 || num == 2)
668 package->Context = MSIINSTALLCONTEXT_MACHINE;
671 MSI_SetPropertyW(package, szAllUsers, szOne);
672 return ERROR_SUCCESS;
675 /****************************************************
676 * TOP level entry points
677 *****************************************************/
679 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
680 LPCWSTR szCommandLine )
682 UINT rc;
683 BOOL ui = FALSE, ui_exists;
684 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
685 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
686 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
688 MSI_SetPropertyW(package, szAction, szInstall);
690 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
692 package->script->InWhatSequence = SEQUENCE_INSTALL;
694 if (szPackagePath)
696 LPWSTR p, dir;
697 LPCWSTR file;
699 dir = strdupW(szPackagePath);
700 p = strrchrW(dir, '\\');
701 if (p)
703 *(++p) = 0;
704 file = szPackagePath + (p - dir);
706 else
708 msi_free(dir);
709 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
710 GetCurrentDirectoryW(MAX_PATH, dir);
711 lstrcatW(dir, cszbs);
712 file = szPackagePath;
715 msi_free( package->PackagePath );
716 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
717 if (!package->PackagePath)
719 msi_free(dir);
720 return ERROR_OUTOFMEMORY;
723 lstrcpyW(package->PackagePath, dir);
724 lstrcatW(package->PackagePath, file);
725 msi_free(dir);
727 msi_set_sourcedir_props(package, FALSE);
730 msi_parse_command_line( package, szCommandLine );
732 msi_apply_transforms( package );
733 msi_apply_patches( package );
735 /* properties may have been added by a transform */
736 msi_clone_properties( package );
737 msi_set_context( package );
739 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
741 package->script->InWhatSequence |= SEQUENCE_UI;
742 rc = ACTION_ProcessUISequence(package);
743 ui = TRUE;
744 ui_exists = ui_sequence_exists(package);
745 if (rc == ERROR_SUCCESS || !ui_exists)
747 package->script->InWhatSequence |= SEQUENCE_EXEC;
748 rc = ACTION_ProcessExecSequence(package,ui_exists);
751 else
752 rc = ACTION_ProcessExecSequence(package,FALSE);
754 package->script->CurrentlyScripting= FALSE;
756 /* process the ending type action */
757 if (rc == ERROR_SUCCESS)
758 ACTION_PerformActionSequence(package,-1,ui);
759 else if (rc == ERROR_INSTALL_USEREXIT)
760 ACTION_PerformActionSequence(package,-2,ui);
761 else if (rc == ERROR_INSTALL_SUSPEND)
762 ACTION_PerformActionSequence(package,-4,ui);
763 else /* failed */
764 ACTION_PerformActionSequence(package,-3,ui);
766 /* finish up running custom actions */
767 ACTION_FinishCustomActions(package);
769 return rc;
772 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
774 UINT rc = ERROR_SUCCESS;
775 MSIRECORD * row = 0;
776 static const WCHAR ExecSeqQuery[] =
777 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
778 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
779 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
780 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
782 static const WCHAR UISeqQuery[] =
783 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
784 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
785 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
786 ' ', '=',' ','%','i',0};
788 if (UI)
789 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
790 else
791 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
793 if (row)
795 LPCWSTR action, cond;
797 TRACE("Running the actions\n");
799 /* check conditions */
800 cond = MSI_RecordGetString(row,2);
802 /* this is a hack to skip errors in the condition code */
803 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
804 goto end;
806 action = MSI_RecordGetString(row,1);
807 if (!action)
809 ERR("failed to fetch action\n");
810 rc = ERROR_FUNCTION_FAILED;
811 goto end;
814 if (UI)
815 rc = ACTION_PerformUIAction(package,action,-1);
816 else
817 rc = ACTION_PerformAction(package,action,-1,FALSE);
818 end:
819 msiobj_release(&row->hdr);
821 else
822 rc = ERROR_SUCCESS;
824 return rc;
827 typedef struct {
828 MSIPACKAGE* package;
829 BOOL UI;
830 } iterate_action_param;
832 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
834 iterate_action_param *iap= (iterate_action_param*)param;
835 UINT rc;
836 LPCWSTR cond, action;
838 action = MSI_RecordGetString(row,1);
839 if (!action)
841 ERR("Error is retrieving action name\n");
842 return ERROR_FUNCTION_FAILED;
845 /* check conditions */
846 cond = MSI_RecordGetString(row,2);
848 /* this is a hack to skip errors in the condition code */
849 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
851 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
852 return ERROR_SUCCESS;
855 if (iap->UI)
856 rc = ACTION_PerformUIAction(iap->package,action,-1);
857 else
858 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
860 msi_dialog_check_messages( NULL );
862 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
863 rc = iap->package->CurrentInstallState;
865 if (rc == ERROR_FUNCTION_NOT_CALLED)
866 rc = ERROR_SUCCESS;
868 if (rc != ERROR_SUCCESS)
869 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
871 return rc;
874 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
876 MSIQUERY * view;
877 UINT r;
878 static const WCHAR query[] =
879 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
880 '`','%','s','`',
881 ' ','W','H','E','R','E',' ',
882 '`','S','e','q','u','e','n','c','e','`',' ',
883 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
884 '`','S','e','q','u','e','n','c','e','`',0};
885 iterate_action_param iap;
888 * FIXME: probably should be checking UILevel in the
889 * ACTION_PerformUIAction/ACTION_PerformAction
890 * rather than saving the UI level here. Those
891 * two functions can be merged too.
893 iap.package = package;
894 iap.UI = TRUE;
896 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
898 r = MSI_OpenQuery( package->db, &view, query, szTable );
899 if (r == ERROR_SUCCESS)
901 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
902 msiobj_release(&view->hdr);
905 return r;
908 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
910 MSIQUERY * view;
911 UINT rc;
912 static const WCHAR ExecSeqQuery[] =
913 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
914 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
915 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
916 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
917 'O','R','D','E','R',' ', 'B','Y',' ',
918 '`','S','e','q','u','e','n','c','e','`',0 };
919 MSIRECORD * row = 0;
920 static const WCHAR IVQuery[] =
921 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
922 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
923 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
924 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
925 ' ','\'', 'I','n','s','t','a','l','l',
926 'V','a','l','i','d','a','t','e','\'', 0};
927 INT seq = 0;
928 iterate_action_param iap;
930 iap.package = package;
931 iap.UI = FALSE;
933 if (package->script->ExecuteSequenceRun)
935 TRACE("Execute Sequence already Run\n");
936 return ERROR_SUCCESS;
939 package->script->ExecuteSequenceRun = TRUE;
941 /* get the sequence number */
942 if (UIran)
944 row = MSI_QueryGetRecord(package->db, IVQuery);
945 if( !row )
946 return ERROR_FUNCTION_FAILED;
947 seq = MSI_RecordGetInteger(row,1);
948 msiobj_release(&row->hdr);
951 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
952 if (rc == ERROR_SUCCESS)
954 TRACE("Running the actions\n");
956 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
957 msiobj_release(&view->hdr);
960 return rc;
963 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
965 MSIQUERY * view;
966 UINT rc;
967 static const WCHAR ExecSeqQuery [] =
968 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
969 '`','I','n','s','t','a','l','l',
970 'U','I','S','e','q','u','e','n','c','e','`',
971 ' ','W','H','E','R','E',' ',
972 '`','S','e','q','u','e','n','c','e','`',' ',
973 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
974 '`','S','e','q','u','e','n','c','e','`',0};
975 iterate_action_param iap;
977 iap.package = package;
978 iap.UI = TRUE;
980 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
982 if (rc == ERROR_SUCCESS)
984 TRACE("Running the actions\n");
986 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
987 msiobj_release(&view->hdr);
990 return rc;
993 /********************************************************
994 * ACTION helper functions and functions that perform the actions
995 *******************************************************/
996 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
997 UINT* rc, UINT script, BOOL force )
999 BOOL ret=FALSE;
1000 UINT arc;
1002 arc = ACTION_CustomAction(package, action, script, force);
1004 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1006 *rc = arc;
1007 ret = TRUE;
1009 return ret;
1013 * A lot of actions are really important even if they don't do anything
1014 * explicit... Lots of properties are set at the beginning of the installation
1015 * CostFinalize does a bunch of work to translate the directories and such
1017 * But until I get write access to the database that is hard, so I am going to
1018 * hack it to see if I can get something to run.
1020 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1022 UINT rc = ERROR_SUCCESS;
1023 BOOL handled;
1025 TRACE("Performing action (%s)\n",debugstr_w(action));
1027 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1029 if (!handled)
1030 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1032 if (!handled)
1034 WARN("unhandled msi action %s\n",debugstr_w(action));
1035 rc = ERROR_FUNCTION_NOT_CALLED;
1038 return rc;
1041 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1043 UINT rc = ERROR_SUCCESS;
1044 BOOL handled = FALSE;
1046 TRACE("Performing action (%s)\n",debugstr_w(action));
1048 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1050 if (!handled)
1051 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1053 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1054 handled = TRUE;
1056 if (!handled)
1058 WARN("unhandled msi action %s\n",debugstr_w(action));
1059 rc = ERROR_FUNCTION_NOT_CALLED;
1062 return rc;
1067 * Actual Action Handlers
1070 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1072 MSIPACKAGE *package = (MSIPACKAGE*)param;
1073 LPCWSTR dir;
1074 LPWSTR full_path;
1075 MSIRECORD *uirow;
1076 MSIFOLDER *folder;
1078 dir = MSI_RecordGetString(row,1);
1079 if (!dir)
1081 ERR("Unable to get folder id\n");
1082 return ERROR_SUCCESS;
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 /* UI stuff */
1095 uirow = MSI_CreateRecord(1);
1096 MSI_RecordSetStringW(uirow,1,full_path);
1097 ui_actiondata(package,szCreateFolders,uirow);
1098 msiobj_release( &uirow->hdr );
1100 if (folder->State == 0)
1101 create_full_pathW(full_path);
1103 folder->State = 3;
1105 msi_free(full_path);
1106 return ERROR_SUCCESS;
1109 /* FIXME: probably should merge this with the above function */
1110 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1112 UINT rc = ERROR_SUCCESS;
1113 MSIFOLDER *folder;
1114 LPWSTR install_path;
1116 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1117 if (!install_path)
1118 return ERROR_FUNCTION_FAILED;
1120 /* create the path */
1121 if (folder->State == 0)
1123 create_full_pathW(install_path);
1124 folder->State = 2;
1126 msi_free(install_path);
1128 return rc;
1131 UINT msi_create_component_directories( MSIPACKAGE *package )
1133 MSICOMPONENT *comp;
1135 /* create all the folders required by the components are going to install */
1136 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1138 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1139 continue;
1140 msi_create_directory( package, comp->Directory );
1143 return ERROR_SUCCESS;
1147 * Also we cannot enable/disable components either, so for now I am just going
1148 * to do all the directories for all the components.
1150 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1152 static const WCHAR ExecSeqQuery[] =
1153 {'S','E','L','E','C','T',' ',
1154 '`','D','i','r','e','c','t','o','r','y','_','`',
1155 ' ','F','R','O','M',' ',
1156 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1157 UINT rc;
1158 MSIQUERY *view;
1160 /* create all the empty folders specified in the CreateFolder table */
1161 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1162 if (rc != ERROR_SUCCESS)
1163 return ERROR_SUCCESS;
1165 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1166 msiobj_release(&view->hdr);
1168 msi_create_component_directories( package );
1170 return rc;
1173 static UINT load_component( MSIRECORD *row, LPVOID param )
1175 MSIPACKAGE *package = param;
1176 MSICOMPONENT *comp;
1178 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1179 if (!comp)
1180 return ERROR_FUNCTION_FAILED;
1182 list_add_tail( &package->components, &comp->entry );
1184 /* fill in the data */
1185 comp->Component = msi_dup_record_field( row, 1 );
1187 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1189 comp->ComponentId = msi_dup_record_field( row, 2 );
1190 comp->Directory = msi_dup_record_field( row, 3 );
1191 comp->Attributes = MSI_RecordGetInteger(row,4);
1192 comp->Condition = msi_dup_record_field( row, 5 );
1193 comp->KeyPath = msi_dup_record_field( row, 6 );
1195 comp->Installed = INSTALLSTATE_UNKNOWN;
1196 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1198 return ERROR_SUCCESS;
1201 static UINT load_all_components( MSIPACKAGE *package )
1203 static const WCHAR query[] = {
1204 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1205 '`','C','o','m','p','o','n','e','n','t','`',0 };
1206 MSIQUERY *view;
1207 UINT r;
1209 if (!list_empty(&package->components))
1210 return ERROR_SUCCESS;
1212 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1213 if (r != ERROR_SUCCESS)
1214 return r;
1216 r = MSI_IterateRecords(view, NULL, load_component, package);
1217 msiobj_release(&view->hdr);
1218 return r;
1221 typedef struct {
1222 MSIPACKAGE *package;
1223 MSIFEATURE *feature;
1224 } _ilfs;
1226 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1228 ComponentList *cl;
1230 cl = msi_alloc( sizeof (*cl) );
1231 if ( !cl )
1232 return ERROR_NOT_ENOUGH_MEMORY;
1233 cl->component = comp;
1234 list_add_tail( &feature->Components, &cl->entry );
1236 return ERROR_SUCCESS;
1239 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1241 FeatureList *fl;
1243 fl = msi_alloc( sizeof(*fl) );
1244 if ( !fl )
1245 return ERROR_NOT_ENOUGH_MEMORY;
1246 fl->feature = child;
1247 list_add_tail( &parent->Children, &fl->entry );
1249 return ERROR_SUCCESS;
1252 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1254 _ilfs* ilfs= (_ilfs*)param;
1255 LPCWSTR component;
1256 MSICOMPONENT *comp;
1258 component = MSI_RecordGetString(row,1);
1260 /* check to see if the component is already loaded */
1261 comp = get_loaded_component( ilfs->package, component );
1262 if (!comp)
1264 ERR("unknown component %s\n", debugstr_w(component));
1265 return ERROR_FUNCTION_FAILED;
1268 add_feature_component( ilfs->feature, comp );
1269 comp->Enabled = TRUE;
1271 return ERROR_SUCCESS;
1274 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1276 MSIFEATURE *feature;
1278 if ( !name )
1279 return NULL;
1281 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1283 if ( !lstrcmpW( feature->Feature, name ) )
1284 return feature;
1287 return NULL;
1290 static UINT load_feature(MSIRECORD * row, LPVOID param)
1292 MSIPACKAGE* package = (MSIPACKAGE*)param;
1293 MSIFEATURE* feature;
1294 static const WCHAR Query1[] =
1295 {'S','E','L','E','C','T',' ',
1296 '`','C','o','m','p','o','n','e','n','t','_','`',
1297 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1298 'C','o','m','p','o','n','e','n','t','s','`',' ',
1299 'W','H','E','R','E',' ',
1300 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1301 MSIQUERY * view;
1302 UINT rc;
1303 _ilfs ilfs;
1305 /* fill in the data */
1307 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1308 if (!feature)
1309 return ERROR_NOT_ENOUGH_MEMORY;
1311 list_init( &feature->Children );
1312 list_init( &feature->Components );
1314 feature->Feature = msi_dup_record_field( row, 1 );
1316 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1318 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1319 feature->Title = msi_dup_record_field( row, 3 );
1320 feature->Description = msi_dup_record_field( row, 4 );
1322 if (!MSI_RecordIsNull(row,5))
1323 feature->Display = MSI_RecordGetInteger(row,5);
1325 feature->Level= MSI_RecordGetInteger(row,6);
1326 feature->Directory = msi_dup_record_field( row, 7 );
1327 feature->Attributes = MSI_RecordGetInteger(row,8);
1329 feature->Installed = INSTALLSTATE_UNKNOWN;
1330 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1332 list_add_tail( &package->features, &feature->entry );
1334 /* load feature components */
1336 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1337 if (rc != ERROR_SUCCESS)
1338 return ERROR_SUCCESS;
1340 ilfs.package = package;
1341 ilfs.feature = feature;
1343 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1344 msiobj_release(&view->hdr);
1346 return ERROR_SUCCESS;
1349 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1351 MSIPACKAGE* package = (MSIPACKAGE*)param;
1352 MSIFEATURE *parent, *child;
1354 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1355 if (!child)
1356 return ERROR_FUNCTION_FAILED;
1358 if (!child->Feature_Parent)
1359 return ERROR_SUCCESS;
1361 parent = find_feature_by_name( package, child->Feature_Parent );
1362 if (!parent)
1363 return ERROR_FUNCTION_FAILED;
1365 add_feature_child( parent, child );
1366 return ERROR_SUCCESS;
1369 static UINT load_all_features( MSIPACKAGE *package )
1371 static const WCHAR query[] = {
1372 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1373 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1374 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1375 MSIQUERY *view;
1376 UINT r;
1378 if (!list_empty(&package->features))
1379 return ERROR_SUCCESS;
1381 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1382 if (r != ERROR_SUCCESS)
1383 return r;
1385 r = MSI_IterateRecords( view, NULL, load_feature, package );
1386 if (r != ERROR_SUCCESS)
1387 return r;
1389 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1390 msiobj_release( &view->hdr );
1392 return r;
1395 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1397 if (!p)
1398 return p;
1399 p = strchrW(p, ch);
1400 if (!p)
1401 return p;
1402 *p = 0;
1403 return p+1;
1406 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1408 static const WCHAR query[] = {
1409 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1410 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1411 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1412 MSIQUERY *view = NULL;
1413 MSIRECORD *row = NULL;
1414 UINT r;
1416 TRACE("%s\n", debugstr_w(file->File));
1418 r = MSI_OpenQuery(package->db, &view, query, file->File);
1419 if (r != ERROR_SUCCESS)
1420 goto done;
1422 r = MSI_ViewExecute(view, NULL);
1423 if (r != ERROR_SUCCESS)
1424 goto done;
1426 r = MSI_ViewFetch(view, &row);
1427 if (r != ERROR_SUCCESS)
1428 goto done;
1430 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1431 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1432 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1433 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1434 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1436 done:
1437 if (view) msiobj_release(&view->hdr);
1438 if (row) msiobj_release(&row->hdr);
1439 return r;
1442 static UINT load_file(MSIRECORD *row, LPVOID param)
1444 MSIPACKAGE* package = (MSIPACKAGE*)param;
1445 LPCWSTR component;
1446 MSIFILE *file;
1448 /* fill in the data */
1450 file = msi_alloc_zero( sizeof (MSIFILE) );
1451 if (!file)
1452 return ERROR_NOT_ENOUGH_MEMORY;
1454 file->File = msi_dup_record_field( row, 1 );
1456 component = MSI_RecordGetString( row, 2 );
1457 file->Component = get_loaded_component( package, component );
1459 if (!file->Component)
1461 WARN("Component not found: %s\n", debugstr_w(component));
1462 msi_free(file->File);
1463 msi_free(file);
1464 return ERROR_SUCCESS;
1467 file->FileName = msi_dup_record_field( row, 3 );
1468 reduce_to_longfilename( file->FileName );
1470 file->ShortName = msi_dup_record_field( row, 3 );
1471 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1473 file->FileSize = MSI_RecordGetInteger( row, 4 );
1474 file->Version = msi_dup_record_field( row, 5 );
1475 file->Language = msi_dup_record_field( row, 6 );
1476 file->Attributes = MSI_RecordGetInteger( row, 7 );
1477 file->Sequence = MSI_RecordGetInteger( row, 8 );
1479 file->state = msifs_invalid;
1481 /* if the compressed bits are not set in the file attributes,
1482 * then read the information from the package word count property
1484 if (file->Attributes &
1485 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1487 file->IsCompressed = TRUE;
1489 else if (file->Attributes & msidbFileAttributesNoncompressed)
1491 file->IsCompressed = FALSE;
1493 else
1495 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1498 load_file_hash(package, file);
1500 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1502 list_add_tail( &package->files, &file->entry );
1504 return ERROR_SUCCESS;
1507 static UINT load_all_files(MSIPACKAGE *package)
1509 MSIQUERY * view;
1510 UINT rc;
1511 static const WCHAR Query[] =
1512 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1513 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1514 '`','S','e','q','u','e','n','c','e','`', 0};
1516 if (!list_empty(&package->files))
1517 return ERROR_SUCCESS;
1519 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1520 if (rc != ERROR_SUCCESS)
1521 return ERROR_SUCCESS;
1523 rc = MSI_IterateRecords(view, NULL, load_file, package);
1524 msiobj_release(&view->hdr);
1526 return ERROR_SUCCESS;
1529 static UINT load_folder( MSIRECORD *row, LPVOID param )
1531 MSIPACKAGE *package = param;
1532 static const WCHAR szDot[] = { '.',0 };
1533 static WCHAR szEmpty[] = { 0 };
1534 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1535 MSIFOLDER *folder;
1537 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1538 if (!folder)
1539 return ERROR_NOT_ENOUGH_MEMORY;
1541 folder->Directory = msi_dup_record_field( row, 1 );
1543 TRACE("%s\n", debugstr_w(folder->Directory));
1545 p = msi_dup_record_field(row, 3);
1547 /* split src and target dir */
1548 tgt_short = p;
1549 src_short = folder_split_path( p, ':' );
1551 /* split the long and short paths */
1552 tgt_long = folder_split_path( tgt_short, '|' );
1553 src_long = folder_split_path( src_short, '|' );
1555 /* check for no-op dirs */
1556 if (!lstrcmpW(szDot, tgt_short))
1557 tgt_short = szEmpty;
1558 if (!lstrcmpW(szDot, src_short))
1559 src_short = szEmpty;
1561 if (!tgt_long)
1562 tgt_long = tgt_short;
1564 if (!src_short) {
1565 src_short = tgt_short;
1566 src_long = tgt_long;
1569 if (!src_long)
1570 src_long = src_short;
1572 /* FIXME: use the target short path too */
1573 folder->TargetDefault = strdupW(tgt_long);
1574 folder->SourceShortPath = strdupW(src_short);
1575 folder->SourceLongPath = strdupW(src_long);
1576 msi_free(p);
1578 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1579 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1580 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1582 folder->Parent = msi_dup_record_field( row, 2 );
1584 folder->Property = msi_dup_property( package, folder->Directory );
1586 list_add_tail( &package->folders, &folder->entry );
1588 TRACE("returning %p\n", folder);
1590 return ERROR_SUCCESS;
1593 static UINT load_all_folders( MSIPACKAGE *package )
1595 static const WCHAR query[] = {
1596 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1597 '`','D','i','r','e','c','t','o','r','y','`',0 };
1598 MSIQUERY *view;
1599 UINT r;
1601 if (!list_empty(&package->folders))
1602 return ERROR_SUCCESS;
1604 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1605 if (r != ERROR_SUCCESS)
1606 return r;
1608 r = MSI_IterateRecords(view, NULL, load_folder, package);
1609 msiobj_release(&view->hdr);
1610 return r;
1614 * I am not doing any of the costing functionality yet.
1615 * Mostly looking at doing the Component and Feature loading
1617 * The native MSI does A LOT of modification to tables here. Mostly adding
1618 * a lot of temporary columns to the Feature and Component tables.
1620 * note: Native msi also tracks the short filename. But I am only going to
1621 * track the long ones. Also looking at this directory table
1622 * it appears that the directory table does not get the parents
1623 * resolved base on property only based on their entries in the
1624 * directory table.
1626 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1628 static const WCHAR szCosting[] =
1629 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1630 static const WCHAR szZero[] = { '0', 0 };
1632 MSI_SetPropertyW(package, szCosting, szZero);
1633 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1635 load_all_folders( package );
1636 load_all_components( package );
1637 load_all_features( package );
1638 load_all_files( package );
1640 return ERROR_SUCCESS;
1643 static UINT execute_script(MSIPACKAGE *package, UINT script )
1645 UINT i;
1646 UINT rc = ERROR_SUCCESS;
1648 TRACE("Executing Script %i\n",script);
1650 if (!package->script)
1652 ERR("no script!\n");
1653 return ERROR_FUNCTION_FAILED;
1656 for (i = 0; i < package->script->ActionCount[script]; i++)
1658 LPWSTR action;
1659 action = package->script->Actions[script][i];
1660 ui_actionstart(package, action);
1661 TRACE("Executing Action (%s)\n",debugstr_w(action));
1662 rc = ACTION_PerformAction(package, action, script, TRUE);
1663 if (rc != ERROR_SUCCESS)
1664 break;
1666 msi_free_action_script(package, script);
1667 return rc;
1670 static UINT ACTION_FileCost(MSIPACKAGE *package)
1672 return ERROR_SUCCESS;
1675 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1677 MSICOMPONENT *comp;
1678 INSTALLSTATE state;
1679 UINT r;
1681 state = MsiQueryProductStateW(package->ProductCode);
1683 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1685 if (!comp->ComponentId)
1686 continue;
1688 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1689 comp->Installed = INSTALLSTATE_ABSENT;
1690 else
1692 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1693 package->Context, comp->ComponentId,
1694 &comp->Installed);
1695 if (r != ERROR_SUCCESS)
1696 comp->Installed = INSTALLSTATE_ABSENT;
1701 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1703 MSIFEATURE *feature;
1704 INSTALLSTATE state;
1706 state = MsiQueryProductStateW(package->ProductCode);
1708 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1710 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1711 feature->Installed = INSTALLSTATE_ABSENT;
1712 else
1714 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1715 feature->Feature);
1720 static BOOL process_state_property(MSIPACKAGE* package, int level,
1721 LPCWSTR property, INSTALLSTATE state)
1723 static const WCHAR all[]={'A','L','L',0};
1724 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1725 LPWSTR override;
1726 MSIFEATURE *feature;
1728 override = msi_dup_property( package, property );
1729 if (!override)
1730 return FALSE;
1732 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1734 if (lstrcmpW(property, remove) &&
1735 (feature->Level <= 0 || feature->Level > level))
1736 continue;
1738 if (strcmpiW(override,all)==0)
1739 msi_feature_set_state(package, feature, state);
1740 else
1742 LPWSTR ptr = override;
1743 LPWSTR ptr2 = strchrW(override,',');
1745 while (ptr)
1747 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1748 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1750 msi_feature_set_state(package, feature, state);
1751 break;
1753 if (ptr2)
1755 ptr=ptr2+1;
1756 ptr2 = strchrW(ptr,',');
1758 else
1759 break;
1763 msi_free(override);
1765 return TRUE;
1768 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1770 int level;
1771 static const WCHAR szlevel[] =
1772 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1773 static const WCHAR szAddLocal[] =
1774 {'A','D','D','L','O','C','A','L',0};
1775 static const WCHAR szAddSource[] =
1776 {'A','D','D','S','O','U','R','C','E',0};
1777 static const WCHAR szRemove[] =
1778 {'R','E','M','O','V','E',0};
1779 static const WCHAR szReinstall[] =
1780 {'R','E','I','N','S','T','A','L','L',0};
1781 BOOL override = FALSE;
1782 MSICOMPONENT* component;
1783 MSIFEATURE *feature;
1786 /* I do not know if this is where it should happen.. but */
1788 TRACE("Checking Install Level\n");
1790 level = msi_get_property_int(package, szlevel, 1);
1792 /* ok here is the _real_ rub
1793 * all these activation/deactivation things happen in order and things
1794 * later on the list override things earlier on the list.
1795 * 1) INSTALLLEVEL processing
1796 * 2) ADDLOCAL
1797 * 3) REMOVE
1798 * 4) ADDSOURCE
1799 * 5) ADDDEFAULT
1800 * 6) REINSTALL
1801 * 7) COMPADDLOCAL
1802 * 8) COMPADDSOURCE
1803 * 9) FILEADDLOCAL
1804 * 10) FILEADDSOURCE
1805 * 11) FILEADDDEFAULT
1807 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1808 * REMOVE are the big ones, since we don't handle administrative installs
1809 * yet anyway.
1811 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1812 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1813 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1814 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1816 if (!override)
1818 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1820 BOOL feature_state = ((feature->Level > 0) &&
1821 (feature->Level <= level));
1823 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1825 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1826 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1827 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1828 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1829 else
1830 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1834 /* disable child features of unselected parent features */
1835 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1837 FeatureList *fl;
1839 if (feature->Level > 0 && feature->Level <= level)
1840 continue;
1842 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1843 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1846 else
1848 /* set the Preselected Property */
1849 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1850 static const WCHAR szOne[] = { '1', 0 };
1852 MSI_SetPropertyW(package,szPreselected,szOne);
1856 * now we want to enable or disable components base on feature
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 ComponentList *cl;
1863 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1864 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1866 if (!feature->Level)
1867 continue;
1869 /* features with components that have compressed files are made local */
1870 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1872 if (cl->component->Enabled &&
1873 cl->component->ForceLocalState &&
1874 feature->Action == INSTALLSTATE_SOURCE)
1876 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1877 break;
1881 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1883 component = cl->component;
1885 if (!component->Enabled)
1886 continue;
1888 switch (feature->Action)
1890 case INSTALLSTATE_ABSENT:
1891 component->anyAbsent = 1;
1892 break;
1893 case INSTALLSTATE_ADVERTISED:
1894 component->hasAdvertiseFeature = 1;
1895 break;
1896 case INSTALLSTATE_SOURCE:
1897 component->hasSourceFeature = 1;
1898 break;
1899 case INSTALLSTATE_LOCAL:
1900 component->hasLocalFeature = 1;
1901 break;
1902 case INSTALLSTATE_DEFAULT:
1903 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1904 component->hasAdvertiseFeature = 1;
1905 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1906 component->hasSourceFeature = 1;
1907 else
1908 component->hasLocalFeature = 1;
1909 break;
1910 default:
1911 break;
1916 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1918 /* if the component isn't enabled, leave it alone */
1919 if (!component->Enabled)
1920 continue;
1922 /* check if it's local or source */
1923 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1924 (component->hasLocalFeature || component->hasSourceFeature))
1926 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1927 !component->ForceLocalState)
1928 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1929 else
1930 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1931 continue;
1934 /* if any feature is local, the component must be local too */
1935 if (component->hasLocalFeature)
1937 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1938 continue;
1941 if (component->hasSourceFeature)
1943 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1944 continue;
1947 if (component->hasAdvertiseFeature)
1949 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1950 continue;
1953 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1954 if (component->anyAbsent)
1955 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->Action == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1966 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1967 debugstr_w(component->Component), component->Installed, component->Action);
1971 return ERROR_SUCCESS;
1974 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1976 MSIPACKAGE *package = (MSIPACKAGE*)param;
1977 LPCWSTR name;
1978 LPWSTR path;
1979 MSIFOLDER *f;
1981 name = MSI_RecordGetString(row,1);
1983 f = get_loaded_folder(package, name);
1984 if (!f) return ERROR_SUCCESS;
1986 /* reset the ResolvedTarget */
1987 msi_free(f->ResolvedTarget);
1988 f->ResolvedTarget = NULL;
1990 /* This helper function now does ALL the work */
1991 TRACE("Dir %s ...\n",debugstr_w(name));
1992 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1993 TRACE("resolves to %s\n",debugstr_w(path));
1994 msi_free(path);
1996 return ERROR_SUCCESS;
1999 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2001 MSIPACKAGE *package = (MSIPACKAGE*)param;
2002 LPCWSTR name;
2003 MSIFEATURE *feature;
2005 name = MSI_RecordGetString( row, 1 );
2007 feature = get_loaded_feature( package, name );
2008 if (!feature)
2009 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2010 else
2012 LPCWSTR Condition;
2013 Condition = MSI_RecordGetString(row,3);
2015 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2017 int level = MSI_RecordGetInteger(row,2);
2018 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2019 feature->Level = level;
2022 return ERROR_SUCCESS;
2025 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2027 static const WCHAR name_fmt[] =
2028 {'%','u','.','%','u','.','%','u','.','%','u',0};
2029 static const WCHAR name[] = {'\\',0};
2030 VS_FIXEDFILEINFO *lpVer;
2031 WCHAR filever[0x100];
2032 LPVOID version;
2033 DWORD versize;
2034 DWORD handle;
2035 UINT sz;
2037 TRACE("%s\n", debugstr_w(filename));
2039 versize = GetFileVersionInfoSizeW( filename, &handle );
2040 if (!versize)
2041 return NULL;
2043 version = msi_alloc( versize );
2044 GetFileVersionInfoW( filename, 0, versize, version );
2046 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2048 msi_free( version );
2049 return NULL;
2052 sprintfW( filever, name_fmt,
2053 HIWORD(lpVer->dwFileVersionMS),
2054 LOWORD(lpVer->dwFileVersionMS),
2055 HIWORD(lpVer->dwFileVersionLS),
2056 LOWORD(lpVer->dwFileVersionLS));
2058 msi_free( version );
2060 return strdupW( filever );
2063 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2065 LPWSTR file_version;
2066 MSIFILE *file;
2068 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2070 MSICOMPONENT* comp = file->Component;
2071 LPWSTR p;
2073 if (!comp)
2074 continue;
2076 if (file->IsCompressed)
2077 comp->ForceLocalState = TRUE;
2079 /* calculate target */
2080 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2082 msi_free(file->TargetPath);
2084 TRACE("file %s is named %s\n",
2085 debugstr_w(file->File), debugstr_w(file->FileName));
2087 file->TargetPath = build_directory_name(2, p, file->FileName);
2089 msi_free(p);
2091 TRACE("file %s resolves to %s\n",
2092 debugstr_w(file->File), debugstr_w(file->TargetPath));
2094 /* don't check files of components that aren't installed */
2095 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2096 comp->Installed == INSTALLSTATE_ABSENT)
2098 file->state = msifs_missing; /* assume files are missing */
2099 continue;
2102 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2104 file->state = msifs_missing;
2105 comp->Cost += file->FileSize;
2106 continue;
2109 if (file->Version &&
2110 (file_version = msi_get_disk_file_version( file->TargetPath )))
2112 TRACE("new %s old %s\n", debugstr_w(file->Version),
2113 debugstr_w(file_version));
2114 /* FIXME: seems like a bad way to compare version numbers */
2115 if (lstrcmpiW(file_version, file->Version)<0)
2117 file->state = msifs_overwrite;
2118 comp->Cost += file->FileSize;
2120 else
2121 file->state = msifs_present;
2122 msi_free( file_version );
2124 else
2125 file->state = msifs_present;
2128 return ERROR_SUCCESS;
2132 * A lot is done in this function aside from just the costing.
2133 * The costing needs to be implemented at some point but for now I am going
2134 * to focus on the directory building
2137 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2139 static const WCHAR ExecSeqQuery[] =
2140 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2141 '`','D','i','r','e','c','t','o','r','y','`',0};
2142 static const WCHAR ConditionQuery[] =
2143 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2144 '`','C','o','n','d','i','t','i','o','n','`',0};
2145 static const WCHAR szCosting[] =
2146 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2147 static const WCHAR szlevel[] =
2148 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2149 static const WCHAR szOutOfDiskSpace[] =
2150 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2151 static const WCHAR szOne[] = { '1', 0 };
2152 static const WCHAR szZero[] = { '0', 0 };
2153 MSICOMPONENT *comp;
2154 UINT rc;
2155 MSIQUERY * view;
2156 LPWSTR level;
2158 TRACE("Building Directory properties\n");
2160 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2161 if (rc == ERROR_SUCCESS)
2163 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2164 package);
2165 msiobj_release(&view->hdr);
2168 /* read components states from the registry */
2169 ACTION_GetComponentInstallStates(package);
2170 ACTION_GetFeatureInstallStates(package);
2172 TRACE("File calculations\n");
2173 msi_check_file_install_states( package );
2175 TRACE("Evaluating Condition Table\n");
2177 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2178 if (rc == ERROR_SUCCESS)
2180 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2181 package);
2182 msiobj_release(&view->hdr);
2185 TRACE("Enabling or Disabling Components\n");
2186 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2188 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2190 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2191 comp->Enabled = FALSE;
2193 else
2194 comp->Enabled = TRUE;
2197 MSI_SetPropertyW(package,szCosting,szOne);
2198 /* set default run level if not set */
2199 level = msi_dup_property( package, szlevel );
2200 if (!level)
2201 MSI_SetPropertyW(package,szlevel, szOne);
2202 msi_free(level);
2204 /* FIXME: check volume disk space */
2205 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2207 return MSI_SetFeatureStates(package);
2210 /* OK this value is "interpreted" and then formatted based on the
2211 first few characters */
2212 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2213 DWORD *size)
2215 LPSTR data = NULL;
2217 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2219 if (value[1]=='x')
2221 LPWSTR ptr;
2222 CHAR byte[5];
2223 LPWSTR deformated = NULL;
2224 int count;
2226 deformat_string(package, &value[2], &deformated);
2228 /* binary value type */
2229 ptr = deformated;
2230 *type = REG_BINARY;
2231 if (strlenW(ptr)%2)
2232 *size = (strlenW(ptr)/2)+1;
2233 else
2234 *size = strlenW(ptr)/2;
2236 data = msi_alloc(*size);
2238 byte[0] = '0';
2239 byte[1] = 'x';
2240 byte[4] = 0;
2241 count = 0;
2242 /* if uneven pad with a zero in front */
2243 if (strlenW(ptr)%2)
2245 byte[2]= '0';
2246 byte[3]= *ptr;
2247 ptr++;
2248 data[count] = (BYTE)strtol(byte,NULL,0);
2249 count ++;
2250 TRACE("Uneven byte count\n");
2252 while (*ptr)
2254 byte[2]= *ptr;
2255 ptr++;
2256 byte[3]= *ptr;
2257 ptr++;
2258 data[count] = (BYTE)strtol(byte,NULL,0);
2259 count ++;
2261 msi_free(deformated);
2263 TRACE("Data %i bytes(%i)\n",*size,count);
2265 else
2267 LPWSTR deformated;
2268 LPWSTR p;
2269 DWORD d = 0;
2270 deformat_string(package, &value[1], &deformated);
2272 *type=REG_DWORD;
2273 *size = sizeof(DWORD);
2274 data = msi_alloc(*size);
2275 p = deformated;
2276 if (*p == '-')
2277 p++;
2278 while (*p)
2280 if ( (*p < '0') || (*p > '9') )
2281 break;
2282 d *= 10;
2283 d += (*p - '0');
2284 p++;
2286 if (deformated[0] == '-')
2287 d = -d;
2288 *(LPDWORD)data = d;
2289 TRACE("DWORD %i\n",*(LPDWORD)data);
2291 msi_free(deformated);
2294 else
2296 static const WCHAR szMulti[] = {'[','~',']',0};
2297 LPCWSTR ptr;
2298 *type=REG_SZ;
2300 if (value[0]=='#')
2302 if (value[1]=='%')
2304 ptr = &value[2];
2305 *type=REG_EXPAND_SZ;
2307 else
2308 ptr = &value[1];
2310 else
2311 ptr=value;
2313 if (strstrW(value,szMulti))
2314 *type = REG_MULTI_SZ;
2316 /* remove initial delimiter */
2317 if (!strncmpW(value, szMulti, 3))
2318 ptr = value + 3;
2320 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2322 /* add double NULL terminator */
2323 if (*type == REG_MULTI_SZ)
2325 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2326 data = msi_realloc_zero(data, *size);
2329 return data;
2332 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2334 MSIPACKAGE *package = (MSIPACKAGE*)param;
2335 static const WCHAR szHCR[] =
2336 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2337 'R','O','O','T','\\',0};
2338 static const WCHAR szHCU[] =
2339 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2340 'U','S','E','R','\\',0};
2341 static const WCHAR szHLM[] =
2342 {'H','K','E','Y','_','L','O','C','A','L','_',
2343 'M','A','C','H','I','N','E','\\',0};
2344 static const WCHAR szHU[] =
2345 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2347 LPSTR value_data = NULL;
2348 HKEY root_key, hkey;
2349 DWORD type,size;
2350 LPWSTR deformated;
2351 LPCWSTR szRoot, component, name, key, value;
2352 MSICOMPONENT *comp;
2353 MSIRECORD * uirow;
2354 LPWSTR uikey;
2355 INT root;
2356 BOOL check_first = FALSE;
2357 UINT rc;
2359 ui_progress(package,2,0,0,0);
2361 value = NULL;
2362 key = NULL;
2363 uikey = NULL;
2364 name = NULL;
2366 component = MSI_RecordGetString(row, 6);
2367 comp = get_loaded_component(package,component);
2368 if (!comp)
2369 return ERROR_SUCCESS;
2371 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2373 TRACE("Skipping write due to disabled component %s\n",
2374 debugstr_w(component));
2376 comp->Action = comp->Installed;
2378 return ERROR_SUCCESS;
2381 comp->Action = INSTALLSTATE_LOCAL;
2383 name = MSI_RecordGetString(row, 4);
2384 if( MSI_RecordIsNull(row,5) && name )
2386 /* null values can have special meanings */
2387 if (name[0]=='-' && name[1] == 0)
2388 return ERROR_SUCCESS;
2389 else if ((name[0]=='+' && name[1] == 0) ||
2390 (name[0] == '*' && name[1] == 0))
2391 name = NULL;
2392 check_first = TRUE;
2395 root = MSI_RecordGetInteger(row,2);
2396 key = MSI_RecordGetString(row, 3);
2398 /* get the root key */
2399 switch (root)
2401 case -1:
2403 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2404 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2405 if (all_users && all_users[0] == '1')
2407 root_key = HKEY_LOCAL_MACHINE;
2408 szRoot = szHLM;
2410 else
2412 root_key = HKEY_CURRENT_USER;
2413 szRoot = szHCU;
2415 msi_free(all_users);
2417 break;
2418 case 0: root_key = HKEY_CLASSES_ROOT;
2419 szRoot = szHCR;
2420 break;
2421 case 1: root_key = HKEY_CURRENT_USER;
2422 szRoot = szHCU;
2423 break;
2424 case 2: root_key = HKEY_LOCAL_MACHINE;
2425 szRoot = szHLM;
2426 break;
2427 case 3: root_key = HKEY_USERS;
2428 szRoot = szHU;
2429 break;
2430 default:
2431 ERR("Unknown root %i\n",root);
2432 root_key=NULL;
2433 szRoot = NULL;
2434 break;
2436 if (!root_key)
2437 return ERROR_SUCCESS;
2439 deformat_string(package, key , &deformated);
2440 size = strlenW(deformated) + strlenW(szRoot) + 1;
2441 uikey = msi_alloc(size*sizeof(WCHAR));
2442 strcpyW(uikey,szRoot);
2443 strcatW(uikey,deformated);
2445 if (RegCreateKeyW( root_key, deformated, &hkey))
2447 ERR("Could not create key %s\n",debugstr_w(deformated));
2448 msi_free(deformated);
2449 msi_free(uikey);
2450 return ERROR_SUCCESS;
2452 msi_free(deformated);
2454 value = MSI_RecordGetString(row,5);
2455 if (value)
2456 value_data = parse_value(package, value, &type, &size);
2457 else
2459 static const WCHAR szEmpty[] = {0};
2460 value_data = (LPSTR)strdupW(szEmpty);
2461 size = sizeof(szEmpty);
2462 type = REG_SZ;
2465 deformat_string(package, name, &deformated);
2467 if (!check_first)
2469 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2470 debugstr_w(uikey));
2471 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2473 else
2475 DWORD sz = 0;
2476 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2477 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2479 TRACE("value %s of %s checked already exists\n",
2480 debugstr_w(deformated), debugstr_w(uikey));
2482 else
2484 TRACE("Checked and setting value %s of %s\n",
2485 debugstr_w(deformated), debugstr_w(uikey));
2486 if (deformated || size)
2487 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2490 RegCloseKey(hkey);
2492 uirow = MSI_CreateRecord(3);
2493 MSI_RecordSetStringW(uirow,2,deformated);
2494 MSI_RecordSetStringW(uirow,1,uikey);
2496 if (type == REG_SZ)
2497 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2498 else
2499 MSI_RecordSetStringW(uirow,3,value);
2501 ui_actiondata(package,szWriteRegistryValues,uirow);
2502 msiobj_release( &uirow->hdr );
2504 msi_free(value_data);
2505 msi_free(deformated);
2506 msi_free(uikey);
2508 return ERROR_SUCCESS;
2511 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2513 UINT rc;
2514 MSIQUERY * view;
2515 static const WCHAR ExecSeqQuery[] =
2516 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2517 '`','R','e','g','i','s','t','r','y','`',0 };
2519 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2520 if (rc != ERROR_SUCCESS)
2521 return ERROR_SUCCESS;
2523 /* increment progress bar each time action data is sent */
2524 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2526 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2528 msiobj_release(&view->hdr);
2529 return rc;
2532 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2534 package->script->CurrentlyScripting = TRUE;
2536 return ERROR_SUCCESS;
2540 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2542 MSICOMPONENT *comp;
2543 DWORD progress = 0;
2544 DWORD total = 0;
2545 static const WCHAR q1[]=
2546 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2547 '`','R','e','g','i','s','t','r','y','`',0};
2548 UINT rc;
2549 MSIQUERY * view;
2550 MSIFEATURE *feature;
2551 MSIFILE *file;
2553 TRACE("InstallValidate\n");
2555 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2556 if (rc == ERROR_SUCCESS)
2558 MSI_IterateRecords( view, &progress, NULL, package );
2559 msiobj_release( &view->hdr );
2560 total += progress * REG_PROGRESS_VALUE;
2563 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2564 total += COMPONENT_PROGRESS_VALUE;
2566 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2567 total += file->FileSize;
2569 ui_progress(package,0,total,0,0);
2571 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2573 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2574 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2575 feature->ActionRequest);
2578 return ERROR_SUCCESS;
2581 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2583 MSIPACKAGE* package = (MSIPACKAGE*)param;
2584 LPCWSTR cond = NULL;
2585 LPCWSTR message = NULL;
2586 UINT r;
2588 static const WCHAR title[]=
2589 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2591 cond = MSI_RecordGetString(row,1);
2593 r = MSI_EvaluateConditionW(package,cond);
2594 if (r == MSICONDITION_FALSE)
2596 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2598 LPWSTR deformated;
2599 message = MSI_RecordGetString(row,2);
2600 deformat_string(package,message,&deformated);
2601 MessageBoxW(NULL,deformated,title,MB_OK);
2602 msi_free(deformated);
2605 return ERROR_INSTALL_FAILURE;
2608 return ERROR_SUCCESS;
2611 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2613 UINT rc;
2614 MSIQUERY * view = NULL;
2615 static const WCHAR ExecSeqQuery[] =
2616 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2617 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2619 TRACE("Checking launch conditions\n");
2621 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2622 if (rc != ERROR_SUCCESS)
2623 return ERROR_SUCCESS;
2625 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2626 msiobj_release(&view->hdr);
2628 return rc;
2631 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2634 if (!cmp->KeyPath)
2635 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2637 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2639 MSIRECORD * row = 0;
2640 UINT root,len;
2641 LPWSTR deformated,buffer,deformated_name;
2642 LPCWSTR key,name;
2643 static const WCHAR ExecSeqQuery[] =
2644 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2645 '`','R','e','g','i','s','t','r','y','`',' ',
2646 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2647 ' ','=',' ' ,'\'','%','s','\'',0 };
2648 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2649 static const WCHAR fmt2[]=
2650 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2652 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2653 if (!row)
2654 return NULL;
2656 root = MSI_RecordGetInteger(row,2);
2657 key = MSI_RecordGetString(row, 3);
2658 name = MSI_RecordGetString(row, 4);
2659 deformat_string(package, key , &deformated);
2660 deformat_string(package, name, &deformated_name);
2662 len = strlenW(deformated) + 6;
2663 if (deformated_name)
2664 len+=strlenW(deformated_name);
2666 buffer = msi_alloc( len *sizeof(WCHAR));
2668 if (deformated_name)
2669 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2670 else
2671 sprintfW(buffer,fmt,root,deformated);
2673 msi_free(deformated);
2674 msi_free(deformated_name);
2675 msiobj_release(&row->hdr);
2677 return buffer;
2679 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2681 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2682 return NULL;
2684 else
2686 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2688 if (file)
2689 return strdupW( file->TargetPath );
2691 return NULL;
2694 static HKEY openSharedDLLsKey(void)
2696 HKEY hkey=0;
2697 static const WCHAR path[] =
2698 {'S','o','f','t','w','a','r','e','\\',
2699 'M','i','c','r','o','s','o','f','t','\\',
2700 'W','i','n','d','o','w','s','\\',
2701 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2702 'S','h','a','r','e','d','D','L','L','s',0};
2704 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2705 return hkey;
2708 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2710 HKEY hkey;
2711 DWORD count=0;
2712 DWORD type;
2713 DWORD sz = sizeof(count);
2714 DWORD rc;
2716 hkey = openSharedDLLsKey();
2717 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2718 if (rc != ERROR_SUCCESS)
2719 count = 0;
2720 RegCloseKey(hkey);
2721 return count;
2724 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2726 HKEY hkey;
2728 hkey = openSharedDLLsKey();
2729 if (count > 0)
2730 msi_reg_set_val_dword( hkey, path, count );
2731 else
2732 RegDeleteValueW(hkey,path);
2733 RegCloseKey(hkey);
2734 return count;
2738 * Return TRUE if the count should be written out and FALSE if not
2740 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2742 MSIFEATURE *feature;
2743 INT count = 0;
2744 BOOL write = FALSE;
2746 /* only refcount DLLs */
2747 if (comp->KeyPath == NULL ||
2748 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2749 comp->Attributes & msidbComponentAttributesODBCDataSource)
2750 write = FALSE;
2751 else
2753 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2754 write = (count > 0);
2756 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2757 write = TRUE;
2760 /* increment counts */
2761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2763 ComponentList *cl;
2765 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2766 continue;
2768 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2770 if ( cl->component == comp )
2771 count++;
2775 /* decrement counts */
2776 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2778 ComponentList *cl;
2780 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2781 continue;
2783 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2785 if ( cl->component == comp )
2786 count--;
2790 /* ref count all the files in the component */
2791 if (write)
2793 MSIFILE *file;
2795 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2797 if (file->Component == comp)
2798 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2802 /* add a count for permanent */
2803 if (comp->Attributes & msidbComponentAttributesPermanent)
2804 count ++;
2806 comp->RefCount = count;
2808 if (write)
2809 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2812 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2814 WCHAR squished_pc[GUID_SIZE];
2815 WCHAR squished_cc[GUID_SIZE];
2816 UINT rc;
2817 MSICOMPONENT *comp;
2818 HKEY hkey;
2820 TRACE("\n");
2822 squash_guid(package->ProductCode,squished_pc);
2823 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2825 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2827 MSIRECORD * uirow;
2829 ui_progress(package,2,0,0,0);
2830 if (!comp->ComponentId)
2831 continue;
2833 squash_guid(comp->ComponentId,squished_cc);
2835 msi_free(comp->FullKeypath);
2836 comp->FullKeypath = resolve_keypath( package, comp );
2838 ACTION_RefCountComponent( package, comp );
2840 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2841 debugstr_w(comp->Component),
2842 debugstr_w(squished_cc),
2843 debugstr_w(comp->FullKeypath),
2844 comp->RefCount);
2846 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2847 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2849 if (!comp->FullKeypath)
2850 continue;
2852 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2853 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2854 else
2855 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2857 if (rc != ERROR_SUCCESS)
2858 continue;
2860 if (comp->Attributes & msidbComponentAttributesPermanent)
2862 static const WCHAR szPermKey[] =
2863 { '0','0','0','0','0','0','0','0','0','0','0','0',
2864 '0','0','0','0','0','0','0','0','0','0','0','0',
2865 '0','0','0','0','0','0','0','0',0 };
2867 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2870 if (comp->Action == INSTALLSTATE_LOCAL)
2871 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2872 else
2874 MSIFILE *file;
2875 MSIRECORD *row;
2876 LPWSTR ptr, ptr2;
2877 WCHAR source[MAX_PATH];
2878 WCHAR base[MAX_PATH];
2879 LPWSTR sourcepath;
2881 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2882 static const WCHAR query[] = {
2883 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2884 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2885 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2886 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2887 '`','D','i','s','k','I','d','`',0};
2889 file = get_loaded_file(package, comp->KeyPath);
2890 if (!file)
2891 continue;
2893 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2894 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2895 ptr2 = strrchrW(source, '\\') + 1;
2896 msiobj_release(&row->hdr);
2898 lstrcpyW(base, package->PackagePath);
2899 ptr = strrchrW(base, '\\');
2900 *(ptr + 1) = '\0';
2902 sourcepath = resolve_file_source(package, file);
2903 ptr = sourcepath + lstrlenW(base);
2904 lstrcpyW(ptr2, ptr);
2905 msi_free(sourcepath);
2907 msi_reg_set_val_str(hkey, squished_pc, source);
2909 RegCloseKey(hkey);
2911 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2913 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2914 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2915 else
2916 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2919 /* UI stuff */
2920 uirow = MSI_CreateRecord(3);
2921 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2922 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2923 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2924 ui_actiondata(package,szProcessComponents,uirow);
2925 msiobj_release( &uirow->hdr );
2928 return ERROR_SUCCESS;
2931 typedef struct {
2932 CLSID clsid;
2933 LPWSTR source;
2935 LPWSTR path;
2936 ITypeLib *ptLib;
2937 } typelib_struct;
2939 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2940 LPWSTR lpszName, LONG_PTR lParam)
2942 TLIBATTR *attr;
2943 typelib_struct *tl_struct = (typelib_struct*) lParam;
2944 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2945 int sz;
2946 HRESULT res;
2948 if (!IS_INTRESOURCE(lpszName))
2950 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2951 return TRUE;
2954 sz = strlenW(tl_struct->source)+4;
2955 sz *= sizeof(WCHAR);
2957 if ((INT_PTR)lpszName == 1)
2958 tl_struct->path = strdupW(tl_struct->source);
2959 else
2961 tl_struct->path = msi_alloc(sz);
2962 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2965 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2966 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2967 if (FAILED(res))
2969 msi_free(tl_struct->path);
2970 tl_struct->path = NULL;
2972 return TRUE;
2975 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2976 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2978 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2979 return FALSE;
2982 msi_free(tl_struct->path);
2983 tl_struct->path = NULL;
2985 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2986 ITypeLib_Release(tl_struct->ptLib);
2988 return TRUE;
2991 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2993 MSIPACKAGE* package = (MSIPACKAGE*)param;
2994 LPCWSTR component;
2995 MSICOMPONENT *comp;
2996 MSIFILE *file;
2997 typelib_struct tl_struct;
2998 ITypeLib *tlib;
2999 HMODULE module;
3000 HRESULT hr;
3002 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3004 component = MSI_RecordGetString(row,3);
3005 comp = get_loaded_component(package,component);
3006 if (!comp)
3007 return ERROR_SUCCESS;
3009 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3011 TRACE("Skipping typelib reg due to disabled component\n");
3013 comp->Action = comp->Installed;
3015 return ERROR_SUCCESS;
3018 comp->Action = INSTALLSTATE_LOCAL;
3020 file = get_loaded_file( package, comp->KeyPath );
3021 if (!file)
3022 return ERROR_SUCCESS;
3024 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3025 if (module)
3027 LPCWSTR guid;
3028 guid = MSI_RecordGetString(row,1);
3029 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3030 tl_struct.source = strdupW( file->TargetPath );
3031 tl_struct.path = NULL;
3033 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3034 (LONG_PTR)&tl_struct);
3036 if (tl_struct.path)
3038 LPWSTR help = NULL;
3039 LPCWSTR helpid;
3040 HRESULT res;
3042 helpid = MSI_RecordGetString(row,6);
3044 if (helpid)
3045 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3046 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3047 msi_free(help);
3049 if (FAILED(res))
3050 ERR("Failed to register type library %s\n",
3051 debugstr_w(tl_struct.path));
3052 else
3054 ui_actiondata(package,szRegisterTypeLibraries,row);
3056 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3059 ITypeLib_Release(tl_struct.ptLib);
3060 msi_free(tl_struct.path);
3062 else
3063 ERR("Failed to load type library %s\n",
3064 debugstr_w(tl_struct.source));
3066 FreeLibrary(module);
3067 msi_free(tl_struct.source);
3069 else
3071 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3072 if (FAILED(hr))
3074 ERR("Failed to load type library: %08x\n", hr);
3075 return ERROR_FUNCTION_FAILED;
3078 ITypeLib_Release(tlib);
3081 return ERROR_SUCCESS;
3084 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3087 * OK this is a bit confusing.. I am given a _Component key and I believe
3088 * that the file that is being registered as a type library is the "key file
3089 * of that component" which I interpret to mean "The file in the KeyPath of
3090 * that component".
3092 UINT rc;
3093 MSIQUERY * view;
3094 static const WCHAR Query[] =
3095 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3096 '`','T','y','p','e','L','i','b','`',0};
3098 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3099 if (rc != ERROR_SUCCESS)
3100 return ERROR_SUCCESS;
3102 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3103 msiobj_release(&view->hdr);
3104 return rc;
3107 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3109 MSIPACKAGE *package = (MSIPACKAGE*)param;
3110 LPWSTR target_file, target_folder, filename;
3111 LPCWSTR buffer, extension;
3112 MSICOMPONENT *comp;
3113 static const WCHAR szlnk[]={'.','l','n','k',0};
3114 IShellLinkW *sl = NULL;
3115 IPersistFile *pf = NULL;
3116 HRESULT res;
3118 buffer = MSI_RecordGetString(row,4);
3119 comp = get_loaded_component(package,buffer);
3120 if (!comp)
3121 return ERROR_SUCCESS;
3123 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3125 TRACE("Skipping shortcut creation due to disabled component\n");
3127 comp->Action = comp->Installed;
3129 return ERROR_SUCCESS;
3132 comp->Action = INSTALLSTATE_LOCAL;
3134 ui_actiondata(package,szCreateShortcuts,row);
3136 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3137 &IID_IShellLinkW, (LPVOID *) &sl );
3139 if (FAILED( res ))
3141 ERR("CLSID_ShellLink not available\n");
3142 goto err;
3145 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3146 if (FAILED( res ))
3148 ERR("QueryInterface(IID_IPersistFile) failed\n");
3149 goto err;
3152 buffer = MSI_RecordGetString(row,2);
3153 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3155 /* may be needed because of a bug somewhere else */
3156 create_full_pathW(target_folder);
3158 filename = msi_dup_record_field( row, 3 );
3159 reduce_to_longfilename(filename);
3161 extension = strchrW(filename,'.');
3162 if (!extension || strcmpiW(extension,szlnk))
3164 int len = strlenW(filename);
3165 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3166 memcpy(filename + len, szlnk, sizeof(szlnk));
3168 target_file = build_directory_name(2, target_folder, filename);
3169 msi_free(target_folder);
3170 msi_free(filename);
3172 buffer = MSI_RecordGetString(row,5);
3173 if (strchrW(buffer,'['))
3175 LPWSTR deformated;
3176 deformat_string(package,buffer,&deformated);
3177 IShellLinkW_SetPath(sl,deformated);
3178 msi_free(deformated);
3180 else
3182 FIXME("poorly handled shortcut format, advertised shortcut\n");
3183 IShellLinkW_SetPath(sl,comp->FullKeypath);
3186 if (!MSI_RecordIsNull(row,6))
3188 LPWSTR deformated;
3189 buffer = MSI_RecordGetString(row,6);
3190 deformat_string(package,buffer,&deformated);
3191 IShellLinkW_SetArguments(sl,deformated);
3192 msi_free(deformated);
3195 if (!MSI_RecordIsNull(row,7))
3197 buffer = MSI_RecordGetString(row,7);
3198 IShellLinkW_SetDescription(sl,buffer);
3201 if (!MSI_RecordIsNull(row,8))
3202 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3204 if (!MSI_RecordIsNull(row,9))
3206 LPWSTR Path;
3207 INT index;
3209 buffer = MSI_RecordGetString(row,9);
3211 Path = build_icon_path(package,buffer);
3212 index = MSI_RecordGetInteger(row,10);
3214 /* no value means 0 */
3215 if (index == MSI_NULL_INTEGER)
3216 index = 0;
3218 IShellLinkW_SetIconLocation(sl,Path,index);
3219 msi_free(Path);
3222 if (!MSI_RecordIsNull(row,11))
3223 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3225 if (!MSI_RecordIsNull(row,12))
3227 LPWSTR Path;
3228 buffer = MSI_RecordGetString(row,12);
3229 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3230 if (Path)
3231 IShellLinkW_SetWorkingDirectory(sl,Path);
3232 msi_free(Path);
3235 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3236 IPersistFile_Save(pf,target_file,FALSE);
3238 msi_free(target_file);
3240 err:
3241 if (pf)
3242 IPersistFile_Release( pf );
3243 if (sl)
3244 IShellLinkW_Release( sl );
3246 return ERROR_SUCCESS;
3249 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3251 UINT rc;
3252 HRESULT res;
3253 MSIQUERY * view;
3254 static const WCHAR Query[] =
3255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3256 '`','S','h','o','r','t','c','u','t','`',0};
3258 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3259 if (rc != ERROR_SUCCESS)
3260 return ERROR_SUCCESS;
3262 res = CoInitialize( NULL );
3263 if (FAILED (res))
3265 ERR("CoInitialize failed\n");
3266 return ERROR_FUNCTION_FAILED;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3270 msiobj_release(&view->hdr);
3272 CoUninitialize();
3274 return rc;
3277 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3279 MSIPACKAGE* package = (MSIPACKAGE*)param;
3280 HANDLE the_file;
3281 LPWSTR FilePath;
3282 LPCWSTR FileName;
3283 CHAR buffer[1024];
3284 DWORD sz;
3285 UINT rc;
3286 MSIRECORD *uirow;
3288 FileName = MSI_RecordGetString(row,1);
3289 if (!FileName)
3291 ERR("Unable to get FileName\n");
3292 return ERROR_SUCCESS;
3295 FilePath = build_icon_path(package,FileName);
3297 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3299 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3300 FILE_ATTRIBUTE_NORMAL, NULL);
3302 if (the_file == INVALID_HANDLE_VALUE)
3304 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3305 msi_free(FilePath);
3306 return ERROR_SUCCESS;
3311 DWORD write;
3312 sz = 1024;
3313 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3314 if (rc != ERROR_SUCCESS)
3316 ERR("Failed to get stream\n");
3317 CloseHandle(the_file);
3318 DeleteFileW(FilePath);
3319 break;
3321 WriteFile(the_file,buffer,sz,&write,NULL);
3322 } while (sz == 1024);
3324 msi_free(FilePath);
3326 CloseHandle(the_file);
3328 uirow = MSI_CreateRecord(1);
3329 MSI_RecordSetStringW(uirow,1,FileName);
3330 ui_actiondata(package,szPublishProduct,uirow);
3331 msiobj_release( &uirow->hdr );
3333 return ERROR_SUCCESS;
3336 static UINT msi_publish_icons(MSIPACKAGE *package)
3338 UINT r;
3339 MSIQUERY *view;
3341 static const WCHAR query[]= {
3342 'S','E','L','E','C','T',' ','*',' ',
3343 'F','R','O','M',' ','`','I','c','o','n','`',0};
3345 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3346 if (r == ERROR_SUCCESS)
3348 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3349 msiobj_release(&view->hdr);
3352 return ERROR_SUCCESS;
3355 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3357 UINT r;
3358 HKEY source;
3359 LPWSTR buffer;
3360 MSIMEDIADISK *disk;
3361 MSISOURCELISTINFO *info;
3363 static const WCHAR szEmpty[] = {0};
3364 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3366 r = RegCreateKeyW(hkey, szSourceList, &source);
3367 if (r != ERROR_SUCCESS)
3368 return r;
3370 RegCloseKey(source);
3372 buffer = strrchrW(package->PackagePath, '\\') + 1;
3373 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3374 package->Context, MSICODE_PRODUCT,
3375 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3376 if (r != ERROR_SUCCESS)
3377 return r;
3379 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3380 package->Context, MSICODE_PRODUCT,
3381 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3382 if (r != ERROR_SUCCESS)
3383 return r;
3385 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3386 package->Context, MSICODE_PRODUCT,
3387 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3388 if (r != ERROR_SUCCESS)
3389 return r;
3391 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3393 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3394 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3395 info->options, info->value);
3396 else
3397 MsiSourceListSetInfoW(package->ProductCode, NULL,
3398 info->context, info->options,
3399 info->property, info->value);
3402 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3404 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3405 disk->context, disk->options,
3406 disk->disk_id, disk->volume_label, disk->disk_prompt);
3409 return ERROR_SUCCESS;
3412 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3414 MSIHANDLE hdb, suminfo;
3415 WCHAR guids[MAX_PATH];
3416 WCHAR packcode[SQUISH_GUID_SIZE];
3417 LPWSTR buffer;
3418 LPWSTR ptr;
3419 DWORD langid;
3420 DWORD size;
3421 UINT r;
3423 static const WCHAR szProductLanguage[] =
3424 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3425 static const WCHAR szARPProductIcon[] =
3426 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3427 static const WCHAR szProductVersion[] =
3428 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3429 static const WCHAR szAssignment[] =
3430 {'A','s','s','i','g','n','m','e','n','t',0};
3431 static const WCHAR szAdvertiseFlags[] =
3432 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3433 static const WCHAR szClients[] =
3434 {'C','l','i','e','n','t','s',0};
3435 static const WCHAR szColon[] = {':',0};
3437 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3438 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3439 msi_free(buffer);
3441 langid = msi_get_property_int(package, szProductLanguage, 0);
3442 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3444 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3445 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3447 /* FIXME */
3448 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3450 buffer = msi_dup_property(package, szARPProductIcon);
3451 if (buffer)
3453 LPWSTR path = build_icon_path(package,buffer);
3454 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3455 msi_free(path);
3456 msi_free(buffer);
3459 buffer = msi_dup_property(package, szProductVersion);
3460 if (buffer)
3462 DWORD verdword = msi_version_str_to_dword(buffer);
3463 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3464 msi_free(buffer);
3467 msi_reg_set_val_dword(hkey, szAssignment, 0);
3468 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3469 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3470 msi_reg_set_val_str(hkey, szClients, szColon);
3472 hdb = alloc_msihandle(&package->db->hdr);
3473 if (!hdb)
3474 return ERROR_NOT_ENOUGH_MEMORY;
3476 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3477 MsiCloseHandle(hdb);
3478 if (r != ERROR_SUCCESS)
3479 goto done;
3481 size = MAX_PATH;
3482 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3483 NULL, guids, &size);
3484 if (r != ERROR_SUCCESS)
3485 goto done;
3487 ptr = strchrW(guids, ';');
3488 if (ptr) *ptr = 0;
3489 squash_guid(guids, packcode);
3490 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3492 done:
3493 MsiCloseHandle(suminfo);
3494 return ERROR_SUCCESS;
3497 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3499 UINT r;
3500 HKEY hkey;
3501 LPWSTR upgrade;
3502 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3504 static const WCHAR szUpgradeCode[] =
3505 {'U','p','g','r','a','d','e','C','o','d','e',0};
3507 upgrade = msi_dup_property(package, szUpgradeCode);
3508 if (!upgrade)
3509 return ERROR_SUCCESS;
3511 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3513 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3514 if (r != ERROR_SUCCESS)
3515 goto done;
3517 else
3519 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3520 if (r != ERROR_SUCCESS)
3521 goto done;
3524 squash_guid(package->ProductCode, squashed_pc);
3525 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3527 RegCloseKey(hkey);
3529 done:
3530 msi_free(upgrade);
3531 return r;
3534 static BOOL msi_check_publish(MSIPACKAGE *package)
3536 MSIFEATURE *feature;
3538 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3540 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3541 return TRUE;
3544 return FALSE;
3547 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3549 MSIFEATURE *feature;
3551 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3553 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3554 return FALSE;
3557 return TRUE;
3561 * 99% of the work done here is only done for
3562 * advertised installs. However this is where the
3563 * Icon table is processed and written out
3564 * so that is what I am going to do here.
3566 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3568 UINT rc;
3569 HKEY hukey=0;
3570 HKEY hudkey=0;
3572 /* FIXME: also need to publish if the product is in advertise mode */
3573 if (!msi_check_publish(package))
3574 return ERROR_SUCCESS;
3576 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3578 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3579 if (rc != ERROR_SUCCESS)
3580 goto end;
3582 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3583 if (rc != ERROR_SUCCESS)
3584 goto end;
3586 else
3588 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3589 if (rc != ERROR_SUCCESS)
3590 goto end;
3592 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3593 if (rc != ERROR_SUCCESS)
3594 goto end;
3597 rc = msi_publish_upgrade_code(package);
3598 if (rc != ERROR_SUCCESS)
3599 goto end;
3601 rc = msi_publish_product_properties(package, hukey);
3602 if (rc != ERROR_SUCCESS)
3603 goto end;
3605 rc = msi_publish_sourcelist(package, hukey);
3606 if (rc != ERROR_SUCCESS)
3607 goto end;
3609 rc = msi_publish_icons(package);
3611 end:
3612 RegCloseKey(hukey);
3613 RegCloseKey(hudkey);
3615 return rc;
3618 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3620 MSIPACKAGE *package = (MSIPACKAGE*)param;
3621 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3622 LPWSTR deformated_section, deformated_key, deformated_value;
3623 LPWSTR folder, fullname = NULL;
3624 MSIRECORD * uirow;
3625 INT action;
3626 MSICOMPONENT *comp;
3627 static const WCHAR szWindowsFolder[] =
3628 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3630 component = MSI_RecordGetString(row, 8);
3631 comp = get_loaded_component(package,component);
3633 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3635 TRACE("Skipping ini file due to disabled component %s\n",
3636 debugstr_w(component));
3638 comp->Action = comp->Installed;
3640 return ERROR_SUCCESS;
3643 comp->Action = INSTALLSTATE_LOCAL;
3645 identifier = MSI_RecordGetString(row,1);
3646 filename = MSI_RecordGetString(row,2);
3647 dirproperty = MSI_RecordGetString(row,3);
3648 section = MSI_RecordGetString(row,4);
3649 key = MSI_RecordGetString(row,5);
3650 value = MSI_RecordGetString(row,6);
3651 action = MSI_RecordGetInteger(row,7);
3653 deformat_string(package,section,&deformated_section);
3654 deformat_string(package,key,&deformated_key);
3655 deformat_string(package,value,&deformated_value);
3657 if (dirproperty)
3659 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3660 if (!folder)
3661 folder = msi_dup_property( package, dirproperty );
3663 else
3664 folder = msi_dup_property( package, szWindowsFolder );
3666 if (!folder)
3668 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3669 goto cleanup;
3672 fullname = build_directory_name(2, folder, filename);
3674 if (action == 0)
3676 TRACE("Adding value %s to section %s in %s\n",
3677 debugstr_w(deformated_key), debugstr_w(deformated_section),
3678 debugstr_w(fullname));
3679 WritePrivateProfileStringW(deformated_section, deformated_key,
3680 deformated_value, fullname);
3682 else if (action == 1)
3684 WCHAR returned[10];
3685 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3686 returned, 10, fullname);
3687 if (returned[0] == 0)
3689 TRACE("Adding value %s to section %s in %s\n",
3690 debugstr_w(deformated_key), debugstr_w(deformated_section),
3691 debugstr_w(fullname));
3693 WritePrivateProfileStringW(deformated_section, deformated_key,
3694 deformated_value, fullname);
3697 else if (action == 3)
3698 FIXME("Append to existing section not yet implemented\n");
3700 uirow = MSI_CreateRecord(4);
3701 MSI_RecordSetStringW(uirow,1,identifier);
3702 MSI_RecordSetStringW(uirow,2,deformated_section);
3703 MSI_RecordSetStringW(uirow,3,deformated_key);
3704 MSI_RecordSetStringW(uirow,4,deformated_value);
3705 ui_actiondata(package,szWriteIniValues,uirow);
3706 msiobj_release( &uirow->hdr );
3707 cleanup:
3708 msi_free(fullname);
3709 msi_free(folder);
3710 msi_free(deformated_key);
3711 msi_free(deformated_value);
3712 msi_free(deformated_section);
3713 return ERROR_SUCCESS;
3716 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3718 UINT rc;
3719 MSIQUERY * view;
3720 static const WCHAR ExecSeqQuery[] =
3721 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3722 '`','I','n','i','F','i','l','e','`',0};
3724 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3725 if (rc != ERROR_SUCCESS)
3727 TRACE("no IniFile table\n");
3728 return ERROR_SUCCESS;
3731 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3732 msiobj_release(&view->hdr);
3733 return rc;
3736 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3738 MSIPACKAGE *package = (MSIPACKAGE*)param;
3739 LPCWSTR filename;
3740 LPWSTR FullName;
3741 MSIFILE *file;
3742 DWORD len;
3743 static const WCHAR ExeStr[] =
3744 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3745 static const WCHAR close[] = {'\"',0};
3746 STARTUPINFOW si;
3747 PROCESS_INFORMATION info;
3748 BOOL brc;
3749 MSIRECORD *uirow;
3750 LPWSTR uipath, p;
3752 memset(&si,0,sizeof(STARTUPINFOW));
3754 filename = MSI_RecordGetString(row,1);
3755 file = get_loaded_file( package, filename );
3757 if (!file)
3759 ERR("Unable to find file id %s\n",debugstr_w(filename));
3760 return ERROR_SUCCESS;
3763 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3765 FullName = msi_alloc(len*sizeof(WCHAR));
3766 strcpyW(FullName,ExeStr);
3767 strcatW( FullName, file->TargetPath );
3768 strcatW(FullName,close);
3770 TRACE("Registering %s\n",debugstr_w(FullName));
3771 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3772 &si, &info);
3774 if (brc)
3776 CloseHandle(info.hThread);
3777 msi_dialog_check_messages(info.hProcess);
3778 CloseHandle(info.hProcess);
3781 msi_free(FullName);
3783 /* the UI chunk */
3784 uirow = MSI_CreateRecord( 2 );
3785 uipath = strdupW( file->TargetPath );
3786 p = strrchrW(uipath,'\\');
3787 if (p)
3788 p[0]=0;
3789 MSI_RecordSetStringW( uirow, 1, &p[1] );
3790 MSI_RecordSetStringW( uirow, 2, uipath);
3791 ui_actiondata( package, szSelfRegModules, uirow);
3792 msiobj_release( &uirow->hdr );
3793 msi_free( uipath );
3794 /* FIXME: call ui_progress? */
3796 return ERROR_SUCCESS;
3799 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3801 UINT rc;
3802 MSIQUERY * view;
3803 static const WCHAR ExecSeqQuery[] =
3804 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3805 '`','S','e','l','f','R','e','g','`',0};
3807 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3808 if (rc != ERROR_SUCCESS)
3810 TRACE("no SelfReg table\n");
3811 return ERROR_SUCCESS;
3814 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3815 msiobj_release(&view->hdr);
3817 return ERROR_SUCCESS;
3820 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3822 MSIFEATURE *feature;
3823 UINT rc;
3824 HKEY hkey;
3825 HKEY userdata = NULL;
3827 if (!msi_check_publish(package))
3828 return ERROR_SUCCESS;
3830 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3832 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3833 &hkey, TRUE);
3834 if (rc != ERROR_SUCCESS)
3835 goto end;
3837 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3838 &userdata, TRUE);
3839 if (rc != ERROR_SUCCESS)
3840 goto end;
3842 else
3844 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3845 if (rc != ERROR_SUCCESS)
3846 goto end;
3848 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3849 &userdata, TRUE);
3850 if (rc != ERROR_SUCCESS)
3851 goto end;
3854 /* here the guids are base 85 encoded */
3855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3857 ComponentList *cl;
3858 LPWSTR data = NULL;
3859 GUID clsid;
3860 INT size;
3861 BOOL absent = FALSE;
3862 MSIRECORD *uirow;
3864 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3865 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3866 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3867 absent = TRUE;
3869 size = 1;
3870 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3872 size += 21;
3874 if (feature->Feature_Parent)
3875 size += strlenW( feature->Feature_Parent )+2;
3877 data = msi_alloc(size * sizeof(WCHAR));
3879 data[0] = 0;
3880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3882 MSICOMPONENT* component = cl->component;
3883 WCHAR buf[21];
3885 buf[0] = 0;
3886 if (component->ComponentId)
3888 TRACE("From %s\n",debugstr_w(component->ComponentId));
3889 CLSIDFromString(component->ComponentId, &clsid);
3890 encode_base85_guid(&clsid,buf);
3891 TRACE("to %s\n",debugstr_w(buf));
3892 strcatW(data,buf);
3896 if (feature->Feature_Parent)
3898 static const WCHAR sep[] = {'\2',0};
3899 strcatW(data,sep);
3900 strcatW(data,feature->Feature_Parent);
3903 msi_reg_set_val_str( userdata, feature->Feature, data );
3904 msi_free(data);
3906 size = 0;
3907 if (feature->Feature_Parent)
3908 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3909 if (!absent)
3911 static const WCHAR emptyW[] = {0};
3912 size += sizeof(WCHAR);
3913 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3914 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3916 else
3918 size += 2*sizeof(WCHAR);
3919 data = msi_alloc(size);
3920 data[0] = 0x6;
3921 data[1] = 0;
3922 if (feature->Feature_Parent)
3923 strcpyW( &data[1], feature->Feature_Parent );
3924 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3925 (LPBYTE)data,size);
3926 msi_free(data);
3929 /* the UI chunk */
3930 uirow = MSI_CreateRecord( 1 );
3931 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3932 ui_actiondata( package, szPublishFeatures, uirow);
3933 msiobj_release( &uirow->hdr );
3934 /* FIXME: call ui_progress? */
3937 end:
3938 RegCloseKey(hkey);
3939 RegCloseKey(userdata);
3940 return rc;
3943 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3945 UINT r;
3946 HKEY hkey;
3948 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3950 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3951 if (r == ERROR_SUCCESS)
3953 RegDeleteValueW(hkey, feature->Feature);
3954 RegCloseKey(hkey);
3957 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3958 if (r == ERROR_SUCCESS)
3960 RegDeleteValueW(hkey, feature->Feature);
3961 RegCloseKey(hkey);
3964 return ERROR_SUCCESS;
3967 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3969 MSIFEATURE *feature;
3971 if (!msi_check_unpublish(package))
3972 return ERROR_SUCCESS;
3974 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3976 msi_unpublish_feature(package, feature);
3979 return ERROR_SUCCESS;
3982 static UINT msi_get_local_package_name( LPWSTR path )
3984 static const WCHAR szInstaller[] = {
3985 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3986 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3987 DWORD time, len, i;
3988 HANDLE handle;
3990 time = GetTickCount();
3991 GetWindowsDirectoryW( path, MAX_PATH );
3992 lstrcatW( path, szInstaller );
3993 CreateDirectoryW( path, NULL );
3995 len = lstrlenW(path);
3996 for (i=0; i<0x10000; i++)
3998 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3999 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4000 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4001 if (handle != INVALID_HANDLE_VALUE)
4003 CloseHandle(handle);
4004 break;
4006 if (GetLastError() != ERROR_FILE_EXISTS &&
4007 GetLastError() != ERROR_SHARING_VIOLATION)
4008 return ERROR_FUNCTION_FAILED;
4011 return ERROR_SUCCESS;
4014 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4016 WCHAR packagefile[MAX_PATH];
4017 UINT r;
4019 r = msi_get_local_package_name( packagefile );
4020 if (r != ERROR_SUCCESS)
4021 return r;
4023 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4025 r = CopyFileW( package->db->path, packagefile, FALSE);
4027 if (!r)
4029 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4030 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4031 return ERROR_FUNCTION_FAILED;
4034 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4036 return ERROR_SUCCESS;
4039 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4041 LPWSTR prop, val, key;
4042 SYSTEMTIME systime;
4043 DWORD size, langid;
4044 WCHAR date[9];
4045 LPWSTR buffer;
4047 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4048 static const WCHAR szWindowsInstaller[] =
4049 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4050 static const WCHAR modpath_fmt[] =
4051 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4052 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4053 static const WCHAR szModifyPath[] =
4054 {'M','o','d','i','f','y','P','a','t','h',0};
4055 static const WCHAR szUninstallString[] =
4056 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4057 static const WCHAR szEstimatedSize[] =
4058 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4059 static const WCHAR szProductLanguage[] =
4060 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4061 static const WCHAR szProductVersion[] =
4062 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4063 static const WCHAR szProductName[] =
4064 {'P','r','o','d','u','c','t','N','a','m','e',0};
4065 static const WCHAR szDisplayName[] =
4066 {'D','i','s','p','l','a','y','N','a','m','e',0};
4067 static const WCHAR szDisplayVersion[] =
4068 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4069 static const WCHAR szManufacturer[] =
4070 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4072 static const LPCSTR propval[] = {
4073 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4074 "ARPCONTACT", "Contact",
4075 "ARPCOMMENTS", "Comments",
4076 "ProductName", "DisplayName",
4077 "ProductVersion", "DisplayVersion",
4078 "ARPHELPLINK", "HelpLink",
4079 "ARPHELPTELEPHONE", "HelpTelephone",
4080 "ARPINSTALLLOCATION", "InstallLocation",
4081 "SourceDir", "InstallSource",
4082 "Manufacturer", "Publisher",
4083 "ARPREADME", "Readme",
4084 "ARPSIZE", "Size",
4085 "ARPURLINFOABOUT", "URLInfoAbout",
4086 "ARPURLUPDATEINFO", "URLUpdateInfo",
4087 NULL,
4089 const LPCSTR *p = propval;
4091 while (*p)
4093 prop = strdupAtoW(*p++);
4094 key = strdupAtoW(*p++);
4095 val = msi_dup_property(package, prop);
4096 msi_reg_set_val_str(hkey, key, val);
4097 msi_free(val);
4098 msi_free(key);
4099 msi_free(prop);
4102 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4104 size = deformat_string(package, modpath_fmt, &buffer);
4105 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4106 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4107 msi_free(buffer);
4109 /* FIXME: Write real Estimated Size when we have it */
4110 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4112 buffer = msi_dup_property(package, szProductName);
4113 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4114 msi_free(buffer);
4116 buffer = msi_dup_property(package, cszSourceDir);
4117 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4118 msi_free(buffer);
4120 buffer = msi_dup_property(package, szManufacturer);
4121 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4122 msi_free(buffer);
4124 GetLocalTime(&systime);
4125 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4126 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4128 langid = msi_get_property_int(package, szProductLanguage, 0);
4129 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4131 buffer = msi_dup_property(package, szProductVersion);
4132 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4133 if (buffer)
4135 DWORD verdword = msi_version_str_to_dword(buffer);
4137 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4138 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4139 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4140 msi_free(buffer);
4143 return ERROR_SUCCESS;
4146 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4148 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4149 LPWSTR upgrade_code;
4150 HKEY hkey, props;
4151 HKEY upgrade;
4152 UINT rc;
4154 static const WCHAR szUpgradeCode[] = {
4155 'U','p','g','r','a','d','e','C','o','d','e',0};
4157 /* FIXME: also need to publish if the product is in advertise mode */
4158 if (!msi_check_publish(package))
4159 return ERROR_SUCCESS;
4161 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4162 if (rc != ERROR_SUCCESS)
4163 return rc;
4165 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4167 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4168 if (rc != ERROR_SUCCESS)
4169 goto done;
4171 else
4173 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4174 if (rc != ERROR_SUCCESS)
4175 goto done;
4178 msi_make_package_local(package, props);
4180 rc = msi_publish_install_properties(package, hkey);
4181 if (rc != ERROR_SUCCESS)
4182 goto done;
4184 rc = msi_publish_install_properties(package, props);
4185 if (rc != ERROR_SUCCESS)
4186 goto done;
4188 upgrade_code = msi_dup_property(package, szUpgradeCode);
4189 if (upgrade_code)
4191 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4192 squash_guid(package->ProductCode, squashed_pc);
4193 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4194 RegCloseKey(upgrade);
4195 msi_free(upgrade_code);
4198 done:
4199 RegCloseKey(hkey);
4201 return ERROR_SUCCESS;
4204 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4206 return execute_script(package,INSTALL_SCRIPT);
4209 static UINT msi_unpublish_product(MSIPACKAGE *package)
4211 LPWSTR upgrade;
4212 LPWSTR remove = NULL;
4213 LPWSTR *features = NULL;
4214 BOOL full_uninstall = TRUE;
4215 MSIFEATURE *feature;
4217 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4218 static const WCHAR szAll[] = {'A','L','L',0};
4219 static const WCHAR szUpgradeCode[] =
4220 {'U','p','g','r','a','d','e','C','o','d','e',0};
4222 remove = msi_dup_property(package, szRemove);
4223 if (!remove)
4224 return ERROR_SUCCESS;
4226 features = msi_split_string(remove, ',');
4227 if (!features)
4229 msi_free(remove);
4230 ERR("REMOVE feature list is empty!\n");
4231 return ERROR_FUNCTION_FAILED;
4234 if (!lstrcmpW(features[0], szAll))
4235 full_uninstall = TRUE;
4236 else
4238 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4240 if (feature->Action != INSTALLSTATE_ABSENT)
4241 full_uninstall = FALSE;
4245 if (!full_uninstall)
4246 goto done;
4248 MSIREG_DeleteProductKey(package->ProductCode);
4249 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4250 MSIREG_DeleteUninstallKey(package->ProductCode);
4252 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4254 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4255 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4257 else
4259 MSIREG_DeleteUserProductKey(package->ProductCode);
4260 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4263 upgrade = msi_dup_property(package, szUpgradeCode);
4264 if (upgrade)
4266 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4267 msi_free(upgrade);
4270 done:
4271 msi_free(remove);
4272 msi_free(features);
4273 return ERROR_SUCCESS;
4276 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4278 UINT rc;
4280 rc = msi_unpublish_product(package);
4281 if (rc != ERROR_SUCCESS)
4282 return rc;
4284 /* turn off scheduling */
4285 package->script->CurrentlyScripting= FALSE;
4287 /* first do the same as an InstallExecute */
4288 rc = ACTION_InstallExecute(package);
4289 if (rc != ERROR_SUCCESS)
4290 return rc;
4292 /* then handle Commit Actions */
4293 rc = execute_script(package,COMMIT_SCRIPT);
4295 return rc;
4298 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4300 static const WCHAR RunOnce[] = {
4301 'S','o','f','t','w','a','r','e','\\',
4302 'M','i','c','r','o','s','o','f','t','\\',
4303 'W','i','n','d','o','w','s','\\',
4304 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4305 'R','u','n','O','n','c','e',0};
4306 static const WCHAR InstallRunOnce[] = {
4307 'S','o','f','t','w','a','r','e','\\',
4308 'M','i','c','r','o','s','o','f','t','\\',
4309 'W','i','n','d','o','w','s','\\',
4310 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4311 'I','n','s','t','a','l','l','e','r','\\',
4312 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4314 static const WCHAR msiexec_fmt[] = {
4315 '%','s',
4316 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4317 '\"','%','s','\"',0};
4318 static const WCHAR install_fmt[] = {
4319 '/','I',' ','\"','%','s','\"',' ',
4320 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4321 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4322 WCHAR buffer[256], sysdir[MAX_PATH];
4323 HKEY hkey;
4324 WCHAR squished_pc[100];
4326 squash_guid(package->ProductCode,squished_pc);
4328 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4329 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4330 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4331 squished_pc);
4333 msi_reg_set_val_str( hkey, squished_pc, buffer );
4334 RegCloseKey(hkey);
4336 TRACE("Reboot command %s\n",debugstr_w(buffer));
4338 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4339 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4341 msi_reg_set_val_str( hkey, squished_pc, buffer );
4342 RegCloseKey(hkey);
4344 return ERROR_INSTALL_SUSPEND;
4347 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4349 DWORD attrib;
4350 UINT rc;
4353 * We are currently doing what should be done here in the top level Install
4354 * however for Administrative and uninstalls this step will be needed
4356 if (!package->PackagePath)
4357 return ERROR_SUCCESS;
4359 msi_set_sourcedir_props(package, TRUE);
4361 attrib = GetFileAttributesW(package->db->path);
4362 if (attrib == INVALID_FILE_ATTRIBUTES)
4364 LPWSTR prompt;
4365 LPWSTR msg;
4366 DWORD size = 0;
4368 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4369 package->Context, MSICODE_PRODUCT,
4370 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4371 if (rc == ERROR_MORE_DATA)
4373 prompt = msi_alloc(size * sizeof(WCHAR));
4374 MsiSourceListGetInfoW(package->ProductCode, NULL,
4375 package->Context, MSICODE_PRODUCT,
4376 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4378 else
4379 prompt = strdupW(package->db->path);
4381 msg = generate_error_string(package,1302,1,prompt);
4382 while(attrib == INVALID_FILE_ATTRIBUTES)
4384 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4385 if (rc == IDCANCEL)
4387 rc = ERROR_INSTALL_USEREXIT;
4388 break;
4390 attrib = GetFileAttributesW(package->db->path);
4392 msi_free(prompt);
4393 rc = ERROR_SUCCESS;
4395 else
4396 return ERROR_SUCCESS;
4398 return rc;
4401 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4403 HKEY hkey=0;
4404 LPWSTR buffer;
4405 LPWSTR productid;
4406 UINT rc,i;
4408 static const WCHAR szPropKeys[][80] =
4410 {'P','r','o','d','u','c','t','I','D',0},
4411 {'U','S','E','R','N','A','M','E',0},
4412 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4413 {0},
4416 static const WCHAR szRegKeys[][80] =
4418 {'P','r','o','d','u','c','t','I','D',0},
4419 {'R','e','g','O','w','n','e','r',0},
4420 {'R','e','g','C','o','m','p','a','n','y',0},
4421 {0},
4424 if (msi_check_unpublish(package))
4426 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4427 return ERROR_SUCCESS;
4430 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4431 if (!productid)
4432 return ERROR_SUCCESS;
4434 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4435 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4436 else
4437 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4439 if (rc != ERROR_SUCCESS)
4440 goto end;
4442 for( i = 0; szPropKeys[i][0]; i++ )
4444 buffer = msi_dup_property( package, szPropKeys[i] );
4445 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4446 msi_free( buffer );
4449 end:
4450 msi_free(productid);
4451 RegCloseKey(hkey);
4453 /* FIXME: call ui_actiondata */
4455 return rc;
4459 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4461 UINT rc;
4463 package->script->InWhatSequence |= SEQUENCE_EXEC;
4464 rc = ACTION_ProcessExecSequence(package,FALSE);
4465 return rc;
4469 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4471 MSIPACKAGE *package = (MSIPACKAGE*)param;
4472 LPCWSTR compgroupid=NULL;
4473 LPCWSTR feature=NULL;
4474 LPCWSTR text = NULL;
4475 LPCWSTR qualifier = NULL;
4476 LPCWSTR component = NULL;
4477 LPWSTR advertise = NULL;
4478 LPWSTR output = NULL;
4479 HKEY hkey;
4480 UINT rc = ERROR_SUCCESS;
4481 MSICOMPONENT *comp;
4482 DWORD sz = 0;
4483 MSIRECORD *uirow;
4485 component = MSI_RecordGetString(rec,3);
4486 comp = get_loaded_component(package,component);
4488 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4489 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4490 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4492 TRACE("Skipping: Component %s not scheduled for install\n",
4493 debugstr_w(component));
4495 return ERROR_SUCCESS;
4498 compgroupid = MSI_RecordGetString(rec,1);
4499 qualifier = MSI_RecordGetString(rec,2);
4501 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4502 if (rc != ERROR_SUCCESS)
4503 goto end;
4505 text = MSI_RecordGetString(rec,4);
4506 feature = MSI_RecordGetString(rec,5);
4508 advertise = create_component_advertise_string(package, comp, feature);
4510 sz = strlenW(advertise);
4512 if (text)
4513 sz += lstrlenW(text);
4515 sz+=3;
4516 sz *= sizeof(WCHAR);
4518 output = msi_alloc_zero(sz);
4519 strcpyW(output,advertise);
4520 msi_free(advertise);
4522 if (text)
4523 strcatW(output,text);
4525 msi_reg_set_val_multi_str( hkey, qualifier, output );
4527 end:
4528 RegCloseKey(hkey);
4529 msi_free(output);
4531 /* the UI chunk */
4532 uirow = MSI_CreateRecord( 2 );
4533 MSI_RecordSetStringW( uirow, 1, compgroupid );
4534 MSI_RecordSetStringW( uirow, 2, qualifier);
4535 ui_actiondata( package, szPublishComponents, uirow);
4536 msiobj_release( &uirow->hdr );
4537 /* FIXME: call ui_progress? */
4539 return rc;
4543 * At present I am ignorning the advertised components part of this and only
4544 * focusing on the qualified component sets
4546 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4548 UINT rc;
4549 MSIQUERY * view;
4550 static const WCHAR ExecSeqQuery[] =
4551 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4552 '`','P','u','b','l','i','s','h',
4553 'C','o','m','p','o','n','e','n','t','`',0};
4555 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4556 if (rc != ERROR_SUCCESS)
4557 return ERROR_SUCCESS;
4559 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4560 msiobj_release(&view->hdr);
4562 return rc;
4565 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4567 MSIPACKAGE *package = (MSIPACKAGE*)param;
4568 MSIRECORD *row;
4569 MSIFILE *file;
4570 SC_HANDLE hscm, service = NULL;
4571 LPCWSTR comp, depends, pass;
4572 LPWSTR name = NULL, disp = NULL;
4573 LPCWSTR load_order, serv_name, key;
4574 DWORD serv_type, start_type;
4575 DWORD err_control;
4577 static const WCHAR query[] =
4578 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4579 '`','C','o','m','p','o','n','e','n','t','`',' ',
4580 'W','H','E','R','E',' ',
4581 '`','C','o','m','p','o','n','e','n','t','`',' ',
4582 '=','\'','%','s','\'',0};
4584 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4585 if (!hscm)
4587 ERR("Failed to open the SC Manager!\n");
4588 goto done;
4591 start_type = MSI_RecordGetInteger(rec, 5);
4592 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4593 goto done;
4595 depends = MSI_RecordGetString(rec, 8);
4596 if (depends && *depends)
4597 FIXME("Dependency list unhandled!\n");
4599 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4600 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4601 serv_type = MSI_RecordGetInteger(rec, 4);
4602 err_control = MSI_RecordGetInteger(rec, 6);
4603 load_order = MSI_RecordGetString(rec, 7);
4604 serv_name = MSI_RecordGetString(rec, 9);
4605 pass = MSI_RecordGetString(rec, 10);
4606 comp = MSI_RecordGetString(rec, 12);
4608 /* fetch the service path */
4609 row = MSI_QueryGetRecord(package->db, query, comp);
4610 if (!row)
4612 ERR("Control query failed!\n");
4613 goto done;
4616 key = MSI_RecordGetString(row, 6);
4618 file = get_loaded_file(package, key);
4619 msiobj_release(&row->hdr);
4620 if (!file)
4622 ERR("Failed to load the service file\n");
4623 goto done;
4626 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4627 start_type, err_control, file->TargetPath,
4628 load_order, NULL, NULL, serv_name, pass);
4629 if (!service)
4631 if (GetLastError() != ERROR_SERVICE_EXISTS)
4632 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4635 done:
4636 CloseServiceHandle(service);
4637 CloseServiceHandle(hscm);
4638 msi_free(name);
4639 msi_free(disp);
4641 return ERROR_SUCCESS;
4644 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4646 UINT rc;
4647 MSIQUERY * view;
4648 static const WCHAR ExecSeqQuery[] =
4649 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4650 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4652 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4653 if (rc != ERROR_SUCCESS)
4654 return ERROR_SUCCESS;
4656 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4657 msiobj_release(&view->hdr);
4659 return rc;
4662 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4663 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4665 LPCWSTR *vector, *temp_vector;
4666 LPWSTR p, q;
4667 DWORD sep_len;
4669 static const WCHAR separator[] = {'[','~',']',0};
4671 *numargs = 0;
4672 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4674 if (!args)
4675 return NULL;
4677 vector = msi_alloc(sizeof(LPWSTR));
4678 if (!vector)
4679 return NULL;
4681 p = args;
4684 (*numargs)++;
4685 vector[*numargs - 1] = p;
4687 if ((q = strstrW(p, separator)))
4689 *q = '\0';
4691 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4692 if (!temp_vector)
4694 msi_free(vector);
4695 return NULL;
4697 vector = temp_vector;
4699 p = q + sep_len;
4701 } while (q);
4703 return vector;
4706 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4708 MSIPACKAGE *package = (MSIPACKAGE *)param;
4709 MSICOMPONENT *comp;
4710 SC_HANDLE scm, service = NULL;
4711 LPCWSTR name, *vector = NULL;
4712 LPWSTR args;
4713 DWORD event, numargs;
4714 UINT r = ERROR_FUNCTION_FAILED;
4716 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4717 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4718 return ERROR_SUCCESS;
4720 name = MSI_RecordGetString(rec, 2);
4721 event = MSI_RecordGetInteger(rec, 3);
4722 args = strdupW(MSI_RecordGetString(rec, 4));
4724 if (!(event & msidbServiceControlEventStart))
4725 return ERROR_SUCCESS;
4727 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4728 if (!scm)
4730 ERR("Failed to open the service control manager\n");
4731 goto done;
4734 service = OpenServiceW(scm, name, SERVICE_START);
4735 if (!service)
4737 ERR("Failed to open service %s\n", debugstr_w(name));
4738 goto done;
4741 vector = msi_service_args_to_vector(args, &numargs);
4743 if (!StartServiceW(service, numargs, vector))
4745 ERR("Failed to start service %s\n", debugstr_w(name));
4746 goto done;
4749 r = ERROR_SUCCESS;
4751 done:
4752 CloseServiceHandle(service);
4753 CloseServiceHandle(scm);
4755 msi_free(args);
4756 msi_free(vector);
4757 return r;
4760 static UINT ACTION_StartServices( MSIPACKAGE *package )
4762 UINT rc;
4763 MSIQUERY *view;
4765 static const WCHAR query[] = {
4766 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4767 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4769 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4770 if (rc != ERROR_SUCCESS)
4771 return ERROR_SUCCESS;
4773 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4774 msiobj_release(&view->hdr);
4776 return rc;
4779 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4781 DWORD i, needed, count;
4782 ENUM_SERVICE_STATUSW *dependencies;
4783 SERVICE_STATUS ss;
4784 SC_HANDLE depserv;
4786 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4787 0, &needed, &count))
4788 return TRUE;
4790 if (GetLastError() != ERROR_MORE_DATA)
4791 return FALSE;
4793 dependencies = msi_alloc(needed);
4794 if (!dependencies)
4795 return FALSE;
4797 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4798 needed, &needed, &count))
4799 goto error;
4801 for (i = 0; i < count; i++)
4803 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4804 SERVICE_STOP | SERVICE_QUERY_STATUS);
4805 if (!depserv)
4806 goto error;
4808 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4809 goto error;
4812 return TRUE;
4814 error:
4815 msi_free(dependencies);
4816 return FALSE;
4819 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4821 MSIPACKAGE *package = (MSIPACKAGE *)param;
4822 MSICOMPONENT *comp;
4823 SERVICE_STATUS status;
4824 SERVICE_STATUS_PROCESS ssp;
4825 SC_HANDLE scm = NULL, service = NULL;
4826 LPWSTR name, args;
4827 DWORD event, needed;
4829 event = MSI_RecordGetInteger(rec, 3);
4830 if (!(event & msidbServiceControlEventStop))
4831 return ERROR_SUCCESS;
4833 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4834 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4835 return ERROR_SUCCESS;
4837 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4838 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4839 args = strdupW(MSI_RecordGetString(rec, 4));
4841 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4842 if (!scm)
4844 WARN("Failed to open the SCM: %d\n", GetLastError());
4845 goto done;
4848 service = OpenServiceW(scm, name,
4849 SERVICE_STOP |
4850 SERVICE_QUERY_STATUS |
4851 SERVICE_ENUMERATE_DEPENDENTS);
4852 if (!service)
4854 WARN("Failed to open service (%s): %d\n",
4855 debugstr_w(name), GetLastError());
4856 goto done;
4859 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4860 sizeof(SERVICE_STATUS_PROCESS), &needed))
4862 WARN("Failed to query service status (%s): %d\n",
4863 debugstr_w(name), GetLastError());
4864 goto done;
4867 if (ssp.dwCurrentState == SERVICE_STOPPED)
4868 goto done;
4870 stop_service_dependents(scm, service);
4872 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4873 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4875 done:
4876 CloseServiceHandle(service);
4877 CloseServiceHandle(scm);
4878 msi_free(name);
4879 msi_free(args);
4881 return ERROR_SUCCESS;
4884 static UINT ACTION_StopServices( MSIPACKAGE *package )
4886 UINT rc;
4887 MSIQUERY *view;
4889 static const WCHAR query[] = {
4890 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4891 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4893 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4894 if (rc != ERROR_SUCCESS)
4895 return ERROR_SUCCESS;
4897 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4898 msiobj_release(&view->hdr);
4900 return rc;
4903 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4905 MSIFILE *file;
4907 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4909 if (!lstrcmpW(file->File, filename))
4910 return file;
4913 return NULL;
4916 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4918 MSIPACKAGE *package = (MSIPACKAGE*)param;
4919 LPWSTR driver, driver_path, ptr;
4920 WCHAR outpath[MAX_PATH];
4921 MSIFILE *driver_file, *setup_file;
4922 LPCWSTR desc;
4923 DWORD len, usage;
4924 UINT r = ERROR_SUCCESS;
4926 static const WCHAR driver_fmt[] = {
4927 'D','r','i','v','e','r','=','%','s',0};
4928 static const WCHAR setup_fmt[] = {
4929 'S','e','t','u','p','=','%','s',0};
4930 static const WCHAR usage_fmt[] = {
4931 'F','i','l','e','U','s','a','g','e','=','1',0};
4933 desc = MSI_RecordGetString(rec, 3);
4935 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4936 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4938 if (!driver_file || !setup_file)
4940 ERR("ODBC Driver entry not found!\n");
4941 return ERROR_FUNCTION_FAILED;
4944 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4945 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4946 lstrlenW(usage_fmt) + 1;
4947 driver = msi_alloc(len * sizeof(WCHAR));
4948 if (!driver)
4949 return ERROR_OUTOFMEMORY;
4951 ptr = driver;
4952 lstrcpyW(ptr, desc);
4953 ptr += lstrlenW(ptr) + 1;
4955 sprintfW(ptr, driver_fmt, driver_file->FileName);
4956 ptr += lstrlenW(ptr) + 1;
4958 sprintfW(ptr, setup_fmt, setup_file->FileName);
4959 ptr += lstrlenW(ptr) + 1;
4961 lstrcpyW(ptr, usage_fmt);
4962 ptr += lstrlenW(ptr) + 1;
4963 *ptr = '\0';
4965 driver_path = strdupW(driver_file->TargetPath);
4966 ptr = strrchrW(driver_path, '\\');
4967 if (ptr) *ptr = '\0';
4969 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4970 NULL, ODBC_INSTALL_COMPLETE, &usage))
4972 ERR("Failed to install SQL driver!\n");
4973 r = ERROR_FUNCTION_FAILED;
4976 msi_free(driver);
4977 msi_free(driver_path);
4979 return r;
4982 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4984 MSIPACKAGE *package = (MSIPACKAGE*)param;
4985 LPWSTR translator, translator_path, ptr;
4986 WCHAR outpath[MAX_PATH];
4987 MSIFILE *translator_file, *setup_file;
4988 LPCWSTR desc;
4989 DWORD len, usage;
4990 UINT r = ERROR_SUCCESS;
4992 static const WCHAR translator_fmt[] = {
4993 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4994 static const WCHAR setup_fmt[] = {
4995 'S','e','t','u','p','=','%','s',0};
4997 desc = MSI_RecordGetString(rec, 3);
4999 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5000 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5002 if (!translator_file || !setup_file)
5004 ERR("ODBC Translator entry not found!\n");
5005 return ERROR_FUNCTION_FAILED;
5008 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5009 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5010 translator = msi_alloc(len * sizeof(WCHAR));
5011 if (!translator)
5012 return ERROR_OUTOFMEMORY;
5014 ptr = translator;
5015 lstrcpyW(ptr, desc);
5016 ptr += lstrlenW(ptr) + 1;
5018 sprintfW(ptr, translator_fmt, translator_file->FileName);
5019 ptr += lstrlenW(ptr) + 1;
5021 sprintfW(ptr, setup_fmt, setup_file->FileName);
5022 ptr += lstrlenW(ptr) + 1;
5023 *ptr = '\0';
5025 translator_path = strdupW(translator_file->TargetPath);
5026 ptr = strrchrW(translator_path, '\\');
5027 if (ptr) *ptr = '\0';
5029 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5030 NULL, ODBC_INSTALL_COMPLETE, &usage))
5032 ERR("Failed to install SQL translator!\n");
5033 r = ERROR_FUNCTION_FAILED;
5036 msi_free(translator);
5037 msi_free(translator_path);
5039 return r;
5042 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5044 LPWSTR attrs;
5045 LPCWSTR desc, driver;
5046 WORD request = ODBC_ADD_SYS_DSN;
5047 INT registration;
5048 DWORD len;
5049 UINT r = ERROR_SUCCESS;
5051 static const WCHAR attrs_fmt[] = {
5052 'D','S','N','=','%','s',0 };
5054 desc = MSI_RecordGetString(rec, 3);
5055 driver = MSI_RecordGetString(rec, 4);
5056 registration = MSI_RecordGetInteger(rec, 5);
5058 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5059 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5061 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5062 attrs = msi_alloc(len * sizeof(WCHAR));
5063 if (!attrs)
5064 return ERROR_OUTOFMEMORY;
5066 sprintfW(attrs, attrs_fmt, desc);
5067 attrs[len - 1] = '\0';
5069 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5071 ERR("Failed to install SQL data source!\n");
5072 r = ERROR_FUNCTION_FAILED;
5075 msi_free(attrs);
5077 return r;
5080 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5082 UINT rc;
5083 MSIQUERY *view;
5085 static const WCHAR driver_query[] = {
5086 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5087 'O','D','B','C','D','r','i','v','e','r',0 };
5089 static const WCHAR translator_query[] = {
5090 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5091 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5093 static const WCHAR source_query[] = {
5094 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5095 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5097 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5098 if (rc != ERROR_SUCCESS)
5099 return ERROR_SUCCESS;
5101 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5102 msiobj_release(&view->hdr);
5104 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5105 if (rc != ERROR_SUCCESS)
5106 return ERROR_SUCCESS;
5108 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5109 msiobj_release(&view->hdr);
5111 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5112 if (rc != ERROR_SUCCESS)
5113 return ERROR_SUCCESS;
5115 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5116 msiobj_release(&view->hdr);
5118 return rc;
5121 #define ENV_ACT_SETALWAYS 0x1
5122 #define ENV_ACT_SETABSENT 0x2
5123 #define ENV_ACT_REMOVE 0x4
5124 #define ENV_ACT_REMOVEMATCH 0x8
5126 #define ENV_MOD_MACHINE 0x20000000
5127 #define ENV_MOD_APPEND 0x40000000
5128 #define ENV_MOD_PREFIX 0x80000000
5129 #define ENV_MOD_MASK 0xC0000000
5131 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5133 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5135 LPCWSTR cptr = *name;
5136 LPCWSTR ptr = *value;
5138 static const WCHAR prefix[] = {'[','~',']',0};
5139 static const int prefix_len = 3;
5141 *flags = 0;
5142 while (*cptr)
5144 if (*cptr == '=')
5145 *flags |= ENV_ACT_SETALWAYS;
5146 else if (*cptr == '+')
5147 *flags |= ENV_ACT_SETABSENT;
5148 else if (*cptr == '-')
5149 *flags |= ENV_ACT_REMOVE;
5150 else if (*cptr == '!')
5151 *flags |= ENV_ACT_REMOVEMATCH;
5152 else if (*cptr == '*')
5153 *flags |= ENV_MOD_MACHINE;
5154 else
5155 break;
5157 cptr++;
5158 (*name)++;
5161 if (!*cptr)
5163 ERR("Missing environment variable\n");
5164 return ERROR_FUNCTION_FAILED;
5167 if (!strncmpW(ptr, prefix, prefix_len))
5169 *flags |= ENV_MOD_APPEND;
5170 *value += lstrlenW(prefix);
5172 else if (lstrlenW(*value) >= prefix_len)
5174 ptr += lstrlenW(ptr) - prefix_len;
5175 if (!lstrcmpW(ptr, prefix))
5177 *flags |= ENV_MOD_PREFIX;
5178 /* the "[~]" will be removed by deformat_string */;
5182 if (!*flags ||
5183 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5184 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5185 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5186 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5188 ERR("Invalid flags: %08x\n", *flags);
5189 return ERROR_FUNCTION_FAILED;
5192 return ERROR_SUCCESS;
5195 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5197 MSIPACKAGE *package = param;
5198 LPCWSTR name, value;
5199 LPWSTR data = NULL, newval = NULL;
5200 LPWSTR deformatted = NULL, ptr;
5201 DWORD flags, type, size;
5202 LONG res;
5203 HKEY env = NULL, root;
5204 LPCWSTR environment;
5206 static const WCHAR user_env[] =
5207 {'E','n','v','i','r','o','n','m','e','n','t',0};
5208 static const WCHAR machine_env[] =
5209 {'S','y','s','t','e','m','\\',
5210 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5211 'C','o','n','t','r','o','l','\\',
5212 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5213 'E','n','v','i','r','o','n','m','e','n','t',0};
5214 static const WCHAR semicolon[] = {';',0};
5216 name = MSI_RecordGetString(rec, 2);
5217 value = MSI_RecordGetString(rec, 3);
5219 res = env_set_flags(&name, &value, &flags);
5220 if (res != ERROR_SUCCESS)
5221 goto done;
5223 deformat_string(package, value, &deformatted);
5224 if (!deformatted)
5226 res = ERROR_OUTOFMEMORY;
5227 goto done;
5230 value = deformatted;
5232 if (flags & ENV_MOD_MACHINE)
5234 environment = machine_env;
5235 root = HKEY_LOCAL_MACHINE;
5237 else
5239 environment = user_env;
5240 root = HKEY_CURRENT_USER;
5243 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5244 KEY_ALL_ACCESS, NULL, &env, NULL);
5245 if (res != ERROR_SUCCESS)
5246 goto done;
5248 if (flags & ENV_ACT_REMOVE)
5249 FIXME("Not removing environment variable on uninstall!\n");
5251 size = 0;
5252 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5253 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5254 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5255 goto done;
5257 if (res != ERROR_FILE_NOT_FOUND)
5259 if (flags & ENV_ACT_SETABSENT)
5261 res = ERROR_SUCCESS;
5262 goto done;
5265 data = msi_alloc(size);
5266 if (!data)
5268 RegCloseKey(env);
5269 return ERROR_OUTOFMEMORY;
5272 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5273 if (res != ERROR_SUCCESS)
5274 goto done;
5276 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5278 res = RegDeleteKeyW(env, name);
5279 goto done;
5282 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5283 newval = msi_alloc(size);
5284 ptr = newval;
5285 if (!newval)
5287 res = ERROR_OUTOFMEMORY;
5288 goto done;
5291 if (!(flags & ENV_MOD_MASK))
5292 lstrcpyW(newval, value);
5293 else
5295 if (flags & ENV_MOD_PREFIX)
5297 lstrcpyW(newval, value);
5298 lstrcatW(newval, semicolon);
5299 ptr = newval + lstrlenW(value) + 1;
5302 lstrcpyW(ptr, data);
5304 if (flags & ENV_MOD_APPEND)
5306 lstrcatW(newval, semicolon);
5307 lstrcatW(newval, value);
5311 else
5313 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5314 newval = msi_alloc(size);
5315 if (!newval)
5317 res = ERROR_OUTOFMEMORY;
5318 goto done;
5321 lstrcpyW(newval, value);
5324 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5325 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5327 done:
5328 if (env) RegCloseKey(env);
5329 msi_free(deformatted);
5330 msi_free(data);
5331 msi_free(newval);
5332 return res;
5335 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5337 UINT rc;
5338 MSIQUERY * view;
5339 static const WCHAR ExecSeqQuery[] =
5340 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5341 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5342 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5343 if (rc != ERROR_SUCCESS)
5344 return ERROR_SUCCESS;
5346 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5347 msiobj_release(&view->hdr);
5349 return rc;
5352 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5354 typedef struct
5356 struct list entry;
5357 LPWSTR sourcename;
5358 LPWSTR destname;
5359 LPWSTR source;
5360 LPWSTR dest;
5361 } FILE_LIST;
5363 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5365 BOOL ret;
5367 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5368 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5370 WARN("Source or dest is directory, not moving\n");
5371 return FALSE;
5374 if (options == msidbMoveFileOptionsMove)
5376 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5377 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5378 if (!ret)
5380 WARN("MoveFile failed: %d\n", GetLastError());
5381 return FALSE;
5384 else
5386 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5387 ret = CopyFileW(source, dest, FALSE);
5388 if (!ret)
5390 WARN("CopyFile failed: %d\n", GetLastError());
5391 return FALSE;
5395 return TRUE;
5398 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5400 LPWSTR path, ptr;
5401 DWORD dirlen, pathlen;
5403 ptr = strrchrW(wildcard, '\\');
5404 dirlen = ptr - wildcard + 1;
5406 pathlen = dirlen + lstrlenW(filename) + 1;
5407 path = msi_alloc(pathlen * sizeof(WCHAR));
5409 lstrcpynW(path, wildcard, dirlen + 1);
5410 lstrcatW(path, filename);
5412 return path;
5415 static void free_file_entry(FILE_LIST *file)
5417 msi_free(file->source);
5418 msi_free(file->dest);
5419 msi_free(file);
5422 static void free_list(FILE_LIST *list)
5424 while (!list_empty(&list->entry))
5426 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5428 list_remove(&file->entry);
5429 free_file_entry(file);
5433 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5435 FILE_LIST *new, *file;
5436 LPWSTR ptr, filename;
5437 DWORD size;
5439 new = msi_alloc_zero(sizeof(FILE_LIST));
5440 if (!new)
5441 return FALSE;
5443 new->source = strdupW(source);
5444 ptr = strrchrW(dest, '\\') + 1;
5445 filename = strrchrW(new->source, '\\') + 1;
5447 new->sourcename = filename;
5449 if (*ptr)
5450 new->destname = ptr;
5451 else
5452 new->destname = new->sourcename;
5454 size = (ptr - dest) + lstrlenW(filename) + 1;
5455 new->dest = msi_alloc(size * sizeof(WCHAR));
5456 if (!new->dest)
5458 free_file_entry(new);
5459 return FALSE;
5462 lstrcpynW(new->dest, dest, ptr - dest + 1);
5463 lstrcatW(new->dest, filename);
5465 if (list_empty(&files->entry))
5467 list_add_head(&files->entry, &new->entry);
5468 return TRUE;
5471 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5473 if (lstrcmpW(source, file->source) < 0)
5475 list_add_before(&file->entry, &new->entry);
5476 return TRUE;
5480 list_add_after(&file->entry, &new->entry);
5481 return TRUE;
5484 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5486 WIN32_FIND_DATAW wfd;
5487 HANDLE hfile;
5488 LPWSTR path;
5489 BOOL res;
5490 FILE_LIST files, *file;
5491 DWORD size;
5493 hfile = FindFirstFileW(source, &wfd);
5494 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5496 list_init(&files.entry);
5498 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5500 if (is_dot_dir(wfd.cFileName)) continue;
5502 path = wildcard_to_file(source, wfd.cFileName);
5503 if (!path)
5505 res = FALSE;
5506 goto done;
5509 add_wildcard(&files, path, dest);
5510 msi_free(path);
5513 /* no files match the wildcard */
5514 if (list_empty(&files.entry))
5515 goto done;
5517 /* only the first wildcard match gets renamed to dest */
5518 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5519 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5520 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5521 if (!file->dest)
5523 res = FALSE;
5524 goto done;
5527 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5529 while (!list_empty(&files.entry))
5531 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5533 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5535 list_remove(&file->entry);
5536 free_file_entry(file);
5539 res = TRUE;
5541 done:
5542 free_list(&files);
5543 FindClose(hfile);
5544 return res;
5547 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5549 MSIPACKAGE *package = param;
5550 MSICOMPONENT *comp;
5551 LPCWSTR sourcename;
5552 LPWSTR destname = NULL;
5553 LPWSTR sourcedir = NULL, destdir = NULL;
5554 LPWSTR source = NULL, dest = NULL;
5555 int options;
5556 DWORD size;
5557 BOOL ret, wildcards;
5559 static const WCHAR backslash[] = {'\\',0};
5561 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5562 if (!comp || !comp->Enabled ||
5563 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5565 TRACE("Component not set for install, not moving file\n");
5566 return ERROR_SUCCESS;
5569 sourcename = MSI_RecordGetString(rec, 3);
5570 options = MSI_RecordGetInteger(rec, 7);
5572 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5573 if (!sourcedir)
5574 goto done;
5576 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5577 if (!destdir)
5578 goto done;
5580 if (!sourcename)
5582 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5583 goto done;
5585 source = strdupW(sourcedir);
5586 if (!source)
5587 goto done;
5589 else
5591 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5592 source = msi_alloc(size * sizeof(WCHAR));
5593 if (!source)
5594 goto done;
5596 lstrcpyW(source, sourcedir);
5597 if (source[lstrlenW(source) - 1] != '\\')
5598 lstrcatW(source, backslash);
5599 lstrcatW(source, sourcename);
5602 wildcards = strchrW(source, '*') || strchrW(source, '?');
5604 if (MSI_RecordIsNull(rec, 4))
5606 if (!wildcards)
5608 destname = strdupW(sourcename);
5609 if (!destname)
5610 goto done;
5613 else
5615 destname = strdupW(MSI_RecordGetString(rec, 4));
5616 if (destname)
5617 reduce_to_longfilename(destname);
5620 size = 0;
5621 if (destname)
5622 size = lstrlenW(destname);
5624 size += lstrlenW(destdir) + 2;
5625 dest = msi_alloc(size * sizeof(WCHAR));
5626 if (!dest)
5627 goto done;
5629 lstrcpyW(dest, destdir);
5630 if (dest[lstrlenW(dest) - 1] != '\\')
5631 lstrcatW(dest, backslash);
5633 if (destname)
5634 lstrcatW(dest, destname);
5636 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5638 ret = CreateDirectoryW(destdir, NULL);
5639 if (!ret)
5641 WARN("CreateDirectory failed: %d\n", GetLastError());
5642 return ERROR_SUCCESS;
5646 if (!wildcards)
5647 msi_move_file(source, dest, options);
5648 else
5649 move_files_wildcard(source, dest, options);
5651 done:
5652 msi_free(sourcedir);
5653 msi_free(destdir);
5654 msi_free(destname);
5655 msi_free(source);
5656 msi_free(dest);
5658 return ERROR_SUCCESS;
5661 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5663 UINT rc;
5664 MSIQUERY *view;
5666 static const WCHAR ExecSeqQuery[] =
5667 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5668 '`','M','o','v','e','F','i','l','e','`',0};
5670 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5671 if (rc != ERROR_SUCCESS)
5672 return ERROR_SUCCESS;
5674 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5675 msiobj_release(&view->hdr);
5677 return rc;
5680 typedef struct tagMSIASSEMBLY
5682 struct list entry;
5683 MSICOMPONENT *component;
5684 MSIFEATURE *feature;
5685 MSIFILE *file;
5686 LPWSTR manifest;
5687 LPWSTR application;
5688 DWORD attributes;
5689 BOOL installed;
5690 } MSIASSEMBLY;
5692 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5693 DWORD dwReserved);
5694 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5695 LPVOID pvReserved, HMODULE *phModDll);
5697 static BOOL init_functionpointers(void)
5699 HRESULT hr;
5700 HMODULE hfusion;
5701 HMODULE hmscoree;
5703 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5705 hmscoree = LoadLibraryA("mscoree.dll");
5706 if (!hmscoree)
5708 WARN("mscoree.dll not available\n");
5709 return FALSE;
5712 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5713 if (!pLoadLibraryShim)
5715 WARN("LoadLibraryShim not available\n");
5716 FreeLibrary(hmscoree);
5717 return FALSE;
5720 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5721 if (FAILED(hr))
5723 WARN("fusion.dll not available\n");
5724 FreeLibrary(hmscoree);
5725 return FALSE;
5728 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5730 FreeLibrary(hmscoree);
5731 return TRUE;
5734 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5735 LPWSTR path)
5737 IAssemblyCache *cache;
5738 HRESULT hr;
5739 UINT r = ERROR_FUNCTION_FAILED;
5741 TRACE("installing assembly: %s\n", debugstr_w(path));
5743 if (assembly->feature)
5744 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5746 if (assembly->manifest)
5747 FIXME("Manifest unhandled\n");
5749 if (assembly->application)
5751 FIXME("Assembly should be privately installed\n");
5752 return ERROR_SUCCESS;
5755 if (assembly->attributes == msidbAssemblyAttributesWin32)
5757 FIXME("Win32 assemblies not handled\n");
5758 return ERROR_SUCCESS;
5761 if (!init_functionpointers() || !pCreateAssemblyCache)
5762 return ERROR_FUNCTION_FAILED;
5764 hr = pCreateAssemblyCache(&cache, 0);
5765 if (FAILED(hr))
5766 goto done;
5768 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5769 if (FAILED(hr))
5770 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5772 r = ERROR_SUCCESS;
5774 done:
5775 IAssemblyCache_Release(cache);
5776 return r;
5779 typedef struct tagASSEMBLY_LIST
5781 MSIPACKAGE *package;
5782 struct list *assemblies;
5783 } ASSEMBLY_LIST;
5785 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5787 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5788 MSIASSEMBLY *assembly;
5790 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5791 if (!assembly)
5792 return ERROR_OUTOFMEMORY;
5794 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5796 if (!assembly->component || !assembly->component->Enabled ||
5797 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5799 TRACE("Component not set for install, not publishing assembly\n");
5800 msi_free(assembly);
5801 return ERROR_SUCCESS;
5804 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5805 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5807 if (!assembly->file)
5809 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5810 return ERROR_FUNCTION_FAILED;
5813 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5814 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5815 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5816 assembly->installed = FALSE;
5818 list_add_head(list->assemblies, &assembly->entry);
5820 return ERROR_SUCCESS;
5823 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5825 MSIQUERY *view;
5826 ASSEMBLY_LIST list;
5827 UINT r;
5829 static const WCHAR query[] =
5830 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5831 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5833 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5834 if (r != ERROR_SUCCESS)
5835 return ERROR_SUCCESS;
5837 list.package = package;
5838 list.assemblies = assemblies;
5840 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5841 msiobj_release(&view->hdr);
5843 return r;
5846 static void free_assemblies(struct list *assemblies)
5848 struct list *item, *cursor;
5850 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5852 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5854 list_remove(&assembly->entry);
5855 msi_free(assembly->application);
5856 msi_free(assembly->manifest);
5857 msi_free(assembly);
5861 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5863 MSIASSEMBLY *assembly;
5865 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5867 if (!lstrcmpW(assembly->file->File, file))
5869 *out = assembly;
5870 return TRUE;
5874 return FALSE;
5877 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5878 LPWSTR *path, DWORD *attrs, PVOID user)
5880 MSIASSEMBLY *assembly;
5881 WCHAR temppath[MAX_PATH];
5882 struct list *assemblies = (struct list *)user;
5883 UINT r;
5885 if (!find_assembly(assemblies, file, &assembly))
5886 return FALSE;
5888 GetTempPathW(MAX_PATH, temppath);
5889 PathAddBackslashW(temppath);
5890 lstrcatW(temppath, assembly->file->FileName);
5892 if (action == MSICABEXTRACT_BEGINEXTRACT)
5894 if (assembly->installed)
5895 return FALSE;
5897 *path = strdupW(temppath);
5898 *attrs = assembly->file->Attributes;
5900 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5902 assembly->installed = TRUE;
5904 r = install_assembly(package, assembly, temppath);
5905 if (r != ERROR_SUCCESS)
5906 ERR("Failed to install assembly\n");
5909 return TRUE;
5912 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5914 UINT r;
5915 struct list assemblies = LIST_INIT(assemblies);
5916 MSIASSEMBLY *assembly;
5917 MSIMEDIAINFO *mi;
5919 r = load_assemblies(package, &assemblies);
5920 if (r != ERROR_SUCCESS)
5921 goto done;
5923 if (list_empty(&assemblies))
5924 goto done;
5926 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5927 if (!mi)
5929 r = ERROR_OUTOFMEMORY;
5930 goto done;
5933 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5935 if (assembly->installed && !mi->is_continuous)
5936 continue;
5938 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5939 (assembly->file->IsCompressed && !mi->is_extracted))
5941 MSICABDATA data;
5943 r = ready_media(package, assembly->file, mi);
5944 if (r != ERROR_SUCCESS)
5946 ERR("Failed to ready media\n");
5947 break;
5950 data.mi = mi;
5951 data.package = package;
5952 data.cb = installassembly_cb;
5953 data.user = &assemblies;
5955 if (assembly->file->IsCompressed &&
5956 !msi_cabextract(package, mi, &data))
5958 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5959 r = ERROR_FUNCTION_FAILED;
5960 break;
5964 if (!assembly->file->IsCompressed)
5966 LPWSTR source = resolve_file_source(package, assembly->file);
5968 r = install_assembly(package, assembly, source);
5969 if (r != ERROR_SUCCESS)
5970 ERR("Failed to install assembly\n");
5972 msi_free(source);
5975 /* FIXME: write Installer assembly reg values */
5978 done:
5979 free_assemblies(&assemblies);
5980 return r;
5983 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5984 LPCSTR action, LPCWSTR table )
5986 static const WCHAR query[] = {
5987 'S','E','L','E','C','T',' ','*',' ',
5988 'F','R','O','M',' ','`','%','s','`',0 };
5989 MSIQUERY *view = NULL;
5990 DWORD count = 0;
5991 UINT r;
5993 r = MSI_OpenQuery( package->db, &view, query, table );
5994 if (r == ERROR_SUCCESS)
5996 r = MSI_IterateRecords(view, &count, NULL, package);
5997 msiobj_release(&view->hdr);
6000 if (count)
6001 FIXME("%s -> %u ignored %s table values\n",
6002 action, count, debugstr_w(table));
6004 return ERROR_SUCCESS;
6007 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6009 TRACE("%p\n", package);
6010 return ERROR_SUCCESS;
6013 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6015 static const WCHAR table[] =
6016 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6017 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6020 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6022 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6023 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6026 static UINT ACTION_BindImage( MSIPACKAGE *package )
6028 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6029 return msi_unimplemented_action_stub( package, "BindImage", table );
6032 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6034 static const WCHAR table[] = {
6035 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6036 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6039 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6041 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6042 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6045 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6047 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6048 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6051 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6053 static const WCHAR table[] = {
6054 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6055 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6057 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6059 static const WCHAR table[] = {
6060 'P','r','o','d','u','c','t','I','D',0 };
6061 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6064 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6066 static const WCHAR table[] = {
6067 'E','n','v','i','r','o','n','m','e','n','t',0 };
6068 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6071 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6073 static const WCHAR table[] = {
6074 'M','s','i','A','s','s','e','m','b','l','y',0 };
6075 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6078 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6080 static const WCHAR table[] = { 'F','o','n','t',0 };
6081 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6084 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6086 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6087 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6090 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6092 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6093 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6096 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6098 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6099 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6102 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6104 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6105 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6108 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6110 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6111 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6114 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6116 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6117 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6120 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6122 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6123 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6126 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6128 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6129 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6132 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6134 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6135 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6138 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6140 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6141 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6144 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6146 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6147 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6150 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6152 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6153 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6156 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6158 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6159 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6162 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6164 static const WCHAR table[] = { 'M','I','M','E',0 };
6165 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6168 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6170 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6171 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6174 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6176 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6177 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6180 static const struct _actions StandardActions[] = {
6181 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6182 { szAppSearch, ACTION_AppSearch },
6183 { szBindImage, ACTION_BindImage },
6184 { szCCPSearch, ACTION_CCPSearch },
6185 { szCostFinalize, ACTION_CostFinalize },
6186 { szCostInitialize, ACTION_CostInitialize },
6187 { szCreateFolders, ACTION_CreateFolders },
6188 { szCreateShortcuts, ACTION_CreateShortcuts },
6189 { szDeleteServices, ACTION_DeleteServices },
6190 { szDisableRollback, NULL },
6191 { szDuplicateFiles, ACTION_DuplicateFiles },
6192 { szExecuteAction, ACTION_ExecuteAction },
6193 { szFileCost, ACTION_FileCost },
6194 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6195 { szForceReboot, ACTION_ForceReboot },
6196 { szInstallAdminPackage, NULL },
6197 { szInstallExecute, ACTION_InstallExecute },
6198 { szInstallExecuteAgain, ACTION_InstallExecute },
6199 { szInstallFiles, ACTION_InstallFiles},
6200 { szInstallFinalize, ACTION_InstallFinalize },
6201 { szInstallInitialize, ACTION_InstallInitialize },
6202 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6203 { szInstallValidate, ACTION_InstallValidate },
6204 { szIsolateComponents, ACTION_IsolateComponents },
6205 { szLaunchConditions, ACTION_LaunchConditions },
6206 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6207 { szMoveFiles, ACTION_MoveFiles },
6208 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6209 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6210 { szInstallODBC, ACTION_InstallODBC },
6211 { szInstallServices, ACTION_InstallServices },
6212 { szPatchFiles, ACTION_PatchFiles },
6213 { szProcessComponents, ACTION_ProcessComponents },
6214 { szPublishComponents, ACTION_PublishComponents },
6215 { szPublishFeatures, ACTION_PublishFeatures },
6216 { szPublishProduct, ACTION_PublishProduct },
6217 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6218 { szRegisterComPlus, ACTION_RegisterComPlus},
6219 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6220 { szRegisterFonts, ACTION_RegisterFonts },
6221 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6222 { szRegisterProduct, ACTION_RegisterProduct },
6223 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6224 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6225 { szRegisterUser, ACTION_RegisterUser },
6226 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6227 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6228 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6229 { szRemoveFiles, ACTION_RemoveFiles },
6230 { szRemoveFolders, ACTION_RemoveFolders },
6231 { szRemoveIniValues, ACTION_RemoveIniValues },
6232 { szRemoveODBC, ACTION_RemoveODBC },
6233 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6234 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6235 { szResolveSource, ACTION_ResolveSource },
6236 { szRMCCPSearch, ACTION_RMCCPSearch },
6237 { szScheduleReboot, NULL },
6238 { szSelfRegModules, ACTION_SelfRegModules },
6239 { szSelfUnregModules, ACTION_SelfUnregModules },
6240 { szSetODBCFolders, NULL },
6241 { szStartServices, ACTION_StartServices },
6242 { szStopServices, ACTION_StopServices },
6243 { szUnpublishComponents, ACTION_UnpublishComponents },
6244 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6245 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6246 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6247 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6248 { szUnregisterFonts, ACTION_UnregisterFonts },
6249 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6250 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6251 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6252 { szValidateProductID, ACTION_ValidateProductID },
6253 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6254 { szWriteIniValues, ACTION_WriteIniValues },
6255 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6256 { NULL, NULL },
6259 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6260 UINT* rc, BOOL force )
6262 BOOL ret = FALSE;
6263 BOOL run = force;
6264 int i;
6266 if (!run && !package->script->CurrentlyScripting)
6267 run = TRUE;
6269 if (!run)
6271 if (strcmpW(action,szInstallFinalize) == 0 ||
6272 strcmpW(action,szInstallExecute) == 0 ||
6273 strcmpW(action,szInstallExecuteAgain) == 0)
6274 run = TRUE;
6277 i = 0;
6278 while (StandardActions[i].action != NULL)
6280 if (strcmpW(StandardActions[i].action, action)==0)
6282 if (!run)
6284 ui_actioninfo(package, action, TRUE, 0);
6285 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6286 ui_actioninfo(package, action, FALSE, *rc);
6288 else
6290 ui_actionstart(package, action);
6291 if (StandardActions[i].handler)
6293 *rc = StandardActions[i].handler(package);
6295 else
6297 FIXME("unhandled standard action %s\n",debugstr_w(action));
6298 *rc = ERROR_SUCCESS;
6301 ret = TRUE;
6302 break;
6304 i++;
6306 return ret;