msi: Don't enable a feature's components if that feature is disabled.
[wine/multimedia.git] / dlls / msi / action.c
blob0e931a1f6c1842fb72a412fc774a2a888790a30c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * Prototypes
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137 'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163 'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211 'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223 'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
236 struct _actions {
237 LPCWSTR action;
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
243 * helper functions
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
253 MSIRECORD * row;
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
256 if (!row)
257 return;
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
263 UINT rc)
265 MSIRECORD * row;
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268 '%','s', '.',0};
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272 '%','i','.',0};
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 WCHAR message[1024];
276 WCHAR timet[0x100];
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279 if (start)
280 sprintfW(message,template_s,timet,action);
281 else
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
293 LPCWSTR ptr,ptr2;
294 BOOL quote;
295 DWORD len;
296 LPWSTR prop = NULL, val = NULL;
298 if (!szCommandLine)
299 return ERROR_SUCCESS;
301 ptr = szCommandLine;
303 while (*ptr)
305 if (*ptr==' ')
307 ptr++;
308 continue;
311 TRACE("Looking at %s\n",debugstr_w(ptr));
313 ptr2 = strchrW(ptr,'=');
314 if (!ptr2)
316 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
317 break;
320 quote = FALSE;
322 len = ptr2-ptr;
323 prop = msi_alloc((len+1)*sizeof(WCHAR));
324 memcpy(prop,ptr,len*sizeof(WCHAR));
325 prop[len]=0;
326 ptr2++;
328 len = 0;
329 ptr = ptr2;
330 while (*ptr && (quote || (!quote && *ptr!=' ')))
332 if (*ptr == '"')
333 quote = !quote;
334 ptr++;
335 len++;
338 if (*ptr2=='"')
340 ptr2++;
341 len -= 2;
343 val = msi_alloc((len+1)*sizeof(WCHAR));
344 memcpy(val,ptr2,len*sizeof(WCHAR));
345 val[len] = 0;
347 if (lstrlenW(prop) > 0)
349 TRACE("Found commandline property (%s) = (%s)\n",
350 debugstr_w(prop), debugstr_w(val));
351 MSI_SetPropertyW(package,prop,val);
353 msi_free(val);
354 msi_free(prop);
357 return ERROR_SUCCESS;
361 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
363 LPCWSTR pc;
364 LPWSTR p, *ret = NULL;
365 UINT count = 0;
367 if (!str)
368 return ret;
370 /* count the number of substrings */
371 for ( pc = str, count = 0; pc; count++ )
373 pc = strchrW( pc, sep );
374 if (pc)
375 pc++;
378 /* allocate space for an array of substring pointers and the substrings */
379 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
380 (lstrlenW(str)+1) * sizeof(WCHAR) );
381 if (!ret)
382 return ret;
384 /* copy the string and set the pointers */
385 p = (LPWSTR) &ret[count+1];
386 lstrcpyW( p, str );
387 for( count = 0; (ret[count] = p); count++ )
389 p = strchrW( p, sep );
390 if (p)
391 *p++ = 0;
394 return ret;
397 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
399 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
400 LPWSTR prod_code, patch_product;
401 UINT ret;
403 prod_code = msi_dup_property( package, szProductCode );
404 patch_product = msi_get_suminfo_product( patch );
406 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
408 if ( strstrW( patch_product, prod_code ) )
409 ret = ERROR_SUCCESS;
410 else
411 ret = ERROR_FUNCTION_FAILED;
413 msi_free( patch_product );
414 msi_free( prod_code );
416 return ret;
419 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
420 MSIDATABASE *patch_db, LPCWSTR name )
422 UINT ret = ERROR_FUNCTION_FAILED;
423 IStorage *stg = NULL;
424 HRESULT r;
426 TRACE("%p %s\n", package, debugstr_w(name) );
428 if (*name++ != ':')
430 ERR("expected a colon in %s\n", debugstr_w(name));
431 return ERROR_FUNCTION_FAILED;
434 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
435 if (SUCCEEDED(r))
437 ret = msi_check_transform_applicable( package, stg );
438 if (ret == ERROR_SUCCESS)
439 msi_table_apply_transform( package->db, stg );
440 else
441 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
442 IStorage_Release( stg );
444 else
445 ERR("failed to open substorage %s\n", debugstr_w(name));
447 return ERROR_SUCCESS;
450 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
452 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
453 LPWSTR guid_list, *guids, product_code;
454 UINT i, ret = ERROR_FUNCTION_FAILED;
456 product_code = msi_dup_property( package, szProdCode );
457 if (!product_code)
459 /* FIXME: the property ProductCode should be written into the DB somewhere */
460 ERR("no product code to check\n");
461 return ERROR_SUCCESS;
464 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
465 guids = msi_split_string( guid_list, ';' );
466 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
468 if (!lstrcmpW( guids[i], product_code ))
469 ret = ERROR_SUCCESS;
471 msi_free( guids );
472 msi_free( guid_list );
473 msi_free( product_code );
475 return ret;
478 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
480 MSISUMMARYINFO *si;
481 LPWSTR str, *substorage;
482 UINT i, r = ERROR_SUCCESS;
484 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
485 if (!si)
486 return ERROR_FUNCTION_FAILED;
488 msi_check_patch_applicable( package, si );
490 /* enumerate the substorage */
491 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
492 substorage = msi_split_string( str, ';' );
493 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
494 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
495 msi_free( substorage );
496 msi_free( str );
498 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
500 msiobj_release( &si->hdr );
502 return r;
505 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
507 MSIDATABASE *patch_db = NULL;
508 UINT r;
510 TRACE("%p %s\n", package, debugstr_w( file ) );
512 /* FIXME:
513 * We probably want to make sure we only open a patch collection here.
514 * Patch collections (.msp) and databases (.msi) have different GUIDs
515 * but currently MSI_OpenDatabaseW will accept both.
517 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
518 if ( r != ERROR_SUCCESS )
520 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
521 return r;
524 msi_parse_patch_summary( package, patch_db );
527 * There might be a CAB file in the patch package,
528 * so append it to the list of storage to search for streams.
530 append_storage_to_db( package->db, patch_db->storage );
532 msiobj_release( &patch_db->hdr );
534 return ERROR_SUCCESS;
537 /* get the PATCH property, and apply all the patches it specifies */
538 static UINT msi_apply_patches( MSIPACKAGE *package )
540 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
541 LPWSTR patch_list, *patches;
542 UINT i, r = ERROR_SUCCESS;
544 patch_list = msi_dup_property( package, szPatch );
546 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
548 patches = msi_split_string( patch_list, ';' );
549 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
550 r = msi_apply_patch_package( package, patches[i] );
552 msi_free( patches );
553 msi_free( patch_list );
555 return r;
558 static UINT msi_apply_transforms( MSIPACKAGE *package )
560 static const WCHAR szTransforms[] = {
561 'T','R','A','N','S','F','O','R','M','S',0 };
562 LPWSTR xform_list, *xforms;
563 UINT i, r = ERROR_SUCCESS;
565 xform_list = msi_dup_property( package, szTransforms );
566 xforms = msi_split_string( xform_list, ';' );
568 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
570 if (xforms[i][0] == ':')
571 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
572 else
573 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
576 msi_free( xforms );
577 msi_free( xform_list );
579 return r;
582 static BOOL ui_sequence_exists( MSIPACKAGE *package )
584 MSIQUERY *view;
585 UINT rc;
587 static const WCHAR ExecSeqQuery [] =
588 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l',
590 'U','I','S','e','q','u','e','n','c','e','`',
591 ' ','W','H','E','R','E',' ',
592 '`','S','e','q','u','e','n','c','e','`',' ',
593 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
594 '`','S','e','q','u','e','n','c','e','`',0};
596 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
597 if (rc == ERROR_SUCCESS)
599 msiobj_release(&view->hdr);
600 return TRUE;
603 return FALSE;
606 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
608 LPWSTR p, db;
609 LPWSTR source, check;
610 DWORD len;
612 static const WCHAR szOriginalDatabase[] =
613 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
615 db = msi_dup_property( package, szOriginalDatabase );
616 if (!db)
617 return ERROR_OUTOFMEMORY;
619 p = strrchrW( db, '\\' );
620 if (!p)
622 p = strrchrW( db, '/' );
623 if (!p)
625 msi_free(db);
626 return ERROR_SUCCESS;
630 len = p - db + 2;
631 source = msi_alloc( len * sizeof(WCHAR) );
632 lstrcpynW( source, db, len );
634 check = msi_dup_property( package, cszSourceDir );
635 if (!check || replace)
636 MSI_SetPropertyW( package, cszSourceDir, source );
638 msi_free( check );
640 check = msi_dup_property( package, cszSOURCEDIR );
641 if (!check || replace)
642 MSI_SetPropertyW( package, cszSOURCEDIR, source );
644 msi_free( check );
645 msi_free( source );
646 msi_free( db );
648 return ERROR_SUCCESS;
651 /****************************************************
652 * TOP level entry points
653 *****************************************************/
655 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
656 LPCWSTR szCommandLine )
658 UINT rc;
659 BOOL ui = FALSE, ui_exists;
660 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
661 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
662 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
664 MSI_SetPropertyW(package, szAction, szInstall);
666 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
668 package->script->InWhatSequence = SEQUENCE_INSTALL;
670 if (szPackagePath)
672 LPWSTR p, dir;
673 LPCWSTR file;
675 dir = strdupW(szPackagePath);
676 p = strrchrW(dir, '\\');
677 if (p)
679 *(++p) = 0;
680 file = szPackagePath + (p - dir);
682 else
684 msi_free(dir);
685 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
686 GetCurrentDirectoryW(MAX_PATH, dir);
687 lstrcatW(dir, cszbs);
688 file = szPackagePath;
691 msi_free( package->PackagePath );
692 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
693 if (!package->PackagePath)
695 msi_free(dir);
696 return ERROR_OUTOFMEMORY;
699 lstrcpyW(package->PackagePath, dir);
700 lstrcatW(package->PackagePath, file);
701 msi_free(dir);
703 msi_set_sourcedir_props(package, FALSE);
706 msi_parse_command_line( package, szCommandLine );
708 msi_apply_transforms( package );
709 msi_apply_patches( package );
711 /* properties may have been added by a transform */
712 msi_clone_properties( package );
714 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
716 package->script->InWhatSequence |= SEQUENCE_UI;
717 rc = ACTION_ProcessUISequence(package);
718 ui = TRUE;
719 ui_exists = ui_sequence_exists(package);
720 if (rc == ERROR_SUCCESS || !ui_exists)
722 package->script->InWhatSequence |= SEQUENCE_EXEC;
723 rc = ACTION_ProcessExecSequence(package,ui_exists);
726 else
727 rc = ACTION_ProcessExecSequence(package,FALSE);
729 package->script->CurrentlyScripting= FALSE;
731 /* process the ending type action */
732 if (rc == ERROR_SUCCESS)
733 ACTION_PerformActionSequence(package,-1,ui);
734 else if (rc == ERROR_INSTALL_USEREXIT)
735 ACTION_PerformActionSequence(package,-2,ui);
736 else if (rc == ERROR_INSTALL_SUSPEND)
737 ACTION_PerformActionSequence(package,-4,ui);
738 else /* failed */
739 ACTION_PerformActionSequence(package,-3,ui);
741 /* finish up running custom actions */
742 ACTION_FinishCustomActions(package);
744 return rc;
747 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
749 UINT rc = ERROR_SUCCESS;
750 MSIRECORD * row = 0;
751 static const WCHAR ExecSeqQuery[] =
752 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
753 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
754 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
755 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
757 static const WCHAR UISeqQuery[] =
758 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
759 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
760 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
761 ' ', '=',' ','%','i',0};
763 if (UI)
764 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
765 else
766 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
768 if (row)
770 LPCWSTR action, cond;
772 TRACE("Running the actions\n");
774 /* check conditions */
775 cond = MSI_RecordGetString(row,2);
777 /* this is a hack to skip errors in the condition code */
778 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
779 goto end;
781 action = MSI_RecordGetString(row,1);
782 if (!action)
784 ERR("failed to fetch action\n");
785 rc = ERROR_FUNCTION_FAILED;
786 goto end;
789 if (UI)
790 rc = ACTION_PerformUIAction(package,action,-1);
791 else
792 rc = ACTION_PerformAction(package,action,-1,FALSE);
793 end:
794 msiobj_release(&row->hdr);
796 else
797 rc = ERROR_SUCCESS;
799 return rc;
802 typedef struct {
803 MSIPACKAGE* package;
804 BOOL UI;
805 } iterate_action_param;
807 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
809 iterate_action_param *iap= (iterate_action_param*)param;
810 UINT rc;
811 LPCWSTR cond, action;
813 action = MSI_RecordGetString(row,1);
814 if (!action)
816 ERR("Error is retrieving action name\n");
817 return ERROR_FUNCTION_FAILED;
820 /* check conditions */
821 cond = MSI_RecordGetString(row,2);
823 /* this is a hack to skip errors in the condition code */
824 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
826 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
827 return ERROR_SUCCESS;
830 if (iap->UI)
831 rc = ACTION_PerformUIAction(iap->package,action,-1);
832 else
833 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
835 msi_dialog_check_messages( NULL );
837 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
838 rc = iap->package->CurrentInstallState;
840 if (rc == ERROR_FUNCTION_NOT_CALLED)
841 rc = ERROR_SUCCESS;
843 if (rc != ERROR_SUCCESS)
844 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
846 return rc;
849 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
851 MSIQUERY * view;
852 UINT r;
853 static const WCHAR query[] =
854 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
855 '`','%','s','`',
856 ' ','W','H','E','R','E',' ',
857 '`','S','e','q','u','e','n','c','e','`',' ',
858 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
859 '`','S','e','q','u','e','n','c','e','`',0};
860 iterate_action_param iap;
863 * FIXME: probably should be checking UILevel in the
864 * ACTION_PerformUIAction/ACTION_PerformAction
865 * rather than saving the UI level here. Those
866 * two functions can be merged too.
868 iap.package = package;
869 iap.UI = TRUE;
871 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
873 r = MSI_OpenQuery( package->db, &view, query, szTable );
874 if (r == ERROR_SUCCESS)
876 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
877 msiobj_release(&view->hdr);
880 return r;
883 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
885 MSIQUERY * view;
886 UINT rc;
887 static const WCHAR ExecSeqQuery[] =
888 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
889 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
890 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
891 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
892 'O','R','D','E','R',' ', 'B','Y',' ',
893 '`','S','e','q','u','e','n','c','e','`',0 };
894 MSIRECORD * row = 0;
895 static const WCHAR IVQuery[] =
896 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
897 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
898 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
899 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
900 ' ','\'', 'I','n','s','t','a','l','l',
901 'V','a','l','i','d','a','t','e','\'', 0};
902 INT seq = 0;
903 iterate_action_param iap;
905 iap.package = package;
906 iap.UI = FALSE;
908 if (package->script->ExecuteSequenceRun)
910 TRACE("Execute Sequence already Run\n");
911 return ERROR_SUCCESS;
914 package->script->ExecuteSequenceRun = TRUE;
916 /* get the sequence number */
917 if (UIran)
919 row = MSI_QueryGetRecord(package->db, IVQuery);
920 if( !row )
921 return ERROR_FUNCTION_FAILED;
922 seq = MSI_RecordGetInteger(row,1);
923 msiobj_release(&row->hdr);
926 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
927 if (rc == ERROR_SUCCESS)
929 TRACE("Running the actions\n");
931 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
932 msiobj_release(&view->hdr);
935 return rc;
938 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
940 MSIQUERY * view;
941 UINT rc;
942 static const WCHAR ExecSeqQuery [] =
943 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
944 '`','I','n','s','t','a','l','l',
945 'U','I','S','e','q','u','e','n','c','e','`',
946 ' ','W','H','E','R','E',' ',
947 '`','S','e','q','u','e','n','c','e','`',' ',
948 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
949 '`','S','e','q','u','e','n','c','e','`',0};
950 iterate_action_param iap;
952 iap.package = package;
953 iap.UI = TRUE;
955 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
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 /********************************************************
969 * ACTION helper functions and functions that perform the actions
970 *******************************************************/
971 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
972 UINT* rc, UINT script, BOOL force )
974 BOOL ret=FALSE;
975 UINT arc;
977 arc = ACTION_CustomAction(package, action, script, force);
979 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
981 *rc = arc;
982 ret = TRUE;
984 return ret;
988 * A lot of actions are really important even if they don't do anything
989 * explicit... Lots of properties are set at the beginning of the installation
990 * CostFinalize does a bunch of work to translate the directories and such
992 * But until I get write access to the database that is hard, so I am going to
993 * hack it to see if I can get something to run.
995 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
997 UINT rc = ERROR_SUCCESS;
998 BOOL handled;
1000 TRACE("Performing action (%s)\n",debugstr_w(action));
1002 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1004 if (!handled)
1005 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1007 if (!handled)
1009 WARN("unhandled msi action %s\n",debugstr_w(action));
1010 rc = ERROR_FUNCTION_NOT_CALLED;
1013 return rc;
1016 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1018 UINT rc = ERROR_SUCCESS;
1019 BOOL handled = FALSE;
1021 TRACE("Performing action (%s)\n",debugstr_w(action));
1023 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1025 if (!handled)
1026 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1028 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1029 handled = TRUE;
1031 if (!handled)
1033 WARN("unhandled msi action %s\n",debugstr_w(action));
1034 rc = ERROR_FUNCTION_NOT_CALLED;
1037 return rc;
1042 * Actual Action Handlers
1045 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1047 MSIPACKAGE *package = (MSIPACKAGE*)param;
1048 LPCWSTR dir;
1049 LPWSTR full_path;
1050 MSIRECORD *uirow;
1051 MSIFOLDER *folder;
1053 dir = MSI_RecordGetString(row,1);
1054 if (!dir)
1056 ERR("Unable to get folder id\n");
1057 return ERROR_SUCCESS;
1060 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1061 if (!full_path)
1063 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1064 return ERROR_SUCCESS;
1067 TRACE("Folder is %s\n",debugstr_w(full_path));
1069 /* UI stuff */
1070 uirow = MSI_CreateRecord(1);
1071 MSI_RecordSetStringW(uirow,1,full_path);
1072 ui_actiondata(package,szCreateFolders,uirow);
1073 msiobj_release( &uirow->hdr );
1075 if (folder->State == 0)
1076 create_full_pathW(full_path);
1078 folder->State = 3;
1080 msi_free(full_path);
1081 return ERROR_SUCCESS;
1084 /* FIXME: probably should merge this with the above function */
1085 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1087 UINT rc = ERROR_SUCCESS;
1088 MSIFOLDER *folder;
1089 LPWSTR install_path;
1091 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1092 if (!install_path)
1093 return ERROR_FUNCTION_FAILED;
1095 /* create the path */
1096 if (folder->State == 0)
1098 create_full_pathW(install_path);
1099 folder->State = 2;
1101 msi_free(install_path);
1103 return rc;
1106 UINT msi_create_component_directories( MSIPACKAGE *package )
1108 MSICOMPONENT *comp;
1110 /* create all the folders required by the components are going to install */
1111 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1113 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1114 continue;
1115 msi_create_directory( package, comp->Directory );
1118 return ERROR_SUCCESS;
1122 * Also we cannot enable/disable components either, so for now I am just going
1123 * to do all the directories for all the components.
1125 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1127 static const WCHAR ExecSeqQuery[] =
1128 {'S','E','L','E','C','T',' ',
1129 '`','D','i','r','e','c','t','o','r','y','_','`',
1130 ' ','F','R','O','M',' ',
1131 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1132 UINT rc;
1133 MSIQUERY *view;
1135 /* create all the empty folders specified in the CreateFolder table */
1136 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1137 if (rc != ERROR_SUCCESS)
1138 return ERROR_SUCCESS;
1140 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1141 msiobj_release(&view->hdr);
1143 msi_create_component_directories( package );
1145 return rc;
1148 static UINT load_component( MSIRECORD *row, LPVOID param )
1150 MSIPACKAGE *package = param;
1151 MSICOMPONENT *comp;
1153 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1154 if (!comp)
1155 return ERROR_FUNCTION_FAILED;
1157 list_add_tail( &package->components, &comp->entry );
1159 /* fill in the data */
1160 comp->Component = msi_dup_record_field( row, 1 );
1162 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1164 comp->ComponentId = msi_dup_record_field( row, 2 );
1165 comp->Directory = msi_dup_record_field( row, 3 );
1166 comp->Attributes = MSI_RecordGetInteger(row,4);
1167 comp->Condition = msi_dup_record_field( row, 5 );
1168 comp->KeyPath = msi_dup_record_field( row, 6 );
1170 comp->Installed = INSTALLSTATE_UNKNOWN;
1171 msi_component_set_state( comp, INSTALLSTATE_UNKNOWN );
1173 return ERROR_SUCCESS;
1176 static UINT load_all_components( MSIPACKAGE *package )
1178 static const WCHAR query[] = {
1179 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1180 '`','C','o','m','p','o','n','e','n','t','`',0 };
1181 MSIQUERY *view;
1182 UINT r;
1184 if (!list_empty(&package->components))
1185 return ERROR_SUCCESS;
1187 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1188 if (r != ERROR_SUCCESS)
1189 return r;
1191 r = MSI_IterateRecords(view, NULL, load_component, package);
1192 msiobj_release(&view->hdr);
1193 return r;
1196 typedef struct {
1197 MSIPACKAGE *package;
1198 MSIFEATURE *feature;
1199 } _ilfs;
1201 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1203 ComponentList *cl;
1205 cl = msi_alloc( sizeof (*cl) );
1206 if ( !cl )
1207 return ERROR_NOT_ENOUGH_MEMORY;
1208 cl->component = comp;
1209 list_add_tail( &feature->Components, &cl->entry );
1211 return ERROR_SUCCESS;
1214 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1216 FeatureList *fl;
1218 fl = msi_alloc( sizeof(*fl) );
1219 if ( !fl )
1220 return ERROR_NOT_ENOUGH_MEMORY;
1221 fl->feature = child;
1222 list_add_tail( &parent->Children, &fl->entry );
1224 return ERROR_SUCCESS;
1227 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1229 _ilfs* ilfs= (_ilfs*)param;
1230 LPCWSTR component;
1231 MSICOMPONENT *comp;
1233 component = MSI_RecordGetString(row,1);
1235 /* check to see if the component is already loaded */
1236 comp = get_loaded_component( ilfs->package, component );
1237 if (!comp)
1239 ERR("unknown component %s\n", debugstr_w(component));
1240 return ERROR_FUNCTION_FAILED;
1243 add_feature_component( ilfs->feature, comp );
1244 comp->Enabled = TRUE;
1246 return ERROR_SUCCESS;
1249 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1251 MSIFEATURE *feature;
1253 if ( !name )
1254 return NULL;
1256 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1258 if ( !lstrcmpW( feature->Feature, name ) )
1259 return feature;
1262 return NULL;
1265 static UINT load_feature(MSIRECORD * row, LPVOID param)
1267 MSIPACKAGE* package = (MSIPACKAGE*)param;
1268 MSIFEATURE* feature;
1269 static const WCHAR Query1[] =
1270 {'S','E','L','E','C','T',' ',
1271 '`','C','o','m','p','o','n','e','n','t','_','`',
1272 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1273 'C','o','m','p','o','n','e','n','t','s','`',' ',
1274 'W','H','E','R','E',' ',
1275 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1276 MSIQUERY * view;
1277 UINT rc;
1278 _ilfs ilfs;
1280 /* fill in the data */
1282 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1283 if (!feature)
1284 return ERROR_NOT_ENOUGH_MEMORY;
1286 list_init( &feature->Children );
1287 list_init( &feature->Components );
1289 feature->Feature = msi_dup_record_field( row, 1 );
1291 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1293 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1294 feature->Title = msi_dup_record_field( row, 3 );
1295 feature->Description = msi_dup_record_field( row, 4 );
1297 if (!MSI_RecordIsNull(row,5))
1298 feature->Display = MSI_RecordGetInteger(row,5);
1300 feature->Level= MSI_RecordGetInteger(row,6);
1301 feature->Directory = msi_dup_record_field( row, 7 );
1302 feature->Attributes = MSI_RecordGetInteger(row,8);
1304 feature->Installed = INSTALLSTATE_UNKNOWN;
1305 msi_feature_set_state( feature, INSTALLSTATE_UNKNOWN );
1307 list_add_tail( &package->features, &feature->entry );
1309 /* load feature components */
1311 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1312 if (rc != ERROR_SUCCESS)
1313 return ERROR_SUCCESS;
1315 ilfs.package = package;
1316 ilfs.feature = feature;
1318 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1319 msiobj_release(&view->hdr);
1321 return ERROR_SUCCESS;
1324 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1326 MSIPACKAGE* package = (MSIPACKAGE*)param;
1327 MSIFEATURE *parent, *child;
1329 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1330 if (!child)
1331 return ERROR_FUNCTION_FAILED;
1333 if (!child->Feature_Parent)
1334 return ERROR_SUCCESS;
1336 parent = find_feature_by_name( package, child->Feature_Parent );
1337 if (!parent)
1338 return ERROR_FUNCTION_FAILED;
1340 add_feature_child( parent, child );
1341 return ERROR_SUCCESS;
1344 static UINT load_all_features( MSIPACKAGE *package )
1346 static const WCHAR query[] = {
1347 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1348 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1349 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1350 MSIQUERY *view;
1351 UINT r;
1353 if (!list_empty(&package->features))
1354 return ERROR_SUCCESS;
1356 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1357 if (r != ERROR_SUCCESS)
1358 return r;
1360 r = MSI_IterateRecords( view, NULL, load_feature, package );
1361 if (r != ERROR_SUCCESS)
1362 return r;
1364 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1365 msiobj_release( &view->hdr );
1367 return r;
1370 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1372 if (!p)
1373 return p;
1374 p = strchrW(p, ch);
1375 if (!p)
1376 return p;
1377 *p = 0;
1378 return p+1;
1381 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1385 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1386 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1387 MSIQUERY *view = NULL;
1388 MSIRECORD *row = NULL;
1389 UINT r;
1391 TRACE("%s\n", debugstr_w(file->File));
1393 r = MSI_OpenQuery(package->db, &view, query, file->File);
1394 if (r != ERROR_SUCCESS)
1395 goto done;
1397 r = MSI_ViewExecute(view, NULL);
1398 if (r != ERROR_SUCCESS)
1399 goto done;
1401 r = MSI_ViewFetch(view, &row);
1402 if (r != ERROR_SUCCESS)
1403 goto done;
1405 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1406 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1407 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1408 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1409 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1411 done:
1412 if (view) msiobj_release(&view->hdr);
1413 if (row) msiobj_release(&row->hdr);
1414 return r;
1417 static UINT load_file(MSIRECORD *row, LPVOID param)
1419 MSIPACKAGE* package = (MSIPACKAGE*)param;
1420 LPCWSTR component;
1421 MSIFILE *file;
1423 /* fill in the data */
1425 file = msi_alloc_zero( sizeof (MSIFILE) );
1426 if (!file)
1427 return ERROR_NOT_ENOUGH_MEMORY;
1429 file->File = msi_dup_record_field( row, 1 );
1431 component = MSI_RecordGetString( row, 2 );
1432 file->Component = get_loaded_component( package, component );
1434 if (!file->Component)
1435 ERR("Unfound Component %s\n",debugstr_w(component));
1437 file->FileName = msi_dup_record_field( row, 3 );
1438 reduce_to_longfilename( file->FileName );
1440 file->ShortName = msi_dup_record_field( row, 3 );
1441 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1443 file->FileSize = MSI_RecordGetInteger( row, 4 );
1444 file->Version = msi_dup_record_field( row, 5 );
1445 file->Language = msi_dup_record_field( row, 6 );
1446 file->Attributes = MSI_RecordGetInteger( row, 7 );
1447 file->Sequence = MSI_RecordGetInteger( row, 8 );
1449 file->state = msifs_invalid;
1451 /* if the compressed bits are not set in the file attributes,
1452 * then read the information from the package word count property
1454 if (file->Attributes & msidbFileAttributesCompressed)
1456 file->IsCompressed = TRUE;
1458 else if (file->Attributes & msidbFileAttributesNoncompressed)
1460 file->IsCompressed = FALSE;
1462 else
1464 file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
1467 load_file_hash(package, file);
1469 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1471 list_add_tail( &package->files, &file->entry );
1473 return ERROR_SUCCESS;
1476 static UINT load_all_files(MSIPACKAGE *package)
1478 MSIQUERY * view;
1479 UINT rc;
1480 static const WCHAR Query[] =
1481 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1482 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1483 '`','S','e','q','u','e','n','c','e','`', 0};
1485 if (!list_empty(&package->files))
1486 return ERROR_SUCCESS;
1488 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1489 if (rc != ERROR_SUCCESS)
1490 return ERROR_SUCCESS;
1492 rc = MSI_IterateRecords(view, NULL, load_file, package);
1493 msiobj_release(&view->hdr);
1495 return ERROR_SUCCESS;
1498 static UINT load_folder( MSIRECORD *row, LPVOID param )
1500 MSIPACKAGE *package = param;
1501 static const WCHAR szDot[] = { '.',0 };
1502 static WCHAR szEmpty[] = { 0 };
1503 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1504 MSIFOLDER *folder;
1506 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1507 if (!folder)
1508 return ERROR_NOT_ENOUGH_MEMORY;
1510 folder->Directory = msi_dup_record_field( row, 1 );
1512 TRACE("%s\n", debugstr_w(folder->Directory));
1514 p = msi_dup_record_field(row, 3);
1516 /* split src and target dir */
1517 tgt_short = p;
1518 src_short = folder_split_path( p, ':' );
1520 /* split the long and short paths */
1521 tgt_long = folder_split_path( tgt_short, '|' );
1522 src_long = folder_split_path( src_short, '|' );
1524 /* check for no-op dirs */
1525 if (!lstrcmpW(szDot, tgt_short))
1526 tgt_short = szEmpty;
1527 if (!lstrcmpW(szDot, src_short))
1528 src_short = szEmpty;
1530 if (!tgt_long)
1531 tgt_long = tgt_short;
1533 if (!src_short) {
1534 src_short = tgt_short;
1535 src_long = tgt_long;
1538 if (!src_long)
1539 src_long = src_short;
1541 /* FIXME: use the target short path too */
1542 folder->TargetDefault = strdupW(tgt_long);
1543 folder->SourceShortPath = strdupW(src_short);
1544 folder->SourceLongPath = strdupW(src_long);
1545 msi_free(p);
1547 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1548 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1549 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1551 folder->Parent = msi_dup_record_field( row, 2 );
1553 folder->Property = msi_dup_property( package, folder->Directory );
1555 list_add_tail( &package->folders, &folder->entry );
1557 TRACE("returning %p\n", folder);
1559 return ERROR_SUCCESS;
1562 static UINT load_all_folders( MSIPACKAGE *package )
1564 static const WCHAR query[] = {
1565 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1566 '`','D','i','r','e','c','t','o','r','y','`',0 };
1567 MSIQUERY *view;
1568 UINT r;
1570 if (!list_empty(&package->folders))
1571 return ERROR_SUCCESS;
1573 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1574 if (r != ERROR_SUCCESS)
1575 return r;
1577 r = MSI_IterateRecords(view, NULL, load_folder, package);
1578 msiobj_release(&view->hdr);
1579 return r;
1583 * I am not doing any of the costing functionality yet.
1584 * Mostly looking at doing the Component and Feature loading
1586 * The native MSI does A LOT of modification to tables here. Mostly adding
1587 * a lot of temporary columns to the Feature and Component tables.
1589 * note: Native msi also tracks the short filename. But I am only going to
1590 * track the long ones. Also looking at this directory table
1591 * it appears that the directory table does not get the parents
1592 * resolved base on property only based on their entries in the
1593 * directory table.
1595 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1597 static const WCHAR szCosting[] =
1598 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1599 static const WCHAR szZero[] = { '0', 0 };
1601 MSI_SetPropertyW(package, szCosting, szZero);
1602 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1604 load_all_components( package );
1605 load_all_features( package );
1606 load_all_files( package );
1607 load_all_folders( package );
1609 return ERROR_SUCCESS;
1612 static UINT execute_script(MSIPACKAGE *package, UINT script )
1614 UINT i;
1615 UINT rc = ERROR_SUCCESS;
1617 TRACE("Executing Script %i\n",script);
1619 if (!package->script)
1621 ERR("no script!\n");
1622 return ERROR_FUNCTION_FAILED;
1625 for (i = 0; i < package->script->ActionCount[script]; i++)
1627 LPWSTR action;
1628 action = package->script->Actions[script][i];
1629 ui_actionstart(package, action);
1630 TRACE("Executing Action (%s)\n",debugstr_w(action));
1631 rc = ACTION_PerformAction(package, action, script, TRUE);
1632 if (rc != ERROR_SUCCESS)
1633 break;
1635 msi_free_action_script(package, script);
1636 return rc;
1639 static UINT ACTION_FileCost(MSIPACKAGE *package)
1641 return ERROR_SUCCESS;
1644 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1646 MSICOMPONENT *comp;
1648 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1650 INSTALLSTATE res;
1652 if (!comp->ComponentId)
1653 continue;
1655 res = MsiGetComponentPathW( package->ProductCode,
1656 comp->ComponentId, NULL, NULL);
1657 if (res < 0)
1658 res = INSTALLSTATE_ABSENT;
1659 comp->Installed = res;
1663 /* scan for and update current install states */
1664 static void ACTION_UpdateFeatureInstallStates(MSIPACKAGE *package)
1666 MSICOMPONENT *comp;
1667 MSIFEATURE *feature;
1669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1671 ComponentList *cl;
1672 INSTALLSTATE res = INSTALLSTATE_ABSENT;
1674 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1676 comp= cl->component;
1678 if (!comp->ComponentId)
1680 res = INSTALLSTATE_ABSENT;
1681 break;
1684 if (res == INSTALLSTATE_ABSENT)
1685 res = comp->Installed;
1686 else
1688 if (res == comp->Installed)
1689 continue;
1691 if (res != INSTALLSTATE_DEFAULT && res != INSTALLSTATE_LOCAL &&
1692 res != INSTALLSTATE_SOURCE)
1694 res = INSTALLSTATE_INCOMPLETE;
1698 feature->Installed = res;
1702 static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
1703 INSTALLSTATE state)
1705 static const WCHAR all[]={'A','L','L',0};
1706 LPWSTR override;
1707 MSIFEATURE *feature;
1709 override = msi_dup_property( package, property );
1710 if (!override)
1711 return FALSE;
1713 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1715 if (strcmpiW(override,all)==0)
1716 msi_feature_set_state( feature, state );
1717 else
1719 LPWSTR ptr = override;
1720 LPWSTR ptr2 = strchrW(override,',');
1722 while (ptr)
1724 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1725 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1727 msi_feature_set_state( feature, state );
1728 break;
1730 if (ptr2)
1732 ptr=ptr2+1;
1733 ptr2 = strchrW(ptr,',');
1735 else
1736 break;
1740 msi_free(override);
1742 return TRUE;
1745 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1747 int install_level;
1748 static const WCHAR szlevel[] =
1749 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1750 static const WCHAR szAddLocal[] =
1751 {'A','D','D','L','O','C','A','L',0};
1752 static const WCHAR szAddSource[] =
1753 {'A','D','D','S','O','U','R','C','E',0};
1754 static const WCHAR szRemove[] =
1755 {'R','E','M','O','V','E',0};
1756 static const WCHAR szReinstall[] =
1757 {'R','E','I','N','S','T','A','L','L',0};
1758 BOOL override = FALSE;
1759 MSICOMPONENT* component;
1760 MSIFEATURE *feature;
1763 /* I do not know if this is where it should happen.. but */
1765 TRACE("Checking Install Level\n");
1767 install_level = msi_get_property_int( package, szlevel, 1 );
1769 /* ok here is the _real_ rub
1770 * all these activation/deactivation things happen in order and things
1771 * later on the list override things earlier on the list.
1772 * 1) INSTALLLEVEL processing
1773 * 2) ADDLOCAL
1774 * 3) REMOVE
1775 * 4) ADDSOURCE
1776 * 5) ADDDEFAULT
1777 * 6) REINSTALL
1778 * 7) COMPADDLOCAL
1779 * 8) COMPADDSOURCE
1780 * 9) FILEADDLOCAL
1781 * 10) FILEADDSOURCE
1782 * 11) FILEADDDEFAULT
1784 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1785 * REMOVE are the big ones, since we don't handle administrative installs
1786 * yet anyway.
1788 override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
1789 override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
1790 override |= process_state_property(package,szAddSource,INSTALLSTATE_SOURCE);
1791 override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
1793 if (!override)
1795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1797 BOOL feature_state = ((feature->Level > 0) &&
1798 (feature->Level <= install_level));
1800 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1802 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1803 msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
1804 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1805 msi_feature_set_state( feature, INSTALLSTATE_ADVERTISED );
1806 else
1807 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1811 /* disable child features of unselected parent features */
1812 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1814 FeatureList *fl;
1816 if (feature->Level > 0 && feature->Level <= install_level)
1817 continue;
1819 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1820 msi_feature_set_state( fl->feature, INSTALLSTATE_UNKNOWN );
1823 else
1825 /* set the Preselected Property */
1826 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1827 static const WCHAR szOne[] = { '1', 0 };
1829 MSI_SetPropertyW(package,szPreselected,szOne);
1833 * now we want to enable or disable components base on feature
1836 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1838 ComponentList *cl;
1840 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1841 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1843 if (!feature->Level)
1844 continue;
1846 /* features with components that have compressed files are made local */
1847 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1849 if (cl->component->Enabled &&
1850 cl->component->ForceLocalState &&
1851 feature->Action == INSTALLSTATE_SOURCE)
1853 msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
1854 break;
1858 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1860 component = cl->component;
1862 if (!component->Enabled)
1863 continue;
1865 switch (feature->Action)
1867 case INSTALLSTATE_ABSENT:
1868 component->anyAbsent = 1;
1869 break;
1870 case INSTALLSTATE_ADVERTISED:
1871 component->hasAdvertiseFeature = 1;
1872 break;
1873 case INSTALLSTATE_SOURCE:
1874 component->hasSourceFeature = 1;
1875 break;
1876 case INSTALLSTATE_LOCAL:
1877 component->hasLocalFeature = 1;
1878 break;
1879 case INSTALLSTATE_DEFAULT:
1880 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1881 component->hasAdvertiseFeature = 1;
1882 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1883 component->hasSourceFeature = 1;
1884 else
1885 component->hasLocalFeature = 1;
1886 break;
1887 default:
1888 break;
1893 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1895 /* if the component isn't enabled, leave it alone */
1896 if (!component->Enabled)
1897 continue;
1899 /* check if it's local or source */
1900 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1901 (component->hasLocalFeature || component->hasSourceFeature))
1903 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1904 !component->ForceLocalState)
1905 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1906 else
1907 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1908 continue;
1911 /* if any feature is local, the component must be local too */
1912 if (component->hasLocalFeature)
1914 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1915 continue;
1918 if (component->hasSourceFeature)
1920 msi_component_set_state( component, INSTALLSTATE_SOURCE );
1921 continue;
1924 if (component->hasAdvertiseFeature)
1926 msi_component_set_state( component, INSTALLSTATE_ADVERTISED );
1927 continue;
1930 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1931 if (component->anyAbsent)
1932 msi_component_set_state(component, INSTALLSTATE_ABSENT);
1935 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1937 if (component->Action == INSTALLSTATE_DEFAULT)
1939 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1940 msi_component_set_state( component, INSTALLSTATE_LOCAL );
1943 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1944 debugstr_w(component->Component), component->Installed, component->Action);
1948 return ERROR_SUCCESS;
1951 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1953 MSIPACKAGE *package = (MSIPACKAGE*)param;
1954 LPCWSTR name;
1955 LPWSTR path;
1956 MSIFOLDER *f;
1958 name = MSI_RecordGetString(row,1);
1960 f = get_loaded_folder(package, name);
1961 if (!f) return ERROR_SUCCESS;
1963 /* reset the ResolvedTarget */
1964 msi_free(f->ResolvedTarget);
1965 f->ResolvedTarget = NULL;
1967 /* This helper function now does ALL the work */
1968 TRACE("Dir %s ...\n",debugstr_w(name));
1969 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1970 TRACE("resolves to %s\n",debugstr_w(path));
1971 msi_free(path);
1973 return ERROR_SUCCESS;
1976 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1978 MSIPACKAGE *package = (MSIPACKAGE*)param;
1979 LPCWSTR name;
1980 MSIFEATURE *feature;
1982 name = MSI_RecordGetString( row, 1 );
1984 feature = get_loaded_feature( package, name );
1985 if (!feature)
1986 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1987 else
1989 LPCWSTR Condition;
1990 Condition = MSI_RecordGetString(row,3);
1992 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1994 int level = MSI_RecordGetInteger(row,2);
1995 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1996 feature->Level = level;
1999 return ERROR_SUCCESS;
2002 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2004 static const WCHAR name_fmt[] =
2005 {'%','u','.','%','u','.','%','u','.','%','u',0};
2006 static WCHAR name[] = {'\\',0};
2007 VS_FIXEDFILEINFO *lpVer;
2008 WCHAR filever[0x100];
2009 LPVOID version;
2010 DWORD versize;
2011 DWORD handle;
2012 UINT sz;
2014 TRACE("%s\n", debugstr_w(filename));
2016 versize = GetFileVersionInfoSizeW( filename, &handle );
2017 if (!versize)
2018 return NULL;
2020 version = msi_alloc( versize );
2021 GetFileVersionInfoW( filename, 0, versize, version );
2023 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2025 msi_free( version );
2026 return NULL;
2029 sprintfW( filever, name_fmt,
2030 HIWORD(lpVer->dwFileVersionMS),
2031 LOWORD(lpVer->dwFileVersionMS),
2032 HIWORD(lpVer->dwFileVersionLS),
2033 LOWORD(lpVer->dwFileVersionLS));
2035 msi_free( version );
2037 return strdupW( filever );
2040 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2042 LPWSTR file_version;
2043 MSIFILE *file;
2045 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2047 MSICOMPONENT* comp = file->Component;
2048 LPWSTR p;
2050 if (!comp)
2051 continue;
2053 if (file->IsCompressed)
2054 comp->ForceLocalState = TRUE;
2056 /* calculate target */
2057 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2059 msi_free(file->TargetPath);
2061 TRACE("file %s is named %s\n",
2062 debugstr_w(file->File), debugstr_w(file->FileName));
2064 file->TargetPath = build_directory_name(2, p, file->FileName);
2066 msi_free(p);
2068 TRACE("file %s resolves to %s\n",
2069 debugstr_w(file->File), debugstr_w(file->TargetPath));
2071 /* don't check files of components that aren't installed */
2072 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2073 comp->Installed == INSTALLSTATE_ABSENT)
2075 file->state = msifs_missing; /* assume files are missing */
2076 continue;
2079 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2081 file->state = msifs_missing;
2082 comp->Cost += file->FileSize;
2083 comp->Installed = INSTALLSTATE_INCOMPLETE;
2084 continue;
2087 if (file->Version &&
2088 (file_version = msi_get_disk_file_version( file->TargetPath )))
2090 TRACE("new %s old %s\n", debugstr_w(file->Version),
2091 debugstr_w(file_version));
2092 /* FIXME: seems like a bad way to compare version numbers */
2093 if (lstrcmpiW(file_version, file->Version)<0)
2095 file->state = msifs_overwrite;
2096 comp->Cost += file->FileSize;
2097 comp->Installed = INSTALLSTATE_INCOMPLETE;
2099 else
2100 file->state = msifs_present;
2101 msi_free( file_version );
2103 else
2104 file->state = msifs_present;
2107 return ERROR_SUCCESS;
2111 * A lot is done in this function aside from just the costing.
2112 * The costing needs to be implemented at some point but for now I am going
2113 * to focus on the directory building
2116 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2118 static const WCHAR ExecSeqQuery[] =
2119 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2120 '`','D','i','r','e','c','t','o','r','y','`',0};
2121 static const WCHAR ConditionQuery[] =
2122 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2123 '`','C','o','n','d','i','t','i','o','n','`',0};
2124 static const WCHAR szCosting[] =
2125 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2126 static const WCHAR szlevel[] =
2127 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2128 static const WCHAR szOutOfDiskSpace[] =
2129 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2130 static const WCHAR szOne[] = { '1', 0 };
2131 static const WCHAR szZero[] = { '0', 0 };
2132 MSICOMPONENT *comp;
2133 UINT rc;
2134 MSIQUERY * view;
2135 LPWSTR level;
2137 if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
2138 return ERROR_SUCCESS;
2140 TRACE("Building Directory properties\n");
2142 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2143 if (rc == ERROR_SUCCESS)
2145 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2146 package);
2147 msiobj_release(&view->hdr);
2150 /* read components states from the registry */
2151 ACTION_GetComponentInstallStates(package);
2153 TRACE("File calculations\n");
2154 msi_check_file_install_states( package );
2156 TRACE("Evaluating Condition Table\n");
2158 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2159 if (rc == ERROR_SUCCESS)
2161 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2162 package);
2163 msiobj_release(&view->hdr);
2166 TRACE("Enabling or Disabling Components\n");
2167 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2169 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2171 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2172 comp->Enabled = FALSE;
2176 MSI_SetPropertyW(package,szCosting,szOne);
2177 /* set default run level if not set */
2178 level = msi_dup_property( package, szlevel );
2179 if (!level)
2180 MSI_SetPropertyW(package,szlevel, szOne);
2181 msi_free(level);
2183 /* FIXME: check volume disk space */
2184 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2186 ACTION_UpdateFeatureInstallStates(package);
2188 return MSI_SetFeatureStates(package);
2191 /* OK this value is "interpreted" and then formatted based on the
2192 first few characters */
2193 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2194 DWORD *size)
2196 LPSTR data = NULL;
2198 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2200 if (value[1]=='x')
2202 LPWSTR ptr;
2203 CHAR byte[5];
2204 LPWSTR deformated = NULL;
2205 int count;
2207 deformat_string(package, &value[2], &deformated);
2209 /* binary value type */
2210 ptr = deformated;
2211 *type = REG_BINARY;
2212 if (strlenW(ptr)%2)
2213 *size = (strlenW(ptr)/2)+1;
2214 else
2215 *size = strlenW(ptr)/2;
2217 data = msi_alloc(*size);
2219 byte[0] = '0';
2220 byte[1] = 'x';
2221 byte[4] = 0;
2222 count = 0;
2223 /* if uneven pad with a zero in front */
2224 if (strlenW(ptr)%2)
2226 byte[2]= '0';
2227 byte[3]= *ptr;
2228 ptr++;
2229 data[count] = (BYTE)strtol(byte,NULL,0);
2230 count ++;
2231 TRACE("Uneven byte count\n");
2233 while (*ptr)
2235 byte[2]= *ptr;
2236 ptr++;
2237 byte[3]= *ptr;
2238 ptr++;
2239 data[count] = (BYTE)strtol(byte,NULL,0);
2240 count ++;
2242 msi_free(deformated);
2244 TRACE("Data %i bytes(%i)\n",*size,count);
2246 else
2248 LPWSTR deformated;
2249 LPWSTR p;
2250 DWORD d = 0;
2251 deformat_string(package, &value[1], &deformated);
2253 *type=REG_DWORD;
2254 *size = sizeof(DWORD);
2255 data = msi_alloc(*size);
2256 p = deformated;
2257 if (*p == '-')
2258 p++;
2259 while (*p)
2261 if ( (*p < '0') || (*p > '9') )
2262 break;
2263 d *= 10;
2264 d += (*p - '0');
2265 p++;
2267 if (deformated[0] == '-')
2268 d = -d;
2269 *(LPDWORD)data = d;
2270 TRACE("DWORD %i\n",*(LPDWORD)data);
2272 msi_free(deformated);
2275 else
2277 static const WCHAR szMulti[] = {'[','~',']',0};
2278 LPCWSTR ptr;
2279 *type=REG_SZ;
2281 if (value[0]=='#')
2283 if (value[1]=='%')
2285 ptr = &value[2];
2286 *type=REG_EXPAND_SZ;
2288 else
2289 ptr = &value[1];
2291 else
2292 ptr=value;
2294 if (strstrW(value,szMulti))
2295 *type = REG_MULTI_SZ;
2297 /* remove initial delimiter */
2298 if (!strncmpW(value, szMulti, 3))
2299 ptr = value + 3;
2301 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2303 /* add double NULL terminator */
2304 if (*type == REG_MULTI_SZ)
2306 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2307 data = msi_realloc_zero(data, *size);
2310 return data;
2313 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2315 MSIPACKAGE *package = (MSIPACKAGE*)param;
2316 static const WCHAR szHCR[] =
2317 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2318 'R','O','O','T','\\',0};
2319 static const WCHAR szHCU[] =
2320 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2321 'U','S','E','R','\\',0};
2322 static const WCHAR szHLM[] =
2323 {'H','K','E','Y','_','L','O','C','A','L','_',
2324 'M','A','C','H','I','N','E','\\',0};
2325 static const WCHAR szHU[] =
2326 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2328 LPSTR value_data = NULL;
2329 HKEY root_key, hkey;
2330 DWORD type,size;
2331 LPWSTR deformated;
2332 LPCWSTR szRoot, component, name, key, value;
2333 MSICOMPONENT *comp;
2334 MSIRECORD * uirow;
2335 LPWSTR uikey;
2336 INT root;
2337 BOOL check_first = FALSE;
2338 UINT rc;
2340 ui_progress(package,2,0,0,0);
2342 value = NULL;
2343 key = NULL;
2344 uikey = NULL;
2345 name = NULL;
2347 component = MSI_RecordGetString(row, 6);
2348 comp = get_loaded_component(package,component);
2349 if (!comp)
2350 return ERROR_SUCCESS;
2352 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2354 TRACE("Skipping write due to disabled component %s\n",
2355 debugstr_w(component));
2357 comp->Action = comp->Installed;
2359 return ERROR_SUCCESS;
2362 comp->Action = INSTALLSTATE_LOCAL;
2364 name = MSI_RecordGetString(row, 4);
2365 if( MSI_RecordIsNull(row,5) && name )
2367 /* null values can have special meanings */
2368 if (name[0]=='-' && name[1] == 0)
2369 return ERROR_SUCCESS;
2370 else if ((name[0]=='+' && name[1] == 0) ||
2371 (name[0] == '*' && name[1] == 0))
2372 name = NULL;
2373 check_first = TRUE;
2376 root = MSI_RecordGetInteger(row,2);
2377 key = MSI_RecordGetString(row, 3);
2379 /* get the root key */
2380 switch (root)
2382 case -1:
2384 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2385 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2386 if (all_users && all_users[0] == '1')
2388 root_key = HKEY_LOCAL_MACHINE;
2389 szRoot = szHLM;
2391 else
2393 root_key = HKEY_CURRENT_USER;
2394 szRoot = szHCU;
2396 msi_free(all_users);
2398 break;
2399 case 0: root_key = HKEY_CLASSES_ROOT;
2400 szRoot = szHCR;
2401 break;
2402 case 1: root_key = HKEY_CURRENT_USER;
2403 szRoot = szHCU;
2404 break;
2405 case 2: root_key = HKEY_LOCAL_MACHINE;
2406 szRoot = szHLM;
2407 break;
2408 case 3: root_key = HKEY_USERS;
2409 szRoot = szHU;
2410 break;
2411 default:
2412 ERR("Unknown root %i\n",root);
2413 root_key=NULL;
2414 szRoot = NULL;
2415 break;
2417 if (!root_key)
2418 return ERROR_SUCCESS;
2420 deformat_string(package, key , &deformated);
2421 size = strlenW(deformated) + strlenW(szRoot) + 1;
2422 uikey = msi_alloc(size*sizeof(WCHAR));
2423 strcpyW(uikey,szRoot);
2424 strcatW(uikey,deformated);
2426 if (RegCreateKeyW( root_key, deformated, &hkey))
2428 ERR("Could not create key %s\n",debugstr_w(deformated));
2429 msi_free(deformated);
2430 msi_free(uikey);
2431 return ERROR_SUCCESS;
2433 msi_free(deformated);
2435 value = MSI_RecordGetString(row,5);
2436 if (value)
2437 value_data = parse_value(package, value, &type, &size);
2438 else
2440 static const WCHAR szEmpty[] = {0};
2441 value_data = (LPSTR)strdupW(szEmpty);
2442 size = sizeof(szEmpty);
2443 type = REG_SZ;
2446 deformat_string(package, name, &deformated);
2448 if (!check_first)
2450 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2451 debugstr_w(uikey));
2452 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2454 else
2456 DWORD sz = 0;
2457 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2458 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2460 TRACE("value %s of %s checked already exists\n",
2461 debugstr_w(deformated), debugstr_w(uikey));
2463 else
2465 TRACE("Checked and setting value %s of %s\n",
2466 debugstr_w(deformated), debugstr_w(uikey));
2467 if (deformated || size)
2468 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2471 RegCloseKey(hkey);
2473 uirow = MSI_CreateRecord(3);
2474 MSI_RecordSetStringW(uirow,2,deformated);
2475 MSI_RecordSetStringW(uirow,1,uikey);
2477 if (type == REG_SZ)
2478 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2479 else
2480 MSI_RecordSetStringW(uirow,3,value);
2482 ui_actiondata(package,szWriteRegistryValues,uirow);
2483 msiobj_release( &uirow->hdr );
2485 msi_free(value_data);
2486 msi_free(deformated);
2487 msi_free(uikey);
2489 return ERROR_SUCCESS;
2492 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2494 UINT rc;
2495 MSIQUERY * view;
2496 static const WCHAR ExecSeqQuery[] =
2497 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2498 '`','R','e','g','i','s','t','r','y','`',0 };
2500 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2501 if (rc != ERROR_SUCCESS)
2502 return ERROR_SUCCESS;
2504 /* increment progress bar each time action data is sent */
2505 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2507 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2509 msiobj_release(&view->hdr);
2510 return rc;
2513 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2515 package->script->CurrentlyScripting = TRUE;
2517 return ERROR_SUCCESS;
2521 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2523 MSICOMPONENT *comp;
2524 DWORD progress = 0;
2525 DWORD total = 0;
2526 static const WCHAR q1[]=
2527 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2528 '`','R','e','g','i','s','t','r','y','`',0};
2529 UINT rc;
2530 MSIQUERY * view;
2531 MSIFEATURE *feature;
2532 MSIFILE *file;
2534 TRACE("InstallValidate\n");
2536 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2537 if (rc == ERROR_SUCCESS)
2539 MSI_IterateRecords( view, &progress, NULL, package );
2540 msiobj_release( &view->hdr );
2541 total += progress * REG_PROGRESS_VALUE;
2544 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2545 total += COMPONENT_PROGRESS_VALUE;
2547 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2548 total += file->FileSize;
2550 ui_progress(package,0,total,0,0);
2552 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2554 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2555 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2556 feature->ActionRequest);
2559 return ERROR_SUCCESS;
2562 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2564 MSIPACKAGE* package = (MSIPACKAGE*)param;
2565 LPCWSTR cond = NULL;
2566 LPCWSTR message = NULL;
2567 UINT r;
2569 static const WCHAR title[]=
2570 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2572 cond = MSI_RecordGetString(row,1);
2574 r = MSI_EvaluateConditionW(package,cond);
2575 if (r == MSICONDITION_FALSE)
2577 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2579 LPWSTR deformated;
2580 message = MSI_RecordGetString(row,2);
2581 deformat_string(package,message,&deformated);
2582 MessageBoxW(NULL,deformated,title,MB_OK);
2583 msi_free(deformated);
2586 return ERROR_INSTALL_FAILURE;
2589 return ERROR_SUCCESS;
2592 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2594 UINT rc;
2595 MSIQUERY * view = NULL;
2596 static const WCHAR ExecSeqQuery[] =
2597 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2598 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2600 TRACE("Checking launch conditions\n");
2602 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2603 if (rc != ERROR_SUCCESS)
2604 return ERROR_SUCCESS;
2606 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2607 msiobj_release(&view->hdr);
2609 return rc;
2612 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2615 if (!cmp->KeyPath)
2616 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2618 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2620 MSIRECORD * row = 0;
2621 UINT root,len;
2622 LPWSTR deformated,buffer,deformated_name;
2623 LPCWSTR key,name;
2624 static const WCHAR ExecSeqQuery[] =
2625 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2626 '`','R','e','g','i','s','t','r','y','`',' ',
2627 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2628 ' ','=',' ' ,'\'','%','s','\'',0 };
2629 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2630 static const WCHAR fmt2[]=
2631 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2633 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2634 if (!row)
2635 return NULL;
2637 root = MSI_RecordGetInteger(row,2);
2638 key = MSI_RecordGetString(row, 3);
2639 name = MSI_RecordGetString(row, 4);
2640 deformat_string(package, key , &deformated);
2641 deformat_string(package, name, &deformated_name);
2643 len = strlenW(deformated) + 6;
2644 if (deformated_name)
2645 len+=strlenW(deformated_name);
2647 buffer = msi_alloc( len *sizeof(WCHAR));
2649 if (deformated_name)
2650 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2651 else
2652 sprintfW(buffer,fmt,root,deformated);
2654 msi_free(deformated);
2655 msi_free(deformated_name);
2656 msiobj_release(&row->hdr);
2658 return buffer;
2660 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2662 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2663 return NULL;
2665 else
2667 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2669 if (file)
2670 return strdupW( file->TargetPath );
2672 return NULL;
2675 static HKEY openSharedDLLsKey(void)
2677 HKEY hkey=0;
2678 static const WCHAR path[] =
2679 {'S','o','f','t','w','a','r','e','\\',
2680 'M','i','c','r','o','s','o','f','t','\\',
2681 'W','i','n','d','o','w','s','\\',
2682 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2683 'S','h','a','r','e','d','D','L','L','s',0};
2685 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2686 return hkey;
2689 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2691 HKEY hkey;
2692 DWORD count=0;
2693 DWORD type;
2694 DWORD sz = sizeof(count);
2695 DWORD rc;
2697 hkey = openSharedDLLsKey();
2698 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2699 if (rc != ERROR_SUCCESS)
2700 count = 0;
2701 RegCloseKey(hkey);
2702 return count;
2705 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2707 HKEY hkey;
2709 hkey = openSharedDLLsKey();
2710 if (count > 0)
2711 msi_reg_set_val_dword( hkey, path, count );
2712 else
2713 RegDeleteValueW(hkey,path);
2714 RegCloseKey(hkey);
2715 return count;
2719 * Return TRUE if the count should be written out and FALSE if not
2721 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2723 MSIFEATURE *feature;
2724 INT count = 0;
2725 BOOL write = FALSE;
2727 /* only refcount DLLs */
2728 if (comp->KeyPath == NULL ||
2729 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2730 comp->Attributes & msidbComponentAttributesODBCDataSource)
2731 write = FALSE;
2732 else
2734 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2735 write = (count > 0);
2737 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2738 write = TRUE;
2741 /* increment counts */
2742 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2744 ComponentList *cl;
2746 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2747 continue;
2749 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2751 if ( cl->component == comp )
2752 count++;
2756 /* decrement counts */
2757 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2759 ComponentList *cl;
2761 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2762 continue;
2764 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2766 if ( cl->component == comp )
2767 count--;
2771 /* ref count all the files in the component */
2772 if (write)
2774 MSIFILE *file;
2776 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2778 if (file->Component == comp)
2779 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2783 /* add a count for permanent */
2784 if (comp->Attributes & msidbComponentAttributesPermanent)
2785 count ++;
2787 comp->RefCount = count;
2789 if (write)
2790 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2794 * Ok further analysis makes me think that this work is
2795 * actually done in the PublishComponents and PublishFeatures
2796 * step, and not here. It appears like the keypath and all that is
2797 * resolved in this step, however actually written in the Publish steps.
2798 * But we will leave it here for now because it is unclear
2800 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2802 WCHAR squished_pc[GUID_SIZE];
2803 WCHAR squished_cc[GUID_SIZE];
2804 UINT rc;
2805 MSICOMPONENT *comp;
2806 HKEY hkey=0,hkey2=0;
2808 TRACE("\n");
2810 /* writes the Component and Features values to the registry */
2812 rc = MSIREG_OpenComponents(&hkey);
2813 if (rc != ERROR_SUCCESS)
2814 return rc;
2816 squash_guid(package->ProductCode,squished_pc);
2817 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2819 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2821 MSIRECORD * uirow;
2823 ui_progress(package,2,0,0,0);
2824 if (!comp->ComponentId)
2825 continue;
2827 squash_guid(comp->ComponentId,squished_cc);
2829 msi_free(comp->FullKeypath);
2830 comp->FullKeypath = resolve_keypath( package, comp );
2832 /* do the refcounting */
2833 ACTION_RefCountComponent( package, comp );
2835 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2836 debugstr_w(comp->Component),
2837 debugstr_w(squished_cc),
2838 debugstr_w(comp->FullKeypath),
2839 comp->RefCount);
2841 * Write the keypath out if the component is to be registered
2842 * and delete the key if the component is to be unregistered
2844 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2846 rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
2847 if (rc != ERROR_SUCCESS)
2848 continue;
2850 if (!comp->FullKeypath)
2851 continue;
2853 msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
2855 if (comp->Attributes & msidbComponentAttributesPermanent)
2857 static const WCHAR szPermKey[] =
2858 { '0','0','0','0','0','0','0','0','0','0','0','0',
2859 '0','0','0','0','0','0','0','0','0','0','0','0',
2860 '0','0','0','0','0','0','0','0',0 };
2862 msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
2865 RegCloseKey(hkey2);
2867 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey2, TRUE);
2868 if (rc != ERROR_SUCCESS)
2869 continue;
2871 msi_reg_set_val_str(hkey2, squished_pc, comp->FullKeypath);
2872 RegCloseKey(hkey2);
2874 else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
2876 DWORD res;
2878 rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
2879 if (rc != ERROR_SUCCESS)
2880 continue;
2882 RegDeleteValueW(hkey2,squished_pc);
2884 /* if the key is empty delete it */
2885 res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
2886 RegCloseKey(hkey2);
2887 if (res == ERROR_NO_MORE_ITEMS)
2888 RegDeleteKeyW(hkey,squished_cc);
2890 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2893 /* UI stuff */
2894 uirow = MSI_CreateRecord(3);
2895 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2896 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2897 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2898 ui_actiondata(package,szProcessComponents,uirow);
2899 msiobj_release( &uirow->hdr );
2901 RegCloseKey(hkey);
2902 return rc;
2905 typedef struct {
2906 CLSID clsid;
2907 LPWSTR source;
2909 LPWSTR path;
2910 ITypeLib *ptLib;
2911 } typelib_struct;
2913 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2914 LPWSTR lpszName, LONG_PTR lParam)
2916 TLIBATTR *attr;
2917 typelib_struct *tl_struct = (typelib_struct*) lParam;
2918 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2919 int sz;
2920 HRESULT res;
2922 if (!IS_INTRESOURCE(lpszName))
2924 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2925 return TRUE;
2928 sz = strlenW(tl_struct->source)+4;
2929 sz *= sizeof(WCHAR);
2931 if ((INT_PTR)lpszName == 1)
2932 tl_struct->path = strdupW(tl_struct->source);
2933 else
2935 tl_struct->path = msi_alloc(sz);
2936 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2939 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2940 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2941 if (!SUCCEEDED(res))
2943 msi_free(tl_struct->path);
2944 tl_struct->path = NULL;
2946 return TRUE;
2949 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2950 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2952 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2953 return FALSE;
2956 msi_free(tl_struct->path);
2957 tl_struct->path = NULL;
2959 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2960 ITypeLib_Release(tl_struct->ptLib);
2962 return TRUE;
2965 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2967 MSIPACKAGE* package = (MSIPACKAGE*)param;
2968 LPCWSTR component;
2969 MSICOMPONENT *comp;
2970 MSIFILE *file;
2971 typelib_struct tl_struct;
2972 HMODULE module;
2973 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
2975 component = MSI_RecordGetString(row,3);
2976 comp = get_loaded_component(package,component);
2977 if (!comp)
2978 return ERROR_SUCCESS;
2980 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2982 TRACE("Skipping typelib reg due to disabled component\n");
2984 comp->Action = comp->Installed;
2986 return ERROR_SUCCESS;
2989 comp->Action = INSTALLSTATE_LOCAL;
2991 file = get_loaded_file( package, comp->KeyPath );
2992 if (!file)
2993 return ERROR_SUCCESS;
2995 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2996 if (module)
2998 LPCWSTR guid;
2999 guid = MSI_RecordGetString(row,1);
3000 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3001 tl_struct.source = strdupW( file->TargetPath );
3002 tl_struct.path = NULL;
3004 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3005 (LONG_PTR)&tl_struct);
3007 if (tl_struct.path)
3009 LPWSTR help = NULL;
3010 LPCWSTR helpid;
3011 HRESULT res;
3013 helpid = MSI_RecordGetString(row,6);
3015 if (helpid)
3016 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3017 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3018 msi_free(help);
3020 if (!SUCCEEDED(res))
3021 ERR("Failed to register type library %s\n",
3022 debugstr_w(tl_struct.path));
3023 else
3025 ui_actiondata(package,szRegisterTypeLibraries,row);
3027 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3030 ITypeLib_Release(tl_struct.ptLib);
3031 msi_free(tl_struct.path);
3033 else
3034 ERR("Failed to load type library %s\n",
3035 debugstr_w(tl_struct.source));
3037 FreeLibrary(module);
3038 msi_free(tl_struct.source);
3040 else
3041 ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
3043 return ERROR_SUCCESS;
3046 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3049 * OK this is a bit confusing.. I am given a _Component key and I believe
3050 * that the file that is being registered as a type library is the "key file
3051 * of that component" which I interpret to mean "The file in the KeyPath of
3052 * that component".
3054 UINT rc;
3055 MSIQUERY * view;
3056 static const WCHAR Query[] =
3057 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3058 '`','T','y','p','e','L','i','b','`',0};
3060 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3061 if (rc != ERROR_SUCCESS)
3062 return ERROR_SUCCESS;
3064 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3065 msiobj_release(&view->hdr);
3066 return rc;
3069 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3071 MSIPACKAGE *package = (MSIPACKAGE*)param;
3072 LPWSTR target_file, target_folder, filename;
3073 LPCWSTR buffer, extension;
3074 MSICOMPONENT *comp;
3075 static const WCHAR szlnk[]={'.','l','n','k',0};
3076 IShellLinkW *sl = NULL;
3077 IPersistFile *pf = NULL;
3078 HRESULT res;
3080 buffer = MSI_RecordGetString(row,4);
3081 comp = get_loaded_component(package,buffer);
3082 if (!comp)
3083 return ERROR_SUCCESS;
3085 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3087 TRACE("Skipping shortcut creation due to disabled component\n");
3089 comp->Action = comp->Installed;
3091 return ERROR_SUCCESS;
3094 comp->Action = INSTALLSTATE_LOCAL;
3096 ui_actiondata(package,szCreateShortcuts,row);
3098 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3099 &IID_IShellLinkW, (LPVOID *) &sl );
3101 if (FAILED( res ))
3103 ERR("CLSID_ShellLink not available\n");
3104 goto err;
3107 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3108 if (FAILED( res ))
3110 ERR("QueryInterface(IID_IPersistFile) failed\n");
3111 goto err;
3114 buffer = MSI_RecordGetString(row,2);
3115 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3117 /* may be needed because of a bug somewhere else */
3118 create_full_pathW(target_folder);
3120 filename = msi_dup_record_field( row, 3 );
3121 reduce_to_longfilename(filename);
3123 extension = strchrW(filename,'.');
3124 if (!extension || strcmpiW(extension,szlnk))
3126 int len = strlenW(filename);
3127 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3128 memcpy(filename + len, szlnk, sizeof(szlnk));
3130 target_file = build_directory_name(2, target_folder, filename);
3131 msi_free(target_folder);
3132 msi_free(filename);
3134 buffer = MSI_RecordGetString(row,5);
3135 if (strchrW(buffer,'['))
3137 LPWSTR deformated;
3138 deformat_string(package,buffer,&deformated);
3139 IShellLinkW_SetPath(sl,deformated);
3140 msi_free(deformated);
3142 else
3144 FIXME("poorly handled shortcut format, advertised shortcut\n");
3145 IShellLinkW_SetPath(sl,comp->FullKeypath);
3148 if (!MSI_RecordIsNull(row,6))
3150 LPWSTR deformated;
3151 buffer = MSI_RecordGetString(row,6);
3152 deformat_string(package,buffer,&deformated);
3153 IShellLinkW_SetArguments(sl,deformated);
3154 msi_free(deformated);
3157 if (!MSI_RecordIsNull(row,7))
3159 buffer = MSI_RecordGetString(row,7);
3160 IShellLinkW_SetDescription(sl,buffer);
3163 if (!MSI_RecordIsNull(row,8))
3164 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3166 if (!MSI_RecordIsNull(row,9))
3168 LPWSTR Path;
3169 INT index;
3171 buffer = MSI_RecordGetString(row,9);
3173 Path = build_icon_path(package,buffer);
3174 index = MSI_RecordGetInteger(row,10);
3176 /* no value means 0 */
3177 if (index == MSI_NULL_INTEGER)
3178 index = 0;
3180 IShellLinkW_SetIconLocation(sl,Path,index);
3181 msi_free(Path);
3184 if (!MSI_RecordIsNull(row,11))
3185 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3187 if (!MSI_RecordIsNull(row,12))
3189 LPWSTR Path;
3190 buffer = MSI_RecordGetString(row,12);
3191 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3192 if (Path)
3193 IShellLinkW_SetWorkingDirectory(sl,Path);
3194 msi_free(Path);
3197 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3198 IPersistFile_Save(pf,target_file,FALSE);
3200 msi_free(target_file);
3202 err:
3203 if (pf)
3204 IPersistFile_Release( pf );
3205 if (sl)
3206 IShellLinkW_Release( sl );
3208 return ERROR_SUCCESS;
3211 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3213 UINT rc;
3214 HRESULT res;
3215 MSIQUERY * view;
3216 static const WCHAR Query[] =
3217 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3218 '`','S','h','o','r','t','c','u','t','`',0};
3220 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3221 if (rc != ERROR_SUCCESS)
3222 return ERROR_SUCCESS;
3224 res = CoInitialize( NULL );
3225 if (FAILED (res))
3227 ERR("CoInitialize failed\n");
3228 return ERROR_FUNCTION_FAILED;
3231 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3232 msiobj_release(&view->hdr);
3234 CoUninitialize();
3236 return rc;
3239 static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
3241 MSIPACKAGE* package = (MSIPACKAGE*)param;
3242 HANDLE the_file;
3243 LPWSTR FilePath;
3244 LPCWSTR FileName;
3245 CHAR buffer[1024];
3246 DWORD sz;
3247 UINT rc;
3248 MSIRECORD *uirow;
3250 FileName = MSI_RecordGetString(row,1);
3251 if (!FileName)
3253 ERR("Unable to get FileName\n");
3254 return ERROR_SUCCESS;
3257 FilePath = build_icon_path(package,FileName);
3259 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3261 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3262 FILE_ATTRIBUTE_NORMAL, NULL);
3264 if (the_file == INVALID_HANDLE_VALUE)
3266 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3267 msi_free(FilePath);
3268 return ERROR_SUCCESS;
3273 DWORD write;
3274 sz = 1024;
3275 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3276 if (rc != ERROR_SUCCESS)
3278 ERR("Failed to get stream\n");
3279 CloseHandle(the_file);
3280 DeleteFileW(FilePath);
3281 break;
3283 WriteFile(the_file,buffer,sz,&write,NULL);
3284 } while (sz == 1024);
3286 msi_free(FilePath);
3288 CloseHandle(the_file);
3290 uirow = MSI_CreateRecord(1);
3291 MSI_RecordSetStringW(uirow,1,FileName);
3292 ui_actiondata(package,szPublishProduct,uirow);
3293 msiobj_release( &uirow->hdr );
3295 return ERROR_SUCCESS;
3298 static BOOL msi_check_publish(MSIPACKAGE *package)
3300 MSIFEATURE *feature;
3302 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3304 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3305 return TRUE;
3308 return FALSE;
3312 * 99% of the work done here is only done for
3313 * advertised installs. However this is where the
3314 * Icon table is processed and written out
3315 * so that is what I am going to do here.
3317 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3319 UINT rc;
3320 LPWSTR packname;
3321 MSIQUERY * view;
3322 MSISOURCELISTINFO *info;
3323 MSIMEDIADISK *disk;
3324 static const WCHAR Query[]=
3325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3326 '`','I','c','o','n','`',0};
3327 /* for registry stuff */
3328 HKEY hkey=0;
3329 HKEY hukey=0;
3330 HKEY hudkey=0, props=0;
3331 HKEY source;
3332 static const WCHAR szProductLanguage[] =
3333 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3334 static const WCHAR szARPProductIcon[] =
3335 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3336 static const WCHAR szProductVersion[] =
3337 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3338 static const WCHAR szSourceList[] =
3339 {'S','o','u','r','c','e','L','i','s','t',0};
3340 static const WCHAR szEmpty[] = {0};
3341 DWORD langid;
3342 LPWSTR buffer;
3343 DWORD size;
3344 MSIHANDLE hDb, hSumInfo;
3346 /* FIXME: also need to publish if the product is in advertise mode */
3347 if (!msi_check_publish(package))
3348 return ERROR_SUCCESS;
3350 /* write out icon files */
3352 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3353 if (rc == ERROR_SUCCESS)
3355 MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
3356 msiobj_release(&view->hdr);
3359 /* ok there is a lot more done here but i need to figure out what */
3361 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3363 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3364 if (rc != ERROR_SUCCESS)
3365 goto end;
3367 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
3368 if (rc != ERROR_SUCCESS)
3369 goto end;
3371 else
3373 rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
3374 if (rc != ERROR_SUCCESS)
3375 goto end;
3377 rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
3378 if (rc != ERROR_SUCCESS)
3379 goto end;
3381 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3382 if (rc != ERROR_SUCCESS)
3383 goto end;
3386 rc = RegCreateKeyW(hukey, szSourceList, &source);
3387 if (rc != ERROR_SUCCESS)
3388 goto end;
3390 RegCloseKey(source);
3392 rc = MSIREG_OpenUserDataProductKey(package->ProductCode,&hudkey,TRUE);
3393 if (rc != ERROR_SUCCESS)
3394 goto end;
3396 buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
3397 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
3398 msi_free(buffer);
3400 langid = msi_get_property_int( package, szProductLanguage, 0 );
3401 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_LANGUAGEW, langid );
3403 packname = strrchrW( package->PackagePath, '\\' ) + 1;
3404 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGENAMEW, packname );
3406 /* FIXME */
3407 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0 );
3408 msi_reg_set_val_dword( props, INSTALLPROPERTY_INSTANCETYPEW, 0 );
3410 buffer = msi_dup_property( package, szARPProductIcon );
3411 if (buffer)
3413 LPWSTR path = build_icon_path(package,buffer);
3414 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
3415 msi_free( path );
3417 msi_free(buffer);
3419 buffer = msi_dup_property( package, szProductVersion );
3420 if (buffer)
3422 DWORD verdword = msi_version_str_to_dword(buffer);
3423 msi_reg_set_val_dword( hukey, INSTALLPROPERTY_VERSIONW, verdword );
3425 msi_free(buffer);
3427 buffer = strrchrW( package->PackagePath, '\\') + 1;
3428 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3429 package->Context, MSICODE_PRODUCT,
3430 INSTALLPROPERTY_PACKAGENAMEW, buffer );
3431 if (rc != ERROR_SUCCESS)
3432 goto end;
3434 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3435 package->Context, MSICODE_PRODUCT,
3436 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty );
3437 if (rc != ERROR_SUCCESS)
3438 goto end;
3440 rc = MsiSourceListSetInfoW( package->ProductCode, NULL,
3441 package->Context, MSICODE_PRODUCT,
3442 INSTALLPROPERTY_DISKPROMPTW, szEmpty );
3443 if (rc != ERROR_SUCCESS)
3444 goto end;
3446 /* FIXME: Need to write more keys to the user registry */
3448 hDb= alloc_msihandle( &package->db->hdr );
3449 if (!hDb) {
3450 rc = ERROR_NOT_ENOUGH_MEMORY;
3451 goto end;
3453 rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo);
3454 MsiCloseHandle(hDb);
3455 if (rc == ERROR_SUCCESS)
3457 WCHAR guidbuffer[0x200];
3458 size = 0x200;
3459 rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
3460 guidbuffer, &size);
3461 if (rc == ERROR_SUCCESS)
3463 /* for now we only care about the first guid */
3464 LPWSTR ptr = strchrW(guidbuffer,';');
3465 if (ptr) *ptr = 0;
3466 msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, guidbuffer );
3468 else
3470 ERR("Unable to query Revision_Number...\n");
3471 rc = ERROR_SUCCESS;
3473 MsiCloseHandle(hSumInfo);
3475 else
3477 ERR("Unable to open Summary Information\n");
3478 rc = ERROR_SUCCESS;
3481 /* publish the SourceList info */
3482 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3484 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3485 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3486 info->options, info->value);
3487 else
3488 MsiSourceListSetInfoW(package->ProductCode, NULL,
3489 info->context, info->options,
3490 info->property, info->value);
3493 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3495 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3496 disk->context, disk->options,
3497 disk->disk_id, disk->volume_label, disk->disk_prompt);
3500 end:
3501 RegCloseKey(hkey);
3502 RegCloseKey(hukey);
3503 RegCloseKey(hudkey);
3504 RegCloseKey(props);
3506 return rc;
3509 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3511 MSIPACKAGE *package = (MSIPACKAGE*)param;
3512 LPCWSTR component,section,key,value,identifier,filename,dirproperty;
3513 LPWSTR deformated_section, deformated_key, deformated_value;
3514 LPWSTR folder, fullname = NULL;
3515 MSIRECORD * uirow;
3516 INT action;
3517 MSICOMPONENT *comp;
3518 static const WCHAR szWindowsFolder[] =
3519 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3521 component = MSI_RecordGetString(row, 8);
3522 comp = get_loaded_component(package,component);
3524 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3526 TRACE("Skipping ini file due to disabled component %s\n",
3527 debugstr_w(component));
3529 comp->Action = comp->Installed;
3531 return ERROR_SUCCESS;
3534 comp->Action = INSTALLSTATE_LOCAL;
3536 identifier = MSI_RecordGetString(row,1);
3537 filename = MSI_RecordGetString(row,2);
3538 dirproperty = MSI_RecordGetString(row,3);
3539 section = MSI_RecordGetString(row,4);
3540 key = MSI_RecordGetString(row,5);
3541 value = MSI_RecordGetString(row,6);
3542 action = MSI_RecordGetInteger(row,7);
3544 deformat_string(package,section,&deformated_section);
3545 deformat_string(package,key,&deformated_key);
3546 deformat_string(package,value,&deformated_value);
3548 if (dirproperty)
3550 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3551 if (!folder)
3552 folder = msi_dup_property( package, dirproperty );
3554 else
3555 folder = msi_dup_property( package, szWindowsFolder );
3557 if (!folder)
3559 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3560 goto cleanup;
3563 fullname = build_directory_name(2, folder, filename);
3565 if (action == 0)
3567 TRACE("Adding value %s to section %s in %s\n",
3568 debugstr_w(deformated_key), debugstr_w(deformated_section),
3569 debugstr_w(fullname));
3570 WritePrivateProfileStringW(deformated_section, deformated_key,
3571 deformated_value, fullname);
3573 else if (action == 1)
3575 WCHAR returned[10];
3576 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3577 returned, 10, fullname);
3578 if (returned[0] == 0)
3580 TRACE("Adding value %s to section %s in %s\n",
3581 debugstr_w(deformated_key), debugstr_w(deformated_section),
3582 debugstr_w(fullname));
3584 WritePrivateProfileStringW(deformated_section, deformated_key,
3585 deformated_value, fullname);
3588 else if (action == 3)
3589 FIXME("Append to existing section not yet implemented\n");
3591 uirow = MSI_CreateRecord(4);
3592 MSI_RecordSetStringW(uirow,1,identifier);
3593 MSI_RecordSetStringW(uirow,2,deformated_section);
3594 MSI_RecordSetStringW(uirow,3,deformated_key);
3595 MSI_RecordSetStringW(uirow,4,deformated_value);
3596 ui_actiondata(package,szWriteIniValues,uirow);
3597 msiobj_release( &uirow->hdr );
3598 cleanup:
3599 msi_free(fullname);
3600 msi_free(folder);
3601 msi_free(deformated_key);
3602 msi_free(deformated_value);
3603 msi_free(deformated_section);
3604 return ERROR_SUCCESS;
3607 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3609 UINT rc;
3610 MSIQUERY * view;
3611 static const WCHAR ExecSeqQuery[] =
3612 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3613 '`','I','n','i','F','i','l','e','`',0};
3615 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3616 if (rc != ERROR_SUCCESS)
3618 TRACE("no IniFile table\n");
3619 return ERROR_SUCCESS;
3622 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3623 msiobj_release(&view->hdr);
3624 return rc;
3627 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3629 MSIPACKAGE *package = (MSIPACKAGE*)param;
3630 LPCWSTR filename;
3631 LPWSTR FullName;
3632 MSIFILE *file;
3633 DWORD len;
3634 static const WCHAR ExeStr[] =
3635 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3636 static const WCHAR close[] = {'\"',0};
3637 STARTUPINFOW si;
3638 PROCESS_INFORMATION info;
3639 BOOL brc;
3640 MSIRECORD *uirow;
3641 LPWSTR uipath, p;
3643 memset(&si,0,sizeof(STARTUPINFOW));
3645 filename = MSI_RecordGetString(row,1);
3646 file = get_loaded_file( package, filename );
3648 if (!file)
3650 ERR("Unable to find file id %s\n",debugstr_w(filename));
3651 return ERROR_SUCCESS;
3654 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3656 FullName = msi_alloc(len*sizeof(WCHAR));
3657 strcpyW(FullName,ExeStr);
3658 strcatW( FullName, file->TargetPath );
3659 strcatW(FullName,close);
3661 TRACE("Registering %s\n",debugstr_w(FullName));
3662 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3663 &si, &info);
3665 if (brc)
3666 msi_dialog_check_messages(info.hProcess);
3668 msi_free(FullName);
3670 /* the UI chunk */
3671 uirow = MSI_CreateRecord( 2 );
3672 uipath = strdupW( file->TargetPath );
3673 p = strrchrW(uipath,'\\');
3674 if (p)
3675 p[0]=0;
3676 MSI_RecordSetStringW( uirow, 1, &p[1] );
3677 MSI_RecordSetStringW( uirow, 2, uipath);
3678 ui_actiondata( package, szSelfRegModules, uirow);
3679 msiobj_release( &uirow->hdr );
3680 msi_free( uipath );
3681 /* FIXME: call ui_progress? */
3683 return ERROR_SUCCESS;
3686 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3688 UINT rc;
3689 MSIQUERY * view;
3690 static const WCHAR ExecSeqQuery[] =
3691 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3692 '`','S','e','l','f','R','e','g','`',0};
3694 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3695 if (rc != ERROR_SUCCESS)
3697 TRACE("no SelfReg table\n");
3698 return ERROR_SUCCESS;
3701 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3702 msiobj_release(&view->hdr);
3704 return ERROR_SUCCESS;
3707 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3709 MSIFEATURE *feature;
3710 UINT rc;
3711 HKEY hkey=0;
3712 HKEY hukey=0;
3713 HKEY userdata=0;
3715 if (!msi_check_publish(package))
3716 return ERROR_SUCCESS;
3718 rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
3719 if (rc != ERROR_SUCCESS)
3720 goto end;
3722 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
3723 if (rc != ERROR_SUCCESS)
3724 goto end;
3726 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &userdata, TRUE);
3727 if (rc != ERROR_SUCCESS)
3728 goto end;
3730 /* here the guids are base 85 encoded */
3731 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3733 ComponentList *cl;
3734 LPWSTR data = NULL;
3735 GUID clsid;
3736 INT size;
3737 BOOL absent = FALSE;
3738 MSIRECORD *uirow;
3740 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3741 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3742 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3743 absent = TRUE;
3745 size = 1;
3746 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3748 size += 21;
3750 if (feature->Feature_Parent)
3751 size += strlenW( feature->Feature_Parent )+2;
3753 data = msi_alloc(size * sizeof(WCHAR));
3755 data[0] = 0;
3756 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3758 MSICOMPONENT* component = cl->component;
3759 WCHAR buf[21];
3761 buf[0] = 0;
3762 if (component->ComponentId)
3764 TRACE("From %s\n",debugstr_w(component->ComponentId));
3765 CLSIDFromString(component->ComponentId, &clsid);
3766 encode_base85_guid(&clsid,buf);
3767 TRACE("to %s\n",debugstr_w(buf));
3768 strcatW(data,buf);
3772 if (feature->Feature_Parent)
3774 static const WCHAR sep[] = {'\2',0};
3775 strcatW(data,sep);
3776 strcatW(data,feature->Feature_Parent);
3779 msi_reg_set_val_str( hkey, feature->Feature, data );
3780 msi_reg_set_val_str( userdata, feature->Feature, data );
3781 msi_free(data);
3783 size = 0;
3784 if (feature->Feature_Parent)
3785 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3786 if (!absent)
3788 static const WCHAR emptyW[] = {0};
3789 size += sizeof(WCHAR);
3790 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3791 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3793 else
3795 size += 2*sizeof(WCHAR);
3796 data = msi_alloc(size);
3797 data[0] = 0x6;
3798 data[1] = 0;
3799 if (feature->Feature_Parent)
3800 strcpyW( &data[1], feature->Feature_Parent );
3801 RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
3802 (LPBYTE)data,size);
3803 msi_free(data);
3806 /* the UI chunk */
3807 uirow = MSI_CreateRecord( 1 );
3808 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3809 ui_actiondata( package, szPublishFeatures, uirow);
3810 msiobj_release( &uirow->hdr );
3811 /* FIXME: call ui_progress? */
3814 end:
3815 RegCloseKey(hkey);
3816 RegCloseKey(hukey);
3817 return rc;
3820 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3822 UINT r;
3823 HKEY hkey;
3825 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3827 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3828 if (r == ERROR_SUCCESS)
3830 RegDeleteValueW(hkey, feature->Feature);
3831 RegCloseKey(hkey);
3834 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3835 if (r == ERROR_SUCCESS)
3837 RegDeleteValueW(hkey, feature->Feature);
3838 RegCloseKey(hkey);
3841 return ERROR_SUCCESS;
3844 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3846 MSIFEATURE *feature;
3848 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3850 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3851 return FALSE;
3854 return TRUE;
3857 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3859 MSIFEATURE *feature;
3861 if (!msi_check_unpublish(package))
3862 return ERROR_SUCCESS;
3864 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3866 msi_unpublish_feature(package, feature);
3869 return ERROR_SUCCESS;
3872 static UINT msi_get_local_package_name( LPWSTR path )
3874 static const WCHAR szInstaller[] = {
3875 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3876 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
3877 DWORD time, len, i;
3878 HANDLE handle;
3880 time = GetTickCount();
3881 GetWindowsDirectoryW( path, MAX_PATH );
3882 lstrcatW( path, szInstaller );
3883 CreateDirectoryW( path, NULL );
3885 len = lstrlenW(path);
3886 for (i=0; i<0x10000; i++)
3888 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
3889 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
3890 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
3891 if (handle != INVALID_HANDLE_VALUE)
3893 CloseHandle(handle);
3894 break;
3896 if (GetLastError() != ERROR_FILE_EXISTS &&
3897 GetLastError() != ERROR_SHARING_VIOLATION)
3898 return ERROR_FUNCTION_FAILED;
3901 return ERROR_SUCCESS;
3904 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
3906 WCHAR packagefile[MAX_PATH];
3907 HKEY props;
3908 UINT r;
3910 r = msi_get_local_package_name( packagefile );
3911 if (r != ERROR_SUCCESS)
3912 return r;
3914 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
3916 r = CopyFileW( package->db->path, packagefile, FALSE);
3918 if (!r)
3920 ERR("Unable to copy package (%s -> %s) (error %d)\n",
3921 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
3922 return ERROR_FUNCTION_FAILED;
3925 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
3927 r = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
3928 if (r != ERROR_SUCCESS)
3929 return r;
3931 msi_reg_set_val_str(props, INSTALLPROPERTY_LOCALPACKAGEW, packagefile);
3932 RegCloseKey(props);
3933 return ERROR_SUCCESS;
3936 static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
3938 LPWSTR prop, val, key;
3939 static const LPCSTR propval[] = {
3940 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
3941 "ARPCONTACT", "Contact",
3942 "ARPCOMMENTS", "Comments",
3943 "ProductName", "DisplayName",
3944 "ProductVersion", "DisplayVersion",
3945 "ARPHELPLINK", "HelpLink",
3946 "ARPHELPTELEPHONE", "HelpTelephone",
3947 "ARPINSTALLLOCATION", "InstallLocation",
3948 "SourceDir", "InstallSource",
3949 "Manufacturer", "Publisher",
3950 "ARPREADME", "Readme",
3951 "ARPSIZE", "Size",
3952 "ARPURLINFOABOUT", "URLInfoAbout",
3953 "ARPURLUPDATEINFO", "URLUpdateInfo",
3954 NULL,
3956 const LPCSTR *p = propval;
3958 while( *p )
3960 prop = strdupAtoW( *p++ );
3961 key = strdupAtoW( *p++ );
3962 val = msi_dup_property( package, prop );
3963 msi_reg_set_val_str( hkey, key, val );
3964 msi_free(val);
3965 msi_free(key);
3966 msi_free(prop);
3968 return ERROR_SUCCESS;
3971 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
3973 HKEY hkey=0;
3974 HKEY hudkey=0, props=0;
3975 LPWSTR buffer = NULL;
3976 UINT rc;
3977 DWORD size, langid;
3978 static const WCHAR szWindowsInstaller[] =
3979 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
3980 static const WCHAR szUpgradeCode[] =
3981 {'U','p','g','r','a','d','e','C','o','d','e',0};
3982 static const WCHAR modpath_fmt[] =
3983 {'M','s','i','E','x','e','c','.','e','x','e',' ',
3984 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
3985 static const WCHAR szModifyPath[] =
3986 {'M','o','d','i','f','y','P','a','t','h',0};
3987 static const WCHAR szUninstallString[] =
3988 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
3989 static const WCHAR szEstimatedSize[] =
3990 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
3991 static const WCHAR szProductLanguage[] =
3992 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3993 static const WCHAR szProductVersion[] =
3994 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3995 static const WCHAR szProductName[] =
3996 {'P','r','o','d','u','c','t','N','a','m','e',0};
3997 static const WCHAR szDisplayName[] =
3998 {'D','i','s','p','l','a','y','N','a','m','e',0};
3999 static const WCHAR szDisplayVersion[] =
4000 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4001 static const WCHAR szManufacturer[] =
4002 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4004 SYSTEMTIME systime;
4005 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4006 LPWSTR upgrade_code;
4007 WCHAR szDate[9];
4009 /* FIXME: also need to publish if the product is in advertise mode */
4010 if (!msi_check_publish(package))
4011 return ERROR_SUCCESS;
4013 rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
4014 if (rc != ERROR_SUCCESS)
4015 return rc;
4017 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4019 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4020 if (rc != ERROR_SUCCESS)
4021 return rc;
4023 else
4025 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4026 if (rc != ERROR_SUCCESS)
4027 return rc;
4030 /* dump all the info i can grab */
4031 /* FIXME: Flesh out more information */
4033 msi_write_uninstall_property_vals( package, hkey );
4035 msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
4037 msi_make_package_local( package, hkey );
4039 /* do ModifyPath and UninstallString */
4040 size = deformat_string(package,modpath_fmt,&buffer);
4041 RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4042 RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
4043 msi_free(buffer);
4045 /* FIXME: Write real Estimated Size when we have it */
4046 msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
4048 buffer = msi_dup_property( package, szProductName );
4049 msi_reg_set_val_str( props, szDisplayName, buffer );
4050 msi_free(buffer);
4052 buffer = msi_dup_property( package, cszSourceDir );
4053 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4054 msi_free(buffer);
4056 buffer = msi_dup_property( package, szManufacturer );
4057 msi_reg_set_val_str( props, INSTALLPROPERTY_PUBLISHERW, buffer);
4058 msi_free(buffer);
4060 GetLocalTime(&systime);
4061 sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
4062 msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
4063 msi_reg_set_val_str( props, INSTALLPROPERTY_INSTALLDATEW, szDate );
4065 langid = msi_get_property_int( package, szProductLanguage, 0 );
4066 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
4068 buffer = msi_dup_property( package, szProductVersion );
4069 msi_reg_set_val_str( props, szDisplayVersion, buffer );
4070 if (buffer)
4072 DWORD verdword = msi_version_str_to_dword(buffer);
4074 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
4075 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONW, verdword );
4076 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4077 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
4078 msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4079 msi_reg_set_val_dword( props, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
4081 msi_free(buffer);
4083 /* Handle Upgrade Codes */
4084 upgrade_code = msi_dup_property( package, szUpgradeCode );
4085 if (upgrade_code)
4087 HKEY hkey2;
4088 WCHAR squashed[33];
4089 MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4090 squash_guid(package->ProductCode,squashed);
4091 msi_reg_set_val_str( hkey2, squashed, NULL );
4092 RegCloseKey(hkey2);
4093 MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
4094 squash_guid(package->ProductCode,squashed);
4095 msi_reg_set_val_str( hkey2, squashed, NULL );
4096 RegCloseKey(hkey2);
4098 msi_free(upgrade_code);
4101 RegCloseKey(hkey);
4103 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
4104 if (rc != ERROR_SUCCESS)
4105 return rc;
4107 RegCloseKey(hudkey);
4109 msi_reg_set_val_dword( props, szWindowsInstaller, 1 );
4110 RegCloseKey(props);
4112 return ERROR_SUCCESS;
4115 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4117 return execute_script(package,INSTALL_SCRIPT);
4120 static UINT msi_unpublish_product(MSIPACKAGE *package)
4122 LPWSTR remove = NULL;
4123 LPWSTR *features = NULL;
4124 BOOL full_uninstall = TRUE;
4125 MSIFEATURE *feature;
4127 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4128 static const WCHAR szAll[] = {'A','L','L',0};
4130 remove = msi_dup_property(package, szRemove);
4131 if (!remove)
4132 return ERROR_SUCCESS;
4134 features = msi_split_string(remove, ',');
4135 if (!features)
4137 msi_free(remove);
4138 ERR("REMOVE feature list is empty!\n");
4139 return ERROR_FUNCTION_FAILED;
4142 if (!lstrcmpW(features[0], szAll))
4143 full_uninstall = TRUE;
4144 else
4146 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4148 if (feature->Action != INSTALLSTATE_ABSENT)
4149 full_uninstall = FALSE;
4153 if (!full_uninstall)
4154 goto done;
4156 MSIREG_DeleteProductKey(package->ProductCode);
4157 MSIREG_DeleteUserProductKey(package->ProductCode);
4158 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4159 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4160 MSIREG_DeleteUninstallKey(package->ProductCode);
4162 done:
4163 msi_free(remove);
4164 msi_free(features);
4165 return ERROR_SUCCESS;
4168 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4170 UINT rc;
4172 rc = msi_unpublish_product(package);
4173 if (rc != ERROR_SUCCESS)
4174 return rc;
4176 /* turn off scheduling */
4177 package->script->CurrentlyScripting= FALSE;
4179 /* first do the same as an InstallExecute */
4180 rc = ACTION_InstallExecute(package);
4181 if (rc != ERROR_SUCCESS)
4182 return rc;
4184 /* then handle Commit Actions */
4185 rc = execute_script(package,COMMIT_SCRIPT);
4187 return rc;
4190 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4192 static const WCHAR RunOnce[] = {
4193 'S','o','f','t','w','a','r','e','\\',
4194 'M','i','c','r','o','s','o','f','t','\\',
4195 'W','i','n','d','o','w','s','\\',
4196 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4197 'R','u','n','O','n','c','e',0};
4198 static const WCHAR InstallRunOnce[] = {
4199 'S','o','f','t','w','a','r','e','\\',
4200 'M','i','c','r','o','s','o','f','t','\\',
4201 'W','i','n','d','o','w','s','\\',
4202 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4203 'I','n','s','t','a','l','l','e','r','\\',
4204 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4206 static const WCHAR msiexec_fmt[] = {
4207 '%','s',
4208 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4209 '\"','%','s','\"',0};
4210 static const WCHAR install_fmt[] = {
4211 '/','I',' ','\"','%','s','\"',' ',
4212 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4213 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4214 WCHAR buffer[256], sysdir[MAX_PATH];
4215 HKEY hkey;
4216 WCHAR squished_pc[100];
4218 squash_guid(package->ProductCode,squished_pc);
4220 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4221 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4222 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4223 squished_pc);
4225 msi_reg_set_val_str( hkey, squished_pc, buffer );
4226 RegCloseKey(hkey);
4228 TRACE("Reboot command %s\n",debugstr_w(buffer));
4230 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4231 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4233 msi_reg_set_val_str( hkey, squished_pc, buffer );
4234 RegCloseKey(hkey);
4236 return ERROR_INSTALL_SUSPEND;
4239 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4241 DWORD attrib;
4242 UINT rc;
4245 * We are currently doing what should be done here in the top level Install
4246 * however for Administrative and uninstalls this step will be needed
4248 if (!package->PackagePath)
4249 return ERROR_SUCCESS;
4251 msi_set_sourcedir_props(package, TRUE);
4253 attrib = GetFileAttributesW(package->db->path);
4254 if (attrib == INVALID_FILE_ATTRIBUTES)
4256 LPWSTR prompt;
4257 LPWSTR msg;
4258 DWORD size = 0;
4260 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4261 package->Context, MSICODE_PRODUCT,
4262 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4263 if (rc == ERROR_MORE_DATA)
4265 prompt = msi_alloc(size * sizeof(WCHAR));
4266 MsiSourceListGetInfoW(package->ProductCode, NULL,
4267 package->Context, MSICODE_PRODUCT,
4268 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4270 else
4271 prompt = strdupW(package->db->path);
4273 msg = generate_error_string(package,1302,1,prompt);
4274 while(attrib == INVALID_FILE_ATTRIBUTES)
4276 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4277 if (rc == IDCANCEL)
4279 rc = ERROR_INSTALL_USEREXIT;
4280 break;
4282 attrib = GetFileAttributesW(package->db->path);
4284 msi_free(prompt);
4285 rc = ERROR_SUCCESS;
4287 else
4288 return ERROR_SUCCESS;
4290 return rc;
4293 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4295 HKEY hkey=0;
4296 LPWSTR buffer;
4297 LPWSTR productid;
4298 UINT rc,i;
4300 static const WCHAR szPropKeys[][80] =
4302 {'P','r','o','d','u','c','t','I','D',0},
4303 {'U','S','E','R','N','A','M','E',0},
4304 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4305 {0},
4308 static const WCHAR szRegKeys[][80] =
4310 {'P','r','o','d','u','c','t','I','D',0},
4311 {'R','e','g','O','w','n','e','r',0},
4312 {'R','e','g','C','o','m','p','a','n','y',0},
4313 {0},
4316 if (msi_check_unpublish(package))
4318 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4319 return ERROR_SUCCESS;
4322 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4323 if (!productid)
4324 return ERROR_SUCCESS;
4326 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4327 if (rc != ERROR_SUCCESS)
4328 goto end;
4330 for( i = 0; szPropKeys[i][0]; i++ )
4332 buffer = msi_dup_property( package, szPropKeys[i] );
4333 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4334 msi_free( buffer );
4337 end:
4338 msi_free(productid);
4339 RegCloseKey(hkey);
4341 /* FIXME: call ui_actiondata */
4343 return rc;
4347 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4349 UINT rc;
4351 package->script->InWhatSequence |= SEQUENCE_EXEC;
4352 rc = ACTION_ProcessExecSequence(package,FALSE);
4353 return rc;
4357 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4359 MSIPACKAGE *package = (MSIPACKAGE*)param;
4360 LPCWSTR compgroupid=NULL;
4361 LPCWSTR feature=NULL;
4362 LPCWSTR text = NULL;
4363 LPCWSTR qualifier = NULL;
4364 LPCWSTR component = NULL;
4365 LPWSTR advertise = NULL;
4366 LPWSTR output = NULL;
4367 HKEY hkey;
4368 UINT rc = ERROR_SUCCESS;
4369 MSICOMPONENT *comp;
4370 DWORD sz = 0;
4371 MSIRECORD *uirow;
4373 component = MSI_RecordGetString(rec,3);
4374 comp = get_loaded_component(package,component);
4376 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4377 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4378 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4380 TRACE("Skipping: Component %s not scheduled for install\n",
4381 debugstr_w(component));
4383 return ERROR_SUCCESS;
4386 compgroupid = MSI_RecordGetString(rec,1);
4387 qualifier = MSI_RecordGetString(rec,2);
4389 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4390 if (rc != ERROR_SUCCESS)
4391 goto end;
4393 text = MSI_RecordGetString(rec,4);
4394 feature = MSI_RecordGetString(rec,5);
4396 advertise = create_component_advertise_string(package, comp, feature);
4398 sz = strlenW(advertise);
4400 if (text)
4401 sz += lstrlenW(text);
4403 sz+=3;
4404 sz *= sizeof(WCHAR);
4406 output = msi_alloc_zero(sz);
4407 strcpyW(output,advertise);
4408 msi_free(advertise);
4410 if (text)
4411 strcatW(output,text);
4413 msi_reg_set_val_multi_str( hkey, qualifier, output );
4415 end:
4416 RegCloseKey(hkey);
4417 msi_free(output);
4419 /* the UI chunk */
4420 uirow = MSI_CreateRecord( 2 );
4421 MSI_RecordSetStringW( uirow, 1, compgroupid );
4422 MSI_RecordSetStringW( uirow, 2, qualifier);
4423 ui_actiondata( package, szPublishComponents, uirow);
4424 msiobj_release( &uirow->hdr );
4425 /* FIXME: call ui_progress? */
4427 return rc;
4431 * At present I am ignorning the advertised components part of this and only
4432 * focusing on the qualified component sets
4434 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4436 UINT rc;
4437 MSIQUERY * view;
4438 static const WCHAR ExecSeqQuery[] =
4439 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4440 '`','P','u','b','l','i','s','h',
4441 'C','o','m','p','o','n','e','n','t','`',0};
4443 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4444 if (rc != ERROR_SUCCESS)
4445 return ERROR_SUCCESS;
4447 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4448 msiobj_release(&view->hdr);
4450 return rc;
4453 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4455 MSIPACKAGE *package = (MSIPACKAGE*)param;
4456 MSIRECORD *row;
4457 MSIFILE *file;
4458 SC_HANDLE hscm, service = NULL;
4459 LPCWSTR comp, depends, pass;
4460 LPWSTR name = NULL, disp = NULL;
4461 LPCWSTR load_order, serv_name, key;
4462 DWORD serv_type, start_type;
4463 DWORD err_control;
4465 static const WCHAR query[] =
4466 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4467 '`','C','o','m','p','o','n','e','n','t','`',' ',
4468 'W','H','E','R','E',' ',
4469 '`','C','o','m','p','o','n','e','n','t','`',' ',
4470 '=','\'','%','s','\'',0};
4472 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4473 if (!hscm)
4475 ERR("Failed to open the SC Manager!\n");
4476 goto done;
4479 start_type = MSI_RecordGetInteger(rec, 5);
4480 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4481 goto done;
4483 depends = MSI_RecordGetString(rec, 8);
4484 if (depends && *depends)
4485 FIXME("Dependency list unhandled!\n");
4487 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4488 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4489 serv_type = MSI_RecordGetInteger(rec, 4);
4490 err_control = MSI_RecordGetInteger(rec, 6);
4491 load_order = MSI_RecordGetString(rec, 7);
4492 serv_name = MSI_RecordGetString(rec, 9);
4493 pass = MSI_RecordGetString(rec, 10);
4494 comp = MSI_RecordGetString(rec, 12);
4496 /* fetch the service path */
4497 row = MSI_QueryGetRecord(package->db, query, comp);
4498 if (!row)
4500 ERR("Control query failed!\n");
4501 goto done;
4504 key = MSI_RecordGetString(row, 6);
4506 file = get_loaded_file(package, key);
4507 msiobj_release(&row->hdr);
4508 if (!file)
4510 ERR("Failed to load the service file\n");
4511 goto done;
4514 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4515 start_type, err_control, file->TargetPath,
4516 load_order, NULL, NULL, serv_name, pass);
4517 if (!service)
4519 if (GetLastError() != ERROR_SERVICE_EXISTS)
4520 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4523 done:
4524 CloseServiceHandle(service);
4525 CloseServiceHandle(hscm);
4526 msi_free(name);
4527 msi_free(disp);
4529 return ERROR_SUCCESS;
4532 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4534 UINT rc;
4535 MSIQUERY * view;
4536 static const WCHAR ExecSeqQuery[] =
4537 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4538 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4540 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4541 if (rc != ERROR_SUCCESS)
4542 return ERROR_SUCCESS;
4544 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4545 msiobj_release(&view->hdr);
4547 return rc;
4550 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4551 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4553 LPCWSTR *vector, *temp_vector;
4554 LPWSTR p, q;
4555 DWORD sep_len;
4557 static const WCHAR separator[] = {'[','~',']',0};
4559 *numargs = 0;
4560 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4562 if (!args)
4563 return NULL;
4565 vector = msi_alloc(sizeof(LPWSTR));
4566 if (!vector)
4567 return NULL;
4569 p = args;
4572 (*numargs)++;
4573 vector[*numargs - 1] = p;
4575 if ((q = strstrW(p, separator)))
4577 *q = '\0';
4579 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4580 if (!temp_vector)
4582 msi_free(vector);
4583 return NULL;
4585 vector = temp_vector;
4587 p = q + sep_len;
4589 } while (q);
4591 return vector;
4594 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4596 MSIPACKAGE *package = (MSIPACKAGE *)param;
4597 MSICOMPONENT *comp;
4598 SC_HANDLE scm, service = NULL;
4599 LPCWSTR name, *vector = NULL;
4600 LPWSTR args;
4601 DWORD event, numargs;
4602 UINT r = ERROR_FUNCTION_FAILED;
4604 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4605 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4606 return ERROR_SUCCESS;
4608 name = MSI_RecordGetString(rec, 2);
4609 event = MSI_RecordGetInteger(rec, 3);
4610 args = strdupW(MSI_RecordGetString(rec, 4));
4612 if (!(event & msidbServiceControlEventStart))
4613 return ERROR_SUCCESS;
4615 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4616 if (!scm)
4618 ERR("Failed to open the service control manager\n");
4619 goto done;
4622 service = OpenServiceW(scm, name, SERVICE_START);
4623 if (!service)
4625 ERR("Failed to open service %s\n", debugstr_w(name));
4626 goto done;
4629 vector = msi_service_args_to_vector(args, &numargs);
4631 if (!StartServiceW(service, numargs, vector))
4633 ERR("Failed to start service %s\n", debugstr_w(name));
4634 goto done;
4637 r = ERROR_SUCCESS;
4639 done:
4640 CloseServiceHandle(service);
4641 CloseServiceHandle(scm);
4643 msi_free(args);
4644 msi_free(vector);
4645 return r;
4648 static UINT ACTION_StartServices( MSIPACKAGE *package )
4650 UINT rc;
4651 MSIQUERY *view;
4653 static const WCHAR query[] = {
4654 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4655 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4657 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4658 if (rc != ERROR_SUCCESS)
4659 return ERROR_SUCCESS;
4661 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4662 msiobj_release(&view->hdr);
4664 return rc;
4667 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4669 DWORD i, needed, count;
4670 ENUM_SERVICE_STATUSW *dependencies;
4671 SERVICE_STATUS ss;
4672 SC_HANDLE depserv;
4674 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4675 0, &needed, &count))
4676 return TRUE;
4678 if (GetLastError() != ERROR_MORE_DATA)
4679 return FALSE;
4681 dependencies = msi_alloc(needed);
4682 if (!dependencies)
4683 return FALSE;
4685 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4686 needed, &needed, &count))
4687 goto error;
4689 for (i = 0; i < count; i++)
4691 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4692 SERVICE_STOP | SERVICE_QUERY_STATUS);
4693 if (!depserv)
4694 goto error;
4696 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4697 goto error;
4700 return TRUE;
4702 error:
4703 msi_free(dependencies);
4704 return FALSE;
4707 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4709 MSIPACKAGE *package = (MSIPACKAGE *)param;
4710 MSICOMPONENT *comp;
4711 SERVICE_STATUS status;
4712 SERVICE_STATUS_PROCESS ssp;
4713 SC_HANDLE scm = NULL, service = NULL;
4714 LPWSTR name, args;
4715 DWORD event, needed;
4717 event = MSI_RecordGetInteger(rec, 3);
4718 if (!(event & msidbServiceControlEventStop))
4719 return ERROR_SUCCESS;
4721 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4722 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4723 return ERROR_SUCCESS;
4725 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4726 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4727 args = strdupW(MSI_RecordGetString(rec, 4));
4729 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4730 if (!scm)
4732 WARN("Failed to open the SCM: %d\n", GetLastError());
4733 goto done;
4736 service = OpenServiceW(scm, name,
4737 SERVICE_STOP |
4738 SERVICE_QUERY_STATUS |
4739 SERVICE_ENUMERATE_DEPENDENTS);
4740 if (!service)
4742 WARN("Failed to open service (%s): %d\n",
4743 debugstr_w(name), GetLastError());
4744 goto done;
4747 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4748 sizeof(SERVICE_STATUS_PROCESS), &needed))
4750 WARN("Failed to query service status (%s): %d\n",
4751 debugstr_w(name), GetLastError());
4752 goto done;
4755 if (ssp.dwCurrentState == SERVICE_STOPPED)
4756 goto done;
4758 stop_service_dependents(scm, service);
4760 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4761 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4763 done:
4764 CloseServiceHandle(service);
4765 CloseServiceHandle(scm);
4766 msi_free(name);
4767 msi_free(args);
4769 return ERROR_SUCCESS;
4772 static UINT ACTION_StopServices( MSIPACKAGE *package )
4774 UINT rc;
4775 MSIQUERY *view;
4777 static const WCHAR query[] = {
4778 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4779 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4781 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4782 if (rc != ERROR_SUCCESS)
4783 return ERROR_SUCCESS;
4785 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4786 msiobj_release(&view->hdr);
4788 return rc;
4791 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4793 MSIFILE *file;
4795 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4797 if (!lstrcmpW(file->File, filename))
4798 return file;
4801 return NULL;
4804 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4806 MSIPACKAGE *package = (MSIPACKAGE*)param;
4807 LPWSTR driver, driver_path, ptr;
4808 WCHAR outpath[MAX_PATH];
4809 MSIFILE *driver_file, *setup_file;
4810 LPCWSTR desc;
4811 DWORD len, usage;
4812 UINT r = ERROR_SUCCESS;
4814 static const WCHAR driver_fmt[] = {
4815 'D','r','i','v','e','r','=','%','s',0};
4816 static const WCHAR setup_fmt[] = {
4817 'S','e','t','u','p','=','%','s',0};
4818 static const WCHAR usage_fmt[] = {
4819 'F','i','l','e','U','s','a','g','e','=','1',0};
4821 desc = MSI_RecordGetString(rec, 3);
4823 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4824 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4826 if (!driver_file || !setup_file)
4828 ERR("ODBC Driver entry not found!\n");
4829 return ERROR_FUNCTION_FAILED;
4832 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4833 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4834 lstrlenW(usage_fmt) + 1;
4835 driver = msi_alloc(len * sizeof(WCHAR));
4836 if (!driver)
4837 return ERROR_OUTOFMEMORY;
4839 ptr = driver;
4840 lstrcpyW(ptr, desc);
4841 ptr += lstrlenW(ptr) + 1;
4843 sprintfW(ptr, driver_fmt, driver_file->FileName);
4844 ptr += lstrlenW(ptr) + 1;
4846 sprintfW(ptr, setup_fmt, setup_file->FileName);
4847 ptr += lstrlenW(ptr) + 1;
4849 lstrcpyW(ptr, usage_fmt);
4850 ptr += lstrlenW(ptr) + 1;
4851 *ptr = '\0';
4853 driver_path = strdupW(driver_file->TargetPath);
4854 ptr = strrchrW(driver_path, '\\');
4855 if (ptr) *ptr = '\0';
4857 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4858 NULL, ODBC_INSTALL_COMPLETE, &usage))
4860 ERR("Failed to install SQL driver!\n");
4861 r = ERROR_FUNCTION_FAILED;
4864 msi_free(driver);
4865 msi_free(driver_path);
4867 return r;
4870 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4872 MSIPACKAGE *package = (MSIPACKAGE*)param;
4873 LPWSTR translator, translator_path, ptr;
4874 WCHAR outpath[MAX_PATH];
4875 MSIFILE *translator_file, *setup_file;
4876 LPCWSTR desc;
4877 DWORD len, usage;
4878 UINT r = ERROR_SUCCESS;
4880 static const WCHAR translator_fmt[] = {
4881 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
4882 static const WCHAR setup_fmt[] = {
4883 'S','e','t','u','p','=','%','s',0};
4885 desc = MSI_RecordGetString(rec, 3);
4887 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4888 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4890 if (!translator_file || !setup_file)
4892 ERR("ODBC Translator entry not found!\n");
4893 return ERROR_FUNCTION_FAILED;
4896 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
4897 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
4898 translator = msi_alloc(len * sizeof(WCHAR));
4899 if (!translator)
4900 return ERROR_OUTOFMEMORY;
4902 ptr = translator;
4903 lstrcpyW(ptr, desc);
4904 ptr += lstrlenW(ptr) + 1;
4906 sprintfW(ptr, translator_fmt, translator_file->FileName);
4907 ptr += lstrlenW(ptr) + 1;
4909 sprintfW(ptr, setup_fmt, setup_file->FileName);
4910 ptr += lstrlenW(ptr) + 1;
4911 *ptr = '\0';
4913 translator_path = strdupW(translator_file->TargetPath);
4914 ptr = strrchrW(translator_path, '\\');
4915 if (ptr) *ptr = '\0';
4917 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
4918 NULL, ODBC_INSTALL_COMPLETE, &usage))
4920 ERR("Failed to install SQL translator!\n");
4921 r = ERROR_FUNCTION_FAILED;
4924 msi_free(translator);
4925 msi_free(translator_path);
4927 return r;
4930 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
4932 LPWSTR attrs;
4933 LPCWSTR desc, driver;
4934 WORD request = ODBC_ADD_SYS_DSN;
4935 INT registration;
4936 DWORD len;
4937 UINT r = ERROR_SUCCESS;
4939 static const WCHAR attrs_fmt[] = {
4940 'D','S','N','=','%','s',0 };
4942 desc = MSI_RecordGetString(rec, 3);
4943 driver = MSI_RecordGetString(rec, 4);
4944 registration = MSI_RecordGetInteger(rec, 5);
4946 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
4947 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
4949 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
4950 attrs = msi_alloc(len * sizeof(WCHAR));
4951 if (!attrs)
4952 return ERROR_OUTOFMEMORY;
4954 sprintfW(attrs, attrs_fmt, desc);
4955 attrs[len - 1] = '\0';
4957 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
4959 ERR("Failed to install SQL data source!\n");
4960 r = ERROR_FUNCTION_FAILED;
4963 msi_free(attrs);
4965 return r;
4968 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
4970 UINT rc;
4971 MSIQUERY *view;
4973 static const WCHAR driver_query[] = {
4974 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4975 'O','D','B','C','D','r','i','v','e','r',0 };
4977 static const WCHAR translator_query[] = {
4978 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4979 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
4981 static const WCHAR source_query[] = {
4982 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4983 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
4985 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
4986 if (rc != ERROR_SUCCESS)
4987 return ERROR_SUCCESS;
4989 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
4990 msiobj_release(&view->hdr);
4992 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
4993 if (rc != ERROR_SUCCESS)
4994 return ERROR_SUCCESS;
4996 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
4997 msiobj_release(&view->hdr);
4999 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5000 if (rc != ERROR_SUCCESS)
5001 return ERROR_SUCCESS;
5003 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5004 msiobj_release(&view->hdr);
5006 return rc;
5009 #define ENV_ACT_SETALWAYS 0x1
5010 #define ENV_ACT_SETABSENT 0x2
5011 #define ENV_ACT_REMOVE 0x4
5012 #define ENV_ACT_REMOVEMATCH 0x8
5014 #define ENV_MOD_MACHINE 0x20000000
5015 #define ENV_MOD_APPEND 0x40000000
5016 #define ENV_MOD_PREFIX 0x80000000
5017 #define ENV_MOD_MASK 0xC0000000
5019 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5021 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5023 LPCWSTR cptr = *name;
5024 LPCWSTR ptr = *value;
5026 static const WCHAR prefix[] = {'[','~',']',0};
5027 static const int prefix_len = 3;
5029 *flags = 0;
5030 while (*cptr)
5032 if (*cptr == '=')
5033 *flags |= ENV_ACT_SETALWAYS;
5034 else if (*cptr == '+')
5035 *flags |= ENV_ACT_SETABSENT;
5036 else if (*cptr == '-')
5037 *flags |= ENV_ACT_REMOVE;
5038 else if (*cptr == '!')
5039 *flags |= ENV_ACT_REMOVEMATCH;
5040 else if (*cptr == '*')
5041 *flags |= ENV_MOD_MACHINE;
5042 else
5043 break;
5045 cptr++;
5046 (*name)++;
5049 if (!*cptr)
5051 ERR("Missing environment variable\n");
5052 return ERROR_FUNCTION_FAILED;
5055 if (!strncmpW(ptr, prefix, prefix_len))
5057 *flags |= ENV_MOD_APPEND;
5058 *value += lstrlenW(prefix);
5060 else if (lstrlenW(*value) >= prefix_len)
5062 ptr += lstrlenW(ptr) - prefix_len;
5063 if (!lstrcmpW(ptr, prefix))
5065 *flags |= ENV_MOD_PREFIX;
5066 /* the "[~]" will be removed by deformat_string */;
5070 if (!*flags ||
5071 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5072 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5073 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5074 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5076 ERR("Invalid flags: %08x\n", *flags);
5077 return ERROR_FUNCTION_FAILED;
5080 return ERROR_SUCCESS;
5083 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5085 MSIPACKAGE *package = param;
5086 LPCWSTR name, value;
5087 LPWSTR data = NULL, newval = NULL;
5088 LPWSTR deformatted = NULL, ptr;
5089 DWORD flags, type, size;
5090 LONG res;
5091 HKEY env = NULL, root;
5092 LPCWSTR environment;
5094 static const WCHAR user_env[] =
5095 {'E','n','v','i','r','o','n','m','e','n','t',0};
5096 static const WCHAR machine_env[] =
5097 {'S','y','s','t','e','m','\\',
5098 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5099 'C','o','n','t','r','o','l','\\',
5100 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5101 'E','n','v','i','r','o','n','m','e','n','t',0};
5102 static const WCHAR semicolon[] = {';',0};
5104 name = MSI_RecordGetString(rec, 2);
5105 value = MSI_RecordGetString(rec, 3);
5107 res = env_set_flags(&name, &value, &flags);
5108 if (res != ERROR_SUCCESS)
5109 goto done;
5111 deformat_string(package, value, &deformatted);
5112 if (!deformatted)
5114 res = ERROR_OUTOFMEMORY;
5115 goto done;
5118 value = deformatted;
5120 if (flags & ENV_MOD_MACHINE)
5122 environment = machine_env;
5123 root = HKEY_LOCAL_MACHINE;
5125 else
5127 environment = user_env;
5128 root = HKEY_CURRENT_USER;
5131 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5132 KEY_ALL_ACCESS, NULL, &env, NULL);
5133 if (res != ERROR_SUCCESS)
5134 goto done;
5136 if (flags & ENV_ACT_REMOVE)
5137 FIXME("Not removing environment variable on uninstall!\n");
5139 size = 0;
5140 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5141 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5142 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5143 goto done;
5145 if (res != ERROR_FILE_NOT_FOUND)
5147 if (flags & ENV_ACT_SETABSENT)
5149 res = ERROR_SUCCESS;
5150 goto done;
5153 data = msi_alloc(size);
5154 if (!data)
5156 RegCloseKey(env);
5157 return ERROR_OUTOFMEMORY;
5160 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5161 if (res != ERROR_SUCCESS)
5162 goto done;
5164 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5166 res = RegDeleteKeyW(env, name);
5167 goto done;
5170 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5171 newval = msi_alloc(size);
5172 ptr = newval;
5173 if (!newval)
5175 res = ERROR_OUTOFMEMORY;
5176 goto done;
5179 if (!(flags & ENV_MOD_MASK))
5180 lstrcpyW(newval, value);
5181 else
5183 if (flags & ENV_MOD_PREFIX)
5185 lstrcpyW(newval, value);
5186 lstrcatW(newval, semicolon);
5187 ptr = newval + lstrlenW(value) + 1;
5190 lstrcpyW(ptr, data);
5192 if (flags & ENV_MOD_APPEND)
5194 lstrcatW(newval, semicolon);
5195 lstrcatW(newval, value);
5199 else
5201 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5202 newval = msi_alloc(size);
5203 if (!newval)
5205 res = ERROR_OUTOFMEMORY;
5206 goto done;
5209 lstrcpyW(newval, value);
5212 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5213 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5215 done:
5216 if (env) RegCloseKey(env);
5217 msi_free(deformatted);
5218 msi_free(data);
5219 msi_free(newval);
5220 return res;
5223 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5225 UINT rc;
5226 MSIQUERY * view;
5227 static const WCHAR ExecSeqQuery[] =
5228 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5229 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5230 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5231 if (rc != ERROR_SUCCESS)
5232 return ERROR_SUCCESS;
5234 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5235 msiobj_release(&view->hdr);
5237 return rc;
5240 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5242 typedef struct
5244 struct list entry;
5245 LPWSTR sourcename;
5246 LPWSTR destname;
5247 LPWSTR source;
5248 LPWSTR dest;
5249 } FILE_LIST;
5251 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5253 BOOL ret;
5255 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5256 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5258 WARN("Source or dest is directory, not moving\n");
5259 return FALSE;
5262 if (options == msidbMoveFileOptionsMove)
5264 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5265 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5266 if (!ret)
5268 WARN("MoveFile failed: %d\n", GetLastError());
5269 return FALSE;
5272 else
5274 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5275 ret = CopyFileW(source, dest, FALSE);
5276 if (!ret)
5278 WARN("CopyFile failed: %d\n", GetLastError());
5279 return FALSE;
5283 return TRUE;
5286 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5288 LPWSTR path, ptr;
5289 DWORD dirlen, pathlen;
5291 ptr = strrchrW(wildcard, '\\');
5292 dirlen = ptr - wildcard + 1;
5294 pathlen = dirlen + lstrlenW(filename) + 1;
5295 path = msi_alloc(pathlen * sizeof(WCHAR));
5297 lstrcpynW(path, wildcard, dirlen + 1);
5298 lstrcatW(path, filename);
5300 return path;
5303 static void free_file_entry(FILE_LIST *file)
5305 msi_free(file->source);
5306 msi_free(file->dest);
5307 msi_free(file);
5310 static void free_list(FILE_LIST *list)
5312 while (!list_empty(&list->entry))
5314 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5316 list_remove(&file->entry);
5317 free_file_entry(file);
5321 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5323 FILE_LIST *new, *file;
5324 LPWSTR ptr, filename;
5325 DWORD size;
5327 new = msi_alloc_zero(sizeof(FILE_LIST));
5328 if (!new)
5329 return FALSE;
5331 new->source = strdupW(source);
5332 ptr = strrchrW(dest, '\\') + 1;
5333 filename = strrchrW(new->source, '\\') + 1;
5335 new->sourcename = filename;
5337 if (*ptr)
5338 new->destname = ptr;
5339 else
5340 new->destname = new->sourcename;
5342 size = (ptr - dest) + lstrlenW(filename) + 1;
5343 new->dest = msi_alloc(size * sizeof(WCHAR));
5344 if (!new->dest)
5346 free_file_entry(new);
5347 return FALSE;
5350 lstrcpynW(new->dest, dest, ptr - dest + 1);
5351 lstrcatW(new->dest, filename);
5353 if (list_empty(&files->entry))
5355 list_add_head(&files->entry, &new->entry);
5356 return TRUE;
5359 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5361 if (lstrcmpW(source, file->source) < 0)
5363 list_add_before(&file->entry, &new->entry);
5364 return TRUE;
5368 list_add_after(&file->entry, &new->entry);
5369 return TRUE;
5372 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5374 WIN32_FIND_DATAW wfd;
5375 HANDLE hfile;
5376 LPWSTR path;
5377 BOOL res;
5378 FILE_LIST files, *file;
5379 DWORD size;
5381 hfile = FindFirstFileW(source, &wfd);
5382 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5384 list_init(&files.entry);
5386 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5388 if (is_dot_dir(wfd.cFileName)) continue;
5390 path = wildcard_to_file(source, wfd.cFileName);
5391 if (!path)
5393 res = FALSE;
5394 goto done;
5397 add_wildcard(&files, path, dest);
5398 msi_free(path);
5401 /* no files match the wildcard */
5402 if (list_empty(&files.entry))
5403 goto done;
5405 /* only the first wildcard match gets renamed to dest */
5406 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5407 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5408 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5409 if (!file->dest)
5411 res = FALSE;
5412 goto done;
5415 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5417 while (!list_empty(&files.entry))
5419 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5421 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5423 list_remove(&file->entry);
5424 free_file_entry(file);
5427 res = TRUE;
5429 done:
5430 free_list(&files);
5431 FindClose(hfile);
5432 return res;
5435 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5437 MSIPACKAGE *package = param;
5438 MSICOMPONENT *comp;
5439 LPCWSTR sourcename, destname;
5440 LPWSTR sourcedir = NULL, destdir = NULL;
5441 LPWSTR source = NULL, dest = NULL;
5442 int options;
5443 DWORD size;
5444 BOOL ret, wildcards;
5446 static const WCHAR backslash[] = {'\\',0};
5448 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5449 if (!comp || !comp->Enabled ||
5450 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5452 TRACE("Component not set for install, not moving file\n");
5453 return ERROR_SUCCESS;
5456 sourcename = MSI_RecordGetString(rec, 3);
5457 destname = MSI_RecordGetString(rec, 4);
5458 options = MSI_RecordGetInteger(rec, 7);
5460 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5461 if (!sourcedir)
5462 goto done;
5464 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5465 if (!destdir)
5466 goto done;
5468 if (!sourcename)
5470 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5471 goto done;
5473 source = strdupW(sourcedir);
5474 if (!source)
5475 goto done;
5477 else
5479 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5480 source = msi_alloc(size * sizeof(WCHAR));
5481 if (!source)
5482 goto done;
5484 lstrcpyW(source, sourcedir);
5485 if (source[lstrlenW(source) - 1] != '\\')
5486 lstrcatW(source, backslash);
5487 lstrcatW(source, sourcename);
5490 wildcards = strchrW(source, '*') || strchrW(source, '?');
5492 if (!destname && !wildcards)
5494 destname = strdupW(sourcename);
5495 if (!destname)
5496 goto done;
5499 size = 0;
5500 if (destname)
5501 size = lstrlenW(destname);
5503 size += lstrlenW(destdir) + 2;
5504 dest = msi_alloc(size * sizeof(WCHAR));
5505 if (!dest)
5506 goto done;
5508 lstrcpyW(dest, destdir);
5509 if (dest[lstrlenW(dest) - 1] != '\\')
5510 lstrcatW(dest, backslash);
5512 if (destname)
5513 lstrcatW(dest, destname);
5515 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5517 ret = CreateDirectoryW(destdir, NULL);
5518 if (!ret)
5520 WARN("CreateDirectory failed: %d\n", GetLastError());
5521 return ERROR_SUCCESS;
5525 if (!wildcards)
5526 msi_move_file(source, dest, options);
5527 else
5528 move_files_wildcard(source, dest, options);
5530 done:
5531 msi_free(sourcedir);
5532 msi_free(destdir);
5533 msi_free(source);
5534 msi_free(dest);
5536 return ERROR_SUCCESS;
5539 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5541 UINT rc;
5542 MSIQUERY *view;
5544 static const WCHAR ExecSeqQuery[] =
5545 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5546 '`','M','o','v','e','F','i','l','e','`',0};
5548 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5549 if (rc != ERROR_SUCCESS)
5550 return ERROR_SUCCESS;
5552 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5553 msiobj_release(&view->hdr);
5555 return rc;
5558 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5559 DWORD dwReserved);
5560 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5561 LPVOID pvReserved, HMODULE *phModDll);
5563 static BOOL init_functionpointers(void)
5565 HRESULT hr;
5566 HMODULE hfusion;
5567 HMODULE hmscoree;
5569 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5571 hmscoree = LoadLibraryA("mscoree.dll");
5572 if (!hmscoree)
5574 WARN("mscoree.dll not available\n");
5575 return FALSE;
5578 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5579 if (!pLoadLibraryShim)
5581 WARN("LoadLibraryShim not available\n");
5582 FreeLibrary(hmscoree);
5583 return FALSE;
5586 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5587 if (FAILED(hr))
5589 WARN("fusion.dll not available\n");
5590 FreeLibrary(hmscoree);
5591 return FALSE;
5594 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5596 FreeLibrary(hmscoree);
5597 return TRUE;
5600 static UINT install_assembly(LPWSTR path)
5602 IAssemblyCache *cache;
5603 HRESULT hr;
5604 UINT r = ERROR_FUNCTION_FAILED;
5606 if (!init_functionpointers() || !pCreateAssemblyCache)
5607 return ERROR_FUNCTION_FAILED;
5609 hr = pCreateAssemblyCache(&cache, 0);
5610 if (FAILED(hr))
5611 goto done;
5613 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5614 if (FAILED(hr))
5615 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5617 r = ERROR_SUCCESS;
5619 done:
5620 IAssemblyCache_Release(cache);
5621 return r;
5624 static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
5626 MSIPACKAGE *package = param;
5627 MSICOMPONENT *comp;
5628 MSIFEATURE *feature;
5629 MSIFILE *file;
5630 WCHAR path[MAX_PATH];
5631 LPCWSTR app;
5632 DWORD attr;
5633 UINT r;
5635 comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
5636 if (!comp || !comp->Enabled ||
5637 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5639 ERR("Component not set for install, not publishing assembly\n");
5640 return ERROR_SUCCESS;
5643 feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
5644 if (feature)
5645 msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
5647 if (MSI_RecordGetString(rec, 3))
5648 FIXME("Manifest unhandled\n");
5650 app = MSI_RecordGetString(rec, 4);
5651 if (app)
5653 FIXME("Assembly should be privately installed\n");
5654 return ERROR_SUCCESS;
5657 attr = MSI_RecordGetInteger(rec, 5);
5658 if (attr == msidbAssemblyAttributesWin32)
5660 FIXME("Win32 assemblies not handled\n");
5661 return ERROR_SUCCESS;
5664 /* FIXME: extract all files belonging to this component */
5665 file = msi_find_file(package, comp->KeyPath);
5667 GetTempPathW(MAX_PATH, path);
5669 if (file->IsCompressed)
5671 r = msi_extract_file(package, file, path);
5672 if (r != ERROR_SUCCESS)
5674 ERR("Failed to extract temporary assembly\n");
5675 return r;
5678 PathAddBackslashW(path);
5679 lstrcatW(path, file->FileName);
5681 else
5683 PathAddBackslashW(path);
5684 lstrcatW(path, file->FileName);
5686 if (!CopyFileW(file->SourcePath, path, FALSE))
5688 ERR("Failed to copy temporary assembly: %d\n", GetLastError());
5689 return ERROR_FUNCTION_FAILED;
5693 r = install_assembly(path);
5694 if (r != ERROR_SUCCESS)
5695 ERR("Failed to install assembly\n");
5697 /* FIXME: write Installer assembly reg values */
5699 return r;
5702 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5704 UINT rc;
5705 MSIQUERY *view;
5707 static const WCHAR ExecSeqQuery[] =
5708 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5709 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5711 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5712 if (rc != ERROR_SUCCESS)
5713 return ERROR_SUCCESS;
5715 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
5716 msiobj_release(&view->hdr);
5718 return rc;
5721 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5722 LPCSTR action, LPCWSTR table )
5724 static const WCHAR query[] = {
5725 'S','E','L','E','C','T',' ','*',' ',
5726 'F','R','O','M',' ','`','%','s','`',0 };
5727 MSIQUERY *view = NULL;
5728 DWORD count = 0;
5729 UINT r;
5731 r = MSI_OpenQuery( package->db, &view, query, table );
5732 if (r == ERROR_SUCCESS)
5734 r = MSI_IterateRecords(view, &count, NULL, package);
5735 msiobj_release(&view->hdr);
5738 if (count)
5739 FIXME("%s -> %u ignored %s table values\n",
5740 action, count, debugstr_w(table));
5742 return ERROR_SUCCESS;
5745 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
5747 TRACE("%p\n", package);
5748 return ERROR_SUCCESS;
5751 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
5753 static const WCHAR table[] =
5754 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
5755 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
5758 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
5760 static const WCHAR table[] = { 'P','a','t','c','h',0 };
5761 return msi_unimplemented_action_stub( package, "PatchFiles", table );
5764 static UINT ACTION_BindImage( MSIPACKAGE *package )
5766 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
5767 return msi_unimplemented_action_stub( package, "BindImage", table );
5770 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
5772 static const WCHAR table[] = {
5773 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
5774 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
5777 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
5779 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5780 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
5783 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
5785 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
5786 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
5789 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5791 static const WCHAR table[] = {
5792 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5793 return msi_unimplemented_action_stub( package, "DeleteServices", table );
5795 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
5797 static const WCHAR table[] = {
5798 'P','r','o','d','u','c','t','I','D',0 };
5799 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
5802 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
5804 static const WCHAR table[] = {
5805 'E','n','v','i','r','o','n','m','e','n','t',0 };
5806 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
5809 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
5811 static const WCHAR table[] = {
5812 'M','s','i','A','s','s','e','m','b','l','y',0 };
5813 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
5816 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
5818 static const WCHAR table[] = { 'F','o','n','t',0 };
5819 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
5822 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
5824 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
5825 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
5828 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
5830 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5831 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
5834 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
5836 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
5837 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
5840 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
5842 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
5843 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
5846 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
5848 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
5849 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
5852 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
5854 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
5855 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
5858 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
5860 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
5861 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
5864 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5866 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
5867 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
5870 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
5872 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
5873 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
5876 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
5878 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
5879 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
5882 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5884 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
5885 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
5888 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
5890 static const WCHAR table[] = { 'A','p','p','I','d',0 };
5891 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
5894 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
5896 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
5897 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
5900 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
5902 static const WCHAR table[] = { 'M','I','M','E',0 };
5903 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
5906 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
5908 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
5909 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
5912 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
5914 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
5915 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
5918 static const struct _actions StandardActions[] = {
5919 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
5920 { szAppSearch, ACTION_AppSearch },
5921 { szBindImage, ACTION_BindImage },
5922 { szCCPSearch, ACTION_CCPSearch },
5923 { szCostFinalize, ACTION_CostFinalize },
5924 { szCostInitialize, ACTION_CostInitialize },
5925 { szCreateFolders, ACTION_CreateFolders },
5926 { szCreateShortcuts, ACTION_CreateShortcuts },
5927 { szDeleteServices, ACTION_DeleteServices },
5928 { szDisableRollback, NULL },
5929 { szDuplicateFiles, ACTION_DuplicateFiles },
5930 { szExecuteAction, ACTION_ExecuteAction },
5931 { szFileCost, ACTION_FileCost },
5932 { szFindRelatedProducts, ACTION_FindRelatedProducts },
5933 { szForceReboot, ACTION_ForceReboot },
5934 { szInstallAdminPackage, NULL },
5935 { szInstallExecute, ACTION_InstallExecute },
5936 { szInstallExecuteAgain, ACTION_InstallExecute },
5937 { szInstallFiles, ACTION_InstallFiles},
5938 { szInstallFinalize, ACTION_InstallFinalize },
5939 { szInstallInitialize, ACTION_InstallInitialize },
5940 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
5941 { szInstallValidate, ACTION_InstallValidate },
5942 { szIsolateComponents, ACTION_IsolateComponents },
5943 { szLaunchConditions, ACTION_LaunchConditions },
5944 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
5945 { szMoveFiles, ACTION_MoveFiles },
5946 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
5947 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
5948 { szInstallODBC, ACTION_InstallODBC },
5949 { szInstallServices, ACTION_InstallServices },
5950 { szPatchFiles, ACTION_PatchFiles },
5951 { szProcessComponents, ACTION_ProcessComponents },
5952 { szPublishComponents, ACTION_PublishComponents },
5953 { szPublishFeatures, ACTION_PublishFeatures },
5954 { szPublishProduct, ACTION_PublishProduct },
5955 { szRegisterClassInfo, ACTION_RegisterClassInfo },
5956 { szRegisterComPlus, ACTION_RegisterComPlus},
5957 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
5958 { szRegisterFonts, ACTION_RegisterFonts },
5959 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
5960 { szRegisterProduct, ACTION_RegisterProduct },
5961 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
5962 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
5963 { szRegisterUser, ACTION_RegisterUser },
5964 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
5965 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
5966 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
5967 { szRemoveFiles, ACTION_RemoveFiles },
5968 { szRemoveFolders, ACTION_RemoveFolders },
5969 { szRemoveIniValues, ACTION_RemoveIniValues },
5970 { szRemoveODBC, ACTION_RemoveODBC },
5971 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
5972 { szRemoveShortcuts, ACTION_RemoveShortcuts },
5973 { szResolveSource, ACTION_ResolveSource },
5974 { szRMCCPSearch, ACTION_RMCCPSearch },
5975 { szScheduleReboot, NULL },
5976 { szSelfRegModules, ACTION_SelfRegModules },
5977 { szSelfUnregModules, ACTION_SelfUnregModules },
5978 { szSetODBCFolders, NULL },
5979 { szStartServices, ACTION_StartServices },
5980 { szStopServices, ACTION_StopServices },
5981 { szUnpublishComponents, ACTION_UnpublishComponents },
5982 { szUnpublishFeatures, ACTION_UnpublishFeatures },
5983 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
5984 { szUnregisterComPlus, ACTION_UnregisterComPlus },
5985 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
5986 { szUnregisterFonts, ACTION_UnregisterFonts },
5987 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
5988 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
5989 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
5990 { szValidateProductID, ACTION_ValidateProductID },
5991 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
5992 { szWriteIniValues, ACTION_WriteIniValues },
5993 { szWriteRegistryValues, ACTION_WriteRegistryValues },
5994 { NULL, NULL },
5997 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
5998 UINT* rc, BOOL force )
6000 BOOL ret = FALSE;
6001 BOOL run = force;
6002 int i;
6004 if (!run && !package->script->CurrentlyScripting)
6005 run = TRUE;
6007 if (!run)
6009 if (strcmpW(action,szInstallFinalize) == 0 ||
6010 strcmpW(action,szInstallExecute) == 0 ||
6011 strcmpW(action,szInstallExecuteAgain) == 0)
6012 run = TRUE;
6015 i = 0;
6016 while (StandardActions[i].action != NULL)
6018 if (strcmpW(StandardActions[i].action, action)==0)
6020 if (!run)
6022 ui_actioninfo(package, action, TRUE, 0);
6023 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6024 ui_actioninfo(package, action, FALSE, *rc);
6026 else
6028 ui_actionstart(package, action);
6029 if (StandardActions[i].handler)
6031 *rc = StandardActions[i].handler(package);
6033 else
6035 FIXME("unhandled standard action %s\n",debugstr_w(action));
6036 *rc = ERROR_SUCCESS;
6039 ret = TRUE;
6040 break;
6042 i++;
6044 return ret;