msi: Factor out the code to open the UserData features key.
[wine/multimedia.git] / dlls / msi / action.c
blob5d9ba654ddf03b9e94e78eb714f6229660a7e09e
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,
292 BOOL preserve_case )
294 LPCWSTR ptr,ptr2;
295 BOOL quote;
296 DWORD len;
297 LPWSTR prop = NULL, val = NULL;
299 if (!szCommandLine)
300 return ERROR_SUCCESS;
302 ptr = szCommandLine;
304 while (*ptr)
306 if (*ptr==' ')
308 ptr++;
309 continue;
312 TRACE("Looking at %s\n",debugstr_w(ptr));
314 ptr2 = strchrW(ptr,'=');
315 if (!ptr2)
317 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
318 break;
321 quote = FALSE;
323 len = ptr2-ptr;
324 prop = msi_alloc((len+1)*sizeof(WCHAR));
325 memcpy(prop,ptr,len*sizeof(WCHAR));
326 prop[len]=0;
328 if (!preserve_case)
329 struprW(prop);
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
405 LPWSTR prod_code, patch_product;
406 UINT ret;
408 prod_code = msi_dup_property( package, szProductCode );
409 patch_product = msi_get_suminfo_product( patch );
411 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
413 if ( strstrW( patch_product, prod_code ) )
414 ret = ERROR_SUCCESS;
415 else
416 ret = ERROR_FUNCTION_FAILED;
418 msi_free( patch_product );
419 msi_free( prod_code );
421 return ret;
424 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
425 MSIDATABASE *patch_db, LPCWSTR name )
427 UINT ret = ERROR_FUNCTION_FAILED;
428 IStorage *stg = NULL;
429 HRESULT r;
431 TRACE("%p %s\n", package, debugstr_w(name) );
433 if (*name++ != ':')
435 ERR("expected a colon in %s\n", debugstr_w(name));
436 return ERROR_FUNCTION_FAILED;
439 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
440 if (SUCCEEDED(r))
442 ret = msi_check_transform_applicable( package, stg );
443 if (ret == ERROR_SUCCESS)
444 msi_table_apply_transform( package->db, stg );
445 else
446 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
447 IStorage_Release( stg );
449 else
450 ERR("failed to open substorage %s\n", debugstr_w(name));
452 return ERROR_SUCCESS;
455 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
457 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
458 LPWSTR guid_list, *guids, product_code;
459 UINT i, ret = ERROR_FUNCTION_FAILED;
461 product_code = msi_dup_property( package, szProdCode );
462 if (!product_code)
464 /* FIXME: the property ProductCode should be written into the DB somewhere */
465 ERR("no product code to check\n");
466 return ERROR_SUCCESS;
469 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
470 guids = msi_split_string( guid_list, ';' );
471 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
473 if (!lstrcmpW( guids[i], product_code ))
474 ret = ERROR_SUCCESS;
476 msi_free( guids );
477 msi_free( guid_list );
478 msi_free( product_code );
480 return ret;
483 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
485 MSISUMMARYINFO *si;
486 LPWSTR str, *substorage;
487 UINT i, r = ERROR_SUCCESS;
489 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
490 if (!si)
491 return ERROR_FUNCTION_FAILED;
493 msi_check_patch_applicable( package, si );
495 /* enumerate the substorage */
496 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
497 substorage = msi_split_string( str, ';' );
498 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
499 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
500 msi_free( substorage );
501 msi_free( str );
503 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
505 msiobj_release( &si->hdr );
507 return r;
510 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
512 MSIDATABASE *patch_db = NULL;
513 UINT r;
515 TRACE("%p %s\n", package, debugstr_w( file ) );
517 /* FIXME:
518 * We probably want to make sure we only open a patch collection here.
519 * Patch collections (.msp) and databases (.msi) have different GUIDs
520 * but currently MSI_OpenDatabaseW will accept both.
522 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
523 if ( r != ERROR_SUCCESS )
525 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
526 return r;
529 msi_parse_patch_summary( package, patch_db );
532 * There might be a CAB file in the patch package,
533 * so append it to the list of storage to search for streams.
535 append_storage_to_db( package->db, patch_db->storage );
537 msiobj_release( &patch_db->hdr );
539 return ERROR_SUCCESS;
542 /* get the PATCH property, and apply all the patches it specifies */
543 static UINT msi_apply_patches( MSIPACKAGE *package )
545 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
546 LPWSTR patch_list, *patches;
547 UINT i, r = ERROR_SUCCESS;
549 patch_list = msi_dup_property( package, szPatch );
551 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
553 patches = msi_split_string( patch_list, ';' );
554 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
555 r = msi_apply_patch_package( package, patches[i] );
557 msi_free( patches );
558 msi_free( patch_list );
560 return r;
563 static UINT msi_apply_transforms( MSIPACKAGE *package )
565 static const WCHAR szTransforms[] = {
566 'T','R','A','N','S','F','O','R','M','S',0 };
567 LPWSTR xform_list, *xforms;
568 UINT i, r = ERROR_SUCCESS;
570 xform_list = msi_dup_property( package, szTransforms );
571 xforms = msi_split_string( xform_list, ';' );
573 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
575 if (xforms[i][0] == ':')
576 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
577 else
578 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
581 msi_free( xforms );
582 msi_free( xform_list );
584 return r;
587 static BOOL ui_sequence_exists( MSIPACKAGE *package )
589 MSIQUERY *view;
590 UINT rc;
592 static const WCHAR ExecSeqQuery [] =
593 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
594 '`','I','n','s','t','a','l','l',
595 'U','I','S','e','q','u','e','n','c','e','`',
596 ' ','W','H','E','R','E',' ',
597 '`','S','e','q','u','e','n','c','e','`',' ',
598 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
599 '`','S','e','q','u','e','n','c','e','`',0};
601 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
602 if (rc == ERROR_SUCCESS)
604 msiobj_release(&view->hdr);
605 return TRUE;
608 return FALSE;
611 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
613 LPWSTR p, db;
614 LPWSTR source, check;
615 DWORD len;
617 static const WCHAR szOriginalDatabase[] =
618 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
620 db = msi_dup_property( package, szOriginalDatabase );
621 if (!db)
622 return ERROR_OUTOFMEMORY;
624 p = strrchrW( db, '\\' );
625 if (!p)
627 p = strrchrW( db, '/' );
628 if (!p)
630 msi_free(db);
631 return ERROR_SUCCESS;
635 len = p - db + 2;
636 source = msi_alloc( len * sizeof(WCHAR) );
637 lstrcpynW( source, db, len );
639 check = msi_dup_property( package, cszSourceDir );
640 if (!check || replace)
641 MSI_SetPropertyW( package, cszSourceDir, source );
643 msi_free( check );
645 check = msi_dup_property( package, cszSOURCEDIR );
646 if (!check || replace)
647 MSI_SetPropertyW( package, cszSOURCEDIR, source );
649 msi_free( check );
650 msi_free( source );
651 msi_free( db );
653 return ERROR_SUCCESS;
656 static UINT msi_set_context(MSIPACKAGE *package)
658 WCHAR val[10];
659 DWORD sz = 10;
660 DWORD num;
661 UINT r;
663 static const WCHAR szOne[] = {'1',0};
664 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
666 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
668 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
669 if (r == ERROR_SUCCESS)
671 num = atolW(val);
672 if (num == 1 || num == 2)
673 package->Context = MSIINSTALLCONTEXT_MACHINE;
676 MSI_SetPropertyW(package, szAllUsers, szOne);
677 return ERROR_SUCCESS;
680 /****************************************************
681 * TOP level entry points
682 *****************************************************/
684 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
685 LPCWSTR szCommandLine )
687 UINT rc;
688 BOOL ui = FALSE, ui_exists;
689 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
690 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
691 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
693 MSI_SetPropertyW(package, szAction, szInstall);
695 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
697 package->script->InWhatSequence = SEQUENCE_INSTALL;
699 if (szPackagePath)
701 LPWSTR p, dir;
702 LPCWSTR file;
704 dir = strdupW(szPackagePath);
705 p = strrchrW(dir, '\\');
706 if (p)
708 *(++p) = 0;
709 file = szPackagePath + (p - dir);
711 else
713 msi_free(dir);
714 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
715 GetCurrentDirectoryW(MAX_PATH, dir);
716 lstrcatW(dir, cszbs);
717 file = szPackagePath;
720 msi_free( package->PackagePath );
721 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
722 if (!package->PackagePath)
724 msi_free(dir);
725 return ERROR_OUTOFMEMORY;
728 lstrcpyW(package->PackagePath, dir);
729 lstrcatW(package->PackagePath, file);
730 msi_free(dir);
732 msi_set_sourcedir_props(package, FALSE);
735 msi_parse_command_line( package, szCommandLine, FALSE );
737 msi_apply_transforms( package );
738 msi_apply_patches( package );
740 /* properties may have been added by a transform */
741 msi_clone_properties( package );
742 msi_set_context( package );
744 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
746 package->script->InWhatSequence |= SEQUENCE_UI;
747 rc = ACTION_ProcessUISequence(package);
748 ui = TRUE;
749 ui_exists = ui_sequence_exists(package);
750 if (rc == ERROR_SUCCESS || !ui_exists)
752 package->script->InWhatSequence |= SEQUENCE_EXEC;
753 rc = ACTION_ProcessExecSequence(package,ui_exists);
756 else
757 rc = ACTION_ProcessExecSequence(package,FALSE);
759 package->script->CurrentlyScripting= FALSE;
761 /* process the ending type action */
762 if (rc == ERROR_SUCCESS)
763 ACTION_PerformActionSequence(package,-1,ui);
764 else if (rc == ERROR_INSTALL_USEREXIT)
765 ACTION_PerformActionSequence(package,-2,ui);
766 else if (rc == ERROR_INSTALL_SUSPEND)
767 ACTION_PerformActionSequence(package,-4,ui);
768 else /* failed */
769 ACTION_PerformActionSequence(package,-3,ui);
771 /* finish up running custom actions */
772 ACTION_FinishCustomActions(package);
774 return rc;
777 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
779 UINT rc = ERROR_SUCCESS;
780 MSIRECORD * row = 0;
781 static const WCHAR ExecSeqQuery[] =
782 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
783 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
784 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
785 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
787 static const WCHAR UISeqQuery[] =
788 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
789 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
790 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
791 ' ', '=',' ','%','i',0};
793 if (UI)
794 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
795 else
796 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
798 if (row)
800 LPCWSTR action, cond;
802 TRACE("Running the actions\n");
804 /* check conditions */
805 cond = MSI_RecordGetString(row,2);
807 /* this is a hack to skip errors in the condition code */
808 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
809 goto end;
811 action = MSI_RecordGetString(row,1);
812 if (!action)
814 ERR("failed to fetch action\n");
815 rc = ERROR_FUNCTION_FAILED;
816 goto end;
819 if (UI)
820 rc = ACTION_PerformUIAction(package,action,-1);
821 else
822 rc = ACTION_PerformAction(package,action,-1,FALSE);
823 end:
824 msiobj_release(&row->hdr);
826 else
827 rc = ERROR_SUCCESS;
829 return rc;
832 typedef struct {
833 MSIPACKAGE* package;
834 BOOL UI;
835 } iterate_action_param;
837 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
839 iterate_action_param *iap= (iterate_action_param*)param;
840 UINT rc;
841 LPCWSTR cond, action;
843 action = MSI_RecordGetString(row,1);
844 if (!action)
846 ERR("Error is retrieving action name\n");
847 return ERROR_FUNCTION_FAILED;
850 /* check conditions */
851 cond = MSI_RecordGetString(row,2);
853 /* this is a hack to skip errors in the condition code */
854 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
856 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
857 return ERROR_SUCCESS;
860 if (iap->UI)
861 rc = ACTION_PerformUIAction(iap->package,action,-1);
862 else
863 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
865 msi_dialog_check_messages( NULL );
867 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
868 rc = iap->package->CurrentInstallState;
870 if (rc == ERROR_FUNCTION_NOT_CALLED)
871 rc = ERROR_SUCCESS;
873 if (rc != ERROR_SUCCESS)
874 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
876 return rc;
879 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
881 MSIQUERY * view;
882 UINT r;
883 static const WCHAR query[] =
884 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
885 '`','%','s','`',
886 ' ','W','H','E','R','E',' ',
887 '`','S','e','q','u','e','n','c','e','`',' ',
888 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
889 '`','S','e','q','u','e','n','c','e','`',0};
890 iterate_action_param iap;
893 * FIXME: probably should be checking UILevel in the
894 * ACTION_PerformUIAction/ACTION_PerformAction
895 * rather than saving the UI level here. Those
896 * two functions can be merged too.
898 iap.package = package;
899 iap.UI = TRUE;
901 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
903 r = MSI_OpenQuery( package->db, &view, query, szTable );
904 if (r == ERROR_SUCCESS)
906 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
907 msiobj_release(&view->hdr);
910 return r;
913 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
915 MSIQUERY * view;
916 UINT rc;
917 static const WCHAR ExecSeqQuery[] =
918 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
919 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
920 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
921 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
922 'O','R','D','E','R',' ', 'B','Y',' ',
923 '`','S','e','q','u','e','n','c','e','`',0 };
924 MSIRECORD * row = 0;
925 static const WCHAR IVQuery[] =
926 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
927 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
928 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
929 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
930 ' ','\'', 'I','n','s','t','a','l','l',
931 'V','a','l','i','d','a','t','e','\'', 0};
932 INT seq = 0;
933 iterate_action_param iap;
935 iap.package = package;
936 iap.UI = FALSE;
938 if (package->script->ExecuteSequenceRun)
940 TRACE("Execute Sequence already Run\n");
941 return ERROR_SUCCESS;
944 package->script->ExecuteSequenceRun = TRUE;
946 /* get the sequence number */
947 if (UIran)
949 row = MSI_QueryGetRecord(package->db, IVQuery);
950 if( !row )
951 return ERROR_FUNCTION_FAILED;
952 seq = MSI_RecordGetInteger(row,1);
953 msiobj_release(&row->hdr);
956 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
957 if (rc == ERROR_SUCCESS)
959 TRACE("Running the actions\n");
961 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
962 msiobj_release(&view->hdr);
965 return rc;
968 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
970 MSIQUERY * view;
971 UINT rc;
972 static const WCHAR ExecSeqQuery [] =
973 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
974 '`','I','n','s','t','a','l','l',
975 'U','I','S','e','q','u','e','n','c','e','`',
976 ' ','W','H','E','R','E',' ',
977 '`','S','e','q','u','e','n','c','e','`',' ',
978 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
979 '`','S','e','q','u','e','n','c','e','`',0};
980 iterate_action_param iap;
982 iap.package = package;
983 iap.UI = TRUE;
985 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
987 if (rc == ERROR_SUCCESS)
989 TRACE("Running the actions\n");
991 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
992 msiobj_release(&view->hdr);
995 return rc;
998 /********************************************************
999 * ACTION helper functions and functions that perform the actions
1000 *******************************************************/
1001 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1002 UINT* rc, UINT script, BOOL force )
1004 BOOL ret=FALSE;
1005 UINT arc;
1007 arc = ACTION_CustomAction(package, action, script, force);
1009 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1011 *rc = arc;
1012 ret = TRUE;
1014 return ret;
1018 * A lot of actions are really important even if they don't do anything
1019 * explicit... Lots of properties are set at the beginning of the installation
1020 * CostFinalize does a bunch of work to translate the directories and such
1022 * But until I get write access to the database that is hard, so I am going to
1023 * hack it to see if I can get something to run.
1025 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1027 UINT rc = ERROR_SUCCESS;
1028 BOOL handled;
1030 TRACE("Performing action (%s)\n",debugstr_w(action));
1032 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1034 if (!handled)
1035 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1037 if (!handled)
1039 WARN("unhandled msi action %s\n",debugstr_w(action));
1040 rc = ERROR_FUNCTION_NOT_CALLED;
1043 return rc;
1046 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1048 UINT rc = ERROR_SUCCESS;
1049 BOOL handled = FALSE;
1051 TRACE("Performing action (%s)\n",debugstr_w(action));
1053 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1055 if (!handled)
1056 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1058 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1059 handled = TRUE;
1061 if (!handled)
1063 WARN("unhandled msi action %s\n",debugstr_w(action));
1064 rc = ERROR_FUNCTION_NOT_CALLED;
1067 return rc;
1072 * Actual Action Handlers
1075 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1077 MSIPACKAGE *package = (MSIPACKAGE*)param;
1078 LPCWSTR dir;
1079 LPWSTR full_path;
1080 MSIRECORD *uirow;
1081 MSIFOLDER *folder;
1083 dir = MSI_RecordGetString(row,1);
1084 if (!dir)
1086 ERR("Unable to get folder id\n");
1087 return ERROR_SUCCESS;
1090 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1091 if (!full_path)
1093 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1094 return ERROR_SUCCESS;
1097 TRACE("Folder is %s\n",debugstr_w(full_path));
1099 /* UI stuff */
1100 uirow = MSI_CreateRecord(1);
1101 MSI_RecordSetStringW(uirow,1,full_path);
1102 ui_actiondata(package,szCreateFolders,uirow);
1103 msiobj_release( &uirow->hdr );
1105 if (folder->State == 0)
1106 create_full_pathW(full_path);
1108 folder->State = 3;
1110 msi_free(full_path);
1111 return ERROR_SUCCESS;
1114 /* FIXME: probably should merge this with the above function */
1115 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1117 UINT rc = ERROR_SUCCESS;
1118 MSIFOLDER *folder;
1119 LPWSTR install_path;
1121 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1122 if (!install_path)
1123 return ERROR_FUNCTION_FAILED;
1125 /* create the path */
1126 if (folder->State == 0)
1128 create_full_pathW(install_path);
1129 folder->State = 2;
1131 msi_free(install_path);
1133 return rc;
1136 UINT msi_create_component_directories( MSIPACKAGE *package )
1138 MSICOMPONENT *comp;
1140 /* create all the folders required by the components are going to install */
1141 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1143 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1144 continue;
1145 msi_create_directory( package, comp->Directory );
1148 return ERROR_SUCCESS;
1152 * Also we cannot enable/disable components either, so for now I am just going
1153 * to do all the directories for all the components.
1155 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1157 static const WCHAR ExecSeqQuery[] =
1158 {'S','E','L','E','C','T',' ',
1159 '`','D','i','r','e','c','t','o','r','y','_','`',
1160 ' ','F','R','O','M',' ',
1161 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1162 UINT rc;
1163 MSIQUERY *view;
1165 /* create all the empty folders specified in the CreateFolder table */
1166 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1167 if (rc != ERROR_SUCCESS)
1168 return ERROR_SUCCESS;
1170 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1171 msiobj_release(&view->hdr);
1173 msi_create_component_directories( package );
1175 return rc;
1178 static UINT load_component( MSIRECORD *row, LPVOID param )
1180 MSIPACKAGE *package = param;
1181 MSICOMPONENT *comp;
1183 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1184 if (!comp)
1185 return ERROR_FUNCTION_FAILED;
1187 list_add_tail( &package->components, &comp->entry );
1189 /* fill in the data */
1190 comp->Component = msi_dup_record_field( row, 1 );
1192 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1194 comp->ComponentId = msi_dup_record_field( row, 2 );
1195 comp->Directory = msi_dup_record_field( row, 3 );
1196 comp->Attributes = MSI_RecordGetInteger(row,4);
1197 comp->Condition = msi_dup_record_field( row, 5 );
1198 comp->KeyPath = msi_dup_record_field( row, 6 );
1200 comp->Installed = INSTALLSTATE_UNKNOWN;
1201 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1203 return ERROR_SUCCESS;
1206 static UINT load_all_components( MSIPACKAGE *package )
1208 static const WCHAR query[] = {
1209 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1210 '`','C','o','m','p','o','n','e','n','t','`',0 };
1211 MSIQUERY *view;
1212 UINT r;
1214 if (!list_empty(&package->components))
1215 return ERROR_SUCCESS;
1217 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1218 if (r != ERROR_SUCCESS)
1219 return r;
1221 r = MSI_IterateRecords(view, NULL, load_component, package);
1222 msiobj_release(&view->hdr);
1223 return r;
1226 typedef struct {
1227 MSIPACKAGE *package;
1228 MSIFEATURE *feature;
1229 } _ilfs;
1231 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1233 ComponentList *cl;
1235 cl = msi_alloc( sizeof (*cl) );
1236 if ( !cl )
1237 return ERROR_NOT_ENOUGH_MEMORY;
1238 cl->component = comp;
1239 list_add_tail( &feature->Components, &cl->entry );
1241 return ERROR_SUCCESS;
1244 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1246 FeatureList *fl;
1248 fl = msi_alloc( sizeof(*fl) );
1249 if ( !fl )
1250 return ERROR_NOT_ENOUGH_MEMORY;
1251 fl->feature = child;
1252 list_add_tail( &parent->Children, &fl->entry );
1254 return ERROR_SUCCESS;
1257 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1259 _ilfs* ilfs= (_ilfs*)param;
1260 LPCWSTR component;
1261 MSICOMPONENT *comp;
1263 component = MSI_RecordGetString(row,1);
1265 /* check to see if the component is already loaded */
1266 comp = get_loaded_component( ilfs->package, component );
1267 if (!comp)
1269 ERR("unknown component %s\n", debugstr_w(component));
1270 return ERROR_FUNCTION_FAILED;
1273 add_feature_component( ilfs->feature, comp );
1274 comp->Enabled = TRUE;
1276 return ERROR_SUCCESS;
1279 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1281 MSIFEATURE *feature;
1283 if ( !name )
1284 return NULL;
1286 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1288 if ( !lstrcmpW( feature->Feature, name ) )
1289 return feature;
1292 return NULL;
1295 static UINT load_feature(MSIRECORD * row, LPVOID param)
1297 MSIPACKAGE* package = (MSIPACKAGE*)param;
1298 MSIFEATURE* feature;
1299 static const WCHAR Query1[] =
1300 {'S','E','L','E','C','T',' ',
1301 '`','C','o','m','p','o','n','e','n','t','_','`',
1302 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1303 'C','o','m','p','o','n','e','n','t','s','`',' ',
1304 'W','H','E','R','E',' ',
1305 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1306 MSIQUERY * view;
1307 UINT rc;
1308 _ilfs ilfs;
1310 /* fill in the data */
1312 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1313 if (!feature)
1314 return ERROR_NOT_ENOUGH_MEMORY;
1316 list_init( &feature->Children );
1317 list_init( &feature->Components );
1319 feature->Feature = msi_dup_record_field( row, 1 );
1321 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1323 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1324 feature->Title = msi_dup_record_field( row, 3 );
1325 feature->Description = msi_dup_record_field( row, 4 );
1327 if (!MSI_RecordIsNull(row,5))
1328 feature->Display = MSI_RecordGetInteger(row,5);
1330 feature->Level= MSI_RecordGetInteger(row,6);
1331 feature->Directory = msi_dup_record_field( row, 7 );
1332 feature->Attributes = MSI_RecordGetInteger(row,8);
1334 feature->Installed = INSTALLSTATE_UNKNOWN;
1335 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1337 list_add_tail( &package->features, &feature->entry );
1339 /* load feature components */
1341 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1342 if (rc != ERROR_SUCCESS)
1343 return ERROR_SUCCESS;
1345 ilfs.package = package;
1346 ilfs.feature = feature;
1348 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1349 msiobj_release(&view->hdr);
1351 return ERROR_SUCCESS;
1354 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1356 MSIPACKAGE* package = (MSIPACKAGE*)param;
1357 MSIFEATURE *parent, *child;
1359 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1360 if (!child)
1361 return ERROR_FUNCTION_FAILED;
1363 if (!child->Feature_Parent)
1364 return ERROR_SUCCESS;
1366 parent = find_feature_by_name( package, child->Feature_Parent );
1367 if (!parent)
1368 return ERROR_FUNCTION_FAILED;
1370 add_feature_child( parent, child );
1371 return ERROR_SUCCESS;
1374 static UINT load_all_features( MSIPACKAGE *package )
1376 static const WCHAR query[] = {
1377 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1378 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1379 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1380 MSIQUERY *view;
1381 UINT r;
1383 if (!list_empty(&package->features))
1384 return ERROR_SUCCESS;
1386 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1387 if (r != ERROR_SUCCESS)
1388 return r;
1390 r = MSI_IterateRecords( view, NULL, load_feature, package );
1391 if (r != ERROR_SUCCESS)
1392 return r;
1394 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1395 msiobj_release( &view->hdr );
1397 return r;
1400 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1402 if (!p)
1403 return p;
1404 p = strchrW(p, ch);
1405 if (!p)
1406 return p;
1407 *p = 0;
1408 return p+1;
1411 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1413 static const WCHAR query[] = {
1414 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1415 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1416 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1417 MSIQUERY *view = NULL;
1418 MSIRECORD *row = NULL;
1419 UINT r;
1421 TRACE("%s\n", debugstr_w(file->File));
1423 r = MSI_OpenQuery(package->db, &view, query, file->File);
1424 if (r != ERROR_SUCCESS)
1425 goto done;
1427 r = MSI_ViewExecute(view, NULL);
1428 if (r != ERROR_SUCCESS)
1429 goto done;
1431 r = MSI_ViewFetch(view, &row);
1432 if (r != ERROR_SUCCESS)
1433 goto done;
1435 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1436 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1437 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1438 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1439 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1441 done:
1442 if (view) msiobj_release(&view->hdr);
1443 if (row) msiobj_release(&row->hdr);
1444 return r;
1447 static UINT load_file(MSIRECORD *row, LPVOID param)
1449 MSIPACKAGE* package = (MSIPACKAGE*)param;
1450 LPCWSTR component;
1451 MSIFILE *file;
1453 /* fill in the data */
1455 file = msi_alloc_zero( sizeof (MSIFILE) );
1456 if (!file)
1457 return ERROR_NOT_ENOUGH_MEMORY;
1459 file->File = msi_dup_record_field( row, 1 );
1461 component = MSI_RecordGetString( row, 2 );
1462 file->Component = get_loaded_component( package, component );
1464 if (!file->Component)
1466 WARN("Component not found: %s\n", debugstr_w(component));
1467 msi_free(file->File);
1468 msi_free(file);
1469 return ERROR_SUCCESS;
1472 file->FileName = msi_dup_record_field( row, 3 );
1473 reduce_to_longfilename( file->FileName );
1475 file->ShortName = msi_dup_record_field( row, 3 );
1476 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1478 file->FileSize = MSI_RecordGetInteger( row, 4 );
1479 file->Version = msi_dup_record_field( row, 5 );
1480 file->Language = msi_dup_record_field( row, 6 );
1481 file->Attributes = MSI_RecordGetInteger( row, 7 );
1482 file->Sequence = MSI_RecordGetInteger( row, 8 );
1484 file->state = msifs_invalid;
1486 /* if the compressed bits are not set in the file attributes,
1487 * then read the information from the package word count property
1489 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1491 file->IsCompressed = FALSE;
1493 else if (file->Attributes &
1494 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1496 file->IsCompressed = TRUE;
1498 else if (file->Attributes & msidbFileAttributesNoncompressed)
1500 file->IsCompressed = FALSE;
1502 else
1504 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1507 load_file_hash(package, file);
1509 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1511 list_add_tail( &package->files, &file->entry );
1513 return ERROR_SUCCESS;
1516 static UINT load_all_files(MSIPACKAGE *package)
1518 MSIQUERY * view;
1519 UINT rc;
1520 static const WCHAR Query[] =
1521 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1522 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1523 '`','S','e','q','u','e','n','c','e','`', 0};
1525 if (!list_empty(&package->files))
1526 return ERROR_SUCCESS;
1528 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1529 if (rc != ERROR_SUCCESS)
1530 return ERROR_SUCCESS;
1532 rc = MSI_IterateRecords(view, NULL, load_file, package);
1533 msiobj_release(&view->hdr);
1535 return ERROR_SUCCESS;
1538 static UINT load_folder( MSIRECORD *row, LPVOID param )
1540 MSIPACKAGE *package = param;
1541 static const WCHAR szDot[] = { '.',0 };
1542 static WCHAR szEmpty[] = { 0 };
1543 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1544 MSIFOLDER *folder;
1546 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1547 if (!folder)
1548 return ERROR_NOT_ENOUGH_MEMORY;
1550 folder->Directory = msi_dup_record_field( row, 1 );
1552 TRACE("%s\n", debugstr_w(folder->Directory));
1554 p = msi_dup_record_field(row, 3);
1556 /* split src and target dir */
1557 tgt_short = p;
1558 src_short = folder_split_path( p, ':' );
1560 /* split the long and short paths */
1561 tgt_long = folder_split_path( tgt_short, '|' );
1562 src_long = folder_split_path( src_short, '|' );
1564 /* check for no-op dirs */
1565 if (!lstrcmpW(szDot, tgt_short))
1566 tgt_short = szEmpty;
1567 if (!lstrcmpW(szDot, src_short))
1568 src_short = szEmpty;
1570 if (!tgt_long)
1571 tgt_long = tgt_short;
1573 if (!src_short) {
1574 src_short = tgt_short;
1575 src_long = tgt_long;
1578 if (!src_long)
1579 src_long = src_short;
1581 /* FIXME: use the target short path too */
1582 folder->TargetDefault = strdupW(tgt_long);
1583 folder->SourceShortPath = strdupW(src_short);
1584 folder->SourceLongPath = strdupW(src_long);
1585 msi_free(p);
1587 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1588 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1589 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1591 folder->Parent = msi_dup_record_field( row, 2 );
1593 folder->Property = msi_dup_property( package, folder->Directory );
1595 list_add_tail( &package->folders, &folder->entry );
1597 TRACE("returning %p\n", folder);
1599 return ERROR_SUCCESS;
1602 static UINT load_all_folders( MSIPACKAGE *package )
1604 static const WCHAR query[] = {
1605 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1606 '`','D','i','r','e','c','t','o','r','y','`',0 };
1607 MSIQUERY *view;
1608 UINT r;
1610 if (!list_empty(&package->folders))
1611 return ERROR_SUCCESS;
1613 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1614 if (r != ERROR_SUCCESS)
1615 return r;
1617 r = MSI_IterateRecords(view, NULL, load_folder, package);
1618 msiobj_release(&view->hdr);
1619 return r;
1623 * I am not doing any of the costing functionality yet.
1624 * Mostly looking at doing the Component and Feature loading
1626 * The native MSI does A LOT of modification to tables here. Mostly adding
1627 * a lot of temporary columns to the Feature and Component tables.
1629 * note: Native msi also tracks the short filename. But I am only going to
1630 * track the long ones. Also looking at this directory table
1631 * it appears that the directory table does not get the parents
1632 * resolved base on property only based on their entries in the
1633 * directory table.
1635 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1637 static const WCHAR szCosting[] =
1638 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1639 static const WCHAR szZero[] = { '0', 0 };
1641 MSI_SetPropertyW(package, szCosting, szZero);
1642 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1644 load_all_folders( package );
1645 load_all_components( package );
1646 load_all_features( package );
1647 load_all_files( package );
1649 return ERROR_SUCCESS;
1652 static UINT execute_script(MSIPACKAGE *package, UINT script )
1654 UINT i;
1655 UINT rc = ERROR_SUCCESS;
1657 TRACE("Executing Script %i\n",script);
1659 if (!package->script)
1661 ERR("no script!\n");
1662 return ERROR_FUNCTION_FAILED;
1665 for (i = 0; i < package->script->ActionCount[script]; i++)
1667 LPWSTR action;
1668 action = package->script->Actions[script][i];
1669 ui_actionstart(package, action);
1670 TRACE("Executing Action (%s)\n",debugstr_w(action));
1671 rc = ACTION_PerformAction(package, action, script, TRUE);
1672 if (rc != ERROR_SUCCESS)
1673 break;
1675 msi_free_action_script(package, script);
1676 return rc;
1679 static UINT ACTION_FileCost(MSIPACKAGE *package)
1681 return ERROR_SUCCESS;
1684 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1686 MSICOMPONENT *comp;
1687 INSTALLSTATE state;
1688 UINT r;
1690 state = MsiQueryProductStateW(package->ProductCode);
1692 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1694 if (!comp->ComponentId)
1695 continue;
1697 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1698 comp->Installed = INSTALLSTATE_ABSENT;
1699 else
1701 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1702 package->Context, comp->ComponentId,
1703 &comp->Installed);
1704 if (r != ERROR_SUCCESS)
1705 comp->Installed = INSTALLSTATE_ABSENT;
1710 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1712 MSIFEATURE *feature;
1713 INSTALLSTATE state;
1715 state = MsiQueryProductStateW(package->ProductCode);
1717 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1719 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1720 feature->Installed = INSTALLSTATE_ABSENT;
1721 else
1723 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1724 feature->Feature);
1729 static BOOL process_state_property(MSIPACKAGE* package, int level,
1730 LPCWSTR property, INSTALLSTATE state)
1732 static const WCHAR all[]={'A','L','L',0};
1733 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1734 LPWSTR override;
1735 MSIFEATURE *feature;
1737 override = msi_dup_property( package, property );
1738 if (!override)
1739 return FALSE;
1741 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1743 if (lstrcmpW(property, remove) &&
1744 (feature->Level <= 0 || feature->Level > level))
1745 continue;
1747 if (strcmpiW(override,all)==0)
1748 msi_feature_set_state(package, feature, state);
1749 else
1751 LPWSTR ptr = override;
1752 LPWSTR ptr2 = strchrW(override,',');
1754 while (ptr)
1756 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1757 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1759 msi_feature_set_state(package, feature, state);
1760 break;
1762 if (ptr2)
1764 ptr=ptr2+1;
1765 ptr2 = strchrW(ptr,',');
1767 else
1768 break;
1772 msi_free(override);
1774 return TRUE;
1777 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1779 int level;
1780 static const WCHAR szlevel[] =
1781 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1782 static const WCHAR szAddLocal[] =
1783 {'A','D','D','L','O','C','A','L',0};
1784 static const WCHAR szAddSource[] =
1785 {'A','D','D','S','O','U','R','C','E',0};
1786 static const WCHAR szRemove[] =
1787 {'R','E','M','O','V','E',0};
1788 static const WCHAR szReinstall[] =
1789 {'R','E','I','N','S','T','A','L','L',0};
1790 BOOL override = FALSE;
1791 MSICOMPONENT* component;
1792 MSIFEATURE *feature;
1795 /* I do not know if this is where it should happen.. but */
1797 TRACE("Checking Install Level\n");
1799 level = msi_get_property_int(package, szlevel, 1);
1801 /* ok here is the _real_ rub
1802 * all these activation/deactivation things happen in order and things
1803 * later on the list override things earlier on the list.
1804 * 1) INSTALLLEVEL processing
1805 * 2) ADDLOCAL
1806 * 3) REMOVE
1807 * 4) ADDSOURCE
1808 * 5) ADDDEFAULT
1809 * 6) REINSTALL
1810 * 7) COMPADDLOCAL
1811 * 8) COMPADDSOURCE
1812 * 9) FILEADDLOCAL
1813 * 10) FILEADDSOURCE
1814 * 11) FILEADDDEFAULT
1816 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1817 * REMOVE are the big ones, since we don't handle administrative installs
1818 * yet anyway.
1820 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1821 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1822 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1823 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1825 if (!override)
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 BOOL feature_state = ((feature->Level > 0) &&
1830 (feature->Level <= level));
1832 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1834 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1835 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1836 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1837 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1838 else
1839 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1843 /* disable child features of unselected parent features */
1844 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1846 FeatureList *fl;
1848 if (feature->Level > 0 && feature->Level <= level)
1849 continue;
1851 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1852 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1855 else
1857 /* set the Preselected Property */
1858 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1859 static const WCHAR szOne[] = { '1', 0 };
1861 MSI_SetPropertyW(package,szPreselected,szOne);
1865 * now we want to enable or disable components base on feature
1868 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1870 ComponentList *cl;
1872 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1873 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1875 if (!feature->Level)
1876 continue;
1878 /* features with components that have compressed files are made local */
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 if (cl->component->Enabled &&
1882 cl->component->ForceLocalState &&
1883 feature->Action == INSTALLSTATE_SOURCE)
1885 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1886 break;
1890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1892 component = cl->component;
1894 if (!component->Enabled)
1895 continue;
1897 switch (feature->Action)
1899 case INSTALLSTATE_ABSENT:
1900 component->anyAbsent = 1;
1901 break;
1902 case INSTALLSTATE_ADVERTISED:
1903 component->hasAdvertiseFeature = 1;
1904 break;
1905 case INSTALLSTATE_SOURCE:
1906 component->hasSourceFeature = 1;
1907 break;
1908 case INSTALLSTATE_LOCAL:
1909 component->hasLocalFeature = 1;
1910 break;
1911 case INSTALLSTATE_DEFAULT:
1912 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1913 component->hasAdvertiseFeature = 1;
1914 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1915 component->hasSourceFeature = 1;
1916 else
1917 component->hasLocalFeature = 1;
1918 break;
1919 default:
1920 break;
1925 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1927 /* if the component isn't enabled, leave it alone */
1928 if (!component->Enabled)
1929 continue;
1931 /* check if it's local or source */
1932 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1933 (component->hasLocalFeature || component->hasSourceFeature))
1935 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1936 !component->ForceLocalState)
1937 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1938 else
1939 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1940 continue;
1943 /* if any feature is local, the component must be local too */
1944 if (component->hasLocalFeature)
1946 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1947 continue;
1950 if (component->hasSourceFeature)
1952 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1953 continue;
1956 if (component->hasAdvertiseFeature)
1958 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1959 continue;
1962 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1963 if (component->anyAbsent)
1964 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1967 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1969 if (component->Action == INSTALLSTATE_DEFAULT)
1971 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1972 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1975 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1976 debugstr_w(component->Component), component->Installed, component->Action);
1980 return ERROR_SUCCESS;
1983 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1985 MSIPACKAGE *package = (MSIPACKAGE*)param;
1986 LPCWSTR name;
1987 LPWSTR path;
1988 MSIFOLDER *f;
1990 name = MSI_RecordGetString(row,1);
1992 f = get_loaded_folder(package, name);
1993 if (!f) return ERROR_SUCCESS;
1995 /* reset the ResolvedTarget */
1996 msi_free(f->ResolvedTarget);
1997 f->ResolvedTarget = NULL;
1999 /* This helper function now does ALL the work */
2000 TRACE("Dir %s ...\n",debugstr_w(name));
2001 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2002 TRACE("resolves to %s\n",debugstr_w(path));
2003 msi_free(path);
2005 return ERROR_SUCCESS;
2008 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2010 MSIPACKAGE *package = (MSIPACKAGE*)param;
2011 LPCWSTR name;
2012 MSIFEATURE *feature;
2014 name = MSI_RecordGetString( row, 1 );
2016 feature = get_loaded_feature( package, name );
2017 if (!feature)
2018 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2019 else
2021 LPCWSTR Condition;
2022 Condition = MSI_RecordGetString(row,3);
2024 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2026 int level = MSI_RecordGetInteger(row,2);
2027 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2028 feature->Level = level;
2031 return ERROR_SUCCESS;
2034 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2036 static const WCHAR name_fmt[] =
2037 {'%','u','.','%','u','.','%','u','.','%','u',0};
2038 static const WCHAR name[] = {'\\',0};
2039 VS_FIXEDFILEINFO *lpVer;
2040 WCHAR filever[0x100];
2041 LPVOID version;
2042 DWORD versize;
2043 DWORD handle;
2044 UINT sz;
2046 TRACE("%s\n", debugstr_w(filename));
2048 versize = GetFileVersionInfoSizeW( filename, &handle );
2049 if (!versize)
2050 return NULL;
2052 version = msi_alloc( versize );
2053 GetFileVersionInfoW( filename, 0, versize, version );
2055 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2057 msi_free( version );
2058 return NULL;
2061 sprintfW( filever, name_fmt,
2062 HIWORD(lpVer->dwFileVersionMS),
2063 LOWORD(lpVer->dwFileVersionMS),
2064 HIWORD(lpVer->dwFileVersionLS),
2065 LOWORD(lpVer->dwFileVersionLS));
2067 msi_free( version );
2069 return strdupW( filever );
2072 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2074 LPWSTR file_version;
2075 MSIFILE *file;
2077 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2079 MSICOMPONENT* comp = file->Component;
2080 LPWSTR p;
2082 if (!comp)
2083 continue;
2085 if (file->IsCompressed)
2086 comp->ForceLocalState = TRUE;
2088 /* calculate target */
2089 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2091 msi_free(file->TargetPath);
2093 TRACE("file %s is named %s\n",
2094 debugstr_w(file->File), debugstr_w(file->FileName));
2096 file->TargetPath = build_directory_name(2, p, file->FileName);
2098 msi_free(p);
2100 TRACE("file %s resolves to %s\n",
2101 debugstr_w(file->File), debugstr_w(file->TargetPath));
2103 /* don't check files of components that aren't installed */
2104 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2105 comp->Installed == INSTALLSTATE_ABSENT)
2107 file->state = msifs_missing; /* assume files are missing */
2108 continue;
2111 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2113 file->state = msifs_missing;
2114 comp->Cost += file->FileSize;
2115 continue;
2118 if (file->Version &&
2119 (file_version = msi_get_disk_file_version( file->TargetPath )))
2121 TRACE("new %s old %s\n", debugstr_w(file->Version),
2122 debugstr_w(file_version));
2123 /* FIXME: seems like a bad way to compare version numbers */
2124 if (lstrcmpiW(file_version, file->Version)<0)
2126 file->state = msifs_overwrite;
2127 comp->Cost += file->FileSize;
2129 else
2130 file->state = msifs_present;
2131 msi_free( file_version );
2133 else
2134 file->state = msifs_present;
2137 return ERROR_SUCCESS;
2141 * A lot is done in this function aside from just the costing.
2142 * The costing needs to be implemented at some point but for now I am going
2143 * to focus on the directory building
2146 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2148 static const WCHAR ExecSeqQuery[] =
2149 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2150 '`','D','i','r','e','c','t','o','r','y','`',0};
2151 static const WCHAR ConditionQuery[] =
2152 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2153 '`','C','o','n','d','i','t','i','o','n','`',0};
2154 static const WCHAR szCosting[] =
2155 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2156 static const WCHAR szlevel[] =
2157 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2158 static const WCHAR szOutOfDiskSpace[] =
2159 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2160 static const WCHAR szOne[] = { '1', 0 };
2161 static const WCHAR szZero[] = { '0', 0 };
2162 MSICOMPONENT *comp;
2163 UINT rc;
2164 MSIQUERY * view;
2165 LPWSTR level;
2167 TRACE("Building Directory properties\n");
2169 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2170 if (rc == ERROR_SUCCESS)
2172 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2173 package);
2174 msiobj_release(&view->hdr);
2177 /* read components states from the registry */
2178 ACTION_GetComponentInstallStates(package);
2179 ACTION_GetFeatureInstallStates(package);
2181 TRACE("File calculations\n");
2182 msi_check_file_install_states( package );
2184 TRACE("Evaluating Condition Table\n");
2186 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2187 if (rc == ERROR_SUCCESS)
2189 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2190 package);
2191 msiobj_release(&view->hdr);
2194 TRACE("Enabling or Disabling Components\n");
2195 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2197 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2199 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2200 comp->Enabled = FALSE;
2202 else
2203 comp->Enabled = TRUE;
2206 MSI_SetPropertyW(package,szCosting,szOne);
2207 /* set default run level if not set */
2208 level = msi_dup_property( package, szlevel );
2209 if (!level)
2210 MSI_SetPropertyW(package,szlevel, szOne);
2211 msi_free(level);
2213 /* FIXME: check volume disk space */
2214 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2216 return MSI_SetFeatureStates(package);
2219 /* OK this value is "interpreted" and then formatted based on the
2220 first few characters */
2221 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2222 DWORD *size)
2224 LPSTR data = NULL;
2226 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2228 if (value[1]=='x')
2230 LPWSTR ptr;
2231 CHAR byte[5];
2232 LPWSTR deformated = NULL;
2233 int count;
2235 deformat_string(package, &value[2], &deformated);
2237 /* binary value type */
2238 ptr = deformated;
2239 *type = REG_BINARY;
2240 if (strlenW(ptr)%2)
2241 *size = (strlenW(ptr)/2)+1;
2242 else
2243 *size = strlenW(ptr)/2;
2245 data = msi_alloc(*size);
2247 byte[0] = '0';
2248 byte[1] = 'x';
2249 byte[4] = 0;
2250 count = 0;
2251 /* if uneven pad with a zero in front */
2252 if (strlenW(ptr)%2)
2254 byte[2]= '0';
2255 byte[3]= *ptr;
2256 ptr++;
2257 data[count] = (BYTE)strtol(byte,NULL,0);
2258 count ++;
2259 TRACE("Uneven byte count\n");
2261 while (*ptr)
2263 byte[2]= *ptr;
2264 ptr++;
2265 byte[3]= *ptr;
2266 ptr++;
2267 data[count] = (BYTE)strtol(byte,NULL,0);
2268 count ++;
2270 msi_free(deformated);
2272 TRACE("Data %i bytes(%i)\n",*size,count);
2274 else
2276 LPWSTR deformated;
2277 LPWSTR p;
2278 DWORD d = 0;
2279 deformat_string(package, &value[1], &deformated);
2281 *type=REG_DWORD;
2282 *size = sizeof(DWORD);
2283 data = msi_alloc(*size);
2284 p = deformated;
2285 if (*p == '-')
2286 p++;
2287 while (*p)
2289 if ( (*p < '0') || (*p > '9') )
2290 break;
2291 d *= 10;
2292 d += (*p - '0');
2293 p++;
2295 if (deformated[0] == '-')
2296 d = -d;
2297 *(LPDWORD)data = d;
2298 TRACE("DWORD %i\n",*(LPDWORD)data);
2300 msi_free(deformated);
2303 else
2305 static const WCHAR szMulti[] = {'[','~',']',0};
2306 LPCWSTR ptr;
2307 *type=REG_SZ;
2309 if (value[0]=='#')
2311 if (value[1]=='%')
2313 ptr = &value[2];
2314 *type=REG_EXPAND_SZ;
2316 else
2317 ptr = &value[1];
2319 else
2320 ptr=value;
2322 if (strstrW(value,szMulti))
2323 *type = REG_MULTI_SZ;
2325 /* remove initial delimiter */
2326 if (!strncmpW(value, szMulti, 3))
2327 ptr = value + 3;
2329 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2331 /* add double NULL terminator */
2332 if (*type == REG_MULTI_SZ)
2334 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2335 data = msi_realloc_zero(data, *size);
2338 return data;
2341 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2343 MSIPACKAGE *package = (MSIPACKAGE*)param;
2344 static const WCHAR szHCR[] =
2345 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2346 'R','O','O','T','\\',0};
2347 static const WCHAR szHCU[] =
2348 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2349 'U','S','E','R','\\',0};
2350 static const WCHAR szHLM[] =
2351 {'H','K','E','Y','_','L','O','C','A','L','_',
2352 'M','A','C','H','I','N','E','\\',0};
2353 static const WCHAR szHU[] =
2354 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2356 LPSTR value_data = NULL;
2357 HKEY root_key, hkey;
2358 DWORD type,size;
2359 LPWSTR deformated;
2360 LPCWSTR szRoot, component, name, key, value;
2361 MSICOMPONENT *comp;
2362 MSIRECORD * uirow;
2363 LPWSTR uikey;
2364 INT root;
2365 BOOL check_first = FALSE;
2366 UINT rc;
2368 ui_progress(package,2,0,0,0);
2370 value = NULL;
2371 key = NULL;
2372 uikey = NULL;
2373 name = NULL;
2375 component = MSI_RecordGetString(row, 6);
2376 comp = get_loaded_component(package,component);
2377 if (!comp)
2378 return ERROR_SUCCESS;
2380 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2382 TRACE("Skipping write due to disabled component %s\n",
2383 debugstr_w(component));
2385 comp->Action = comp->Installed;
2387 return ERROR_SUCCESS;
2390 comp->Action = INSTALLSTATE_LOCAL;
2392 name = MSI_RecordGetString(row, 4);
2393 if( MSI_RecordIsNull(row,5) && name )
2395 /* null values can have special meanings */
2396 if (name[0]=='-' && name[1] == 0)
2397 return ERROR_SUCCESS;
2398 else if ((name[0]=='+' && name[1] == 0) ||
2399 (name[0] == '*' && name[1] == 0))
2400 name = NULL;
2401 check_first = TRUE;
2404 root = MSI_RecordGetInteger(row,2);
2405 key = MSI_RecordGetString(row, 3);
2407 /* get the root key */
2408 switch (root)
2410 case -1:
2412 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2413 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2414 if (all_users && all_users[0] == '1')
2416 root_key = HKEY_LOCAL_MACHINE;
2417 szRoot = szHLM;
2419 else
2421 root_key = HKEY_CURRENT_USER;
2422 szRoot = szHCU;
2424 msi_free(all_users);
2426 break;
2427 case 0: root_key = HKEY_CLASSES_ROOT;
2428 szRoot = szHCR;
2429 break;
2430 case 1: root_key = HKEY_CURRENT_USER;
2431 szRoot = szHCU;
2432 break;
2433 case 2: root_key = HKEY_LOCAL_MACHINE;
2434 szRoot = szHLM;
2435 break;
2436 case 3: root_key = HKEY_USERS;
2437 szRoot = szHU;
2438 break;
2439 default:
2440 ERR("Unknown root %i\n",root);
2441 root_key=NULL;
2442 szRoot = NULL;
2443 break;
2445 if (!root_key)
2446 return ERROR_SUCCESS;
2448 deformat_string(package, key , &deformated);
2449 size = strlenW(deformated) + strlenW(szRoot) + 1;
2450 uikey = msi_alloc(size*sizeof(WCHAR));
2451 strcpyW(uikey,szRoot);
2452 strcatW(uikey,deformated);
2454 if (RegCreateKeyW( root_key, deformated, &hkey))
2456 ERR("Could not create key %s\n",debugstr_w(deformated));
2457 msi_free(deformated);
2458 msi_free(uikey);
2459 return ERROR_SUCCESS;
2461 msi_free(deformated);
2463 value = MSI_RecordGetString(row,5);
2464 if (value)
2465 value_data = parse_value(package, value, &type, &size);
2466 else
2468 static const WCHAR szEmpty[] = {0};
2469 value_data = (LPSTR)strdupW(szEmpty);
2470 size = sizeof(szEmpty);
2471 type = REG_SZ;
2474 deformat_string(package, name, &deformated);
2476 if (!check_first)
2478 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2479 debugstr_w(uikey));
2480 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2482 else
2484 DWORD sz = 0;
2485 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2486 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2488 TRACE("value %s of %s checked already exists\n",
2489 debugstr_w(deformated), debugstr_w(uikey));
2491 else
2493 TRACE("Checked and setting value %s of %s\n",
2494 debugstr_w(deformated), debugstr_w(uikey));
2495 if (deformated || size)
2496 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2499 RegCloseKey(hkey);
2501 uirow = MSI_CreateRecord(3);
2502 MSI_RecordSetStringW(uirow,2,deformated);
2503 MSI_RecordSetStringW(uirow,1,uikey);
2505 if (type == REG_SZ)
2506 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2507 else
2508 MSI_RecordSetStringW(uirow,3,value);
2510 ui_actiondata(package,szWriteRegistryValues,uirow);
2511 msiobj_release( &uirow->hdr );
2513 msi_free(value_data);
2514 msi_free(deformated);
2515 msi_free(uikey);
2517 return ERROR_SUCCESS;
2520 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2522 UINT rc;
2523 MSIQUERY * view;
2524 static const WCHAR ExecSeqQuery[] =
2525 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2526 '`','R','e','g','i','s','t','r','y','`',0 };
2528 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2529 if (rc != ERROR_SUCCESS)
2530 return ERROR_SUCCESS;
2532 /* increment progress bar each time action data is sent */
2533 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2535 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2537 msiobj_release(&view->hdr);
2538 return rc;
2541 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2543 package->script->CurrentlyScripting = TRUE;
2545 return ERROR_SUCCESS;
2549 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2551 MSICOMPONENT *comp;
2552 DWORD progress = 0;
2553 DWORD total = 0;
2554 static const WCHAR q1[]=
2555 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2556 '`','R','e','g','i','s','t','r','y','`',0};
2557 UINT rc;
2558 MSIQUERY * view;
2559 MSIFEATURE *feature;
2560 MSIFILE *file;
2562 TRACE("InstallValidate\n");
2564 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2565 if (rc == ERROR_SUCCESS)
2567 MSI_IterateRecords( view, &progress, NULL, package );
2568 msiobj_release( &view->hdr );
2569 total += progress * REG_PROGRESS_VALUE;
2572 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2573 total += COMPONENT_PROGRESS_VALUE;
2575 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2576 total += file->FileSize;
2578 ui_progress(package,0,total,0,0);
2580 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2582 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2583 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2584 feature->ActionRequest);
2587 return ERROR_SUCCESS;
2590 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2592 MSIPACKAGE* package = (MSIPACKAGE*)param;
2593 LPCWSTR cond = NULL;
2594 LPCWSTR message = NULL;
2595 UINT r;
2597 static const WCHAR title[]=
2598 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2600 cond = MSI_RecordGetString(row,1);
2602 r = MSI_EvaluateConditionW(package,cond);
2603 if (r == MSICONDITION_FALSE)
2605 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2607 LPWSTR deformated;
2608 message = MSI_RecordGetString(row,2);
2609 deformat_string(package,message,&deformated);
2610 MessageBoxW(NULL,deformated,title,MB_OK);
2611 msi_free(deformated);
2614 return ERROR_INSTALL_FAILURE;
2617 return ERROR_SUCCESS;
2620 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2622 UINT rc;
2623 MSIQUERY * view = NULL;
2624 static const WCHAR ExecSeqQuery[] =
2625 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2626 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2628 TRACE("Checking launch conditions\n");
2630 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2631 if (rc != ERROR_SUCCESS)
2632 return ERROR_SUCCESS;
2634 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2635 msiobj_release(&view->hdr);
2637 return rc;
2640 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2643 if (!cmp->KeyPath)
2644 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2646 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2648 MSIRECORD * row = 0;
2649 UINT root,len;
2650 LPWSTR deformated,buffer,deformated_name;
2651 LPCWSTR key,name;
2652 static const WCHAR ExecSeqQuery[] =
2653 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2654 '`','R','e','g','i','s','t','r','y','`',' ',
2655 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2656 ' ','=',' ' ,'\'','%','s','\'',0 };
2657 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2658 static const WCHAR fmt2[]=
2659 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2661 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2662 if (!row)
2663 return NULL;
2665 root = MSI_RecordGetInteger(row,2);
2666 key = MSI_RecordGetString(row, 3);
2667 name = MSI_RecordGetString(row, 4);
2668 deformat_string(package, key , &deformated);
2669 deformat_string(package, name, &deformated_name);
2671 len = strlenW(deformated) + 6;
2672 if (deformated_name)
2673 len+=strlenW(deformated_name);
2675 buffer = msi_alloc( len *sizeof(WCHAR));
2677 if (deformated_name)
2678 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2679 else
2680 sprintfW(buffer,fmt,root,deformated);
2682 msi_free(deformated);
2683 msi_free(deformated_name);
2684 msiobj_release(&row->hdr);
2686 return buffer;
2688 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2690 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2691 return NULL;
2693 else
2695 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2697 if (file)
2698 return strdupW( file->TargetPath );
2700 return NULL;
2703 static HKEY openSharedDLLsKey(void)
2705 HKEY hkey=0;
2706 static const WCHAR path[] =
2707 {'S','o','f','t','w','a','r','e','\\',
2708 'M','i','c','r','o','s','o','f','t','\\',
2709 'W','i','n','d','o','w','s','\\',
2710 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2711 'S','h','a','r','e','d','D','L','L','s',0};
2713 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2714 return hkey;
2717 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2719 HKEY hkey;
2720 DWORD count=0;
2721 DWORD type;
2722 DWORD sz = sizeof(count);
2723 DWORD rc;
2725 hkey = openSharedDLLsKey();
2726 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2727 if (rc != ERROR_SUCCESS)
2728 count = 0;
2729 RegCloseKey(hkey);
2730 return count;
2733 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2735 HKEY hkey;
2737 hkey = openSharedDLLsKey();
2738 if (count > 0)
2739 msi_reg_set_val_dword( hkey, path, count );
2740 else
2741 RegDeleteValueW(hkey,path);
2742 RegCloseKey(hkey);
2743 return count;
2747 * Return TRUE if the count should be written out and FALSE if not
2749 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2751 MSIFEATURE *feature;
2752 INT count = 0;
2753 BOOL write = FALSE;
2755 /* only refcount DLLs */
2756 if (comp->KeyPath == NULL ||
2757 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2758 comp->Attributes & msidbComponentAttributesODBCDataSource)
2759 write = FALSE;
2760 else
2762 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2763 write = (count > 0);
2765 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2766 write = TRUE;
2769 /* increment counts */
2770 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2772 ComponentList *cl;
2774 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2775 continue;
2777 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2779 if ( cl->component == comp )
2780 count++;
2784 /* decrement counts */
2785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2787 ComponentList *cl;
2789 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2790 continue;
2792 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2794 if ( cl->component == comp )
2795 count--;
2799 /* ref count all the files in the component */
2800 if (write)
2802 MSIFILE *file;
2804 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2806 if (file->Component == comp)
2807 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2811 /* add a count for permanent */
2812 if (comp->Attributes & msidbComponentAttributesPermanent)
2813 count ++;
2815 comp->RefCount = count;
2817 if (write)
2818 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2821 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2823 WCHAR squished_pc[GUID_SIZE];
2824 WCHAR squished_cc[GUID_SIZE];
2825 UINT rc;
2826 MSICOMPONENT *comp;
2827 HKEY hkey;
2829 TRACE("\n");
2831 squash_guid(package->ProductCode,squished_pc);
2832 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2834 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2836 MSIRECORD * uirow;
2838 ui_progress(package,2,0,0,0);
2839 if (!comp->ComponentId)
2840 continue;
2842 squash_guid(comp->ComponentId,squished_cc);
2844 msi_free(comp->FullKeypath);
2845 comp->FullKeypath = resolve_keypath( package, comp );
2847 ACTION_RefCountComponent( package, comp );
2849 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2850 debugstr_w(comp->Component),
2851 debugstr_w(squished_cc),
2852 debugstr_w(comp->FullKeypath),
2853 comp->RefCount);
2855 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2856 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2858 if (!comp->FullKeypath)
2859 continue;
2861 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2862 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2863 else
2864 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2866 if (rc != ERROR_SUCCESS)
2867 continue;
2869 if (comp->Attributes & msidbComponentAttributesPermanent)
2871 static const WCHAR szPermKey[] =
2872 { '0','0','0','0','0','0','0','0','0','0','0','0',
2873 '0','0','0','0','0','0','0','0','0','0','0','0',
2874 '0','0','0','0','0','0','0','0',0 };
2876 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2879 if (comp->Action == INSTALLSTATE_LOCAL)
2880 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2881 else
2883 MSIFILE *file;
2884 MSIRECORD *row;
2885 LPWSTR ptr, ptr2;
2886 WCHAR source[MAX_PATH];
2887 WCHAR base[MAX_PATH];
2888 LPWSTR sourcepath;
2890 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2891 static const WCHAR query[] = {
2892 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2893 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2894 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2895 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2896 '`','D','i','s','k','I','d','`',0};
2898 file = get_loaded_file(package, comp->KeyPath);
2899 if (!file)
2900 continue;
2902 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2903 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2904 ptr2 = strrchrW(source, '\\') + 1;
2905 msiobj_release(&row->hdr);
2907 lstrcpyW(base, package->PackagePath);
2908 ptr = strrchrW(base, '\\');
2909 *(ptr + 1) = '\0';
2911 sourcepath = resolve_file_source(package, file);
2912 ptr = sourcepath + lstrlenW(base);
2913 lstrcpyW(ptr2, ptr);
2914 msi_free(sourcepath);
2916 msi_reg_set_val_str(hkey, squished_pc, source);
2918 RegCloseKey(hkey);
2920 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2922 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2923 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2924 else
2925 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2928 /* UI stuff */
2929 uirow = MSI_CreateRecord(3);
2930 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2931 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2932 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2933 ui_actiondata(package,szProcessComponents,uirow);
2934 msiobj_release( &uirow->hdr );
2937 return ERROR_SUCCESS;
2940 typedef struct {
2941 CLSID clsid;
2942 LPWSTR source;
2944 LPWSTR path;
2945 ITypeLib *ptLib;
2946 } typelib_struct;
2948 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2949 LPWSTR lpszName, LONG_PTR lParam)
2951 TLIBATTR *attr;
2952 typelib_struct *tl_struct = (typelib_struct*) lParam;
2953 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2954 int sz;
2955 HRESULT res;
2957 if (!IS_INTRESOURCE(lpszName))
2959 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2960 return TRUE;
2963 sz = strlenW(tl_struct->source)+4;
2964 sz *= sizeof(WCHAR);
2966 if ((INT_PTR)lpszName == 1)
2967 tl_struct->path = strdupW(tl_struct->source);
2968 else
2970 tl_struct->path = msi_alloc(sz);
2971 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2974 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2975 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2976 if (FAILED(res))
2978 msi_free(tl_struct->path);
2979 tl_struct->path = NULL;
2981 return TRUE;
2984 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2985 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2987 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2988 return FALSE;
2991 msi_free(tl_struct->path);
2992 tl_struct->path = NULL;
2994 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2995 ITypeLib_Release(tl_struct->ptLib);
2997 return TRUE;
3000 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3002 MSIPACKAGE* package = (MSIPACKAGE*)param;
3003 LPCWSTR component;
3004 MSICOMPONENT *comp;
3005 MSIFILE *file;
3006 typelib_struct tl_struct;
3007 ITypeLib *tlib;
3008 HMODULE module;
3009 HRESULT hr;
3011 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3013 component = MSI_RecordGetString(row,3);
3014 comp = get_loaded_component(package,component);
3015 if (!comp)
3016 return ERROR_SUCCESS;
3018 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3020 TRACE("Skipping typelib reg due to disabled component\n");
3022 comp->Action = comp->Installed;
3024 return ERROR_SUCCESS;
3027 comp->Action = INSTALLSTATE_LOCAL;
3029 file = get_loaded_file( package, comp->KeyPath );
3030 if (!file)
3031 return ERROR_SUCCESS;
3033 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3034 if (module)
3036 LPCWSTR guid;
3037 guid = MSI_RecordGetString(row,1);
3038 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3039 tl_struct.source = strdupW( file->TargetPath );
3040 tl_struct.path = NULL;
3042 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3043 (LONG_PTR)&tl_struct);
3045 if (tl_struct.path)
3047 LPWSTR help = NULL;
3048 LPCWSTR helpid;
3049 HRESULT res;
3051 helpid = MSI_RecordGetString(row,6);
3053 if (helpid)
3054 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3055 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3056 msi_free(help);
3058 if (FAILED(res))
3059 ERR("Failed to register type library %s\n",
3060 debugstr_w(tl_struct.path));
3061 else
3063 ui_actiondata(package,szRegisterTypeLibraries,row);
3065 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3068 ITypeLib_Release(tl_struct.ptLib);
3069 msi_free(tl_struct.path);
3071 else
3072 ERR("Failed to load type library %s\n",
3073 debugstr_w(tl_struct.source));
3075 FreeLibrary(module);
3076 msi_free(tl_struct.source);
3078 else
3080 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3081 if (FAILED(hr))
3083 ERR("Failed to load type library: %08x\n", hr);
3084 return ERROR_FUNCTION_FAILED;
3087 ITypeLib_Release(tlib);
3090 return ERROR_SUCCESS;
3093 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3096 * OK this is a bit confusing.. I am given a _Component key and I believe
3097 * that the file that is being registered as a type library is the "key file
3098 * of that component" which I interpret to mean "The file in the KeyPath of
3099 * that component".
3101 UINT rc;
3102 MSIQUERY * view;
3103 static const WCHAR Query[] =
3104 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3105 '`','T','y','p','e','L','i','b','`',0};
3107 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3108 if (rc != ERROR_SUCCESS)
3109 return ERROR_SUCCESS;
3111 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3112 msiobj_release(&view->hdr);
3113 return rc;
3116 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3118 MSIPACKAGE *package = (MSIPACKAGE*)param;
3119 LPWSTR target_file, target_folder, filename;
3120 LPCWSTR buffer, extension;
3121 MSICOMPONENT *comp;
3122 static const WCHAR szlnk[]={'.','l','n','k',0};
3123 IShellLinkW *sl = NULL;
3124 IPersistFile *pf = NULL;
3125 HRESULT res;
3127 buffer = MSI_RecordGetString(row,4);
3128 comp = get_loaded_component(package,buffer);
3129 if (!comp)
3130 return ERROR_SUCCESS;
3132 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3134 TRACE("Skipping shortcut creation due to disabled component\n");
3136 comp->Action = comp->Installed;
3138 return ERROR_SUCCESS;
3141 comp->Action = INSTALLSTATE_LOCAL;
3143 ui_actiondata(package,szCreateShortcuts,row);
3145 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3146 &IID_IShellLinkW, (LPVOID *) &sl );
3148 if (FAILED( res ))
3150 ERR("CLSID_ShellLink not available\n");
3151 goto err;
3154 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3155 if (FAILED( res ))
3157 ERR("QueryInterface(IID_IPersistFile) failed\n");
3158 goto err;
3161 buffer = MSI_RecordGetString(row,2);
3162 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3164 /* may be needed because of a bug somewhere else */
3165 create_full_pathW(target_folder);
3167 filename = msi_dup_record_field( row, 3 );
3168 reduce_to_longfilename(filename);
3170 extension = strchrW(filename,'.');
3171 if (!extension || strcmpiW(extension,szlnk))
3173 int len = strlenW(filename);
3174 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3175 memcpy(filename + len, szlnk, sizeof(szlnk));
3177 target_file = build_directory_name(2, target_folder, filename);
3178 msi_free(target_folder);
3179 msi_free(filename);
3181 buffer = MSI_RecordGetString(row,5);
3182 if (strchrW(buffer,'['))
3184 LPWSTR deformated;
3185 deformat_string(package,buffer,&deformated);
3186 IShellLinkW_SetPath(sl,deformated);
3187 msi_free(deformated);
3189 else
3191 FIXME("poorly handled shortcut format, advertised shortcut\n");
3192 IShellLinkW_SetPath(sl,comp->FullKeypath);
3195 if (!MSI_RecordIsNull(row,6))
3197 LPWSTR deformated;
3198 buffer = MSI_RecordGetString(row,6);
3199 deformat_string(package,buffer,&deformated);
3200 IShellLinkW_SetArguments(sl,deformated);
3201 msi_free(deformated);
3204 if (!MSI_RecordIsNull(row,7))
3206 buffer = MSI_RecordGetString(row,7);
3207 IShellLinkW_SetDescription(sl,buffer);
3210 if (!MSI_RecordIsNull(row,8))
3211 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3213 if (!MSI_RecordIsNull(row,9))
3215 LPWSTR Path;
3216 INT index;
3218 buffer = MSI_RecordGetString(row,9);
3220 Path = build_icon_path(package,buffer);
3221 index = MSI_RecordGetInteger(row,10);
3223 /* no value means 0 */
3224 if (index == MSI_NULL_INTEGER)
3225 index = 0;
3227 IShellLinkW_SetIconLocation(sl,Path,index);
3228 msi_free(Path);
3231 if (!MSI_RecordIsNull(row,11))
3232 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3234 if (!MSI_RecordIsNull(row,12))
3236 LPWSTR Path;
3237 buffer = MSI_RecordGetString(row,12);
3238 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3239 if (Path)
3240 IShellLinkW_SetWorkingDirectory(sl,Path);
3241 msi_free(Path);
3244 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3245 IPersistFile_Save(pf,target_file,FALSE);
3247 msi_free(target_file);
3249 err:
3250 if (pf)
3251 IPersistFile_Release( pf );
3252 if (sl)
3253 IShellLinkW_Release( sl );
3255 return ERROR_SUCCESS;
3258 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3260 UINT rc;
3261 HRESULT res;
3262 MSIQUERY * view;
3263 static const WCHAR Query[] =
3264 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3265 '`','S','h','o','r','t','c','u','t','`',0};
3267 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3268 if (rc != ERROR_SUCCESS)
3269 return ERROR_SUCCESS;
3271 res = CoInitialize( NULL );
3272 if (FAILED (res))
3274 ERR("CoInitialize failed\n");
3275 return ERROR_FUNCTION_FAILED;
3278 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3279 msiobj_release(&view->hdr);
3281 CoUninitialize();
3283 return rc;
3286 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3288 MSIPACKAGE* package = (MSIPACKAGE*)param;
3289 HANDLE the_file;
3290 LPWSTR FilePath;
3291 LPCWSTR FileName;
3292 CHAR buffer[1024];
3293 DWORD sz;
3294 UINT rc;
3295 MSIRECORD *uirow;
3297 FileName = MSI_RecordGetString(row,1);
3298 if (!FileName)
3300 ERR("Unable to get FileName\n");
3301 return ERROR_SUCCESS;
3304 FilePath = build_icon_path(package,FileName);
3306 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3308 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3309 FILE_ATTRIBUTE_NORMAL, NULL);
3311 if (the_file == INVALID_HANDLE_VALUE)
3313 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3314 msi_free(FilePath);
3315 return ERROR_SUCCESS;
3320 DWORD write;
3321 sz = 1024;
3322 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3323 if (rc != ERROR_SUCCESS)
3325 ERR("Failed to get stream\n");
3326 CloseHandle(the_file);
3327 DeleteFileW(FilePath);
3328 break;
3330 WriteFile(the_file,buffer,sz,&write,NULL);
3331 } while (sz == 1024);
3333 msi_free(FilePath);
3335 CloseHandle(the_file);
3337 uirow = MSI_CreateRecord(1);
3338 MSI_RecordSetStringW(uirow,1,FileName);
3339 ui_actiondata(package,szPublishProduct,uirow);
3340 msiobj_release( &uirow->hdr );
3342 return ERROR_SUCCESS;
3345 static UINT msi_publish_icons(MSIPACKAGE *package)
3347 UINT r;
3348 MSIQUERY *view;
3350 static const WCHAR query[]= {
3351 'S','E','L','E','C','T',' ','*',' ',
3352 'F','R','O','M',' ','`','I','c','o','n','`',0};
3354 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3355 if (r == ERROR_SUCCESS)
3357 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3358 msiobj_release(&view->hdr);
3361 return ERROR_SUCCESS;
3364 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3366 UINT r;
3367 HKEY source;
3368 LPWSTR buffer;
3369 MSIMEDIADISK *disk;
3370 MSISOURCELISTINFO *info;
3372 static const WCHAR szEmpty[] = {0};
3373 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3375 r = RegCreateKeyW(hkey, szSourceList, &source);
3376 if (r != ERROR_SUCCESS)
3377 return r;
3379 RegCloseKey(source);
3381 buffer = strrchrW(package->PackagePath, '\\') + 1;
3382 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3383 package->Context, MSICODE_PRODUCT,
3384 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3385 if (r != ERROR_SUCCESS)
3386 return r;
3388 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3389 package->Context, MSICODE_PRODUCT,
3390 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3391 if (r != ERROR_SUCCESS)
3392 return r;
3394 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3395 package->Context, MSICODE_PRODUCT,
3396 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3397 if (r != ERROR_SUCCESS)
3398 return r;
3400 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3402 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3403 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3404 info->options, info->value);
3405 else
3406 MsiSourceListSetInfoW(package->ProductCode, NULL,
3407 info->context, info->options,
3408 info->property, info->value);
3411 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3413 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3414 disk->context, disk->options,
3415 disk->disk_id, disk->volume_label, disk->disk_prompt);
3418 return ERROR_SUCCESS;
3421 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3423 MSIHANDLE hdb, suminfo;
3424 WCHAR guids[MAX_PATH];
3425 WCHAR packcode[SQUISH_GUID_SIZE];
3426 LPWSTR buffer;
3427 LPWSTR ptr;
3428 DWORD langid;
3429 DWORD size;
3430 UINT r;
3432 static const WCHAR szProductLanguage[] =
3433 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3434 static const WCHAR szARPProductIcon[] =
3435 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3436 static const WCHAR szProductVersion[] =
3437 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3438 static const WCHAR szAssignment[] =
3439 {'A','s','s','i','g','n','m','e','n','t',0};
3440 static const WCHAR szAdvertiseFlags[] =
3441 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3442 static const WCHAR szClients[] =
3443 {'C','l','i','e','n','t','s',0};
3444 static const WCHAR szColon[] = {':',0};
3446 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3447 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3448 msi_free(buffer);
3450 langid = msi_get_property_int(package, szProductLanguage, 0);
3451 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3453 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3454 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3456 /* FIXME */
3457 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3459 buffer = msi_dup_property(package, szARPProductIcon);
3460 if (buffer)
3462 LPWSTR path = build_icon_path(package,buffer);
3463 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3464 msi_free(path);
3465 msi_free(buffer);
3468 buffer = msi_dup_property(package, szProductVersion);
3469 if (buffer)
3471 DWORD verdword = msi_version_str_to_dword(buffer);
3472 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3473 msi_free(buffer);
3476 msi_reg_set_val_dword(hkey, szAssignment, 0);
3477 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3478 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3479 msi_reg_set_val_str(hkey, szClients, szColon);
3481 hdb = alloc_msihandle(&package->db->hdr);
3482 if (!hdb)
3483 return ERROR_NOT_ENOUGH_MEMORY;
3485 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3486 MsiCloseHandle(hdb);
3487 if (r != ERROR_SUCCESS)
3488 goto done;
3490 size = MAX_PATH;
3491 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3492 NULL, guids, &size);
3493 if (r != ERROR_SUCCESS)
3494 goto done;
3496 ptr = strchrW(guids, ';');
3497 if (ptr) *ptr = 0;
3498 squash_guid(guids, packcode);
3499 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3501 done:
3502 MsiCloseHandle(suminfo);
3503 return ERROR_SUCCESS;
3506 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3508 UINT r;
3509 HKEY hkey;
3510 LPWSTR upgrade;
3511 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3513 static const WCHAR szUpgradeCode[] =
3514 {'U','p','g','r','a','d','e','C','o','d','e',0};
3516 upgrade = msi_dup_property(package, szUpgradeCode);
3517 if (!upgrade)
3518 return ERROR_SUCCESS;
3520 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3522 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3523 if (r != ERROR_SUCCESS)
3524 goto done;
3526 else
3528 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3529 if (r != ERROR_SUCCESS)
3530 goto done;
3533 squash_guid(package->ProductCode, squashed_pc);
3534 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3536 RegCloseKey(hkey);
3538 done:
3539 msi_free(upgrade);
3540 return r;
3543 static BOOL msi_check_publish(MSIPACKAGE *package)
3545 MSIFEATURE *feature;
3547 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3549 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3550 return TRUE;
3553 return FALSE;
3556 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3558 MSIFEATURE *feature;
3560 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3562 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3563 return FALSE;
3566 return TRUE;
3570 * 99% of the work done here is only done for
3571 * advertised installs. However this is where the
3572 * Icon table is processed and written out
3573 * so that is what I am going to do here.
3575 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3577 UINT rc;
3578 HKEY hukey=0;
3579 HKEY hudkey=0;
3581 /* FIXME: also need to publish if the product is in advertise mode */
3582 if (!msi_check_publish(package))
3583 return ERROR_SUCCESS;
3585 rc = MSIREG_OpenProductKey(package->ProductCode, package->Context,
3586 &hukey, TRUE);
3587 if (rc != ERROR_SUCCESS)
3588 goto end;
3590 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3592 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3593 if (rc != ERROR_SUCCESS)
3594 goto end;
3596 else
3598 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3599 if (rc != ERROR_SUCCESS)
3600 goto end;
3603 rc = msi_publish_upgrade_code(package);
3604 if (rc != ERROR_SUCCESS)
3605 goto end;
3607 rc = msi_publish_product_properties(package, hukey);
3608 if (rc != ERROR_SUCCESS)
3609 goto end;
3611 rc = msi_publish_sourcelist(package, hukey);
3612 if (rc != ERROR_SUCCESS)
3613 goto end;
3615 rc = msi_publish_icons(package);
3617 end:
3618 RegCloseKey(hukey);
3619 RegCloseKey(hudkey);
3621 return rc;
3624 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3626 MSIPACKAGE *package = (MSIPACKAGE*)param;
3627 LPCWSTR component, section, key, value, identifier, dirproperty;
3628 LPWSTR deformated_section, deformated_key, deformated_value;
3629 LPWSTR folder, filename, fullname = NULL;
3630 LPCWSTR filenameptr;
3631 MSIRECORD * uirow;
3632 INT action;
3633 MSICOMPONENT *comp;
3634 static const WCHAR szWindowsFolder[] =
3635 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3637 component = MSI_RecordGetString(row, 8);
3638 comp = get_loaded_component(package,component);
3640 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3642 TRACE("Skipping ini file due to disabled component %s\n",
3643 debugstr_w(component));
3645 comp->Action = comp->Installed;
3647 return ERROR_SUCCESS;
3650 comp->Action = INSTALLSTATE_LOCAL;
3652 identifier = MSI_RecordGetString(row,1);
3653 dirproperty = MSI_RecordGetString(row,3);
3654 section = MSI_RecordGetString(row,4);
3655 key = MSI_RecordGetString(row,5);
3656 value = MSI_RecordGetString(row,6);
3657 action = MSI_RecordGetInteger(row,7);
3659 deformat_string(package,section,&deformated_section);
3660 deformat_string(package,key,&deformated_key);
3661 deformat_string(package,value,&deformated_value);
3663 filename = msi_dup_record_field(row, 2);
3664 if (filename && (filenameptr = strchrW(filename, '|')))
3665 filenameptr++;
3666 else
3667 filenameptr = filename;
3669 if (dirproperty)
3671 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3672 if (!folder)
3673 folder = msi_dup_property( package, dirproperty );
3675 else
3676 folder = msi_dup_property( package, szWindowsFolder );
3678 if (!folder)
3680 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3681 goto cleanup;
3684 fullname = build_directory_name(2, folder, filenameptr);
3686 if (action == 0)
3688 TRACE("Adding value %s to section %s in %s\n",
3689 debugstr_w(deformated_key), debugstr_w(deformated_section),
3690 debugstr_w(fullname));
3691 WritePrivateProfileStringW(deformated_section, deformated_key,
3692 deformated_value, fullname);
3694 else if (action == 1)
3696 WCHAR returned[10];
3697 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3698 returned, 10, fullname);
3699 if (returned[0] == 0)
3701 TRACE("Adding value %s to section %s in %s\n",
3702 debugstr_w(deformated_key), debugstr_w(deformated_section),
3703 debugstr_w(fullname));
3705 WritePrivateProfileStringW(deformated_section, deformated_key,
3706 deformated_value, fullname);
3709 else if (action == 3)
3710 FIXME("Append to existing section not yet implemented\n");
3712 uirow = MSI_CreateRecord(4);
3713 MSI_RecordSetStringW(uirow,1,identifier);
3714 MSI_RecordSetStringW(uirow,2,deformated_section);
3715 MSI_RecordSetStringW(uirow,3,deformated_key);
3716 MSI_RecordSetStringW(uirow,4,deformated_value);
3717 ui_actiondata(package,szWriteIniValues,uirow);
3718 msiobj_release( &uirow->hdr );
3720 cleanup:
3721 msi_free(filename);
3722 msi_free(fullname);
3723 msi_free(folder);
3724 msi_free(deformated_key);
3725 msi_free(deformated_value);
3726 msi_free(deformated_section);
3727 return ERROR_SUCCESS;
3730 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3732 UINT rc;
3733 MSIQUERY * view;
3734 static const WCHAR ExecSeqQuery[] =
3735 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3736 '`','I','n','i','F','i','l','e','`',0};
3738 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3739 if (rc != ERROR_SUCCESS)
3741 TRACE("no IniFile table\n");
3742 return ERROR_SUCCESS;
3745 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3746 msiobj_release(&view->hdr);
3747 return rc;
3750 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3752 MSIPACKAGE *package = (MSIPACKAGE*)param;
3753 LPCWSTR filename;
3754 LPWSTR FullName;
3755 MSIFILE *file;
3756 DWORD len;
3757 static const WCHAR ExeStr[] =
3758 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3759 static const WCHAR close[] = {'\"',0};
3760 STARTUPINFOW si;
3761 PROCESS_INFORMATION info;
3762 BOOL brc;
3763 MSIRECORD *uirow;
3764 LPWSTR uipath, p;
3766 memset(&si,0,sizeof(STARTUPINFOW));
3768 filename = MSI_RecordGetString(row,1);
3769 file = get_loaded_file( package, filename );
3771 if (!file)
3773 ERR("Unable to find file id %s\n",debugstr_w(filename));
3774 return ERROR_SUCCESS;
3777 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3779 FullName = msi_alloc(len*sizeof(WCHAR));
3780 strcpyW(FullName,ExeStr);
3781 strcatW( FullName, file->TargetPath );
3782 strcatW(FullName,close);
3784 TRACE("Registering %s\n",debugstr_w(FullName));
3785 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3786 &si, &info);
3788 if (brc)
3790 CloseHandle(info.hThread);
3791 msi_dialog_check_messages(info.hProcess);
3792 CloseHandle(info.hProcess);
3795 msi_free(FullName);
3797 /* the UI chunk */
3798 uirow = MSI_CreateRecord( 2 );
3799 uipath = strdupW( file->TargetPath );
3800 p = strrchrW(uipath,'\\');
3801 if (p)
3802 p[0]=0;
3803 MSI_RecordSetStringW( uirow, 1, &p[1] );
3804 MSI_RecordSetStringW( uirow, 2, uipath);
3805 ui_actiondata( package, szSelfRegModules, uirow);
3806 msiobj_release( &uirow->hdr );
3807 msi_free( uipath );
3808 /* FIXME: call ui_progress? */
3810 return ERROR_SUCCESS;
3813 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3815 UINT rc;
3816 MSIQUERY * view;
3817 static const WCHAR ExecSeqQuery[] =
3818 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3819 '`','S','e','l','f','R','e','g','`',0};
3821 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3822 if (rc != ERROR_SUCCESS)
3824 TRACE("no SelfReg table\n");
3825 return ERROR_SUCCESS;
3828 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3829 msiobj_release(&view->hdr);
3831 return ERROR_SUCCESS;
3834 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3836 MSIFEATURE *feature;
3837 UINT rc;
3838 HKEY hkey;
3839 HKEY userdata = NULL;
3841 if (!msi_check_publish(package))
3842 return ERROR_SUCCESS;
3844 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3845 &hkey, TRUE);
3846 if (rc != ERROR_SUCCESS)
3847 goto end;
3849 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3850 &userdata, TRUE);
3851 if (rc != ERROR_SUCCESS)
3852 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_OpenFeaturesKey(package->ProductCode, package->Context,
3951 &hkey, FALSE);
3952 if (r == ERROR_SUCCESS)
3954 RegDeleteValueW(hkey, feature->Feature);
3955 RegCloseKey(hkey);
3958 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3959 &hkey, FALSE);
3960 if (r == ERROR_SUCCESS)
3962 RegDeleteValueW(hkey, feature->Feature);
3963 RegCloseKey(hkey);
3966 return ERROR_SUCCESS;
3969 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3971 MSIFEATURE *feature;
3973 if (!msi_check_unpublish(package))
3974 return ERROR_SUCCESS;
3976 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3978 msi_unpublish_feature(package, feature);
3981 return ERROR_SUCCESS;
3984 static UINT msi_get_local_package_name( LPWSTR path )
3986 static const WCHAR szInstaller[] = {
3987 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3988 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3989 DWORD time, len, i;
3990 HANDLE handle;
3992 time = GetTickCount();
3993 GetWindowsDirectoryW( path, MAX_PATH );
3994 lstrcatW( path, szInstaller );
3995 CreateDirectoryW( path, NULL );
3997 len = lstrlenW(path);
3998 for (i=0; i<0x10000; i++)
4000 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4001 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4002 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4003 if (handle != INVALID_HANDLE_VALUE)
4005 CloseHandle(handle);
4006 break;
4008 if (GetLastError() != ERROR_FILE_EXISTS &&
4009 GetLastError() != ERROR_SHARING_VIOLATION)
4010 return ERROR_FUNCTION_FAILED;
4013 return ERROR_SUCCESS;
4016 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4018 WCHAR packagefile[MAX_PATH];
4019 UINT r;
4021 r = msi_get_local_package_name( packagefile );
4022 if (r != ERROR_SUCCESS)
4023 return r;
4025 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4027 r = CopyFileW( package->db->path, packagefile, FALSE);
4029 if (!r)
4031 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4032 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4033 return ERROR_FUNCTION_FAILED;
4036 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4038 return ERROR_SUCCESS;
4041 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4043 LPWSTR prop, val, key;
4044 SYSTEMTIME systime;
4045 DWORD size, langid;
4046 WCHAR date[9];
4047 LPWSTR buffer;
4049 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4050 static const WCHAR szWindowsInstaller[] =
4051 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4052 static const WCHAR modpath_fmt[] =
4053 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4054 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4055 static const WCHAR szModifyPath[] =
4056 {'M','o','d','i','f','y','P','a','t','h',0};
4057 static const WCHAR szUninstallString[] =
4058 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4059 static const WCHAR szEstimatedSize[] =
4060 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4061 static const WCHAR szProductLanguage[] =
4062 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4063 static const WCHAR szProductVersion[] =
4064 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4065 static const WCHAR szProductName[] =
4066 {'P','r','o','d','u','c','t','N','a','m','e',0};
4067 static const WCHAR szDisplayName[] =
4068 {'D','i','s','p','l','a','y','N','a','m','e',0};
4069 static const WCHAR szDisplayVersion[] =
4070 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4071 static const WCHAR szManufacturer[] =
4072 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4074 static const LPCSTR propval[] = {
4075 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4076 "ARPCONTACT", "Contact",
4077 "ARPCOMMENTS", "Comments",
4078 "ProductName", "DisplayName",
4079 "ProductVersion", "DisplayVersion",
4080 "ARPHELPLINK", "HelpLink",
4081 "ARPHELPTELEPHONE", "HelpTelephone",
4082 "ARPINSTALLLOCATION", "InstallLocation",
4083 "SourceDir", "InstallSource",
4084 "Manufacturer", "Publisher",
4085 "ARPREADME", "Readme",
4086 "ARPSIZE", "Size",
4087 "ARPURLINFOABOUT", "URLInfoAbout",
4088 "ARPURLUPDATEINFO", "URLUpdateInfo",
4089 NULL,
4091 const LPCSTR *p = propval;
4093 while (*p)
4095 prop = strdupAtoW(*p++);
4096 key = strdupAtoW(*p++);
4097 val = msi_dup_property(package, prop);
4098 msi_reg_set_val_str(hkey, key, val);
4099 msi_free(val);
4100 msi_free(key);
4101 msi_free(prop);
4104 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4106 size = deformat_string(package, modpath_fmt, &buffer);
4107 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4108 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4109 msi_free(buffer);
4111 /* FIXME: Write real Estimated Size when we have it */
4112 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4114 buffer = msi_dup_property(package, szProductName);
4115 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4116 msi_free(buffer);
4118 buffer = msi_dup_property(package, cszSourceDir);
4119 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4120 msi_free(buffer);
4122 buffer = msi_dup_property(package, szManufacturer);
4123 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4124 msi_free(buffer);
4126 GetLocalTime(&systime);
4127 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4128 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4130 langid = msi_get_property_int(package, szProductLanguage, 0);
4131 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4133 buffer = msi_dup_property(package, szProductVersion);
4134 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4135 if (buffer)
4137 DWORD verdword = msi_version_str_to_dword(buffer);
4139 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4140 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4141 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4142 msi_free(buffer);
4145 return ERROR_SUCCESS;
4148 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4150 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4151 LPWSTR upgrade_code;
4152 HKEY hkey, props;
4153 HKEY upgrade;
4154 UINT rc;
4156 static const WCHAR szUpgradeCode[] = {
4157 'U','p','g','r','a','d','e','C','o','d','e',0};
4159 /* FIXME: also need to publish if the product is in advertise mode */
4160 if (!msi_check_publish(package))
4161 return ERROR_SUCCESS;
4163 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4164 if (rc != ERROR_SUCCESS)
4165 return rc;
4167 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4169 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4170 if (rc != ERROR_SUCCESS)
4171 goto done;
4173 else
4175 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4176 if (rc != ERROR_SUCCESS)
4177 goto done;
4180 msi_make_package_local(package, props);
4182 rc = msi_publish_install_properties(package, hkey);
4183 if (rc != ERROR_SUCCESS)
4184 goto done;
4186 rc = msi_publish_install_properties(package, props);
4187 if (rc != ERROR_SUCCESS)
4188 goto done;
4190 upgrade_code = msi_dup_property(package, szUpgradeCode);
4191 if (upgrade_code)
4193 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4194 squash_guid(package->ProductCode, squashed_pc);
4195 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4196 RegCloseKey(upgrade);
4197 msi_free(upgrade_code);
4200 done:
4201 RegCloseKey(hkey);
4203 return ERROR_SUCCESS;
4206 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4208 return execute_script(package,INSTALL_SCRIPT);
4211 static UINT msi_unpublish_product(MSIPACKAGE *package)
4213 LPWSTR upgrade;
4214 LPWSTR remove = NULL;
4215 LPWSTR *features = NULL;
4216 BOOL full_uninstall = TRUE;
4217 MSIFEATURE *feature;
4219 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4220 static const WCHAR szAll[] = {'A','L','L',0};
4221 static const WCHAR szUpgradeCode[] =
4222 {'U','p','g','r','a','d','e','C','o','d','e',0};
4224 remove = msi_dup_property(package, szRemove);
4225 if (!remove)
4226 return ERROR_SUCCESS;
4228 features = msi_split_string(remove, ',');
4229 if (!features)
4231 msi_free(remove);
4232 ERR("REMOVE feature list is empty!\n");
4233 return ERROR_FUNCTION_FAILED;
4236 if (!lstrcmpW(features[0], szAll))
4237 full_uninstall = TRUE;
4238 else
4240 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4242 if (feature->Action != INSTALLSTATE_ABSENT)
4243 full_uninstall = FALSE;
4247 if (!full_uninstall)
4248 goto done;
4250 MSIREG_DeleteProductKey(package->ProductCode);
4251 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4252 MSIREG_DeleteUninstallKey(package->ProductCode);
4254 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4256 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4257 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4259 else
4261 MSIREG_DeleteUserProductKey(package->ProductCode);
4262 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4265 upgrade = msi_dup_property(package, szUpgradeCode);
4266 if (upgrade)
4268 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4269 msi_free(upgrade);
4272 done:
4273 msi_free(remove);
4274 msi_free(features);
4275 return ERROR_SUCCESS;
4278 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4280 UINT rc;
4282 rc = msi_unpublish_product(package);
4283 if (rc != ERROR_SUCCESS)
4284 return rc;
4286 /* turn off scheduling */
4287 package->script->CurrentlyScripting= FALSE;
4289 /* first do the same as an InstallExecute */
4290 rc = ACTION_InstallExecute(package);
4291 if (rc != ERROR_SUCCESS)
4292 return rc;
4294 /* then handle Commit Actions */
4295 rc = execute_script(package,COMMIT_SCRIPT);
4297 return rc;
4300 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4302 static const WCHAR RunOnce[] = {
4303 'S','o','f','t','w','a','r','e','\\',
4304 'M','i','c','r','o','s','o','f','t','\\',
4305 'W','i','n','d','o','w','s','\\',
4306 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4307 'R','u','n','O','n','c','e',0};
4308 static const WCHAR InstallRunOnce[] = {
4309 'S','o','f','t','w','a','r','e','\\',
4310 'M','i','c','r','o','s','o','f','t','\\',
4311 'W','i','n','d','o','w','s','\\',
4312 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4313 'I','n','s','t','a','l','l','e','r','\\',
4314 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4316 static const WCHAR msiexec_fmt[] = {
4317 '%','s',
4318 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4319 '\"','%','s','\"',0};
4320 static const WCHAR install_fmt[] = {
4321 '/','I',' ','\"','%','s','\"',' ',
4322 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4323 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4324 WCHAR buffer[256], sysdir[MAX_PATH];
4325 HKEY hkey;
4326 WCHAR squished_pc[100];
4328 squash_guid(package->ProductCode,squished_pc);
4330 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4331 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4332 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4333 squished_pc);
4335 msi_reg_set_val_str( hkey, squished_pc, buffer );
4336 RegCloseKey(hkey);
4338 TRACE("Reboot command %s\n",debugstr_w(buffer));
4340 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4341 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4343 msi_reg_set_val_str( hkey, squished_pc, buffer );
4344 RegCloseKey(hkey);
4346 return ERROR_INSTALL_SUSPEND;
4349 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4351 DWORD attrib;
4352 UINT rc;
4355 * We are currently doing what should be done here in the top level Install
4356 * however for Administrative and uninstalls this step will be needed
4358 if (!package->PackagePath)
4359 return ERROR_SUCCESS;
4361 msi_set_sourcedir_props(package, TRUE);
4363 attrib = GetFileAttributesW(package->db->path);
4364 if (attrib == INVALID_FILE_ATTRIBUTES)
4366 LPWSTR prompt;
4367 LPWSTR msg;
4368 DWORD size = 0;
4370 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4371 package->Context, MSICODE_PRODUCT,
4372 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4373 if (rc == ERROR_MORE_DATA)
4375 prompt = msi_alloc(size * sizeof(WCHAR));
4376 MsiSourceListGetInfoW(package->ProductCode, NULL,
4377 package->Context, MSICODE_PRODUCT,
4378 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4380 else
4381 prompt = strdupW(package->db->path);
4383 msg = generate_error_string(package,1302,1,prompt);
4384 while(attrib == INVALID_FILE_ATTRIBUTES)
4386 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4387 if (rc == IDCANCEL)
4389 rc = ERROR_INSTALL_USEREXIT;
4390 break;
4392 attrib = GetFileAttributesW(package->db->path);
4394 msi_free(prompt);
4395 rc = ERROR_SUCCESS;
4397 else
4398 return ERROR_SUCCESS;
4400 return rc;
4403 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4405 HKEY hkey=0;
4406 LPWSTR buffer;
4407 LPWSTR productid;
4408 UINT rc,i;
4410 static const WCHAR szPropKeys[][80] =
4412 {'P','r','o','d','u','c','t','I','D',0},
4413 {'U','S','E','R','N','A','M','E',0},
4414 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4415 {0},
4418 static const WCHAR szRegKeys[][80] =
4420 {'P','r','o','d','u','c','t','I','D',0},
4421 {'R','e','g','O','w','n','e','r',0},
4422 {'R','e','g','C','o','m','p','a','n','y',0},
4423 {0},
4426 if (msi_check_unpublish(package))
4428 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4429 return ERROR_SUCCESS;
4432 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4433 if (!productid)
4434 return ERROR_SUCCESS;
4436 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4437 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4438 else
4439 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4441 if (rc != ERROR_SUCCESS)
4442 goto end;
4444 for( i = 0; szPropKeys[i][0]; i++ )
4446 buffer = msi_dup_property( package, szPropKeys[i] );
4447 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4448 msi_free( buffer );
4451 end:
4452 msi_free(productid);
4453 RegCloseKey(hkey);
4455 /* FIXME: call ui_actiondata */
4457 return rc;
4461 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4463 UINT rc;
4465 package->script->InWhatSequence |= SEQUENCE_EXEC;
4466 rc = ACTION_ProcessExecSequence(package,FALSE);
4467 return rc;
4471 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4473 MSIPACKAGE *package = (MSIPACKAGE*)param;
4474 LPCWSTR compgroupid=NULL;
4475 LPCWSTR feature=NULL;
4476 LPCWSTR text = NULL;
4477 LPCWSTR qualifier = NULL;
4478 LPCWSTR component = NULL;
4479 LPWSTR advertise = NULL;
4480 LPWSTR output = NULL;
4481 HKEY hkey;
4482 UINT rc = ERROR_SUCCESS;
4483 MSICOMPONENT *comp;
4484 DWORD sz = 0;
4485 MSIRECORD *uirow;
4487 component = MSI_RecordGetString(rec,3);
4488 comp = get_loaded_component(package,component);
4490 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4491 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4492 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4494 TRACE("Skipping: Component %s not scheduled for install\n",
4495 debugstr_w(component));
4497 return ERROR_SUCCESS;
4500 compgroupid = MSI_RecordGetString(rec,1);
4501 qualifier = MSI_RecordGetString(rec,2);
4503 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4504 if (rc != ERROR_SUCCESS)
4505 goto end;
4507 text = MSI_RecordGetString(rec,4);
4508 feature = MSI_RecordGetString(rec,5);
4510 advertise = create_component_advertise_string(package, comp, feature);
4512 sz = strlenW(advertise);
4514 if (text)
4515 sz += lstrlenW(text);
4517 sz+=3;
4518 sz *= sizeof(WCHAR);
4520 output = msi_alloc_zero(sz);
4521 strcpyW(output,advertise);
4522 msi_free(advertise);
4524 if (text)
4525 strcatW(output,text);
4527 msi_reg_set_val_multi_str( hkey, qualifier, output );
4529 end:
4530 RegCloseKey(hkey);
4531 msi_free(output);
4533 /* the UI chunk */
4534 uirow = MSI_CreateRecord( 2 );
4535 MSI_RecordSetStringW( uirow, 1, compgroupid );
4536 MSI_RecordSetStringW( uirow, 2, qualifier);
4537 ui_actiondata( package, szPublishComponents, uirow);
4538 msiobj_release( &uirow->hdr );
4539 /* FIXME: call ui_progress? */
4541 return rc;
4545 * At present I am ignorning the advertised components part of this and only
4546 * focusing on the qualified component sets
4548 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4550 UINT rc;
4551 MSIQUERY * view;
4552 static const WCHAR ExecSeqQuery[] =
4553 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4554 '`','P','u','b','l','i','s','h',
4555 'C','o','m','p','o','n','e','n','t','`',0};
4557 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4558 if (rc != ERROR_SUCCESS)
4559 return ERROR_SUCCESS;
4561 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4562 msiobj_release(&view->hdr);
4564 return rc;
4567 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4569 MSIPACKAGE *package = (MSIPACKAGE*)param;
4570 MSIRECORD *row;
4571 MSIFILE *file;
4572 SC_HANDLE hscm, service = NULL;
4573 LPCWSTR comp, depends, pass;
4574 LPWSTR name = NULL, disp = NULL;
4575 LPCWSTR load_order, serv_name, key;
4576 DWORD serv_type, start_type;
4577 DWORD err_control;
4579 static const WCHAR query[] =
4580 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4581 '`','C','o','m','p','o','n','e','n','t','`',' ',
4582 'W','H','E','R','E',' ',
4583 '`','C','o','m','p','o','n','e','n','t','`',' ',
4584 '=','\'','%','s','\'',0};
4586 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4587 if (!hscm)
4589 ERR("Failed to open the SC Manager!\n");
4590 goto done;
4593 start_type = MSI_RecordGetInteger(rec, 5);
4594 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4595 goto done;
4597 depends = MSI_RecordGetString(rec, 8);
4598 if (depends && *depends)
4599 FIXME("Dependency list unhandled!\n");
4601 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4602 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4603 serv_type = MSI_RecordGetInteger(rec, 4);
4604 err_control = MSI_RecordGetInteger(rec, 6);
4605 load_order = MSI_RecordGetString(rec, 7);
4606 serv_name = MSI_RecordGetString(rec, 9);
4607 pass = MSI_RecordGetString(rec, 10);
4608 comp = MSI_RecordGetString(rec, 12);
4610 /* fetch the service path */
4611 row = MSI_QueryGetRecord(package->db, query, comp);
4612 if (!row)
4614 ERR("Control query failed!\n");
4615 goto done;
4618 key = MSI_RecordGetString(row, 6);
4620 file = get_loaded_file(package, key);
4621 msiobj_release(&row->hdr);
4622 if (!file)
4624 ERR("Failed to load the service file\n");
4625 goto done;
4628 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4629 start_type, err_control, file->TargetPath,
4630 load_order, NULL, NULL, serv_name, pass);
4631 if (!service)
4633 if (GetLastError() != ERROR_SERVICE_EXISTS)
4634 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4637 done:
4638 CloseServiceHandle(service);
4639 CloseServiceHandle(hscm);
4640 msi_free(name);
4641 msi_free(disp);
4643 return ERROR_SUCCESS;
4646 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4648 UINT rc;
4649 MSIQUERY * view;
4650 static const WCHAR ExecSeqQuery[] =
4651 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4652 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4654 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4655 if (rc != ERROR_SUCCESS)
4656 return ERROR_SUCCESS;
4658 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4659 msiobj_release(&view->hdr);
4661 return rc;
4664 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4665 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4667 LPCWSTR *vector, *temp_vector;
4668 LPWSTR p, q;
4669 DWORD sep_len;
4671 static const WCHAR separator[] = {'[','~',']',0};
4673 *numargs = 0;
4674 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4676 if (!args)
4677 return NULL;
4679 vector = msi_alloc(sizeof(LPWSTR));
4680 if (!vector)
4681 return NULL;
4683 p = args;
4686 (*numargs)++;
4687 vector[*numargs - 1] = p;
4689 if ((q = strstrW(p, separator)))
4691 *q = '\0';
4693 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4694 if (!temp_vector)
4696 msi_free(vector);
4697 return NULL;
4699 vector = temp_vector;
4701 p = q + sep_len;
4703 } while (q);
4705 return vector;
4708 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4710 MSIPACKAGE *package = (MSIPACKAGE *)param;
4711 MSICOMPONENT *comp;
4712 SC_HANDLE scm, service = NULL;
4713 LPCWSTR name, *vector = NULL;
4714 LPWSTR args;
4715 DWORD event, numargs;
4716 UINT r = ERROR_FUNCTION_FAILED;
4718 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4719 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4720 return ERROR_SUCCESS;
4722 name = MSI_RecordGetString(rec, 2);
4723 event = MSI_RecordGetInteger(rec, 3);
4724 args = strdupW(MSI_RecordGetString(rec, 4));
4726 if (!(event & msidbServiceControlEventStart))
4727 return ERROR_SUCCESS;
4729 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4730 if (!scm)
4732 ERR("Failed to open the service control manager\n");
4733 goto done;
4736 service = OpenServiceW(scm, name, SERVICE_START);
4737 if (!service)
4739 ERR("Failed to open service %s\n", debugstr_w(name));
4740 goto done;
4743 vector = msi_service_args_to_vector(args, &numargs);
4745 if (!StartServiceW(service, numargs, vector))
4747 ERR("Failed to start service %s\n", debugstr_w(name));
4748 goto done;
4751 r = ERROR_SUCCESS;
4753 done:
4754 CloseServiceHandle(service);
4755 CloseServiceHandle(scm);
4757 msi_free(args);
4758 msi_free(vector);
4759 return r;
4762 static UINT ACTION_StartServices( MSIPACKAGE *package )
4764 UINT rc;
4765 MSIQUERY *view;
4767 static const WCHAR query[] = {
4768 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4769 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4771 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4772 if (rc != ERROR_SUCCESS)
4773 return ERROR_SUCCESS;
4775 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4776 msiobj_release(&view->hdr);
4778 return rc;
4781 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4783 DWORD i, needed, count;
4784 ENUM_SERVICE_STATUSW *dependencies;
4785 SERVICE_STATUS ss;
4786 SC_HANDLE depserv;
4788 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4789 0, &needed, &count))
4790 return TRUE;
4792 if (GetLastError() != ERROR_MORE_DATA)
4793 return FALSE;
4795 dependencies = msi_alloc(needed);
4796 if (!dependencies)
4797 return FALSE;
4799 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4800 needed, &needed, &count))
4801 goto error;
4803 for (i = 0; i < count; i++)
4805 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4806 SERVICE_STOP | SERVICE_QUERY_STATUS);
4807 if (!depserv)
4808 goto error;
4810 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4811 goto error;
4814 return TRUE;
4816 error:
4817 msi_free(dependencies);
4818 return FALSE;
4821 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4823 MSIPACKAGE *package = (MSIPACKAGE *)param;
4824 MSICOMPONENT *comp;
4825 SERVICE_STATUS status;
4826 SERVICE_STATUS_PROCESS ssp;
4827 SC_HANDLE scm = NULL, service = NULL;
4828 LPWSTR name, args;
4829 DWORD event, needed;
4831 event = MSI_RecordGetInteger(rec, 3);
4832 if (!(event & msidbServiceControlEventStop))
4833 return ERROR_SUCCESS;
4835 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4836 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4837 return ERROR_SUCCESS;
4839 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4840 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4841 args = strdupW(MSI_RecordGetString(rec, 4));
4843 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4844 if (!scm)
4846 WARN("Failed to open the SCM: %d\n", GetLastError());
4847 goto done;
4850 service = OpenServiceW(scm, name,
4851 SERVICE_STOP |
4852 SERVICE_QUERY_STATUS |
4853 SERVICE_ENUMERATE_DEPENDENTS);
4854 if (!service)
4856 WARN("Failed to open service (%s): %d\n",
4857 debugstr_w(name), GetLastError());
4858 goto done;
4861 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4862 sizeof(SERVICE_STATUS_PROCESS), &needed))
4864 WARN("Failed to query service status (%s): %d\n",
4865 debugstr_w(name), GetLastError());
4866 goto done;
4869 if (ssp.dwCurrentState == SERVICE_STOPPED)
4870 goto done;
4872 stop_service_dependents(scm, service);
4874 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4875 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4877 done:
4878 CloseServiceHandle(service);
4879 CloseServiceHandle(scm);
4880 msi_free(name);
4881 msi_free(args);
4883 return ERROR_SUCCESS;
4886 static UINT ACTION_StopServices( MSIPACKAGE *package )
4888 UINT rc;
4889 MSIQUERY *view;
4891 static const WCHAR query[] = {
4892 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4893 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4895 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4896 if (rc != ERROR_SUCCESS)
4897 return ERROR_SUCCESS;
4899 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4900 msiobj_release(&view->hdr);
4902 return rc;
4905 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4907 MSIFILE *file;
4909 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4911 if (!lstrcmpW(file->File, filename))
4912 return file;
4915 return NULL;
4918 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4920 MSIPACKAGE *package = (MSIPACKAGE*)param;
4921 LPWSTR driver, driver_path, ptr;
4922 WCHAR outpath[MAX_PATH];
4923 MSIFILE *driver_file, *setup_file;
4924 LPCWSTR desc;
4925 DWORD len, usage;
4926 UINT r = ERROR_SUCCESS;
4928 static const WCHAR driver_fmt[] = {
4929 'D','r','i','v','e','r','=','%','s',0};
4930 static const WCHAR setup_fmt[] = {
4931 'S','e','t','u','p','=','%','s',0};
4932 static const WCHAR usage_fmt[] = {
4933 'F','i','l','e','U','s','a','g','e','=','1',0};
4935 desc = MSI_RecordGetString(rec, 3);
4937 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4938 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4940 if (!driver_file || !setup_file)
4942 ERR("ODBC Driver entry not found!\n");
4943 return ERROR_FUNCTION_FAILED;
4946 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4947 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4948 lstrlenW(usage_fmt) + 1;
4949 driver = msi_alloc(len * sizeof(WCHAR));
4950 if (!driver)
4951 return ERROR_OUTOFMEMORY;
4953 ptr = driver;
4954 lstrcpyW(ptr, desc);
4955 ptr += lstrlenW(ptr) + 1;
4957 sprintfW(ptr, driver_fmt, driver_file->FileName);
4958 ptr += lstrlenW(ptr) + 1;
4960 sprintfW(ptr, setup_fmt, setup_file->FileName);
4961 ptr += lstrlenW(ptr) + 1;
4963 lstrcpyW(ptr, usage_fmt);
4964 ptr += lstrlenW(ptr) + 1;
4965 *ptr = '\0';
4967 driver_path = strdupW(driver_file->TargetPath);
4968 ptr = strrchrW(driver_path, '\\');
4969 if (ptr) *ptr = '\0';
4971 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4972 NULL, ODBC_INSTALL_COMPLETE, &usage))
4974 ERR("Failed to install SQL driver!\n");
4975 r = ERROR_FUNCTION_FAILED;
4978 msi_free(driver);
4979 msi_free(driver_path);
4981 return r;
4984 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4986 MSIPACKAGE *package = (MSIPACKAGE*)param;
4987 LPWSTR translator, translator_path, ptr;
4988 WCHAR outpath[MAX_PATH];
4989 MSIFILE *translator_file, *setup_file;
4990 LPCWSTR desc;
4991 DWORD len, usage;
4992 UINT r = ERROR_SUCCESS;
4994 static const WCHAR translator_fmt[] = {
4995 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4996 static const WCHAR setup_fmt[] = {
4997 'S','e','t','u','p','=','%','s',0};
4999 desc = MSI_RecordGetString(rec, 3);
5001 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5002 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5004 if (!translator_file || !setup_file)
5006 ERR("ODBC Translator entry not found!\n");
5007 return ERROR_FUNCTION_FAILED;
5010 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5011 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5012 translator = msi_alloc(len * sizeof(WCHAR));
5013 if (!translator)
5014 return ERROR_OUTOFMEMORY;
5016 ptr = translator;
5017 lstrcpyW(ptr, desc);
5018 ptr += lstrlenW(ptr) + 1;
5020 sprintfW(ptr, translator_fmt, translator_file->FileName);
5021 ptr += lstrlenW(ptr) + 1;
5023 sprintfW(ptr, setup_fmt, setup_file->FileName);
5024 ptr += lstrlenW(ptr) + 1;
5025 *ptr = '\0';
5027 translator_path = strdupW(translator_file->TargetPath);
5028 ptr = strrchrW(translator_path, '\\');
5029 if (ptr) *ptr = '\0';
5031 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5032 NULL, ODBC_INSTALL_COMPLETE, &usage))
5034 ERR("Failed to install SQL translator!\n");
5035 r = ERROR_FUNCTION_FAILED;
5038 msi_free(translator);
5039 msi_free(translator_path);
5041 return r;
5044 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5046 LPWSTR attrs;
5047 LPCWSTR desc, driver;
5048 WORD request = ODBC_ADD_SYS_DSN;
5049 INT registration;
5050 DWORD len;
5051 UINT r = ERROR_SUCCESS;
5053 static const WCHAR attrs_fmt[] = {
5054 'D','S','N','=','%','s',0 };
5056 desc = MSI_RecordGetString(rec, 3);
5057 driver = MSI_RecordGetString(rec, 4);
5058 registration = MSI_RecordGetInteger(rec, 5);
5060 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5061 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5063 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5064 attrs = msi_alloc(len * sizeof(WCHAR));
5065 if (!attrs)
5066 return ERROR_OUTOFMEMORY;
5068 sprintfW(attrs, attrs_fmt, desc);
5069 attrs[len - 1] = '\0';
5071 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5073 ERR("Failed to install SQL data source!\n");
5074 r = ERROR_FUNCTION_FAILED;
5077 msi_free(attrs);
5079 return r;
5082 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5084 UINT rc;
5085 MSIQUERY *view;
5087 static const WCHAR driver_query[] = {
5088 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5089 'O','D','B','C','D','r','i','v','e','r',0 };
5091 static const WCHAR translator_query[] = {
5092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5093 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5095 static const WCHAR source_query[] = {
5096 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5097 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5099 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5100 if (rc != ERROR_SUCCESS)
5101 return ERROR_SUCCESS;
5103 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5104 msiobj_release(&view->hdr);
5106 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5107 if (rc != ERROR_SUCCESS)
5108 return ERROR_SUCCESS;
5110 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5111 msiobj_release(&view->hdr);
5113 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5114 if (rc != ERROR_SUCCESS)
5115 return ERROR_SUCCESS;
5117 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5118 msiobj_release(&view->hdr);
5120 return rc;
5123 #define ENV_ACT_SETALWAYS 0x1
5124 #define ENV_ACT_SETABSENT 0x2
5125 #define ENV_ACT_REMOVE 0x4
5126 #define ENV_ACT_REMOVEMATCH 0x8
5128 #define ENV_MOD_MACHINE 0x20000000
5129 #define ENV_MOD_APPEND 0x40000000
5130 #define ENV_MOD_PREFIX 0x80000000
5131 #define ENV_MOD_MASK 0xC0000000
5133 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5135 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5137 LPCWSTR cptr = *name;
5138 LPCWSTR ptr = *value;
5140 static const WCHAR prefix[] = {'[','~',']',0};
5141 static const int prefix_len = 3;
5143 *flags = 0;
5144 while (*cptr)
5146 if (*cptr == '=')
5147 *flags |= ENV_ACT_SETALWAYS;
5148 else if (*cptr == '+')
5149 *flags |= ENV_ACT_SETABSENT;
5150 else if (*cptr == '-')
5151 *flags |= ENV_ACT_REMOVE;
5152 else if (*cptr == '!')
5153 *flags |= ENV_ACT_REMOVEMATCH;
5154 else if (*cptr == '*')
5155 *flags |= ENV_MOD_MACHINE;
5156 else
5157 break;
5159 cptr++;
5160 (*name)++;
5163 if (!*cptr)
5165 ERR("Missing environment variable\n");
5166 return ERROR_FUNCTION_FAILED;
5169 if (!strncmpW(ptr, prefix, prefix_len))
5171 *flags |= ENV_MOD_APPEND;
5172 *value += lstrlenW(prefix);
5174 else if (lstrlenW(*value) >= prefix_len)
5176 ptr += lstrlenW(ptr) - prefix_len;
5177 if (!lstrcmpW(ptr, prefix))
5179 *flags |= ENV_MOD_PREFIX;
5180 /* the "[~]" will be removed by deformat_string */;
5184 if (!*flags ||
5185 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5186 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5187 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5188 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5190 ERR("Invalid flags: %08x\n", *flags);
5191 return ERROR_FUNCTION_FAILED;
5194 return ERROR_SUCCESS;
5197 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5199 MSIPACKAGE *package = param;
5200 LPCWSTR name, value;
5201 LPWSTR data = NULL, newval = NULL;
5202 LPWSTR deformatted = NULL, ptr;
5203 DWORD flags, type, size;
5204 LONG res;
5205 HKEY env = NULL, root;
5206 LPCWSTR environment;
5208 static const WCHAR user_env[] =
5209 {'E','n','v','i','r','o','n','m','e','n','t',0};
5210 static const WCHAR machine_env[] =
5211 {'S','y','s','t','e','m','\\',
5212 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5213 'C','o','n','t','r','o','l','\\',
5214 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5215 'E','n','v','i','r','o','n','m','e','n','t',0};
5216 static const WCHAR semicolon[] = {';',0};
5218 name = MSI_RecordGetString(rec, 2);
5219 value = MSI_RecordGetString(rec, 3);
5221 res = env_set_flags(&name, &value, &flags);
5222 if (res != ERROR_SUCCESS)
5223 goto done;
5225 deformat_string(package, value, &deformatted);
5226 if (!deformatted)
5228 res = ERROR_OUTOFMEMORY;
5229 goto done;
5232 value = deformatted;
5234 if (flags & ENV_MOD_MACHINE)
5236 environment = machine_env;
5237 root = HKEY_LOCAL_MACHINE;
5239 else
5241 environment = user_env;
5242 root = HKEY_CURRENT_USER;
5245 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5246 KEY_ALL_ACCESS, NULL, &env, NULL);
5247 if (res != ERROR_SUCCESS)
5248 goto done;
5250 if (flags & ENV_ACT_REMOVE)
5251 FIXME("Not removing environment variable on uninstall!\n");
5253 size = 0;
5254 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5255 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5256 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5257 goto done;
5259 if (res != ERROR_FILE_NOT_FOUND)
5261 if (flags & ENV_ACT_SETABSENT)
5263 res = ERROR_SUCCESS;
5264 goto done;
5267 data = msi_alloc(size);
5268 if (!data)
5270 RegCloseKey(env);
5271 return ERROR_OUTOFMEMORY;
5274 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5275 if (res != ERROR_SUCCESS)
5276 goto done;
5278 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5280 res = RegDeleteKeyW(env, name);
5281 goto done;
5284 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5285 newval = msi_alloc(size);
5286 ptr = newval;
5287 if (!newval)
5289 res = ERROR_OUTOFMEMORY;
5290 goto done;
5293 if (!(flags & ENV_MOD_MASK))
5294 lstrcpyW(newval, value);
5295 else
5297 if (flags & ENV_MOD_PREFIX)
5299 lstrcpyW(newval, value);
5300 lstrcatW(newval, semicolon);
5301 ptr = newval + lstrlenW(value) + 1;
5304 lstrcpyW(ptr, data);
5306 if (flags & ENV_MOD_APPEND)
5308 lstrcatW(newval, semicolon);
5309 lstrcatW(newval, value);
5313 else
5315 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5316 newval = msi_alloc(size);
5317 if (!newval)
5319 res = ERROR_OUTOFMEMORY;
5320 goto done;
5323 lstrcpyW(newval, value);
5326 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5327 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5329 done:
5330 if (env) RegCloseKey(env);
5331 msi_free(deformatted);
5332 msi_free(data);
5333 msi_free(newval);
5334 return res;
5337 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5339 UINT rc;
5340 MSIQUERY * view;
5341 static const WCHAR ExecSeqQuery[] =
5342 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5343 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5344 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5345 if (rc != ERROR_SUCCESS)
5346 return ERROR_SUCCESS;
5348 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5349 msiobj_release(&view->hdr);
5351 return rc;
5354 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5356 typedef struct
5358 struct list entry;
5359 LPWSTR sourcename;
5360 LPWSTR destname;
5361 LPWSTR source;
5362 LPWSTR dest;
5363 } FILE_LIST;
5365 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5367 BOOL ret;
5369 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5370 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5372 WARN("Source or dest is directory, not moving\n");
5373 return FALSE;
5376 if (options == msidbMoveFileOptionsMove)
5378 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5379 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5380 if (!ret)
5382 WARN("MoveFile failed: %d\n", GetLastError());
5383 return FALSE;
5386 else
5388 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5389 ret = CopyFileW(source, dest, FALSE);
5390 if (!ret)
5392 WARN("CopyFile failed: %d\n", GetLastError());
5393 return FALSE;
5397 return TRUE;
5400 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5402 LPWSTR path, ptr;
5403 DWORD dirlen, pathlen;
5405 ptr = strrchrW(wildcard, '\\');
5406 dirlen = ptr - wildcard + 1;
5408 pathlen = dirlen + lstrlenW(filename) + 1;
5409 path = msi_alloc(pathlen * sizeof(WCHAR));
5411 lstrcpynW(path, wildcard, dirlen + 1);
5412 lstrcatW(path, filename);
5414 return path;
5417 static void free_file_entry(FILE_LIST *file)
5419 msi_free(file->source);
5420 msi_free(file->dest);
5421 msi_free(file);
5424 static void free_list(FILE_LIST *list)
5426 while (!list_empty(&list->entry))
5428 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5430 list_remove(&file->entry);
5431 free_file_entry(file);
5435 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5437 FILE_LIST *new, *file;
5438 LPWSTR ptr, filename;
5439 DWORD size;
5441 new = msi_alloc_zero(sizeof(FILE_LIST));
5442 if (!new)
5443 return FALSE;
5445 new->source = strdupW(source);
5446 ptr = strrchrW(dest, '\\') + 1;
5447 filename = strrchrW(new->source, '\\') + 1;
5449 new->sourcename = filename;
5451 if (*ptr)
5452 new->destname = ptr;
5453 else
5454 new->destname = new->sourcename;
5456 size = (ptr - dest) + lstrlenW(filename) + 1;
5457 new->dest = msi_alloc(size * sizeof(WCHAR));
5458 if (!new->dest)
5460 free_file_entry(new);
5461 return FALSE;
5464 lstrcpynW(new->dest, dest, ptr - dest + 1);
5465 lstrcatW(new->dest, filename);
5467 if (list_empty(&files->entry))
5469 list_add_head(&files->entry, &new->entry);
5470 return TRUE;
5473 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5475 if (lstrcmpW(source, file->source) < 0)
5477 list_add_before(&file->entry, &new->entry);
5478 return TRUE;
5482 list_add_after(&file->entry, &new->entry);
5483 return TRUE;
5486 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5488 WIN32_FIND_DATAW wfd;
5489 HANDLE hfile;
5490 LPWSTR path;
5491 BOOL res;
5492 FILE_LIST files, *file;
5493 DWORD size;
5495 hfile = FindFirstFileW(source, &wfd);
5496 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5498 list_init(&files.entry);
5500 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5502 if (is_dot_dir(wfd.cFileName)) continue;
5504 path = wildcard_to_file(source, wfd.cFileName);
5505 if (!path)
5507 res = FALSE;
5508 goto done;
5511 add_wildcard(&files, path, dest);
5512 msi_free(path);
5515 /* no files match the wildcard */
5516 if (list_empty(&files.entry))
5517 goto done;
5519 /* only the first wildcard match gets renamed to dest */
5520 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5521 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5522 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5523 if (!file->dest)
5525 res = FALSE;
5526 goto done;
5529 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5531 while (!list_empty(&files.entry))
5533 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5535 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5537 list_remove(&file->entry);
5538 free_file_entry(file);
5541 res = TRUE;
5543 done:
5544 free_list(&files);
5545 FindClose(hfile);
5546 return res;
5549 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5551 MSIPACKAGE *package = param;
5552 MSICOMPONENT *comp;
5553 LPCWSTR sourcename;
5554 LPWSTR destname = NULL;
5555 LPWSTR sourcedir = NULL, destdir = NULL;
5556 LPWSTR source = NULL, dest = NULL;
5557 int options;
5558 DWORD size;
5559 BOOL ret, wildcards;
5561 static const WCHAR backslash[] = {'\\',0};
5563 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5564 if (!comp || !comp->Enabled ||
5565 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5567 TRACE("Component not set for install, not moving file\n");
5568 return ERROR_SUCCESS;
5571 sourcename = MSI_RecordGetString(rec, 3);
5572 options = MSI_RecordGetInteger(rec, 7);
5574 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5575 if (!sourcedir)
5576 goto done;
5578 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5579 if (!destdir)
5580 goto done;
5582 if (!sourcename)
5584 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5585 goto done;
5587 source = strdupW(sourcedir);
5588 if (!source)
5589 goto done;
5591 else
5593 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5594 source = msi_alloc(size * sizeof(WCHAR));
5595 if (!source)
5596 goto done;
5598 lstrcpyW(source, sourcedir);
5599 if (source[lstrlenW(source) - 1] != '\\')
5600 lstrcatW(source, backslash);
5601 lstrcatW(source, sourcename);
5604 wildcards = strchrW(source, '*') || strchrW(source, '?');
5606 if (MSI_RecordIsNull(rec, 4))
5608 if (!wildcards)
5610 destname = strdupW(sourcename);
5611 if (!destname)
5612 goto done;
5615 else
5617 destname = strdupW(MSI_RecordGetString(rec, 4));
5618 if (destname)
5619 reduce_to_longfilename(destname);
5622 size = 0;
5623 if (destname)
5624 size = lstrlenW(destname);
5626 size += lstrlenW(destdir) + 2;
5627 dest = msi_alloc(size * sizeof(WCHAR));
5628 if (!dest)
5629 goto done;
5631 lstrcpyW(dest, destdir);
5632 if (dest[lstrlenW(dest) - 1] != '\\')
5633 lstrcatW(dest, backslash);
5635 if (destname)
5636 lstrcatW(dest, destname);
5638 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5640 ret = CreateDirectoryW(destdir, NULL);
5641 if (!ret)
5643 WARN("CreateDirectory failed: %d\n", GetLastError());
5644 return ERROR_SUCCESS;
5648 if (!wildcards)
5649 msi_move_file(source, dest, options);
5650 else
5651 move_files_wildcard(source, dest, options);
5653 done:
5654 msi_free(sourcedir);
5655 msi_free(destdir);
5656 msi_free(destname);
5657 msi_free(source);
5658 msi_free(dest);
5660 return ERROR_SUCCESS;
5663 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5665 UINT rc;
5666 MSIQUERY *view;
5668 static const WCHAR ExecSeqQuery[] =
5669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5670 '`','M','o','v','e','F','i','l','e','`',0};
5672 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5673 if (rc != ERROR_SUCCESS)
5674 return ERROR_SUCCESS;
5676 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5677 msiobj_release(&view->hdr);
5679 return rc;
5682 typedef struct tagMSIASSEMBLY
5684 struct list entry;
5685 MSICOMPONENT *component;
5686 MSIFEATURE *feature;
5687 MSIFILE *file;
5688 LPWSTR manifest;
5689 LPWSTR application;
5690 DWORD attributes;
5691 BOOL installed;
5692 } MSIASSEMBLY;
5694 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5695 DWORD dwReserved);
5696 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5697 LPVOID pvReserved, HMODULE *phModDll);
5699 static BOOL init_functionpointers(void)
5701 HRESULT hr;
5702 HMODULE hfusion;
5703 HMODULE hmscoree;
5705 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5707 hmscoree = LoadLibraryA("mscoree.dll");
5708 if (!hmscoree)
5710 WARN("mscoree.dll not available\n");
5711 return FALSE;
5714 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5715 if (!pLoadLibraryShim)
5717 WARN("LoadLibraryShim not available\n");
5718 FreeLibrary(hmscoree);
5719 return FALSE;
5722 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5723 if (FAILED(hr))
5725 WARN("fusion.dll not available\n");
5726 FreeLibrary(hmscoree);
5727 return FALSE;
5730 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5732 FreeLibrary(hmscoree);
5733 return TRUE;
5736 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5737 LPWSTR path)
5739 IAssemblyCache *cache;
5740 HRESULT hr;
5741 UINT r = ERROR_FUNCTION_FAILED;
5743 TRACE("installing assembly: %s\n", debugstr_w(path));
5745 if (assembly->feature)
5746 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5748 if (assembly->manifest)
5749 FIXME("Manifest unhandled\n");
5751 if (assembly->application)
5753 FIXME("Assembly should be privately installed\n");
5754 return ERROR_SUCCESS;
5757 if (assembly->attributes == msidbAssemblyAttributesWin32)
5759 FIXME("Win32 assemblies not handled\n");
5760 return ERROR_SUCCESS;
5763 hr = pCreateAssemblyCache(&cache, 0);
5764 if (FAILED(hr))
5765 goto done;
5767 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5768 if (FAILED(hr))
5769 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5771 r = ERROR_SUCCESS;
5773 done:
5774 IAssemblyCache_Release(cache);
5775 return r;
5778 typedef struct tagASSEMBLY_LIST
5780 MSIPACKAGE *package;
5781 IAssemblyCache *cache;
5782 struct list *assemblies;
5783 } ASSEMBLY_LIST;
5785 typedef struct tagASSEMBLY_NAME
5787 LPWSTR name;
5788 LPWSTR version;
5789 LPWSTR culture;
5790 LPWSTR pubkeytoken;
5791 } ASSEMBLY_NAME;
5793 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5795 ASSEMBLY_NAME *asmname = (ASSEMBLY_NAME *)param;
5796 LPCWSTR name = MSI_RecordGetString(rec, 2);
5797 LPWSTR val = msi_dup_record_field(rec, 3);
5799 static const WCHAR Name[] = {'N','a','m','e',0};
5800 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5801 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5802 static const WCHAR PublicKeyToken[] = {
5803 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5805 if (!lstrcmpW(name, Name))
5806 asmname->name = val;
5807 else if (!lstrcmpW(name, Version))
5808 asmname->version = val;
5809 else if (!lstrcmpW(name, Culture))
5810 asmname->culture = val;
5811 else if (!lstrcmpW(name, PublicKeyToken))
5812 asmname->pubkeytoken = val;
5813 else
5814 msi_free(val);
5816 return ERROR_SUCCESS;
5819 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5821 if (!*str)
5823 *size = lstrlenW(append) + 1;
5824 *str = msi_alloc((*size) * sizeof(WCHAR));
5825 lstrcpyW(*str, append);
5826 return;
5829 (*size) += lstrlenW(append);
5830 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5831 lstrcatW(*str, append);
5834 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5835 MSICOMPONENT *comp)
5837 ASSEMBLY_INFO asminfo;
5838 ASSEMBLY_NAME name;
5839 MSIQUERY *view;
5840 LPWSTR disp;
5841 DWORD size;
5842 BOOL found;
5843 UINT r;
5845 static const WCHAR separator[] = {',',' ',0};
5846 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5847 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5848 static const WCHAR PublicKeyToken[] = {
5849 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5850 static const WCHAR query[] = {
5851 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5852 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5853 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5854 '=','\'','%','s','\'',0};
5856 disp = NULL;
5857 found = FALSE;
5858 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5859 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5861 r = MSI_OpenQuery(db, &view, query, comp->Component);
5862 if (r != ERROR_SUCCESS)
5863 return ERROR_SUCCESS;
5865 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5866 msiobj_release(&view->hdr);
5868 if (!name.name)
5870 ERR("No assembly name specified!\n");
5871 goto done;
5874 append_str(&disp, &size, name.name);
5876 if (name.version)
5878 append_str(&disp, &size, separator);
5879 append_str(&disp, &size, Version);
5880 append_str(&disp, &size, name.version);
5883 if (name.culture)
5885 append_str(&disp, &size, separator);
5886 append_str(&disp, &size, Culture);
5887 append_str(&disp, &size, name.culture);
5890 if (name.pubkeytoken)
5892 append_str(&disp, &size, separator);
5893 append_str(&disp, &size, PublicKeyToken);
5894 append_str(&disp, &size, name.pubkeytoken);
5897 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
5898 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
5899 disp, &asminfo);
5900 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
5902 done:
5903 msiobj_release(&view->hdr);
5904 msi_free(disp);
5905 msi_free(name.name);
5906 msi_free(name.version);
5907 msi_free(name.culture);
5908 msi_free(name.pubkeytoken);
5910 return found;
5913 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5915 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5916 MSIASSEMBLY *assembly;
5918 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5919 if (!assembly)
5920 return ERROR_OUTOFMEMORY;
5922 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5924 if (!assembly->component || !assembly->component->Enabled ||
5925 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5927 TRACE("Component not set for install, not publishing assembly\n");
5928 msi_free(assembly);
5929 return ERROR_SUCCESS;
5932 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5933 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5935 if (!assembly->file)
5937 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5938 return ERROR_FUNCTION_FAILED;
5941 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5942 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5943 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5944 assembly->installed = check_assembly_installed(list->package->db,
5945 list->cache,
5946 assembly->component);
5948 list_add_head(list->assemblies, &assembly->entry);
5949 return ERROR_SUCCESS;
5952 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5954 IAssemblyCache *cache = NULL;
5955 ASSEMBLY_LIST list;
5956 MSIQUERY *view;
5957 HRESULT hr;
5958 UINT r;
5960 static const WCHAR query[] =
5961 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5962 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5964 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5965 if (r != ERROR_SUCCESS)
5966 return ERROR_SUCCESS;
5968 hr = pCreateAssemblyCache(&cache, 0);
5969 if (FAILED(hr))
5970 return ERROR_FUNCTION_FAILED;
5972 list.package = package;
5973 list.cache = cache;
5974 list.assemblies = assemblies;
5976 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5977 msiobj_release(&view->hdr);
5979 IAssemblyCache_Release(cache);
5981 return r;
5984 static void free_assemblies(struct list *assemblies)
5986 struct list *item, *cursor;
5988 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5990 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5992 list_remove(&assembly->entry);
5993 msi_free(assembly->application);
5994 msi_free(assembly->manifest);
5995 msi_free(assembly);
5999 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6001 MSIASSEMBLY *assembly;
6003 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6005 if (!lstrcmpW(assembly->file->File, file))
6007 *out = assembly;
6008 return TRUE;
6012 return FALSE;
6015 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6016 LPWSTR *path, DWORD *attrs, PVOID user)
6018 MSIASSEMBLY *assembly;
6019 WCHAR temppath[MAX_PATH];
6020 struct list *assemblies = (struct list *)user;
6021 UINT r;
6023 if (!find_assembly(assemblies, file, &assembly))
6024 return FALSE;
6026 GetTempPathW(MAX_PATH, temppath);
6027 PathAddBackslashW(temppath);
6028 lstrcatW(temppath, assembly->file->FileName);
6030 if (action == MSICABEXTRACT_BEGINEXTRACT)
6032 if (assembly->installed)
6033 return FALSE;
6035 *path = strdupW(temppath);
6036 *attrs = assembly->file->Attributes;
6038 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6040 assembly->installed = TRUE;
6042 r = install_assembly(package, assembly, temppath);
6043 if (r != ERROR_SUCCESS)
6044 ERR("Failed to install assembly\n");
6047 return TRUE;
6050 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6052 UINT r;
6053 struct list assemblies = LIST_INIT(assemblies);
6054 MSIASSEMBLY *assembly;
6055 MSIMEDIAINFO *mi;
6057 if (!init_functionpointers() || !pCreateAssemblyCache)
6058 return ERROR_FUNCTION_FAILED;
6060 r = load_assemblies(package, &assemblies);
6061 if (r != ERROR_SUCCESS)
6062 goto done;
6064 if (list_empty(&assemblies))
6065 goto done;
6067 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6068 if (!mi)
6070 r = ERROR_OUTOFMEMORY;
6071 goto done;
6074 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6076 if (assembly->installed && !mi->is_continuous)
6077 continue;
6079 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6080 (assembly->file->IsCompressed && !mi->is_extracted))
6082 MSICABDATA data;
6084 r = ready_media(package, assembly->file, mi);
6085 if (r != ERROR_SUCCESS)
6087 ERR("Failed to ready media\n");
6088 break;
6091 data.mi = mi;
6092 data.package = package;
6093 data.cb = installassembly_cb;
6094 data.user = &assemblies;
6096 if (assembly->file->IsCompressed &&
6097 !msi_cabextract(package, mi, &data))
6099 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6100 r = ERROR_FUNCTION_FAILED;
6101 break;
6105 if (!assembly->file->IsCompressed)
6107 LPWSTR source = resolve_file_source(package, assembly->file);
6109 r = install_assembly(package, assembly, source);
6110 if (r != ERROR_SUCCESS)
6111 ERR("Failed to install assembly\n");
6113 msi_free(source);
6116 /* FIXME: write Installer assembly reg values */
6119 done:
6120 free_assemblies(&assemblies);
6121 return r;
6124 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6125 LPCSTR action, LPCWSTR table )
6127 static const WCHAR query[] = {
6128 'S','E','L','E','C','T',' ','*',' ',
6129 'F','R','O','M',' ','`','%','s','`',0 };
6130 MSIQUERY *view = NULL;
6131 DWORD count = 0;
6132 UINT r;
6134 r = MSI_OpenQuery( package->db, &view, query, table );
6135 if (r == ERROR_SUCCESS)
6137 r = MSI_IterateRecords(view, &count, NULL, package);
6138 msiobj_release(&view->hdr);
6141 if (count)
6142 FIXME("%s -> %u ignored %s table values\n",
6143 action, count, debugstr_w(table));
6145 return ERROR_SUCCESS;
6148 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6150 TRACE("%p\n", package);
6151 return ERROR_SUCCESS;
6154 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6156 static const WCHAR table[] =
6157 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6158 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6161 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6163 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6164 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6167 static UINT ACTION_BindImage( MSIPACKAGE *package )
6169 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6170 return msi_unimplemented_action_stub( package, "BindImage", table );
6173 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6175 static const WCHAR table[] = {
6176 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6177 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6180 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6182 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6183 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6186 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6188 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6189 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6192 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6194 static const WCHAR table[] = {
6195 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6196 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6198 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6200 static const WCHAR table[] = {
6201 'P','r','o','d','u','c','t','I','D',0 };
6202 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6205 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6207 static const WCHAR table[] = {
6208 'E','n','v','i','r','o','n','m','e','n','t',0 };
6209 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6212 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6214 static const WCHAR table[] = {
6215 'M','s','i','A','s','s','e','m','b','l','y',0 };
6216 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6219 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6221 static const WCHAR table[] = { 'F','o','n','t',0 };
6222 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6225 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6227 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6228 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6231 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6233 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6234 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6237 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6239 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6240 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6243 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6245 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6246 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6249 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6251 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6252 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6255 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6257 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6258 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6261 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6263 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6264 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6267 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6269 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6270 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6273 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6275 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6276 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6279 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6281 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6282 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6285 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6287 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6288 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6291 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6293 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6294 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6297 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6299 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6300 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6303 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6305 static const WCHAR table[] = { 'M','I','M','E',0 };
6306 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6309 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6311 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6312 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6315 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6317 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6318 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6321 static const struct _actions StandardActions[] = {
6322 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6323 { szAppSearch, ACTION_AppSearch },
6324 { szBindImage, ACTION_BindImage },
6325 { szCCPSearch, ACTION_CCPSearch },
6326 { szCostFinalize, ACTION_CostFinalize },
6327 { szCostInitialize, ACTION_CostInitialize },
6328 { szCreateFolders, ACTION_CreateFolders },
6329 { szCreateShortcuts, ACTION_CreateShortcuts },
6330 { szDeleteServices, ACTION_DeleteServices },
6331 { szDisableRollback, NULL },
6332 { szDuplicateFiles, ACTION_DuplicateFiles },
6333 { szExecuteAction, ACTION_ExecuteAction },
6334 { szFileCost, ACTION_FileCost },
6335 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6336 { szForceReboot, ACTION_ForceReboot },
6337 { szInstallAdminPackage, NULL },
6338 { szInstallExecute, ACTION_InstallExecute },
6339 { szInstallExecuteAgain, ACTION_InstallExecute },
6340 { szInstallFiles, ACTION_InstallFiles},
6341 { szInstallFinalize, ACTION_InstallFinalize },
6342 { szInstallInitialize, ACTION_InstallInitialize },
6343 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6344 { szInstallValidate, ACTION_InstallValidate },
6345 { szIsolateComponents, ACTION_IsolateComponents },
6346 { szLaunchConditions, ACTION_LaunchConditions },
6347 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6348 { szMoveFiles, ACTION_MoveFiles },
6349 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6350 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6351 { szInstallODBC, ACTION_InstallODBC },
6352 { szInstallServices, ACTION_InstallServices },
6353 { szPatchFiles, ACTION_PatchFiles },
6354 { szProcessComponents, ACTION_ProcessComponents },
6355 { szPublishComponents, ACTION_PublishComponents },
6356 { szPublishFeatures, ACTION_PublishFeatures },
6357 { szPublishProduct, ACTION_PublishProduct },
6358 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6359 { szRegisterComPlus, ACTION_RegisterComPlus},
6360 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6361 { szRegisterFonts, ACTION_RegisterFonts },
6362 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6363 { szRegisterProduct, ACTION_RegisterProduct },
6364 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6365 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6366 { szRegisterUser, ACTION_RegisterUser },
6367 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6368 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6369 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6370 { szRemoveFiles, ACTION_RemoveFiles },
6371 { szRemoveFolders, ACTION_RemoveFolders },
6372 { szRemoveIniValues, ACTION_RemoveIniValues },
6373 { szRemoveODBC, ACTION_RemoveODBC },
6374 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6375 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6376 { szResolveSource, ACTION_ResolveSource },
6377 { szRMCCPSearch, ACTION_RMCCPSearch },
6378 { szScheduleReboot, NULL },
6379 { szSelfRegModules, ACTION_SelfRegModules },
6380 { szSelfUnregModules, ACTION_SelfUnregModules },
6381 { szSetODBCFolders, NULL },
6382 { szStartServices, ACTION_StartServices },
6383 { szStopServices, ACTION_StopServices },
6384 { szUnpublishComponents, ACTION_UnpublishComponents },
6385 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6386 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6387 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6388 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6389 { szUnregisterFonts, ACTION_UnregisterFonts },
6390 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6391 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6392 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6393 { szValidateProductID, ACTION_ValidateProductID },
6394 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6395 { szWriteIniValues, ACTION_WriteIniValues },
6396 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6397 { NULL, NULL },
6400 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6401 UINT* rc, BOOL force )
6403 BOOL ret = FALSE;
6404 BOOL run = force;
6405 int i;
6407 if (!run && !package->script->CurrentlyScripting)
6408 run = TRUE;
6410 if (!run)
6412 if (strcmpW(action,szInstallFinalize) == 0 ||
6413 strcmpW(action,szInstallExecute) == 0 ||
6414 strcmpW(action,szInstallExecuteAgain) == 0)
6415 run = TRUE;
6418 i = 0;
6419 while (StandardActions[i].action != NULL)
6421 if (strcmpW(StandardActions[i].action, action)==0)
6423 if (!run)
6425 ui_actioninfo(package, action, TRUE, 0);
6426 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6427 ui_actioninfo(package, action, FALSE, *rc);
6429 else
6431 ui_actionstart(package, action);
6432 if (StandardActions[i].handler)
6434 *rc = StandardActions[i].handler(package);
6436 else
6438 FIXME("unhandled standard action %s\n",debugstr_w(action));
6439 *rc = ERROR_SUCCESS;
6442 ret = TRUE;
6443 break;
6445 i++;
6447 return ret;