push 87b6981010d7405c33b14cddcceec21b47729eba
[wine/hacks.git] / dlls / msi / action.c
blob6ee30079b0691a82a1a3e443dcc2c0afee0f7847
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 * consts and values used
51 static const WCHAR c_colon[] = {'C',':','\\',0};
53 static const WCHAR szCreateFolders[] =
54 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
55 static const WCHAR szCostFinalize[] =
56 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
57 static const WCHAR szWriteRegistryValues[] =
58 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
59 static const WCHAR szCostInitialize[] =
60 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
61 static const WCHAR szFileCost[] =
62 {'F','i','l','e','C','o','s','t',0};
63 static const WCHAR szInstallInitialize[] =
64 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
65 static const WCHAR szInstallValidate[] =
66 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
67 static const WCHAR szLaunchConditions[] =
68 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
69 static const WCHAR szProcessComponents[] =
70 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
71 static const WCHAR szRegisterTypeLibraries[] =
72 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
73 static const WCHAR szCreateShortcuts[] =
74 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
75 static const WCHAR szPublishProduct[] =
76 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
77 static const WCHAR szWriteIniValues[] =
78 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
79 static const WCHAR szSelfRegModules[] =
80 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
81 static const WCHAR szPublishFeatures[] =
82 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
83 static const WCHAR szRegisterProduct[] =
84 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
85 static const WCHAR szInstallExecute[] =
86 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
87 static const WCHAR szInstallExecuteAgain[] =
88 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
89 static const WCHAR szInstallFinalize[] =
90 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
91 static const WCHAR szForceReboot[] =
92 {'F','o','r','c','e','R','e','b','o','o','t',0};
93 static const WCHAR szResolveSource[] =
94 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
95 static const WCHAR szAppSearch[] =
96 {'A','p','p','S','e','a','r','c','h',0};
97 static const WCHAR szAllocateRegistrySpace[] =
98 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
99 static const WCHAR szBindImage[] =
100 {'B','i','n','d','I','m','a','g','e',0};
101 static const WCHAR szCCPSearch[] =
102 {'C','C','P','S','e','a','r','c','h',0};
103 static const WCHAR szDeleteServices[] =
104 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
105 static const WCHAR szDisableRollback[] =
106 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
107 static const WCHAR szExecuteAction[] =
108 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
109 static const WCHAR szInstallAdminPackage[] =
110 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
111 static const WCHAR szInstallSFPCatalogFile[] =
112 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
113 static const WCHAR szIsolateComponents[] =
114 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
115 static const WCHAR szMigrateFeatureStates[] =
116 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
117 static const WCHAR szMoveFiles[] =
118 {'M','o','v','e','F','i','l','e','s',0};
119 static const WCHAR szMsiPublishAssemblies[] =
120 {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
121 static const WCHAR szMsiUnpublishAssemblies[] =
122 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
123 static const WCHAR szInstallODBC[] =
124 {'I','n','s','t','a','l','l','O','D','B','C',0};
125 static const WCHAR szInstallServices[] =
126 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
127 static const WCHAR szPatchFiles[] =
128 {'P','a','t','c','h','F','i','l','e','s',0};
129 static const WCHAR szPublishComponents[] =
130 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
131 static const WCHAR szRegisterComPlus[] =
132 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
133 static const WCHAR szRegisterFonts[] =
134 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
135 static const WCHAR szRegisterUser[] =
136 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
137 static const WCHAR szRemoveDuplicateFiles[] =
138 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
139 static const WCHAR szRemoveEnvironmentStrings[] =
140 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
141 static const WCHAR szRemoveExistingProducts[] =
142 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
143 static const WCHAR szRemoveFolders[] =
144 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
145 static const WCHAR szRemoveIniValues[] =
146 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
147 static const WCHAR szRemoveODBC[] =
148 {'R','e','m','o','v','e','O','D','B','C',0};
149 static const WCHAR szRemoveRegistryValues[] =
150 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
151 static const WCHAR szRemoveShortcuts[] =
152 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
153 static const WCHAR szRMCCPSearch[] =
154 {'R','M','C','C','P','S','e','a','r','c','h',0};
155 static const WCHAR szScheduleReboot[] =
156 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
157 static const WCHAR szSelfUnregModules[] =
158 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
159 static const WCHAR szSetODBCFolders[] =
160 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
161 static const WCHAR szStartServices[] =
162 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
163 static const WCHAR szStopServices[] =
164 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
165 static const WCHAR szUnpublishComponents[] =
166 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
167 static const WCHAR szUnpublishFeatures[] =
168 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
169 static const WCHAR szUnregisterClassInfo[] =
170 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
171 static const WCHAR szUnregisterComPlus[] =
172 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
173 static const WCHAR szUnregisterExtensionInfo[] =
174 {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
175 static const WCHAR szUnregisterFonts[] =
176 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
177 static const WCHAR szUnregisterMIMEInfo[] =
178 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
179 static const WCHAR szUnregisterProgIdInfo[] =
180 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
181 static const WCHAR szUnregisterTypeLibraries[] =
182 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
183 static const WCHAR szValidateProductID[] =
184 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
185 static const WCHAR szWriteEnvironmentStrings[] =
186 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
188 /********************************************************
189 * helper functions
190 ********************************************************/
192 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
194 static const WCHAR Query_t[] =
195 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
197 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
198 ' ','\'','%','s','\'',0};
199 MSIRECORD * row;
201 row = MSI_QueryGetRecord( package->db, Query_t, action );
202 if (!row)
203 return;
204 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
205 msiobj_release(&row->hdr);
208 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
209 UINT rc)
211 MSIRECORD * row;
212 static const WCHAR template_s[]=
213 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
214 '%','s', '.',0};
215 static const WCHAR template_e[]=
216 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
217 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
218 '%','i','.',0};
219 static const WCHAR format[] =
220 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
221 WCHAR message[1024];
222 WCHAR timet[0x100];
224 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
225 if (start)
226 sprintfW(message,template_s,timet,action);
227 else
228 sprintfW(message,template_e,timet,action,rc);
230 row = MSI_CreateRecord(1);
231 MSI_RecordSetStringW(row,1,message);
233 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
234 msiobj_release(&row->hdr);
237 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
238 BOOL preserve_case )
240 LPCWSTR ptr,ptr2;
241 BOOL quote;
242 DWORD len;
243 LPWSTR prop = NULL, val = NULL;
245 if (!szCommandLine)
246 return ERROR_SUCCESS;
248 ptr = szCommandLine;
250 while (*ptr)
252 if (*ptr==' ')
254 ptr++;
255 continue;
258 TRACE("Looking at %s\n",debugstr_w(ptr));
260 ptr2 = strchrW(ptr,'=');
261 if (!ptr2)
263 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
264 break;
267 quote = FALSE;
269 len = ptr2-ptr;
270 prop = msi_alloc((len+1)*sizeof(WCHAR));
271 memcpy(prop,ptr,len*sizeof(WCHAR));
272 prop[len]=0;
274 if (!preserve_case)
275 struprW(prop);
277 ptr2++;
279 len = 0;
280 ptr = ptr2;
281 while (*ptr && (quote || (!quote && *ptr!=' ')))
283 if (*ptr == '"')
284 quote = !quote;
285 ptr++;
286 len++;
289 if (*ptr2=='"')
291 ptr2++;
292 len -= 2;
294 val = msi_alloc((len+1)*sizeof(WCHAR));
295 memcpy(val,ptr2,len*sizeof(WCHAR));
296 val[len] = 0;
298 if (lstrlenW(prop) > 0)
300 TRACE("Found commandline property (%s) = (%s)\n",
301 debugstr_w(prop), debugstr_w(val));
302 MSI_SetPropertyW(package,prop,val);
304 msi_free(val);
305 msi_free(prop);
308 return ERROR_SUCCESS;
312 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
314 LPCWSTR pc;
315 LPWSTR p, *ret = NULL;
316 UINT count = 0;
318 if (!str)
319 return ret;
321 /* count the number of substrings */
322 for ( pc = str, count = 0; pc; count++ )
324 pc = strchrW( pc, sep );
325 if (pc)
326 pc++;
329 /* allocate space for an array of substring pointers and the substrings */
330 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
331 (lstrlenW(str)+1) * sizeof(WCHAR) );
332 if (!ret)
333 return ret;
335 /* copy the string and set the pointers */
336 p = (LPWSTR) &ret[count+1];
337 lstrcpyW( p, str );
338 for( count = 0; (ret[count] = p); count++ )
340 p = strchrW( p, sep );
341 if (p)
342 *p++ = 0;
345 return ret;
348 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
350 static const WCHAR szSystemLanguageID[] =
351 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
353 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
354 UINT ret = ERROR_FUNCTION_FAILED;
356 prod_code = msi_dup_property( package, szProductCode );
357 patch_product = msi_get_suminfo_product( patch );
359 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
361 if ( strstrW( patch_product, prod_code ) )
363 MSISUMMARYINFO *si;
364 const WCHAR *p;
366 si = MSI_GetSummaryInformationW( patch, 0 );
367 if (!si)
369 ERR("no summary information!\n");
370 goto end;
373 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
374 if (!template)
376 ERR("no template property!\n");
377 msiobj_release( &si->hdr );
378 goto end;
381 if (!template[0])
383 ret = ERROR_SUCCESS;
384 msiobj_release( &si->hdr );
385 goto end;
388 langid = msi_dup_property( package, szSystemLanguageID );
389 if (!langid)
391 msiobj_release( &si->hdr );
392 goto end;
395 p = strchrW( template, ';' );
396 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
398 TRACE("applicable transform\n");
399 ret = ERROR_SUCCESS;
402 /* FIXME: check platform */
404 msiobj_release( &si->hdr );
407 end:
408 msi_free( patch_product );
409 msi_free( prod_code );
410 msi_free( template );
411 msi_free( langid );
413 return ret;
416 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
417 MSIDATABASE *patch_db, LPCWSTR name )
419 UINT ret = ERROR_FUNCTION_FAILED;
420 IStorage *stg = NULL;
421 HRESULT r;
423 TRACE("%p %s\n", package, debugstr_w(name) );
425 if (*name++ != ':')
427 ERR("expected a colon in %s\n", debugstr_w(name));
428 return ERROR_FUNCTION_FAILED;
431 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
432 if (SUCCEEDED(r))
434 ret = msi_check_transform_applicable( package, stg );
435 if (ret == ERROR_SUCCESS)
436 msi_table_apply_transform( package->db, stg );
437 else
438 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
439 IStorage_Release( stg );
441 else
442 ERR("failed to open substorage %s\n", debugstr_w(name));
444 return ERROR_SUCCESS;
447 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
449 LPWSTR guid_list, *guids, product_code;
450 UINT i, ret = ERROR_FUNCTION_FAILED;
452 product_code = msi_dup_property( package, szProductCode );
453 if (!product_code)
455 /* FIXME: the property ProductCode should be written into the DB somewhere */
456 ERR("no product code to check\n");
457 return ERROR_SUCCESS;
460 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
461 guids = msi_split_string( guid_list, ';' );
462 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
464 if (!lstrcmpW( guids[i], product_code ))
465 ret = ERROR_SUCCESS;
467 msi_free( guids );
468 msi_free( guid_list );
469 msi_free( product_code );
471 return ret;
474 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
476 MSIQUERY *view;
477 MSIRECORD *rec = NULL;
478 LPWSTR patch;
479 LPCWSTR prop;
480 UINT r;
482 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
483 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
484 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
485 '`','S','o','u','r','c','e','`',' ','I','S',' ',
486 'N','O','T',' ','N','U','L','L',0};
488 r = MSI_DatabaseOpenViewW(package->db, query, &view);
489 if (r != ERROR_SUCCESS)
490 return r;
492 r = MSI_ViewExecute(view, 0);
493 if (r != ERROR_SUCCESS)
494 goto done;
496 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
498 prop = MSI_RecordGetString(rec, 1);
499 patch = msi_dup_property(package, szPatch);
500 MSI_SetPropertyW(package, prop, patch);
501 msi_free(patch);
504 done:
505 if (rec) msiobj_release(&rec->hdr);
506 msiobj_release(&view->hdr);
508 return r;
511 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
513 MSISUMMARYINFO *si;
514 LPWSTR str, *substorage;
515 UINT i, r = ERROR_SUCCESS;
517 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
518 if (!si)
519 return ERROR_FUNCTION_FAILED;
521 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
523 TRACE("Patch not applicable\n");
524 return ERROR_SUCCESS;
527 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
528 if (!package->patch)
529 return ERROR_OUTOFMEMORY;
531 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
532 if (!package->patch->patchcode)
533 return ERROR_OUTOFMEMORY;
535 /* enumerate the substorage */
536 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
537 package->patch->transforms = str;
539 substorage = msi_split_string( str, ';' );
540 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
541 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
543 msi_free( substorage );
544 msiobj_release( &si->hdr );
546 msi_set_media_source_prop(package);
548 return r;
551 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
553 MSIDATABASE *patch_db = NULL;
554 UINT r;
556 TRACE("%p %s\n", package, debugstr_w( file ) );
558 /* FIXME:
559 * We probably want to make sure we only open a patch collection here.
560 * Patch collections (.msp) and databases (.msi) have different GUIDs
561 * but currently MSI_OpenDatabaseW will accept both.
563 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
564 if ( r != ERROR_SUCCESS )
566 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
567 return r;
570 msi_parse_patch_summary( package, patch_db );
573 * There might be a CAB file in the patch package,
574 * so append it to the list of storage to search for streams.
576 append_storage_to_db( package->db, patch_db->storage );
578 msiobj_release( &patch_db->hdr );
580 return ERROR_SUCCESS;
583 /* get the PATCH property, and apply all the patches it specifies */
584 static UINT msi_apply_patches( MSIPACKAGE *package )
586 LPWSTR patch_list, *patches;
587 UINT i, r = ERROR_SUCCESS;
589 patch_list = msi_dup_property( package, szPatch );
591 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
593 patches = msi_split_string( patch_list, ';' );
594 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
595 r = msi_apply_patch_package( package, patches[i] );
597 msi_free( patches );
598 msi_free( patch_list );
600 return r;
603 static UINT msi_apply_transforms( MSIPACKAGE *package )
605 static const WCHAR szTransforms[] = {
606 'T','R','A','N','S','F','O','R','M','S',0 };
607 LPWSTR xform_list, *xforms;
608 UINT i, r = ERROR_SUCCESS;
610 xform_list = msi_dup_property( package, szTransforms );
611 xforms = msi_split_string( xform_list, ';' );
613 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
615 if (xforms[i][0] == ':')
616 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
617 else
618 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
621 msi_free( xforms );
622 msi_free( xform_list );
624 return r;
627 static BOOL ui_sequence_exists( MSIPACKAGE *package )
629 MSIQUERY *view;
630 UINT rc;
632 static const WCHAR ExecSeqQuery [] =
633 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
634 '`','I','n','s','t','a','l','l',
635 'U','I','S','e','q','u','e','n','c','e','`',
636 ' ','W','H','E','R','E',' ',
637 '`','S','e','q','u','e','n','c','e','`',' ',
638 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
639 '`','S','e','q','u','e','n','c','e','`',0};
641 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642 if (rc == ERROR_SUCCESS)
644 msiobj_release(&view->hdr);
645 return TRUE;
648 return FALSE;
651 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
653 LPWSTR p, db;
654 LPWSTR source, check;
655 DWORD len;
657 static const WCHAR szOriginalDatabase[] =
658 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
660 db = msi_dup_property( package, szOriginalDatabase );
661 if (!db)
662 return ERROR_OUTOFMEMORY;
664 p = strrchrW( db, '\\' );
665 if (!p)
667 p = strrchrW( db, '/' );
668 if (!p)
670 msi_free(db);
671 return ERROR_SUCCESS;
675 len = p - db + 2;
676 source = msi_alloc( len * sizeof(WCHAR) );
677 lstrcpynW( source, db, len );
679 check = msi_dup_property( package, cszSourceDir );
680 if (!check || replace)
681 MSI_SetPropertyW( package, cszSourceDir, source );
683 msi_free( check );
685 check = msi_dup_property( package, cszSOURCEDIR );
686 if (!check || replace)
687 MSI_SetPropertyW( package, cszSOURCEDIR, source );
689 msi_free( check );
690 msi_free( source );
691 msi_free( db );
693 return ERROR_SUCCESS;
696 static BOOL needs_ui_sequence(MSIPACKAGE *package)
698 INT level = msi_get_property_int(package, szUILevel, 0);
699 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
702 static UINT msi_set_context(MSIPACKAGE *package)
704 WCHAR val[10];
705 DWORD sz = 10;
706 DWORD num;
707 UINT r;
709 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
711 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
712 if (r == ERROR_SUCCESS)
714 num = atolW(val);
715 if (num == 1 || num == 2)
716 package->Context = MSIINSTALLCONTEXT_MACHINE;
719 return ERROR_SUCCESS;
722 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
724 UINT rc;
725 LPCWSTR cond, action;
726 MSIPACKAGE *package = param;
728 action = MSI_RecordGetString(row,1);
729 if (!action)
731 ERR("Error is retrieving action name\n");
732 return ERROR_FUNCTION_FAILED;
735 /* check conditions */
736 cond = MSI_RecordGetString(row,2);
738 /* this is a hack to skip errors in the condition code */
739 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
741 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
742 return ERROR_SUCCESS;
745 if (needs_ui_sequence(package))
746 rc = ACTION_PerformUIAction(package, action, -1);
747 else
748 rc = ACTION_PerformAction(package, action, -1, FALSE);
750 msi_dialog_check_messages( NULL );
752 if (package->CurrentInstallState != ERROR_SUCCESS)
753 rc = package->CurrentInstallState;
755 if (rc == ERROR_FUNCTION_NOT_CALLED)
756 rc = ERROR_SUCCESS;
758 if (rc != ERROR_SUCCESS)
759 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
761 return rc;
764 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
766 MSIQUERY * view;
767 UINT r;
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','%','s','`',
771 ' ','W','H','E','R','E',' ',
772 '`','S','e','q','u','e','n','c','e','`',' ',
773 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
774 '`','S','e','q','u','e','n','c','e','`',0};
776 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
778 r = MSI_OpenQuery( package->db, &view, query, szTable );
779 if (r == ERROR_SUCCESS)
781 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
782 msiobj_release(&view->hdr);
785 return r;
788 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
790 MSIQUERY * view;
791 UINT rc;
792 static const WCHAR ExecSeqQuery[] =
793 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
794 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
795 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
796 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
797 'O','R','D','E','R',' ', 'B','Y',' ',
798 '`','S','e','q','u','e','n','c','e','`',0 };
799 static const WCHAR IVQuery[] =
800 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
801 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
802 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
803 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
804 ' ','\'', 'I','n','s','t','a','l','l',
805 'V','a','l','i','d','a','t','e','\'', 0};
806 INT seq = 0;
808 if (package->script->ExecuteSequenceRun)
810 TRACE("Execute Sequence already Run\n");
811 return ERROR_SUCCESS;
814 package->script->ExecuteSequenceRun = TRUE;
816 /* get the sequence number */
817 if (UIran)
819 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
820 if( !row )
821 return ERROR_FUNCTION_FAILED;
822 seq = MSI_RecordGetInteger(row,1);
823 msiobj_release(&row->hdr);
826 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
827 if (rc == ERROR_SUCCESS)
829 TRACE("Running the actions\n");
831 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
832 msiobj_release(&view->hdr);
835 return rc;
838 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
840 MSIQUERY * view;
841 UINT rc;
842 static const WCHAR ExecSeqQuery [] =
843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
844 '`','I','n','s','t','a','l','l',
845 'U','I','S','e','q','u','e','n','c','e','`',
846 ' ','W','H','E','R','E',' ',
847 '`','S','e','q','u','e','n','c','e','`',' ',
848 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
849 '`','S','e','q','u','e','n','c','e','`',0};
851 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
852 if (rc == ERROR_SUCCESS)
854 TRACE("Running the actions\n");
856 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
857 msiobj_release(&view->hdr);
860 return rc;
863 /********************************************************
864 * ACTION helper functions and functions that perform the actions
865 *******************************************************/
866 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
867 UINT* rc, UINT script, BOOL force )
869 BOOL ret=FALSE;
870 UINT arc;
872 arc = ACTION_CustomAction(package, action, script, force);
874 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
876 *rc = arc;
877 ret = TRUE;
879 return ret;
883 * Actual Action Handlers
886 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
888 MSIPACKAGE *package = param;
889 LPCWSTR dir;
890 LPWSTR full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
894 dir = MSI_RecordGetString(row,1);
895 if (!dir)
897 ERR("Unable to get folder id\n");
898 return ERROR_SUCCESS;
901 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
902 if (!full_path)
904 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
905 return ERROR_SUCCESS;
908 TRACE("Folder is %s\n",debugstr_w(full_path));
910 /* UI stuff */
911 uirow = MSI_CreateRecord(1);
912 MSI_RecordSetStringW(uirow,1,full_path);
913 ui_actiondata(package,szCreateFolders,uirow);
914 msiobj_release( &uirow->hdr );
916 if (folder->State == 0)
917 create_full_pathW(full_path);
919 folder->State = 3;
921 msi_free(full_path);
922 return ERROR_SUCCESS;
925 /* FIXME: probably should merge this with the above function */
926 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
928 UINT rc = ERROR_SUCCESS;
929 MSIFOLDER *folder;
930 LPWSTR install_path;
932 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
933 if (!install_path)
934 return ERROR_FUNCTION_FAILED;
936 /* create the path */
937 if (folder->State == 0)
939 create_full_pathW(install_path);
940 folder->State = 2;
942 msi_free(install_path);
944 return rc;
947 UINT msi_create_component_directories( MSIPACKAGE *package )
949 MSICOMPONENT *comp;
951 /* create all the folders required by the components are going to install */
952 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
954 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
955 continue;
956 msi_create_directory( package, comp->Directory );
959 return ERROR_SUCCESS;
962 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
964 static const WCHAR ExecSeqQuery[] =
965 {'S','E','L','E','C','T',' ',
966 '`','D','i','r','e','c','t','o','r','y','_','`',
967 ' ','F','R','O','M',' ',
968 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
969 UINT rc;
970 MSIQUERY *view;
972 /* create all the empty folders specified in the CreateFolder table */
973 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
974 if (rc != ERROR_SUCCESS)
975 return ERROR_SUCCESS;
977 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
978 msiobj_release(&view->hdr);
980 return rc;
983 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
985 MSIPACKAGE *package = param;
986 LPCWSTR dir;
987 LPWSTR full_path;
988 MSIRECORD *uirow;
989 MSIFOLDER *folder;
991 dir = MSI_RecordGetString( row, 1 );
992 if (!dir)
994 ERR("Unable to get folder id\n");
995 return ERROR_SUCCESS;
998 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
999 if (!full_path)
1001 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1002 return ERROR_SUCCESS;
1005 TRACE("folder is %s\n", debugstr_w(full_path));
1007 uirow = MSI_CreateRecord( 1 );
1008 MSI_RecordSetStringW( uirow, 1, full_path );
1009 ui_actiondata( package, szRemoveFolders, uirow );
1010 msiobj_release( &uirow->hdr );
1012 RemoveDirectoryW( full_path );
1013 folder->State = 0;
1015 msi_free( full_path );
1016 return ERROR_SUCCESS;
1019 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1021 static const WCHAR query[] =
1022 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1023 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1025 MSIQUERY *view;
1026 UINT rc;
1028 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1029 if (rc != ERROR_SUCCESS)
1030 return ERROR_SUCCESS;
1032 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1033 msiobj_release( &view->hdr );
1035 return rc;
1038 static UINT load_component( MSIRECORD *row, LPVOID param )
1040 MSIPACKAGE *package = param;
1041 MSICOMPONENT *comp;
1043 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1044 if (!comp)
1045 return ERROR_FUNCTION_FAILED;
1047 list_add_tail( &package->components, &comp->entry );
1049 /* fill in the data */
1050 comp->Component = msi_dup_record_field( row, 1 );
1052 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1054 comp->ComponentId = msi_dup_record_field( row, 2 );
1055 comp->Directory = msi_dup_record_field( row, 3 );
1056 comp->Attributes = MSI_RecordGetInteger(row,4);
1057 comp->Condition = msi_dup_record_field( row, 5 );
1058 comp->KeyPath = msi_dup_record_field( row, 6 );
1060 comp->Installed = INSTALLSTATE_UNKNOWN;
1061 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1063 return ERROR_SUCCESS;
1066 static UINT load_all_components( MSIPACKAGE *package )
1068 static const WCHAR query[] = {
1069 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1070 '`','C','o','m','p','o','n','e','n','t','`',0 };
1071 MSIQUERY *view;
1072 UINT r;
1074 if (!list_empty(&package->components))
1075 return ERROR_SUCCESS;
1077 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1078 if (r != ERROR_SUCCESS)
1079 return r;
1081 r = MSI_IterateRecords(view, NULL, load_component, package);
1082 msiobj_release(&view->hdr);
1083 return r;
1086 typedef struct {
1087 MSIPACKAGE *package;
1088 MSIFEATURE *feature;
1089 } _ilfs;
1091 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1093 ComponentList *cl;
1095 cl = msi_alloc( sizeof (*cl) );
1096 if ( !cl )
1097 return ERROR_NOT_ENOUGH_MEMORY;
1098 cl->component = comp;
1099 list_add_tail( &feature->Components, &cl->entry );
1101 return ERROR_SUCCESS;
1104 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1106 FeatureList *fl;
1108 fl = msi_alloc( sizeof(*fl) );
1109 if ( !fl )
1110 return ERROR_NOT_ENOUGH_MEMORY;
1111 fl->feature = child;
1112 list_add_tail( &parent->Children, &fl->entry );
1114 return ERROR_SUCCESS;
1117 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1119 _ilfs* ilfs = param;
1120 LPCWSTR component;
1121 MSICOMPONENT *comp;
1123 component = MSI_RecordGetString(row,1);
1125 /* check to see if the component is already loaded */
1126 comp = get_loaded_component( ilfs->package, component );
1127 if (!comp)
1129 ERR("unknown component %s\n", debugstr_w(component));
1130 return ERROR_FUNCTION_FAILED;
1133 add_feature_component( ilfs->feature, comp );
1134 comp->Enabled = TRUE;
1136 return ERROR_SUCCESS;
1139 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1141 MSIFEATURE *feature;
1143 if ( !name )
1144 return NULL;
1146 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1148 if ( !lstrcmpW( feature->Feature, name ) )
1149 return feature;
1152 return NULL;
1155 static UINT load_feature(MSIRECORD * row, LPVOID param)
1157 MSIPACKAGE* package = param;
1158 MSIFEATURE* feature;
1159 static const WCHAR Query1[] =
1160 {'S','E','L','E','C','T',' ',
1161 '`','C','o','m','p','o','n','e','n','t','_','`',
1162 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1163 'C','o','m','p','o','n','e','n','t','s','`',' ',
1164 'W','H','E','R','E',' ',
1165 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1166 MSIQUERY * view;
1167 UINT rc;
1168 _ilfs ilfs;
1170 /* fill in the data */
1172 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1173 if (!feature)
1174 return ERROR_NOT_ENOUGH_MEMORY;
1176 list_init( &feature->Children );
1177 list_init( &feature->Components );
1179 feature->Feature = msi_dup_record_field( row, 1 );
1181 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1183 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1184 feature->Title = msi_dup_record_field( row, 3 );
1185 feature->Description = msi_dup_record_field( row, 4 );
1187 if (!MSI_RecordIsNull(row,5))
1188 feature->Display = MSI_RecordGetInteger(row,5);
1190 feature->Level= MSI_RecordGetInteger(row,6);
1191 feature->Directory = msi_dup_record_field( row, 7 );
1192 feature->Attributes = MSI_RecordGetInteger(row,8);
1194 feature->Installed = INSTALLSTATE_UNKNOWN;
1195 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1197 list_add_tail( &package->features, &feature->entry );
1199 /* load feature components */
1201 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1202 if (rc != ERROR_SUCCESS)
1203 return ERROR_SUCCESS;
1205 ilfs.package = package;
1206 ilfs.feature = feature;
1208 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1209 msiobj_release(&view->hdr);
1211 return ERROR_SUCCESS;
1214 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1216 MSIPACKAGE* package = param;
1217 MSIFEATURE *parent, *child;
1219 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1220 if (!child)
1221 return ERROR_FUNCTION_FAILED;
1223 if (!child->Feature_Parent)
1224 return ERROR_SUCCESS;
1226 parent = find_feature_by_name( package, child->Feature_Parent );
1227 if (!parent)
1228 return ERROR_FUNCTION_FAILED;
1230 add_feature_child( parent, child );
1231 return ERROR_SUCCESS;
1234 static UINT load_all_features( MSIPACKAGE *package )
1236 static const WCHAR query[] = {
1237 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1238 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1239 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1240 MSIQUERY *view;
1241 UINT r;
1243 if (!list_empty(&package->features))
1244 return ERROR_SUCCESS;
1246 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1247 if (r != ERROR_SUCCESS)
1248 return r;
1250 r = MSI_IterateRecords( view, NULL, load_feature, package );
1251 if (r != ERROR_SUCCESS)
1252 return r;
1254 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1255 msiobj_release( &view->hdr );
1257 return r;
1260 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1262 if (!p)
1263 return p;
1264 p = strchrW(p, ch);
1265 if (!p)
1266 return p;
1267 *p = 0;
1268 return p+1;
1271 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1273 static const WCHAR query[] = {
1274 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1275 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1276 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1277 MSIQUERY *view = NULL;
1278 MSIRECORD *row = NULL;
1279 UINT r;
1281 TRACE("%s\n", debugstr_w(file->File));
1283 r = MSI_OpenQuery(package->db, &view, query, file->File);
1284 if (r != ERROR_SUCCESS)
1285 goto done;
1287 r = MSI_ViewExecute(view, NULL);
1288 if (r != ERROR_SUCCESS)
1289 goto done;
1291 r = MSI_ViewFetch(view, &row);
1292 if (r != ERROR_SUCCESS)
1293 goto done;
1295 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1296 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1297 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1298 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1299 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1301 done:
1302 if (view) msiobj_release(&view->hdr);
1303 if (row) msiobj_release(&row->hdr);
1304 return r;
1307 static UINT load_file(MSIRECORD *row, LPVOID param)
1309 MSIPACKAGE* package = param;
1310 LPCWSTR component;
1311 MSIFILE *file;
1313 /* fill in the data */
1315 file = msi_alloc_zero( sizeof (MSIFILE) );
1316 if (!file)
1317 return ERROR_NOT_ENOUGH_MEMORY;
1319 file->File = msi_dup_record_field( row, 1 );
1321 component = MSI_RecordGetString( row, 2 );
1322 file->Component = get_loaded_component( package, component );
1324 if (!file->Component)
1326 WARN("Component not found: %s\n", debugstr_w(component));
1327 msi_free(file->File);
1328 msi_free(file);
1329 return ERROR_SUCCESS;
1332 file->FileName = msi_dup_record_field( row, 3 );
1333 reduce_to_longfilename( file->FileName );
1335 file->ShortName = msi_dup_record_field( row, 3 );
1336 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1338 file->FileSize = MSI_RecordGetInteger( row, 4 );
1339 file->Version = msi_dup_record_field( row, 5 );
1340 file->Language = msi_dup_record_field( row, 6 );
1341 file->Attributes = MSI_RecordGetInteger( row, 7 );
1342 file->Sequence = MSI_RecordGetInteger( row, 8 );
1344 file->state = msifs_invalid;
1346 /* if the compressed bits are not set in the file attributes,
1347 * then read the information from the package word count property
1349 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1351 file->IsCompressed = FALSE;
1353 else if (file->Attributes &
1354 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1356 file->IsCompressed = TRUE;
1358 else if (file->Attributes & msidbFileAttributesNoncompressed)
1360 file->IsCompressed = FALSE;
1362 else
1364 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1367 load_file_hash(package, file);
1369 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1371 list_add_tail( &package->files, &file->entry );
1373 return ERROR_SUCCESS;
1376 static UINT load_all_files(MSIPACKAGE *package)
1378 MSIQUERY * view;
1379 UINT rc;
1380 static const WCHAR Query[] =
1381 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1382 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1383 '`','S','e','q','u','e','n','c','e','`', 0};
1385 if (!list_empty(&package->files))
1386 return ERROR_SUCCESS;
1388 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1389 if (rc != ERROR_SUCCESS)
1390 return ERROR_SUCCESS;
1392 rc = MSI_IterateRecords(view, NULL, load_file, package);
1393 msiobj_release(&view->hdr);
1395 return ERROR_SUCCESS;
1398 static UINT load_folder( MSIRECORD *row, LPVOID param )
1400 MSIPACKAGE *package = param;
1401 static WCHAR szEmpty[] = { 0 };
1402 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1403 MSIFOLDER *folder;
1405 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1406 if (!folder)
1407 return ERROR_NOT_ENOUGH_MEMORY;
1409 folder->Directory = msi_dup_record_field( row, 1 );
1411 TRACE("%s\n", debugstr_w(folder->Directory));
1413 p = msi_dup_record_field(row, 3);
1415 /* split src and target dir */
1416 tgt_short = p;
1417 src_short = folder_split_path( p, ':' );
1419 /* split the long and short paths */
1420 tgt_long = folder_split_path( tgt_short, '|' );
1421 src_long = folder_split_path( src_short, '|' );
1423 /* check for no-op dirs */
1424 if (!lstrcmpW(szDot, tgt_short))
1425 tgt_short = szEmpty;
1426 if (!lstrcmpW(szDot, src_short))
1427 src_short = szEmpty;
1429 if (!tgt_long)
1430 tgt_long = tgt_short;
1432 if (!src_short) {
1433 src_short = tgt_short;
1434 src_long = tgt_long;
1437 if (!src_long)
1438 src_long = src_short;
1440 /* FIXME: use the target short path too */
1441 folder->TargetDefault = strdupW(tgt_long);
1442 folder->SourceShortPath = strdupW(src_short);
1443 folder->SourceLongPath = strdupW(src_long);
1444 msi_free(p);
1446 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1447 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1448 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1450 folder->Parent = msi_dup_record_field( row, 2 );
1452 folder->Property = msi_dup_property( package, folder->Directory );
1454 list_add_tail( &package->folders, &folder->entry );
1456 TRACE("returning %p\n", folder);
1458 return ERROR_SUCCESS;
1461 static UINT load_all_folders( MSIPACKAGE *package )
1463 static const WCHAR query[] = {
1464 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1465 '`','D','i','r','e','c','t','o','r','y','`',0 };
1466 MSIQUERY *view;
1467 UINT r;
1469 if (!list_empty(&package->folders))
1470 return ERROR_SUCCESS;
1472 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1473 if (r != ERROR_SUCCESS)
1474 return r;
1476 r = MSI_IterateRecords(view, NULL, load_folder, package);
1477 msiobj_release(&view->hdr);
1478 return r;
1482 * I am not doing any of the costing functionality yet.
1483 * Mostly looking at doing the Component and Feature loading
1485 * The native MSI does A LOT of modification to tables here. Mostly adding
1486 * a lot of temporary columns to the Feature and Component tables.
1488 * note: Native msi also tracks the short filename. But I am only going to
1489 * track the long ones. Also looking at this directory table
1490 * it appears that the directory table does not get the parents
1491 * resolved base on property only based on their entries in the
1492 * directory table.
1494 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1496 static const WCHAR szCosting[] =
1497 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1499 MSI_SetPropertyW(package, szCosting, szZero);
1500 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1502 load_all_folders( package );
1503 load_all_components( package );
1504 load_all_features( package );
1505 load_all_files( package );
1507 return ERROR_SUCCESS;
1510 static UINT execute_script(MSIPACKAGE *package, UINT script )
1512 UINT i;
1513 UINT rc = ERROR_SUCCESS;
1515 TRACE("Executing Script %i\n",script);
1517 if (!package->script)
1519 ERR("no script!\n");
1520 return ERROR_FUNCTION_FAILED;
1523 for (i = 0; i < package->script->ActionCount[script]; i++)
1525 LPWSTR action;
1526 action = package->script->Actions[script][i];
1527 ui_actionstart(package, action);
1528 TRACE("Executing Action (%s)\n",debugstr_w(action));
1529 rc = ACTION_PerformAction(package, action, script, TRUE);
1530 if (rc != ERROR_SUCCESS)
1531 break;
1533 msi_free_action_script(package, script);
1534 return rc;
1537 static UINT ACTION_FileCost(MSIPACKAGE *package)
1539 return ERROR_SUCCESS;
1542 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1544 MSICOMPONENT *comp;
1545 INSTALLSTATE state;
1546 UINT r;
1548 state = MsiQueryProductStateW(package->ProductCode);
1550 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1552 if (!comp->ComponentId)
1553 continue;
1555 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1556 comp->Installed = INSTALLSTATE_ABSENT;
1557 else
1559 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1560 package->Context, comp->ComponentId,
1561 &comp->Installed);
1562 if (r != ERROR_SUCCESS)
1563 comp->Installed = INSTALLSTATE_ABSENT;
1568 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1570 MSIFEATURE *feature;
1571 INSTALLSTATE state;
1573 state = MsiQueryProductStateW(package->ProductCode);
1575 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1577 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1578 feature->Installed = INSTALLSTATE_ABSENT;
1579 else
1581 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1582 feature->Feature);
1587 static BOOL process_state_property(MSIPACKAGE* package, int level,
1588 LPCWSTR property, INSTALLSTATE state)
1590 LPWSTR override;
1591 MSIFEATURE *feature;
1593 override = msi_dup_property( package, property );
1594 if (!override)
1595 return FALSE;
1597 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1599 if (lstrcmpW(property, szRemove) &&
1600 (feature->Level <= 0 || feature->Level > level))
1601 continue;
1603 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1605 if (strcmpiW(override, szAll)==0)
1606 msi_feature_set_state(package, feature, state);
1607 else
1609 LPWSTR ptr = override;
1610 LPWSTR ptr2 = strchrW(override,',');
1612 while (ptr)
1614 int len = ptr2 - ptr;
1616 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1617 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1619 msi_feature_set_state(package, feature, state);
1620 break;
1622 if (ptr2)
1624 ptr=ptr2+1;
1625 ptr2 = strchrW(ptr,',');
1627 else
1628 break;
1632 msi_free(override);
1634 return TRUE;
1637 static BOOL process_overrides( MSIPACKAGE *package, int level )
1639 static const WCHAR szAddLocal[] =
1640 {'A','D','D','L','O','C','A','L',0};
1641 static const WCHAR szAddSource[] =
1642 {'A','D','D','S','O','U','R','C','E',0};
1643 static const WCHAR szAdvertise[] =
1644 {'A','D','V','E','R','T','I','S','E',0};
1645 BOOL ret = FALSE;
1647 /* all these activation/deactivation things happen in order and things
1648 * later on the list override things earlier on the list.
1650 * 0 INSTALLLEVEL processing
1651 * 1 ADDLOCAL
1652 * 2 REMOVE
1653 * 3 ADDSOURCE
1654 * 4 ADDDEFAULT
1655 * 5 REINSTALL
1656 * 6 ADVERTISE
1657 * 7 COMPADDLOCAL
1658 * 8 COMPADDSOURCE
1659 * 9 FILEADDLOCAL
1660 * 10 FILEADDSOURCE
1661 * 11 FILEADDDEFAULT
1663 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1664 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1665 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1666 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1667 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1669 if (ret)
1670 MSI_SetPropertyW( package, szPreselected, szOne );
1672 return ret;
1675 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1677 int level;
1678 static const WCHAR szlevel[] =
1679 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1680 MSICOMPONENT* component;
1681 MSIFEATURE *feature;
1683 TRACE("Checking Install Level\n");
1685 level = msi_get_property_int(package, szlevel, 1);
1687 if (!msi_get_property_int( package, szPreselected, 0 ))
1689 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1691 BOOL feature_state = ((feature->Level > 0) &&
1692 (feature->Level <= level));
1694 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1696 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1697 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1698 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1699 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1700 else
1701 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1705 /* disable child features of unselected parent features */
1706 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1708 FeatureList *fl;
1710 if (feature->Level > 0 && feature->Level <= level)
1711 continue;
1713 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1714 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1719 * now we want to enable or disable components base on feature
1722 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1724 ComponentList *cl;
1726 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1727 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1729 if (!feature->Level)
1730 continue;
1732 /* features with components that have compressed files are made local */
1733 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1735 if (cl->component->Enabled &&
1736 cl->component->ForceLocalState &&
1737 feature->Action == INSTALLSTATE_SOURCE)
1739 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1740 break;
1744 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1746 component = cl->component;
1748 if (!component->Enabled)
1749 continue;
1751 switch (feature->Action)
1753 case INSTALLSTATE_ABSENT:
1754 component->anyAbsent = 1;
1755 break;
1756 case INSTALLSTATE_ADVERTISED:
1757 component->hasAdvertiseFeature = 1;
1758 break;
1759 case INSTALLSTATE_SOURCE:
1760 component->hasSourceFeature = 1;
1761 break;
1762 case INSTALLSTATE_LOCAL:
1763 component->hasLocalFeature = 1;
1764 break;
1765 case INSTALLSTATE_DEFAULT:
1766 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1767 component->hasAdvertiseFeature = 1;
1768 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 component->hasSourceFeature = 1;
1770 else
1771 component->hasLocalFeature = 1;
1772 break;
1773 default:
1774 break;
1779 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1781 /* if the component isn't enabled, leave it alone */
1782 if (!component->Enabled)
1783 continue;
1785 /* check if it's local or source */
1786 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1787 (component->hasLocalFeature || component->hasSourceFeature))
1789 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1790 !component->ForceLocalState)
1791 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1792 else
1793 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1794 continue;
1797 /* if any feature is local, the component must be local too */
1798 if (component->hasLocalFeature)
1800 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1801 continue;
1804 if (component->hasSourceFeature)
1806 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1807 continue;
1810 if (component->hasAdvertiseFeature)
1812 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1813 continue;
1816 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1817 if (component->anyAbsent)
1818 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1821 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1823 if (component->Action == INSTALLSTATE_DEFAULT)
1825 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1826 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1829 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1830 debugstr_w(component->Component), component->Installed, component->Action);
1834 return ERROR_SUCCESS;
1837 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1839 MSIPACKAGE *package = param;
1840 LPCWSTR name;
1841 LPWSTR path;
1842 MSIFOLDER *f;
1844 name = MSI_RecordGetString(row,1);
1846 f = get_loaded_folder(package, name);
1847 if (!f) return ERROR_SUCCESS;
1849 /* reset the ResolvedTarget */
1850 msi_free(f->ResolvedTarget);
1851 f->ResolvedTarget = NULL;
1853 /* This helper function now does ALL the work */
1854 TRACE("Dir %s ...\n",debugstr_w(name));
1855 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1856 TRACE("resolves to %s\n",debugstr_w(path));
1857 msi_free(path);
1859 return ERROR_SUCCESS;
1862 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1864 MSIPACKAGE *package = param;
1865 LPCWSTR name;
1866 MSIFEATURE *feature;
1868 name = MSI_RecordGetString( row, 1 );
1870 feature = get_loaded_feature( package, name );
1871 if (!feature)
1872 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1873 else
1875 LPCWSTR Condition;
1876 Condition = MSI_RecordGetString(row,3);
1878 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1880 int level = MSI_RecordGetInteger(row,2);
1881 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1882 feature->Level = level;
1885 return ERROR_SUCCESS;
1888 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
1890 static const WCHAR name_fmt[] =
1891 {'%','u','.','%','u','.','%','u','.','%','u',0};
1892 static const WCHAR name[] = {'\\',0};
1893 VS_FIXEDFILEINFO *lpVer;
1894 WCHAR filever[0x100];
1895 LPVOID version;
1896 DWORD versize;
1897 DWORD handle;
1898 UINT sz;
1900 TRACE("%s\n", debugstr_w(filename));
1902 versize = GetFileVersionInfoSizeW( filename, &handle );
1903 if (!versize)
1904 return NULL;
1906 version = msi_alloc( versize );
1907 GetFileVersionInfoW( filename, 0, versize, version );
1909 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
1911 msi_free( version );
1912 return NULL;
1915 sprintfW( filever, name_fmt,
1916 HIWORD(lpVer->dwFileVersionMS),
1917 LOWORD(lpVer->dwFileVersionMS),
1918 HIWORD(lpVer->dwFileVersionLS),
1919 LOWORD(lpVer->dwFileVersionLS));
1921 msi_free( version );
1923 return strdupW( filever );
1926 static UINT msi_check_file_install_states( MSIPACKAGE *package )
1928 LPWSTR file_version;
1929 MSIFILE *file;
1931 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1933 MSICOMPONENT* comp = file->Component;
1934 LPWSTR p;
1936 if (!comp)
1937 continue;
1939 if (file->IsCompressed)
1940 comp->ForceLocalState = TRUE;
1942 /* calculate target */
1943 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
1945 msi_free(file->TargetPath);
1947 TRACE("file %s is named %s\n",
1948 debugstr_w(file->File), debugstr_w(file->FileName));
1950 file->TargetPath = build_directory_name(2, p, file->FileName);
1952 msi_free(p);
1954 TRACE("file %s resolves to %s\n",
1955 debugstr_w(file->File), debugstr_w(file->TargetPath));
1957 /* don't check files of components that aren't installed */
1958 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
1959 comp->Installed == INSTALLSTATE_ABSENT)
1961 file->state = msifs_missing; /* assume files are missing */
1962 continue;
1965 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
1967 file->state = msifs_missing;
1968 comp->Cost += file->FileSize;
1969 continue;
1972 if (file->Version &&
1973 (file_version = msi_get_disk_file_version( file->TargetPath )))
1975 TRACE("new %s old %s\n", debugstr_w(file->Version),
1976 debugstr_w(file_version));
1977 /* FIXME: seems like a bad way to compare version numbers */
1978 if (lstrcmpiW(file_version, file->Version)<0)
1980 file->state = msifs_overwrite;
1981 comp->Cost += file->FileSize;
1983 else
1984 file->state = msifs_present;
1985 msi_free( file_version );
1987 else
1988 file->state = msifs_present;
1991 return ERROR_SUCCESS;
1995 * A lot is done in this function aside from just the costing.
1996 * The costing needs to be implemented at some point but for now I am going
1997 * to focus on the directory building
2000 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2002 static const WCHAR ExecSeqQuery[] =
2003 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2004 '`','D','i','r','e','c','t','o','r','y','`',0};
2005 static const WCHAR ConditionQuery[] =
2006 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2007 '`','C','o','n','d','i','t','i','o','n','`',0};
2008 static const WCHAR szCosting[] =
2009 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2010 static const WCHAR szlevel[] =
2011 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2012 static const WCHAR szOutOfDiskSpace[] =
2013 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2014 MSICOMPONENT *comp;
2015 UINT rc = ERROR_SUCCESS;
2016 MSIQUERY * view;
2017 LPWSTR level;
2019 TRACE("Building Directory properties\n");
2021 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2022 if (rc == ERROR_SUCCESS)
2024 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2025 package);
2026 msiobj_release(&view->hdr);
2029 /* read components states from the registry */
2030 ACTION_GetComponentInstallStates(package);
2031 ACTION_GetFeatureInstallStates(package);
2033 TRACE("File calculations\n");
2034 msi_check_file_install_states( package );
2036 if (!process_overrides( package, msi_get_property_int( package, szlevel, 1 ) ))
2038 TRACE("Evaluating Condition Table\n");
2040 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2041 if (rc == ERROR_SUCCESS)
2043 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2044 msiobj_release( &view->hdr );
2047 TRACE("Enabling or Disabling Components\n");
2048 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2050 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2052 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2053 comp->Enabled = FALSE;
2055 else
2056 comp->Enabled = TRUE;
2060 MSI_SetPropertyW(package,szCosting,szOne);
2061 /* set default run level if not set */
2062 level = msi_dup_property( package, szlevel );
2063 if (!level)
2064 MSI_SetPropertyW(package,szlevel, szOne);
2065 msi_free(level);
2067 /* FIXME: check volume disk space */
2068 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2070 return MSI_SetFeatureStates(package);
2073 /* OK this value is "interpreted" and then formatted based on the
2074 first few characters */
2075 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2076 DWORD *size)
2078 LPSTR data = NULL;
2080 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2082 if (value[1]=='x')
2084 LPWSTR ptr;
2085 CHAR byte[5];
2086 LPWSTR deformated = NULL;
2087 int count;
2089 deformat_string(package, &value[2], &deformated);
2091 /* binary value type */
2092 ptr = deformated;
2093 *type = REG_BINARY;
2094 if (strlenW(ptr)%2)
2095 *size = (strlenW(ptr)/2)+1;
2096 else
2097 *size = strlenW(ptr)/2;
2099 data = msi_alloc(*size);
2101 byte[0] = '0';
2102 byte[1] = 'x';
2103 byte[4] = 0;
2104 count = 0;
2105 /* if uneven pad with a zero in front */
2106 if (strlenW(ptr)%2)
2108 byte[2]= '0';
2109 byte[3]= *ptr;
2110 ptr++;
2111 data[count] = (BYTE)strtol(byte,NULL,0);
2112 count ++;
2113 TRACE("Uneven byte count\n");
2115 while (*ptr)
2117 byte[2]= *ptr;
2118 ptr++;
2119 byte[3]= *ptr;
2120 ptr++;
2121 data[count] = (BYTE)strtol(byte,NULL,0);
2122 count ++;
2124 msi_free(deformated);
2126 TRACE("Data %i bytes(%i)\n",*size,count);
2128 else
2130 LPWSTR deformated;
2131 LPWSTR p;
2132 DWORD d = 0;
2133 deformat_string(package, &value[1], &deformated);
2135 *type=REG_DWORD;
2136 *size = sizeof(DWORD);
2137 data = msi_alloc(*size);
2138 p = deformated;
2139 if (*p == '-')
2140 p++;
2141 while (*p)
2143 if ( (*p < '0') || (*p > '9') )
2144 break;
2145 d *= 10;
2146 d += (*p - '0');
2147 p++;
2149 if (deformated[0] == '-')
2150 d = -d;
2151 *(LPDWORD)data = d;
2152 TRACE("DWORD %i\n",*(LPDWORD)data);
2154 msi_free(deformated);
2157 else
2159 static const WCHAR szMulti[] = {'[','~',']',0};
2160 LPCWSTR ptr;
2161 *type=REG_SZ;
2163 if (value[0]=='#')
2165 if (value[1]=='%')
2167 ptr = &value[2];
2168 *type=REG_EXPAND_SZ;
2170 else
2171 ptr = &value[1];
2173 else
2174 ptr=value;
2176 if (strstrW(value,szMulti))
2177 *type = REG_MULTI_SZ;
2179 /* remove initial delimiter */
2180 if (!strncmpW(value, szMulti, 3))
2181 ptr = value + 3;
2183 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2185 /* add double NULL terminator */
2186 if (*type == REG_MULTI_SZ)
2188 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2189 data = msi_realloc_zero(data, *size);
2192 return data;
2195 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2197 MSIPACKAGE *package = param;
2198 static const WCHAR szHCR[] =
2199 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2200 'R','O','O','T','\\',0};
2201 static const WCHAR szHCU[] =
2202 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2203 'U','S','E','R','\\',0};
2204 static const WCHAR szHLM[] =
2205 {'H','K','E','Y','_','L','O','C','A','L','_',
2206 'M','A','C','H','I','N','E','\\',0};
2207 static const WCHAR szHU[] =
2208 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2210 LPSTR value_data = NULL;
2211 HKEY root_key, hkey;
2212 DWORD type,size;
2213 LPWSTR deformated;
2214 LPCWSTR szRoot, component, name, key, value;
2215 MSICOMPONENT *comp;
2216 MSIRECORD * uirow;
2217 LPWSTR uikey;
2218 INT root;
2219 BOOL check_first = FALSE;
2220 UINT rc;
2222 ui_progress(package,2,0,0,0);
2224 value = NULL;
2225 key = NULL;
2226 uikey = NULL;
2227 name = NULL;
2229 component = MSI_RecordGetString(row, 6);
2230 comp = get_loaded_component(package,component);
2231 if (!comp)
2232 return ERROR_SUCCESS;
2234 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2236 TRACE("Skipping write due to disabled component %s\n",
2237 debugstr_w(component));
2239 comp->Action = comp->Installed;
2241 return ERROR_SUCCESS;
2244 comp->Action = INSTALLSTATE_LOCAL;
2246 name = MSI_RecordGetString(row, 4);
2247 if( MSI_RecordIsNull(row,5) && name )
2249 /* null values can have special meanings */
2250 if (name[0]=='-' && name[1] == 0)
2251 return ERROR_SUCCESS;
2252 else if ((name[0]=='+' && name[1] == 0) ||
2253 (name[0] == '*' && name[1] == 0))
2254 name = NULL;
2255 check_first = TRUE;
2258 root = MSI_RecordGetInteger(row,2);
2259 key = MSI_RecordGetString(row, 3);
2261 /* get the root key */
2262 switch (root)
2264 case -1:
2266 LPWSTR all_users = msi_dup_property( package, szAllUsers );
2267 if (all_users && all_users[0] == '1')
2269 root_key = HKEY_LOCAL_MACHINE;
2270 szRoot = szHLM;
2272 else
2274 root_key = HKEY_CURRENT_USER;
2275 szRoot = szHCU;
2277 msi_free(all_users);
2279 break;
2280 case 0: root_key = HKEY_CLASSES_ROOT;
2281 szRoot = szHCR;
2282 break;
2283 case 1: root_key = HKEY_CURRENT_USER;
2284 szRoot = szHCU;
2285 break;
2286 case 2: root_key = HKEY_LOCAL_MACHINE;
2287 szRoot = szHLM;
2288 break;
2289 case 3: root_key = HKEY_USERS;
2290 szRoot = szHU;
2291 break;
2292 default:
2293 ERR("Unknown root %i\n",root);
2294 root_key=NULL;
2295 szRoot = NULL;
2296 break;
2298 if (!root_key)
2299 return ERROR_SUCCESS;
2301 deformat_string(package, key , &deformated);
2302 size = strlenW(deformated) + strlenW(szRoot) + 1;
2303 uikey = msi_alloc(size*sizeof(WCHAR));
2304 strcpyW(uikey,szRoot);
2305 strcatW(uikey,deformated);
2307 if (RegCreateKeyW( root_key, deformated, &hkey))
2309 ERR("Could not create key %s\n",debugstr_w(deformated));
2310 msi_free(deformated);
2311 msi_free(uikey);
2312 return ERROR_SUCCESS;
2314 msi_free(deformated);
2316 value = MSI_RecordGetString(row,5);
2317 if (value)
2318 value_data = parse_value(package, value, &type, &size);
2319 else
2321 value_data = (LPSTR)strdupW(szEmpty);
2322 size = sizeof(szEmpty);
2323 type = REG_SZ;
2326 deformat_string(package, name, &deformated);
2328 if (!check_first)
2330 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2331 debugstr_w(uikey));
2332 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2334 else
2336 DWORD sz = 0;
2337 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2338 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2340 TRACE("value %s of %s checked already exists\n",
2341 debugstr_w(deformated), debugstr_w(uikey));
2343 else
2345 TRACE("Checked and setting value %s of %s\n",
2346 debugstr_w(deformated), debugstr_w(uikey));
2347 if (deformated || size)
2348 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2351 RegCloseKey(hkey);
2353 uirow = MSI_CreateRecord(3);
2354 MSI_RecordSetStringW(uirow,2,deformated);
2355 MSI_RecordSetStringW(uirow,1,uikey);
2357 if (type == REG_SZ)
2358 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2359 else
2360 MSI_RecordSetStringW(uirow,3,value);
2362 ui_actiondata(package,szWriteRegistryValues,uirow);
2363 msiobj_release( &uirow->hdr );
2365 msi_free(value_data);
2366 msi_free(deformated);
2367 msi_free(uikey);
2369 return ERROR_SUCCESS;
2372 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2374 UINT rc;
2375 MSIQUERY * view;
2376 static const WCHAR ExecSeqQuery[] =
2377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2378 '`','R','e','g','i','s','t','r','y','`',0 };
2380 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2381 if (rc != ERROR_SUCCESS)
2382 return ERROR_SUCCESS;
2384 /* increment progress bar each time action data is sent */
2385 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2387 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2389 msiobj_release(&view->hdr);
2390 return rc;
2393 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2395 package->script->CurrentlyScripting = TRUE;
2397 return ERROR_SUCCESS;
2401 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2403 MSICOMPONENT *comp;
2404 DWORD progress = 0;
2405 DWORD total = 0;
2406 static const WCHAR q1[]=
2407 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2408 '`','R','e','g','i','s','t','r','y','`',0};
2409 UINT rc;
2410 MSIQUERY * view;
2411 MSIFEATURE *feature;
2412 MSIFILE *file;
2414 TRACE("InstallValidate\n");
2416 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2417 if (rc == ERROR_SUCCESS)
2419 MSI_IterateRecords( view, &progress, NULL, package );
2420 msiobj_release( &view->hdr );
2421 total += progress * REG_PROGRESS_VALUE;
2424 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2425 total += COMPONENT_PROGRESS_VALUE;
2427 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2428 total += file->FileSize;
2430 ui_progress(package,0,total,0,0);
2432 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2434 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2435 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2436 feature->ActionRequest);
2439 return ERROR_SUCCESS;
2442 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2444 MSIPACKAGE* package = param;
2445 LPCWSTR cond = NULL;
2446 LPCWSTR message = NULL;
2447 UINT r;
2449 static const WCHAR title[]=
2450 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2452 cond = MSI_RecordGetString(row,1);
2454 r = MSI_EvaluateConditionW(package,cond);
2455 if (r == MSICONDITION_FALSE)
2457 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2459 LPWSTR deformated;
2460 message = MSI_RecordGetString(row,2);
2461 deformat_string(package,message,&deformated);
2462 MessageBoxW(NULL,deformated,title,MB_OK);
2463 msi_free(deformated);
2466 return ERROR_INSTALL_FAILURE;
2469 return ERROR_SUCCESS;
2472 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2474 UINT rc;
2475 MSIQUERY * view = NULL;
2476 static const WCHAR ExecSeqQuery[] =
2477 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2478 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2480 TRACE("Checking launch conditions\n");
2482 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2483 if (rc != ERROR_SUCCESS)
2484 return ERROR_SUCCESS;
2486 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2487 msiobj_release(&view->hdr);
2489 return rc;
2492 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2495 if (!cmp->KeyPath)
2496 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2498 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2500 MSIRECORD * row = 0;
2501 UINT root,len;
2502 LPWSTR deformated,buffer,deformated_name;
2503 LPCWSTR key,name;
2504 static const WCHAR ExecSeqQuery[] =
2505 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2506 '`','R','e','g','i','s','t','r','y','`',' ',
2507 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2508 ' ','=',' ' ,'\'','%','s','\'',0 };
2509 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2510 static const WCHAR fmt2[]=
2511 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2513 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2514 if (!row)
2515 return NULL;
2517 root = MSI_RecordGetInteger(row,2);
2518 key = MSI_RecordGetString(row, 3);
2519 name = MSI_RecordGetString(row, 4);
2520 deformat_string(package, key , &deformated);
2521 deformat_string(package, name, &deformated_name);
2523 len = strlenW(deformated) + 6;
2524 if (deformated_name)
2525 len+=strlenW(deformated_name);
2527 buffer = msi_alloc( len *sizeof(WCHAR));
2529 if (deformated_name)
2530 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2531 else
2532 sprintfW(buffer,fmt,root,deformated);
2534 msi_free(deformated);
2535 msi_free(deformated_name);
2536 msiobj_release(&row->hdr);
2538 return buffer;
2540 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2542 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2543 return NULL;
2545 else
2547 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2549 if (file)
2550 return strdupW( file->TargetPath );
2552 return NULL;
2555 static HKEY openSharedDLLsKey(void)
2557 HKEY hkey=0;
2558 static const WCHAR path[] =
2559 {'S','o','f','t','w','a','r','e','\\',
2560 'M','i','c','r','o','s','o','f','t','\\',
2561 'W','i','n','d','o','w','s','\\',
2562 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2563 'S','h','a','r','e','d','D','L','L','s',0};
2565 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2566 return hkey;
2569 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2571 HKEY hkey;
2572 DWORD count=0;
2573 DWORD type;
2574 DWORD sz = sizeof(count);
2575 DWORD rc;
2577 hkey = openSharedDLLsKey();
2578 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2579 if (rc != ERROR_SUCCESS)
2580 count = 0;
2581 RegCloseKey(hkey);
2582 return count;
2585 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2587 HKEY hkey;
2589 hkey = openSharedDLLsKey();
2590 if (count > 0)
2591 msi_reg_set_val_dword( hkey, path, count );
2592 else
2593 RegDeleteValueW(hkey,path);
2594 RegCloseKey(hkey);
2595 return count;
2599 * Return TRUE if the count should be written out and FALSE if not
2601 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2603 MSIFEATURE *feature;
2604 INT count = 0;
2605 BOOL write = FALSE;
2607 /* only refcount DLLs */
2608 if (comp->KeyPath == NULL ||
2609 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2610 comp->Attributes & msidbComponentAttributesODBCDataSource)
2611 write = FALSE;
2612 else
2614 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2615 write = (count > 0);
2617 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2618 write = TRUE;
2621 /* increment counts */
2622 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2624 ComponentList *cl;
2626 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2627 continue;
2629 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2631 if ( cl->component == comp )
2632 count++;
2636 /* decrement counts */
2637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2639 ComponentList *cl;
2641 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2642 continue;
2644 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2646 if ( cl->component == comp )
2647 count--;
2651 /* ref count all the files in the component */
2652 if (write)
2654 MSIFILE *file;
2656 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2658 if (file->Component == comp)
2659 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2663 /* add a count for permanent */
2664 if (comp->Attributes & msidbComponentAttributesPermanent)
2665 count ++;
2667 comp->RefCount = count;
2669 if (write)
2670 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2673 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2675 WCHAR squished_pc[GUID_SIZE];
2676 WCHAR squished_cc[GUID_SIZE];
2677 UINT rc;
2678 MSICOMPONENT *comp;
2679 HKEY hkey;
2681 TRACE("\n");
2683 squash_guid(package->ProductCode,squished_pc);
2684 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2686 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2688 MSIRECORD * uirow;
2690 ui_progress(package,2,0,0,0);
2691 if (!comp->ComponentId)
2692 continue;
2694 squash_guid(comp->ComponentId,squished_cc);
2696 msi_free(comp->FullKeypath);
2697 comp->FullKeypath = resolve_keypath( package, comp );
2699 ACTION_RefCountComponent( package, comp );
2701 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2702 debugstr_w(comp->Component),
2703 debugstr_w(squished_cc),
2704 debugstr_w(comp->FullKeypath),
2705 comp->RefCount);
2707 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2708 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2710 if (!comp->FullKeypath)
2711 continue;
2713 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2714 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2715 &hkey, TRUE);
2716 else
2717 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2718 &hkey, TRUE);
2720 if (rc != ERROR_SUCCESS)
2721 continue;
2723 if (comp->Attributes & msidbComponentAttributesPermanent)
2725 static const WCHAR szPermKey[] =
2726 { '0','0','0','0','0','0','0','0','0','0','0','0',
2727 '0','0','0','0','0','0','0','0','0','0','0','0',
2728 '0','0','0','0','0','0','0','0',0 };
2730 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2733 if (comp->Action == INSTALLSTATE_LOCAL)
2734 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2735 else
2737 MSIFILE *file;
2738 MSIRECORD *row;
2739 LPWSTR ptr, ptr2;
2740 WCHAR source[MAX_PATH];
2741 WCHAR base[MAX_PATH];
2742 LPWSTR sourcepath;
2744 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2745 static const WCHAR query[] = {
2746 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2747 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2748 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2749 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2750 '`','D','i','s','k','I','d','`',0};
2752 file = get_loaded_file(package, comp->KeyPath);
2753 if (!file)
2754 continue;
2756 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2757 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2758 ptr2 = strrchrW(source, '\\') + 1;
2759 msiobj_release(&row->hdr);
2761 lstrcpyW(base, package->PackagePath);
2762 ptr = strrchrW(base, '\\');
2763 *(ptr + 1) = '\0';
2765 sourcepath = resolve_file_source(package, file);
2766 ptr = sourcepath + lstrlenW(base);
2767 lstrcpyW(ptr2, ptr);
2768 msi_free(sourcepath);
2770 msi_reg_set_val_str(hkey, squished_pc, source);
2772 RegCloseKey(hkey);
2774 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2776 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2777 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
2778 else
2779 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
2782 /* UI stuff */
2783 uirow = MSI_CreateRecord(3);
2784 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2785 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2786 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2787 ui_actiondata(package,szProcessComponents,uirow);
2788 msiobj_release( &uirow->hdr );
2791 return ERROR_SUCCESS;
2794 typedef struct {
2795 CLSID clsid;
2796 LPWSTR source;
2798 LPWSTR path;
2799 ITypeLib *ptLib;
2800 } typelib_struct;
2802 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2803 LPWSTR lpszName, LONG_PTR lParam)
2805 TLIBATTR *attr;
2806 typelib_struct *tl_struct = (typelib_struct*) lParam;
2807 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2808 int sz;
2809 HRESULT res;
2811 if (!IS_INTRESOURCE(lpszName))
2813 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2814 return TRUE;
2817 sz = strlenW(tl_struct->source)+4;
2818 sz *= sizeof(WCHAR);
2820 if ((INT_PTR)lpszName == 1)
2821 tl_struct->path = strdupW(tl_struct->source);
2822 else
2824 tl_struct->path = msi_alloc(sz);
2825 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2828 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2829 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2830 if (FAILED(res))
2832 msi_free(tl_struct->path);
2833 tl_struct->path = NULL;
2835 return TRUE;
2838 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2839 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2841 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2842 return FALSE;
2845 msi_free(tl_struct->path);
2846 tl_struct->path = NULL;
2848 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2849 ITypeLib_Release(tl_struct->ptLib);
2851 return TRUE;
2854 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2856 MSIPACKAGE* package = param;
2857 LPCWSTR component;
2858 MSICOMPONENT *comp;
2859 MSIFILE *file;
2860 typelib_struct tl_struct;
2861 ITypeLib *tlib;
2862 HMODULE module;
2863 HRESULT hr;
2865 component = MSI_RecordGetString(row,3);
2866 comp = get_loaded_component(package,component);
2867 if (!comp)
2868 return ERROR_SUCCESS;
2870 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2872 TRACE("Skipping typelib reg due to disabled component\n");
2874 comp->Action = comp->Installed;
2876 return ERROR_SUCCESS;
2879 comp->Action = INSTALLSTATE_LOCAL;
2881 file = get_loaded_file( package, comp->KeyPath );
2882 if (!file)
2883 return ERROR_SUCCESS;
2885 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
2886 if (module)
2888 LPCWSTR guid;
2889 guid = MSI_RecordGetString(row,1);
2890 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
2891 tl_struct.source = strdupW( file->TargetPath );
2892 tl_struct.path = NULL;
2894 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
2895 (LONG_PTR)&tl_struct);
2897 if (tl_struct.path)
2899 LPWSTR help = NULL;
2900 LPCWSTR helpid;
2901 HRESULT res;
2903 helpid = MSI_RecordGetString(row,6);
2905 if (helpid)
2906 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
2907 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
2908 msi_free(help);
2910 if (FAILED(res))
2911 ERR("Failed to register type library %s\n",
2912 debugstr_w(tl_struct.path));
2913 else
2915 ui_actiondata(package,szRegisterTypeLibraries,row);
2917 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
2920 ITypeLib_Release(tl_struct.ptLib);
2921 msi_free(tl_struct.path);
2923 else
2924 ERR("Failed to load type library %s\n",
2925 debugstr_w(tl_struct.source));
2927 FreeLibrary(module);
2928 msi_free(tl_struct.source);
2930 else
2932 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
2933 if (FAILED(hr))
2935 ERR("Failed to load type library: %08x\n", hr);
2936 return ERROR_INSTALL_FAILURE;
2939 ITypeLib_Release(tlib);
2942 return ERROR_SUCCESS;
2945 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
2948 * OK this is a bit confusing.. I am given a _Component key and I believe
2949 * that the file that is being registered as a type library is the "key file
2950 * of that component" which I interpret to mean "The file in the KeyPath of
2951 * that component".
2953 UINT rc;
2954 MSIQUERY * view;
2955 static const WCHAR Query[] =
2956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2957 '`','T','y','p','e','L','i','b','`',0};
2959 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
2960 if (rc != ERROR_SUCCESS)
2961 return ERROR_SUCCESS;
2963 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
2964 msiobj_release(&view->hdr);
2965 return rc;
2968 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
2970 MSIPACKAGE *package = param;
2971 LPCWSTR component, guid;
2972 MSICOMPONENT *comp;
2973 GUID libid;
2974 UINT version;
2975 LCID language;
2976 SYSKIND syskind;
2977 HRESULT hr;
2979 component = MSI_RecordGetString( row, 3 );
2980 comp = get_loaded_component( package, component );
2981 if (!comp)
2982 return ERROR_SUCCESS;
2984 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT ))
2986 TRACE("Skipping, component is not scheduled for uninstall\n");
2988 comp->Action = comp->Installed;
2989 return ERROR_SUCCESS;
2991 comp->Action = INSTALLSTATE_ABSENT;
2993 guid = MSI_RecordGetString( row, 1 );
2994 CLSIDFromString( (LPWSTR)guid, &libid );
2995 version = MSI_RecordGetInteger( row, 4 );
2996 language = MSI_RecordGetInteger( row, 2 );
2998 #ifdef _WIN64
2999 syskind = SYS_WIN64;
3000 #else
3001 syskind = SYS_WIN32;
3002 #endif
3004 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3005 if (FAILED(hr))
3007 WARN("Failed to unregister typelib: %08x\n", hr);
3010 return ERROR_SUCCESS;
3013 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3015 UINT rc;
3016 MSIQUERY *view;
3017 static const WCHAR query[] =
3018 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3019 '`','T','y','p','e','L','i','b','`',0};
3021 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3022 if (rc != ERROR_SUCCESS)
3023 return ERROR_SUCCESS;
3025 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3026 msiobj_release( &view->hdr );
3027 return rc;
3030 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3032 MSIPACKAGE *package = param;
3033 LPWSTR target_file, target_folder, filename;
3034 LPCWSTR buffer, extension;
3035 MSICOMPONENT *comp;
3036 static const WCHAR szlnk[]={'.','l','n','k',0};
3037 IShellLinkW *sl = NULL;
3038 IPersistFile *pf = NULL;
3039 HRESULT res;
3041 buffer = MSI_RecordGetString(row,4);
3042 comp = get_loaded_component(package,buffer);
3043 if (!comp)
3044 return ERROR_SUCCESS;
3046 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3048 TRACE("Skipping shortcut creation due to disabled component\n");
3050 comp->Action = comp->Installed;
3052 return ERROR_SUCCESS;
3055 comp->Action = INSTALLSTATE_LOCAL;
3057 ui_actiondata(package,szCreateShortcuts,row);
3059 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3060 &IID_IShellLinkW, (LPVOID *) &sl );
3062 if (FAILED( res ))
3064 ERR("CLSID_ShellLink not available\n");
3065 goto err;
3068 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3069 if (FAILED( res ))
3071 ERR("QueryInterface(IID_IPersistFile) failed\n");
3072 goto err;
3075 buffer = MSI_RecordGetString(row,2);
3076 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3078 /* may be needed because of a bug somewhere else */
3079 create_full_pathW(target_folder);
3081 filename = msi_dup_record_field( row, 3 );
3082 reduce_to_longfilename(filename);
3084 extension = strchrW(filename,'.');
3085 if (!extension || strcmpiW(extension,szlnk))
3087 int len = strlenW(filename);
3088 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3089 memcpy(filename + len, szlnk, sizeof(szlnk));
3091 target_file = build_directory_name(2, target_folder, filename);
3092 msi_free(target_folder);
3093 msi_free(filename);
3095 buffer = MSI_RecordGetString(row,5);
3096 if (strchrW(buffer,'['))
3098 LPWSTR deformated;
3099 deformat_string(package,buffer,&deformated);
3100 IShellLinkW_SetPath(sl,deformated);
3101 msi_free(deformated);
3103 else
3105 FIXME("poorly handled shortcut format, advertised shortcut\n");
3106 IShellLinkW_SetPath(sl,comp->FullKeypath);
3109 if (!MSI_RecordIsNull(row,6))
3111 LPWSTR deformated;
3112 buffer = MSI_RecordGetString(row,6);
3113 deformat_string(package,buffer,&deformated);
3114 IShellLinkW_SetArguments(sl,deformated);
3115 msi_free(deformated);
3118 if (!MSI_RecordIsNull(row,7))
3120 buffer = MSI_RecordGetString(row,7);
3121 IShellLinkW_SetDescription(sl,buffer);
3124 if (!MSI_RecordIsNull(row,8))
3125 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3127 if (!MSI_RecordIsNull(row,9))
3129 LPWSTR Path;
3130 INT index;
3132 buffer = MSI_RecordGetString(row,9);
3134 Path = build_icon_path(package,buffer);
3135 index = MSI_RecordGetInteger(row,10);
3137 /* no value means 0 */
3138 if (index == MSI_NULL_INTEGER)
3139 index = 0;
3141 IShellLinkW_SetIconLocation(sl,Path,index);
3142 msi_free(Path);
3145 if (!MSI_RecordIsNull(row,11))
3146 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3148 if (!MSI_RecordIsNull(row,12))
3150 LPWSTR Path;
3151 buffer = MSI_RecordGetString(row,12);
3152 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3153 if (Path)
3154 IShellLinkW_SetWorkingDirectory(sl,Path);
3155 msi_free(Path);
3158 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3159 IPersistFile_Save(pf,target_file,FALSE);
3161 msi_free(target_file);
3163 err:
3164 if (pf)
3165 IPersistFile_Release( pf );
3166 if (sl)
3167 IShellLinkW_Release( sl );
3169 return ERROR_SUCCESS;
3172 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3174 UINT rc;
3175 HRESULT res;
3176 MSIQUERY * view;
3177 static const WCHAR Query[] =
3178 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3179 '`','S','h','o','r','t','c','u','t','`',0};
3181 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3182 if (rc != ERROR_SUCCESS)
3183 return ERROR_SUCCESS;
3185 res = CoInitialize( NULL );
3187 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3188 msiobj_release(&view->hdr);
3190 if (SUCCEEDED(res))
3191 CoUninitialize();
3193 return rc;
3196 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3198 MSIPACKAGE* package = param;
3199 HANDLE the_file;
3200 LPWSTR FilePath;
3201 LPCWSTR FileName;
3202 CHAR buffer[1024];
3203 DWORD sz;
3204 UINT rc;
3205 MSIRECORD *uirow;
3207 FileName = MSI_RecordGetString(row,1);
3208 if (!FileName)
3210 ERR("Unable to get FileName\n");
3211 return ERROR_SUCCESS;
3214 FilePath = build_icon_path(package,FileName);
3216 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3218 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3219 FILE_ATTRIBUTE_NORMAL, NULL);
3221 if (the_file == INVALID_HANDLE_VALUE)
3223 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3224 msi_free(FilePath);
3225 return ERROR_SUCCESS;
3230 DWORD write;
3231 sz = 1024;
3232 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3233 if (rc != ERROR_SUCCESS)
3235 ERR("Failed to get stream\n");
3236 CloseHandle(the_file);
3237 DeleteFileW(FilePath);
3238 break;
3240 WriteFile(the_file,buffer,sz,&write,NULL);
3241 } while (sz == 1024);
3243 msi_free(FilePath);
3245 CloseHandle(the_file);
3247 uirow = MSI_CreateRecord(1);
3248 MSI_RecordSetStringW(uirow,1,FileName);
3249 ui_actiondata(package,szPublishProduct,uirow);
3250 msiobj_release( &uirow->hdr );
3252 return ERROR_SUCCESS;
3255 static UINT msi_publish_icons(MSIPACKAGE *package)
3257 UINT r;
3258 MSIQUERY *view;
3260 static const WCHAR query[]= {
3261 'S','E','L','E','C','T',' ','*',' ',
3262 'F','R','O','M',' ','`','I','c','o','n','`',0};
3264 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3265 if (r == ERROR_SUCCESS)
3267 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3268 msiobj_release(&view->hdr);
3271 return ERROR_SUCCESS;
3274 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3276 UINT r;
3277 HKEY source;
3278 LPWSTR buffer;
3279 MSIMEDIADISK *disk;
3280 MSISOURCELISTINFO *info;
3282 r = RegCreateKeyW(hkey, szSourceList, &source);
3283 if (r != ERROR_SUCCESS)
3284 return r;
3286 RegCloseKey(source);
3288 buffer = strrchrW(package->PackagePath, '\\') + 1;
3289 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3290 package->Context, MSICODE_PRODUCT,
3291 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3292 if (r != ERROR_SUCCESS)
3293 return r;
3295 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3296 package->Context, MSICODE_PRODUCT,
3297 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3298 if (r != ERROR_SUCCESS)
3299 return r;
3301 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3302 package->Context, MSICODE_PRODUCT,
3303 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3304 if (r != ERROR_SUCCESS)
3305 return r;
3307 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3309 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3310 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3311 info->options, info->value);
3312 else
3313 MsiSourceListSetInfoW(package->ProductCode, NULL,
3314 info->context, info->options,
3315 info->property, info->value);
3318 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3320 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3321 disk->context, disk->options,
3322 disk->disk_id, disk->volume_label, disk->disk_prompt);
3325 return ERROR_SUCCESS;
3328 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3330 MSIHANDLE hdb, suminfo;
3331 WCHAR guids[MAX_PATH];
3332 WCHAR packcode[SQUISH_GUID_SIZE];
3333 LPWSTR buffer;
3334 LPWSTR ptr;
3335 DWORD langid;
3336 DWORD size;
3337 UINT r;
3339 static const WCHAR szProductLanguage[] =
3340 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3341 static const WCHAR szARPProductIcon[] =
3342 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3343 static const WCHAR szProductVersion[] =
3344 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3345 static const WCHAR szAssignment[] =
3346 {'A','s','s','i','g','n','m','e','n','t',0};
3347 static const WCHAR szAdvertiseFlags[] =
3348 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3349 static const WCHAR szClients[] =
3350 {'C','l','i','e','n','t','s',0};
3351 static const WCHAR szColon[] = {':',0};
3353 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3354 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3355 msi_free(buffer);
3357 langid = msi_get_property_int(package, szProductLanguage, 0);
3358 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3360 /* FIXME */
3361 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3363 buffer = msi_dup_property(package, szARPProductIcon);
3364 if (buffer)
3366 LPWSTR path = build_icon_path(package,buffer);
3367 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3368 msi_free(path);
3369 msi_free(buffer);
3372 buffer = msi_dup_property(package, szProductVersion);
3373 if (buffer)
3375 DWORD verdword = msi_version_str_to_dword(buffer);
3376 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3377 msi_free(buffer);
3380 msi_reg_set_val_dword(hkey, szAssignment, 0);
3381 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3382 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3383 msi_reg_set_val_str(hkey, szClients, szColon);
3385 hdb = alloc_msihandle(&package->db->hdr);
3386 if (!hdb)
3387 return ERROR_NOT_ENOUGH_MEMORY;
3389 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3390 MsiCloseHandle(hdb);
3391 if (r != ERROR_SUCCESS)
3392 goto done;
3394 size = MAX_PATH;
3395 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3396 NULL, guids, &size);
3397 if (r != ERROR_SUCCESS)
3398 goto done;
3400 ptr = strchrW(guids, ';');
3401 if (ptr) *ptr = 0;
3402 squash_guid(guids, packcode);
3403 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3405 done:
3406 MsiCloseHandle(suminfo);
3407 return ERROR_SUCCESS;
3410 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3412 UINT r;
3413 HKEY hkey;
3414 LPWSTR upgrade;
3415 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3417 static const WCHAR szUpgradeCode[] =
3418 {'U','p','g','r','a','d','e','C','o','d','e',0};
3420 upgrade = msi_dup_property(package, szUpgradeCode);
3421 if (!upgrade)
3422 return ERROR_SUCCESS;
3424 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3426 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3427 if (r != ERROR_SUCCESS)
3428 goto done;
3430 else
3432 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3433 if (r != ERROR_SUCCESS)
3434 goto done;
3437 squash_guid(package->ProductCode, squashed_pc);
3438 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3440 RegCloseKey(hkey);
3442 done:
3443 msi_free(upgrade);
3444 return r;
3447 static BOOL msi_check_publish(MSIPACKAGE *package)
3449 MSIFEATURE *feature;
3451 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3453 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3454 return TRUE;
3457 return FALSE;
3460 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3462 MSIFEATURE *feature;
3464 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3466 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3467 return FALSE;
3470 return TRUE;
3473 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3475 WCHAR patch_squashed[GUID_SIZE];
3476 HKEY patches;
3477 LONG res;
3478 UINT r = ERROR_FUNCTION_FAILED;
3480 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3481 &patches, NULL);
3482 if (res != ERROR_SUCCESS)
3483 return ERROR_FUNCTION_FAILED;
3485 squash_guid(package->patch->patchcode, patch_squashed);
3487 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3488 (const BYTE *)patch_squashed,
3489 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3490 if (res != ERROR_SUCCESS)
3491 goto done;
3493 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3494 (const BYTE *)package->patch->transforms,
3495 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3496 if (res == ERROR_SUCCESS)
3497 r = ERROR_SUCCESS;
3499 done:
3500 RegCloseKey(patches);
3501 return r;
3505 * 99% of the work done here is only done for
3506 * advertised installs. However this is where the
3507 * Icon table is processed and written out
3508 * so that is what I am going to do here.
3510 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3512 UINT rc;
3513 HKEY hukey=0;
3514 HKEY hudkey=0;
3516 /* FIXME: also need to publish if the product is in advertise mode */
3517 if (!msi_check_publish(package))
3518 return ERROR_SUCCESS;
3520 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3521 &hukey, TRUE);
3522 if (rc != ERROR_SUCCESS)
3523 goto end;
3525 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3526 NULL, &hudkey, TRUE);
3527 if (rc != ERROR_SUCCESS)
3528 goto end;
3530 rc = msi_publish_upgrade_code(package);
3531 if (rc != ERROR_SUCCESS)
3532 goto end;
3534 if (package->patch)
3536 rc = msi_publish_patch(package, hukey, hudkey);
3537 if (rc != ERROR_SUCCESS)
3538 goto end;
3541 rc = msi_publish_product_properties(package, hukey);
3542 if (rc != ERROR_SUCCESS)
3543 goto end;
3545 rc = msi_publish_sourcelist(package, hukey);
3546 if (rc != ERROR_SUCCESS)
3547 goto end;
3549 rc = msi_publish_icons(package);
3551 end:
3552 RegCloseKey(hukey);
3553 RegCloseKey(hudkey);
3555 return rc;
3558 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3560 MSIPACKAGE *package = param;
3561 LPCWSTR component, section, key, value, identifier, dirproperty;
3562 LPWSTR deformated_section, deformated_key, deformated_value;
3563 LPWSTR folder, filename, fullname = NULL;
3564 LPCWSTR filenameptr;
3565 MSIRECORD * uirow;
3566 INT action;
3567 MSICOMPONENT *comp;
3568 static const WCHAR szWindowsFolder[] =
3569 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3571 component = MSI_RecordGetString(row, 8);
3572 comp = get_loaded_component(package,component);
3574 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3576 TRACE("Skipping ini file due to disabled component %s\n",
3577 debugstr_w(component));
3579 comp->Action = comp->Installed;
3581 return ERROR_SUCCESS;
3584 comp->Action = INSTALLSTATE_LOCAL;
3586 identifier = MSI_RecordGetString(row,1);
3587 dirproperty = MSI_RecordGetString(row,3);
3588 section = MSI_RecordGetString(row,4);
3589 key = MSI_RecordGetString(row,5);
3590 value = MSI_RecordGetString(row,6);
3591 action = MSI_RecordGetInteger(row,7);
3593 deformat_string(package,section,&deformated_section);
3594 deformat_string(package,key,&deformated_key);
3595 deformat_string(package,value,&deformated_value);
3597 filename = msi_dup_record_field(row, 2);
3598 if (filename && (filenameptr = strchrW(filename, '|')))
3599 filenameptr++;
3600 else
3601 filenameptr = filename;
3603 if (dirproperty)
3605 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3606 if (!folder)
3607 folder = msi_dup_property( package, dirproperty );
3609 else
3610 folder = msi_dup_property( package, szWindowsFolder );
3612 if (!folder)
3614 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3615 goto cleanup;
3618 fullname = build_directory_name(2, folder, filenameptr);
3620 if (action == 0)
3622 TRACE("Adding value %s to section %s in %s\n",
3623 debugstr_w(deformated_key), debugstr_w(deformated_section),
3624 debugstr_w(fullname));
3625 WritePrivateProfileStringW(deformated_section, deformated_key,
3626 deformated_value, fullname);
3628 else if (action == 1)
3630 WCHAR returned[10];
3631 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3632 returned, 10, fullname);
3633 if (returned[0] == 0)
3635 TRACE("Adding value %s to section %s in %s\n",
3636 debugstr_w(deformated_key), debugstr_w(deformated_section),
3637 debugstr_w(fullname));
3639 WritePrivateProfileStringW(deformated_section, deformated_key,
3640 deformated_value, fullname);
3643 else if (action == 3)
3644 FIXME("Append to existing section not yet implemented\n");
3646 uirow = MSI_CreateRecord(4);
3647 MSI_RecordSetStringW(uirow,1,identifier);
3648 MSI_RecordSetStringW(uirow,2,deformated_section);
3649 MSI_RecordSetStringW(uirow,3,deformated_key);
3650 MSI_RecordSetStringW(uirow,4,deformated_value);
3651 ui_actiondata(package,szWriteIniValues,uirow);
3652 msiobj_release( &uirow->hdr );
3654 cleanup:
3655 msi_free(filename);
3656 msi_free(fullname);
3657 msi_free(folder);
3658 msi_free(deformated_key);
3659 msi_free(deformated_value);
3660 msi_free(deformated_section);
3661 return ERROR_SUCCESS;
3664 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3666 UINT rc;
3667 MSIQUERY * view;
3668 static const WCHAR ExecSeqQuery[] =
3669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3670 '`','I','n','i','F','i','l','e','`',0};
3672 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3673 if (rc != ERROR_SUCCESS)
3675 TRACE("no IniFile table\n");
3676 return ERROR_SUCCESS;
3679 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3680 msiobj_release(&view->hdr);
3681 return rc;
3684 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3686 MSIPACKAGE *package = param;
3687 LPCWSTR filename;
3688 LPWSTR FullName;
3689 MSIFILE *file;
3690 DWORD len;
3691 static const WCHAR ExeStr[] =
3692 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3693 static const WCHAR close[] = {'\"',0};
3694 STARTUPINFOW si;
3695 PROCESS_INFORMATION info;
3696 BOOL brc;
3697 MSIRECORD *uirow;
3698 LPWSTR uipath, p;
3700 memset(&si,0,sizeof(STARTUPINFOW));
3702 filename = MSI_RecordGetString(row,1);
3703 file = get_loaded_file( package, filename );
3705 if (!file)
3707 ERR("Unable to find file id %s\n",debugstr_w(filename));
3708 return ERROR_SUCCESS;
3711 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3713 FullName = msi_alloc(len*sizeof(WCHAR));
3714 strcpyW(FullName,ExeStr);
3715 strcatW( FullName, file->TargetPath );
3716 strcatW(FullName,close);
3718 TRACE("Registering %s\n",debugstr_w(FullName));
3719 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3720 &si, &info);
3722 if (brc)
3724 CloseHandle(info.hThread);
3725 msi_dialog_check_messages(info.hProcess);
3726 CloseHandle(info.hProcess);
3729 msi_free(FullName);
3731 /* the UI chunk */
3732 uirow = MSI_CreateRecord( 2 );
3733 uipath = strdupW( file->TargetPath );
3734 p = strrchrW(uipath,'\\');
3735 if (p)
3736 p[0]=0;
3737 MSI_RecordSetStringW( uirow, 1, &p[1] );
3738 MSI_RecordSetStringW( uirow, 2, uipath);
3739 ui_actiondata( package, szSelfRegModules, uirow);
3740 msiobj_release( &uirow->hdr );
3741 msi_free( uipath );
3742 /* FIXME: call ui_progress? */
3744 return ERROR_SUCCESS;
3747 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3749 UINT rc;
3750 MSIQUERY * view;
3751 static const WCHAR ExecSeqQuery[] =
3752 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3753 '`','S','e','l','f','R','e','g','`',0};
3755 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3756 if (rc != ERROR_SUCCESS)
3758 TRACE("no SelfReg table\n");
3759 return ERROR_SUCCESS;
3762 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3763 msiobj_release(&view->hdr);
3765 return ERROR_SUCCESS;
3768 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3770 static const WCHAR regsvr32[] =
3771 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3772 static const WCHAR close[] = {'\"',0};
3773 MSIPACKAGE *package = param;
3774 LPCWSTR filename;
3775 LPWSTR cmdline;
3776 MSIFILE *file;
3777 DWORD len;
3778 STARTUPINFOW si;
3779 PROCESS_INFORMATION pi;
3780 BOOL ret;
3781 MSIRECORD *uirow;
3782 LPWSTR uipath, p;
3784 memset( &si, 0, sizeof(STARTUPINFOW) );
3786 filename = MSI_RecordGetString( row, 1 );
3787 file = get_loaded_file( package, filename );
3789 if (!file)
3791 ERR("Unable to find file id %s\n", debugstr_w(filename));
3792 return ERROR_SUCCESS;
3795 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3797 cmdline = msi_alloc( len * sizeof(WCHAR) );
3798 strcpyW( cmdline, regsvr32 );
3799 strcatW( cmdline, file->TargetPath );
3800 strcatW( cmdline, close );
3802 TRACE("Unregistering %s\n", debugstr_w(cmdline));
3804 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3805 if (ret)
3807 CloseHandle( pi.hThread );
3808 msi_dialog_check_messages( pi.hProcess );
3809 CloseHandle( pi.hProcess );
3812 msi_free( cmdline );
3814 uirow = MSI_CreateRecord( 2 );
3815 uipath = strdupW( file->TargetPath );
3816 if ((p = strrchrW( uipath, '\\' )))
3818 *p = 0;
3819 MSI_RecordSetStringW( uirow, 1, ++p );
3821 MSI_RecordSetStringW( uirow, 2, uipath );
3822 ui_actiondata( package, szSelfUnregModules, uirow );
3823 msiobj_release( &uirow->hdr );
3824 msi_free( uipath );
3825 /* FIXME call ui_progress? */
3827 return ERROR_SUCCESS;
3830 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3832 UINT rc;
3833 MSIQUERY *view;
3834 static const WCHAR query[] =
3835 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3836 '`','S','e','l','f','R','e','g','`',0};
3838 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3839 if (rc != ERROR_SUCCESS)
3841 TRACE("no SelfReg table\n");
3842 return ERROR_SUCCESS;
3845 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3846 msiobj_release( &view->hdr );
3848 return ERROR_SUCCESS;
3851 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3853 MSIFEATURE *feature;
3854 UINT rc;
3855 HKEY hkey;
3856 HKEY userdata = NULL;
3858 if (!msi_check_publish(package))
3859 return ERROR_SUCCESS;
3861 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3862 &hkey, TRUE);
3863 if (rc != ERROR_SUCCESS)
3864 goto end;
3866 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3867 &userdata, TRUE);
3868 if (rc != ERROR_SUCCESS)
3869 goto end;
3871 /* here the guids are base 85 encoded */
3872 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3874 ComponentList *cl;
3875 LPWSTR data = NULL;
3876 GUID clsid;
3877 INT size;
3878 BOOL absent = FALSE;
3879 MSIRECORD *uirow;
3881 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3882 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3883 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3884 absent = TRUE;
3886 size = 1;
3887 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3889 size += 21;
3891 if (feature->Feature_Parent)
3892 size += strlenW( feature->Feature_Parent )+2;
3894 data = msi_alloc(size * sizeof(WCHAR));
3896 data[0] = 0;
3897 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3899 MSICOMPONENT* component = cl->component;
3900 WCHAR buf[21];
3902 buf[0] = 0;
3903 if (component->ComponentId)
3905 TRACE("From %s\n",debugstr_w(component->ComponentId));
3906 CLSIDFromString(component->ComponentId, &clsid);
3907 encode_base85_guid(&clsid,buf);
3908 TRACE("to %s\n",debugstr_w(buf));
3909 strcatW(data,buf);
3913 if (feature->Feature_Parent)
3915 static const WCHAR sep[] = {'\2',0};
3916 strcatW(data,sep);
3917 strcatW(data,feature->Feature_Parent);
3920 msi_reg_set_val_str( userdata, feature->Feature, data );
3921 msi_free(data);
3923 size = 0;
3924 if (feature->Feature_Parent)
3925 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3926 if (!absent)
3928 size += sizeof(WCHAR);
3929 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3930 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3932 else
3934 size += 2*sizeof(WCHAR);
3935 data = msi_alloc(size);
3936 data[0] = 0x6;
3937 data[1] = 0;
3938 if (feature->Feature_Parent)
3939 strcpyW( &data[1], feature->Feature_Parent );
3940 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3941 (LPBYTE)data,size);
3942 msi_free(data);
3945 /* the UI chunk */
3946 uirow = MSI_CreateRecord( 1 );
3947 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3948 ui_actiondata( package, szPublishFeatures, uirow);
3949 msiobj_release( &uirow->hdr );
3950 /* FIXME: call ui_progress? */
3953 end:
3954 RegCloseKey(hkey);
3955 RegCloseKey(userdata);
3956 return rc;
3959 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3961 UINT r;
3962 HKEY hkey;
3964 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3966 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3967 &hkey, FALSE);
3968 if (r == ERROR_SUCCESS)
3970 RegDeleteValueW(hkey, feature->Feature);
3971 RegCloseKey(hkey);
3974 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3975 &hkey, FALSE);
3976 if (r == ERROR_SUCCESS)
3978 RegDeleteValueW(hkey, feature->Feature);
3979 RegCloseKey(hkey);
3982 return ERROR_SUCCESS;
3985 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3987 MSIFEATURE *feature;
3989 if (!msi_check_unpublish(package))
3990 return ERROR_SUCCESS;
3992 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3994 msi_unpublish_feature(package, feature);
3997 return ERROR_SUCCESS;
4000 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4002 LPWSTR prop, val, key;
4003 SYSTEMTIME systime;
4004 DWORD size, langid;
4005 WCHAR date[9];
4006 LPWSTR buffer;
4008 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4009 static const WCHAR szWindowsInstaller[] =
4010 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4011 static const WCHAR modpath_fmt[] =
4012 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4013 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4014 static const WCHAR szModifyPath[] =
4015 {'M','o','d','i','f','y','P','a','t','h',0};
4016 static const WCHAR szUninstallString[] =
4017 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4018 static const WCHAR szEstimatedSize[] =
4019 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4020 static const WCHAR szProductLanguage[] =
4021 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4022 static const WCHAR szProductVersion[] =
4023 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4024 static const WCHAR szProductName[] =
4025 {'P','r','o','d','u','c','t','N','a','m','e',0};
4026 static const WCHAR szDisplayName[] =
4027 {'D','i','s','p','l','a','y','N','a','m','e',0};
4028 static const WCHAR szDisplayVersion[] =
4029 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4030 static const WCHAR szManufacturer[] =
4031 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4033 static const LPCSTR propval[] = {
4034 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4035 "ARPCONTACT", "Contact",
4036 "ARPCOMMENTS", "Comments",
4037 "ProductName", "DisplayName",
4038 "ProductVersion", "DisplayVersion",
4039 "ARPHELPLINK", "HelpLink",
4040 "ARPHELPTELEPHONE", "HelpTelephone",
4041 "ARPINSTALLLOCATION", "InstallLocation",
4042 "SourceDir", "InstallSource",
4043 "Manufacturer", "Publisher",
4044 "ARPREADME", "Readme",
4045 "ARPSIZE", "Size",
4046 "ARPURLINFOABOUT", "URLInfoAbout",
4047 "ARPURLUPDATEINFO", "URLUpdateInfo",
4048 NULL,
4050 const LPCSTR *p = propval;
4052 while (*p)
4054 prop = strdupAtoW(*p++);
4055 key = strdupAtoW(*p++);
4056 val = msi_dup_property(package, prop);
4057 msi_reg_set_val_str(hkey, key, val);
4058 msi_free(val);
4059 msi_free(key);
4060 msi_free(prop);
4063 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4065 size = deformat_string(package, modpath_fmt, &buffer);
4066 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4067 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4068 msi_free(buffer);
4070 /* FIXME: Write real Estimated Size when we have it */
4071 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4073 buffer = msi_dup_property(package, szProductName);
4074 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4075 msi_free(buffer);
4077 buffer = msi_dup_property(package, cszSourceDir);
4078 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4079 msi_free(buffer);
4081 buffer = msi_dup_property(package, szManufacturer);
4082 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4083 msi_free(buffer);
4085 GetLocalTime(&systime);
4086 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4087 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4089 langid = msi_get_property_int(package, szProductLanguage, 0);
4090 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4092 buffer = msi_dup_property(package, szProductVersion);
4093 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4094 if (buffer)
4096 DWORD verdword = msi_version_str_to_dword(buffer);
4098 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4099 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4100 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4101 msi_free(buffer);
4104 return ERROR_SUCCESS;
4107 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4109 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4110 LPWSTR upgrade_code;
4111 HKEY hkey, props;
4112 HKEY upgrade;
4113 UINT rc;
4115 static const WCHAR szUpgradeCode[] = {
4116 'U','p','g','r','a','d','e','C','o','d','e',0};
4118 /* FIXME: also need to publish if the product is in advertise mode */
4119 if (!msi_check_publish(package))
4120 return ERROR_SUCCESS;
4122 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4123 if (rc != ERROR_SUCCESS)
4124 return rc;
4126 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4127 NULL, &props, TRUE);
4128 if (rc != ERROR_SUCCESS)
4129 goto done;
4131 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4132 msi_free( package->db->localfile );
4133 package->db->localfile = NULL;
4135 rc = msi_publish_install_properties(package, hkey);
4136 if (rc != ERROR_SUCCESS)
4137 goto done;
4139 rc = msi_publish_install_properties(package, props);
4140 if (rc != ERROR_SUCCESS)
4141 goto done;
4143 upgrade_code = msi_dup_property(package, szUpgradeCode);
4144 if (upgrade_code)
4146 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4147 squash_guid(package->ProductCode, squashed_pc);
4148 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4149 RegCloseKey(upgrade);
4150 msi_free(upgrade_code);
4153 done:
4154 RegCloseKey(hkey);
4156 return ERROR_SUCCESS;
4159 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4161 return execute_script(package,INSTALL_SCRIPT);
4164 static UINT msi_unpublish_product(MSIPACKAGE *package)
4166 LPWSTR upgrade;
4167 LPWSTR remove = NULL;
4168 LPWSTR *features = NULL;
4169 BOOL full_uninstall = TRUE;
4170 MSIFEATURE *feature;
4172 static const WCHAR szUpgradeCode[] =
4173 {'U','p','g','r','a','d','e','C','o','d','e',0};
4175 remove = msi_dup_property(package, szRemove);
4176 if (!remove)
4177 return ERROR_SUCCESS;
4179 features = msi_split_string(remove, ',');
4180 if (!features)
4182 msi_free(remove);
4183 ERR("REMOVE feature list is empty!\n");
4184 return ERROR_FUNCTION_FAILED;
4187 if (!lstrcmpW(features[0], szAll))
4188 full_uninstall = TRUE;
4189 else
4191 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4193 if (feature->Action != INSTALLSTATE_ABSENT)
4194 full_uninstall = FALSE;
4198 if (!full_uninstall)
4199 goto done;
4201 MSIREG_DeleteProductKey(package->ProductCode);
4202 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4203 MSIREG_DeleteUninstallKey(package->ProductCode);
4205 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4207 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4208 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4210 else
4212 MSIREG_DeleteUserProductKey(package->ProductCode);
4213 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4216 upgrade = msi_dup_property(package, szUpgradeCode);
4217 if (upgrade)
4219 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4220 msi_free(upgrade);
4223 done:
4224 msi_free(remove);
4225 msi_free(features);
4226 return ERROR_SUCCESS;
4229 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4231 UINT rc;
4233 rc = msi_unpublish_product(package);
4234 if (rc != ERROR_SUCCESS)
4235 return rc;
4237 /* turn off scheduling */
4238 package->script->CurrentlyScripting= FALSE;
4240 /* first do the same as an InstallExecute */
4241 rc = ACTION_InstallExecute(package);
4242 if (rc != ERROR_SUCCESS)
4243 return rc;
4245 /* then handle Commit Actions */
4246 rc = execute_script(package,COMMIT_SCRIPT);
4248 return rc;
4251 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4253 static const WCHAR RunOnce[] = {
4254 'S','o','f','t','w','a','r','e','\\',
4255 'M','i','c','r','o','s','o','f','t','\\',
4256 'W','i','n','d','o','w','s','\\',
4257 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4258 'R','u','n','O','n','c','e',0};
4259 static const WCHAR InstallRunOnce[] = {
4260 'S','o','f','t','w','a','r','e','\\',
4261 'M','i','c','r','o','s','o','f','t','\\',
4262 'W','i','n','d','o','w','s','\\',
4263 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4264 'I','n','s','t','a','l','l','e','r','\\',
4265 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4267 static const WCHAR msiexec_fmt[] = {
4268 '%','s',
4269 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4270 '\"','%','s','\"',0};
4271 static const WCHAR install_fmt[] = {
4272 '/','I',' ','\"','%','s','\"',' ',
4273 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4274 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4275 WCHAR buffer[256], sysdir[MAX_PATH];
4276 HKEY hkey;
4277 WCHAR squished_pc[100];
4279 squash_guid(package->ProductCode,squished_pc);
4281 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4282 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4283 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4284 squished_pc);
4286 msi_reg_set_val_str( hkey, squished_pc, buffer );
4287 RegCloseKey(hkey);
4289 TRACE("Reboot command %s\n",debugstr_w(buffer));
4291 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4292 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4294 msi_reg_set_val_str( hkey, squished_pc, buffer );
4295 RegCloseKey(hkey);
4297 return ERROR_INSTALL_SUSPEND;
4300 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4302 DWORD attrib;
4303 UINT rc;
4306 * We are currently doing what should be done here in the top level Install
4307 * however for Administrative and uninstalls this step will be needed
4309 if (!package->PackagePath)
4310 return ERROR_SUCCESS;
4312 msi_set_sourcedir_props(package, TRUE);
4314 attrib = GetFileAttributesW(package->db->path);
4315 if (attrib == INVALID_FILE_ATTRIBUTES)
4317 LPWSTR prompt;
4318 LPWSTR msg;
4319 DWORD size = 0;
4321 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4322 package->Context, MSICODE_PRODUCT,
4323 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4324 if (rc == ERROR_MORE_DATA)
4326 prompt = msi_alloc(size * sizeof(WCHAR));
4327 MsiSourceListGetInfoW(package->ProductCode, NULL,
4328 package->Context, MSICODE_PRODUCT,
4329 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4331 else
4332 prompt = strdupW(package->db->path);
4334 msg = generate_error_string(package,1302,1,prompt);
4335 while(attrib == INVALID_FILE_ATTRIBUTES)
4337 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4338 if (rc == IDCANCEL)
4340 rc = ERROR_INSTALL_USEREXIT;
4341 break;
4343 attrib = GetFileAttributesW(package->db->path);
4345 msi_free(prompt);
4346 rc = ERROR_SUCCESS;
4348 else
4349 return ERROR_SUCCESS;
4351 return rc;
4354 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4356 HKEY hkey=0;
4357 LPWSTR buffer;
4358 LPWSTR productid;
4359 UINT rc,i;
4361 static const WCHAR szPropKeys[][80] =
4363 {'P','r','o','d','u','c','t','I','D',0},
4364 {'U','S','E','R','N','A','M','E',0},
4365 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4366 {0},
4369 static const WCHAR szRegKeys[][80] =
4371 {'P','r','o','d','u','c','t','I','D',0},
4372 {'R','e','g','O','w','n','e','r',0},
4373 {'R','e','g','C','o','m','p','a','n','y',0},
4374 {0},
4377 if (msi_check_unpublish(package))
4379 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4380 return ERROR_SUCCESS;
4383 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4384 if (!productid)
4385 return ERROR_SUCCESS;
4387 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4388 NULL, &hkey, TRUE);
4389 if (rc != ERROR_SUCCESS)
4390 goto end;
4392 for( i = 0; szPropKeys[i][0]; i++ )
4394 buffer = msi_dup_property( package, szPropKeys[i] );
4395 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4396 msi_free( buffer );
4399 end:
4400 msi_free(productid);
4401 RegCloseKey(hkey);
4403 /* FIXME: call ui_actiondata */
4405 return rc;
4409 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4411 UINT rc;
4413 package->script->InWhatSequence |= SEQUENCE_EXEC;
4414 rc = ACTION_ProcessExecSequence(package,FALSE);
4415 return rc;
4419 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4421 MSIPACKAGE *package = param;
4422 LPCWSTR compgroupid=NULL;
4423 LPCWSTR feature=NULL;
4424 LPCWSTR text = NULL;
4425 LPCWSTR qualifier = NULL;
4426 LPCWSTR component = NULL;
4427 LPWSTR advertise = NULL;
4428 LPWSTR output = NULL;
4429 HKEY hkey;
4430 UINT rc = ERROR_SUCCESS;
4431 MSICOMPONENT *comp;
4432 DWORD sz = 0;
4433 MSIRECORD *uirow;
4435 component = MSI_RecordGetString(rec,3);
4436 comp = get_loaded_component(package,component);
4438 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4439 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4440 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4442 TRACE("Skipping: Component %s not scheduled for install\n",
4443 debugstr_w(component));
4445 return ERROR_SUCCESS;
4448 compgroupid = MSI_RecordGetString(rec,1);
4449 qualifier = MSI_RecordGetString(rec,2);
4451 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4452 if (rc != ERROR_SUCCESS)
4453 goto end;
4455 text = MSI_RecordGetString(rec,4);
4456 feature = MSI_RecordGetString(rec,5);
4458 advertise = create_component_advertise_string(package, comp, feature);
4460 sz = strlenW(advertise);
4462 if (text)
4463 sz += lstrlenW(text);
4465 sz+=3;
4466 sz *= sizeof(WCHAR);
4468 output = msi_alloc_zero(sz);
4469 strcpyW(output,advertise);
4470 msi_free(advertise);
4472 if (text)
4473 strcatW(output,text);
4475 msi_reg_set_val_multi_str( hkey, qualifier, output );
4477 end:
4478 RegCloseKey(hkey);
4479 msi_free(output);
4481 /* the UI chunk */
4482 uirow = MSI_CreateRecord( 2 );
4483 MSI_RecordSetStringW( uirow, 1, compgroupid );
4484 MSI_RecordSetStringW( uirow, 2, qualifier);
4485 ui_actiondata( package, szPublishComponents, uirow);
4486 msiobj_release( &uirow->hdr );
4487 /* FIXME: call ui_progress? */
4489 return rc;
4493 * At present I am ignorning the advertised components part of this and only
4494 * focusing on the qualified component sets
4496 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4498 UINT rc;
4499 MSIQUERY * view;
4500 static const WCHAR ExecSeqQuery[] =
4501 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4502 '`','P','u','b','l','i','s','h',
4503 'C','o','m','p','o','n','e','n','t','`',0};
4505 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4506 if (rc != ERROR_SUCCESS)
4507 return ERROR_SUCCESS;
4509 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4510 msiobj_release(&view->hdr);
4512 return rc;
4515 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4517 MSIPACKAGE *package = param;
4518 MSIRECORD *row;
4519 MSIFILE *file;
4520 SC_HANDLE hscm, service = NULL;
4521 LPCWSTR comp, depends, pass;
4522 LPWSTR name = NULL, disp = NULL;
4523 LPCWSTR load_order, serv_name, key;
4524 DWORD serv_type, start_type;
4525 DWORD err_control;
4527 static const WCHAR query[] =
4528 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4529 '`','C','o','m','p','o','n','e','n','t','`',' ',
4530 'W','H','E','R','E',' ',
4531 '`','C','o','m','p','o','n','e','n','t','`',' ',
4532 '=','\'','%','s','\'',0};
4534 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4535 if (!hscm)
4537 ERR("Failed to open the SC Manager!\n");
4538 goto done;
4541 start_type = MSI_RecordGetInteger(rec, 5);
4542 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4543 goto done;
4545 depends = MSI_RecordGetString(rec, 8);
4546 if (depends && *depends)
4547 FIXME("Dependency list unhandled!\n");
4549 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4550 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4551 serv_type = MSI_RecordGetInteger(rec, 4);
4552 err_control = MSI_RecordGetInteger(rec, 6);
4553 load_order = MSI_RecordGetString(rec, 7);
4554 serv_name = MSI_RecordGetString(rec, 9);
4555 pass = MSI_RecordGetString(rec, 10);
4556 comp = MSI_RecordGetString(rec, 12);
4558 /* fetch the service path */
4559 row = MSI_QueryGetRecord(package->db, query, comp);
4560 if (!row)
4562 ERR("Control query failed!\n");
4563 goto done;
4566 key = MSI_RecordGetString(row, 6);
4568 file = get_loaded_file(package, key);
4569 msiobj_release(&row->hdr);
4570 if (!file)
4572 ERR("Failed to load the service file\n");
4573 goto done;
4576 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4577 start_type, err_control, file->TargetPath,
4578 load_order, NULL, NULL, serv_name, pass);
4579 if (!service)
4581 if (GetLastError() != ERROR_SERVICE_EXISTS)
4582 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4585 done:
4586 CloseServiceHandle(service);
4587 CloseServiceHandle(hscm);
4588 msi_free(name);
4589 msi_free(disp);
4591 return ERROR_SUCCESS;
4594 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4596 UINT rc;
4597 MSIQUERY * view;
4598 static const WCHAR ExecSeqQuery[] =
4599 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4600 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4602 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4603 if (rc != ERROR_SUCCESS)
4604 return ERROR_SUCCESS;
4606 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4607 msiobj_release(&view->hdr);
4609 return rc;
4612 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4613 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4615 LPCWSTR *vector, *temp_vector;
4616 LPWSTR p, q;
4617 DWORD sep_len;
4619 static const WCHAR separator[] = {'[','~',']',0};
4621 *numargs = 0;
4622 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4624 if (!args)
4625 return NULL;
4627 vector = msi_alloc(sizeof(LPWSTR));
4628 if (!vector)
4629 return NULL;
4631 p = args;
4634 (*numargs)++;
4635 vector[*numargs - 1] = p;
4637 if ((q = strstrW(p, separator)))
4639 *q = '\0';
4641 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4642 if (!temp_vector)
4644 msi_free(vector);
4645 return NULL;
4647 vector = temp_vector;
4649 p = q + sep_len;
4651 } while (q);
4653 return vector;
4656 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4658 MSIPACKAGE *package = param;
4659 MSICOMPONENT *comp;
4660 SC_HANDLE scm = NULL, service = NULL;
4661 LPCWSTR *vector = NULL;
4662 LPWSTR name, args;
4663 DWORD event, numargs;
4664 UINT r = ERROR_FUNCTION_FAILED;
4666 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4667 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4668 return ERROR_SUCCESS;
4670 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4671 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4672 event = MSI_RecordGetInteger(rec, 3);
4674 if (!(event & msidbServiceControlEventStart))
4676 r = ERROR_SUCCESS;
4677 goto done;
4680 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4681 if (!scm)
4683 ERR("Failed to open the service control manager\n");
4684 goto done;
4687 service = OpenServiceW(scm, name, SERVICE_START);
4688 if (!service)
4690 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4691 goto done;
4694 vector = msi_service_args_to_vector(args, &numargs);
4696 if (!StartServiceW(service, numargs, vector) &&
4697 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4699 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4700 goto done;
4703 r = ERROR_SUCCESS;
4705 done:
4706 CloseServiceHandle(service);
4707 CloseServiceHandle(scm);
4709 msi_free(name);
4710 msi_free(args);
4711 msi_free(vector);
4712 return r;
4715 static UINT ACTION_StartServices( MSIPACKAGE *package )
4717 UINT rc;
4718 MSIQUERY *view;
4720 static const WCHAR query[] = {
4721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4722 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4724 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4725 if (rc != ERROR_SUCCESS)
4726 return ERROR_SUCCESS;
4728 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4729 msiobj_release(&view->hdr);
4731 return rc;
4734 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4736 DWORD i, needed, count;
4737 ENUM_SERVICE_STATUSW *dependencies;
4738 SERVICE_STATUS ss;
4739 SC_HANDLE depserv;
4741 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4742 0, &needed, &count))
4743 return TRUE;
4745 if (GetLastError() != ERROR_MORE_DATA)
4746 return FALSE;
4748 dependencies = msi_alloc(needed);
4749 if (!dependencies)
4750 return FALSE;
4752 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4753 needed, &needed, &count))
4754 goto error;
4756 for (i = 0; i < count; i++)
4758 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4759 SERVICE_STOP | SERVICE_QUERY_STATUS);
4760 if (!depserv)
4761 goto error;
4763 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4764 goto error;
4767 return TRUE;
4769 error:
4770 msi_free(dependencies);
4771 return FALSE;
4774 static UINT stop_service( LPCWSTR name )
4776 SC_HANDLE scm = NULL, service = NULL;
4777 SERVICE_STATUS status;
4778 SERVICE_STATUS_PROCESS ssp;
4779 DWORD needed;
4781 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4782 if (!scm)
4784 WARN("Failed to open the SCM: %d\n", GetLastError());
4785 goto done;
4788 service = OpenServiceW(scm, name,
4789 SERVICE_STOP |
4790 SERVICE_QUERY_STATUS |
4791 SERVICE_ENUMERATE_DEPENDENTS);
4792 if (!service)
4794 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4795 goto done;
4798 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4799 sizeof(SERVICE_STATUS_PROCESS), &needed))
4801 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4802 goto done;
4805 if (ssp.dwCurrentState == SERVICE_STOPPED)
4806 goto done;
4808 stop_service_dependents(scm, service);
4810 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4811 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4813 done:
4814 CloseServiceHandle(service);
4815 CloseServiceHandle(scm);
4817 return ERROR_SUCCESS;
4820 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4822 MSIPACKAGE *package = param;
4823 MSICOMPONENT *comp;
4824 LPWSTR name;
4825 DWORD event;
4827 event = MSI_RecordGetInteger( rec, 3 );
4828 if (!(event & msidbServiceControlEventStop))
4829 return ERROR_SUCCESS;
4831 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4832 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4833 return ERROR_SUCCESS;
4835 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4836 stop_service( name );
4837 msi_free( name );
4839 return ERROR_SUCCESS;
4842 static UINT ACTION_StopServices( MSIPACKAGE *package )
4844 UINT rc;
4845 MSIQUERY *view;
4847 static const WCHAR query[] = {
4848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4849 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4851 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4852 if (rc != ERROR_SUCCESS)
4853 return ERROR_SUCCESS;
4855 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4856 msiobj_release(&view->hdr);
4858 return rc;
4861 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
4863 MSIPACKAGE *package = param;
4864 MSICOMPONENT *comp;
4865 LPWSTR name = NULL;
4866 DWORD event;
4867 SC_HANDLE scm = NULL, service = NULL;
4869 event = MSI_RecordGetInteger( rec, 3 );
4870 if (!(event & msidbServiceControlEventDelete))
4871 return ERROR_SUCCESS;
4873 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
4874 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4875 return ERROR_SUCCESS;
4877 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
4878 stop_service( name );
4880 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
4881 if (!scm)
4883 WARN("Failed to open the SCM: %d\n", GetLastError());
4884 goto done;
4887 service = OpenServiceW( scm, name, DELETE );
4888 if (!service)
4890 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
4891 goto done;
4894 if (!DeleteService( service ))
4895 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
4897 done:
4898 CloseServiceHandle( service );
4899 CloseServiceHandle( scm );
4900 msi_free( name );
4902 return ERROR_SUCCESS;
4905 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4907 UINT rc;
4908 MSIQUERY *view;
4910 static const WCHAR query[] = {
4911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4912 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4914 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4915 if (rc != ERROR_SUCCESS)
4916 return ERROR_SUCCESS;
4918 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
4919 msiobj_release( &view->hdr );
4921 return rc;
4924 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4926 MSIFILE *file;
4928 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4930 if (!lstrcmpW(file->File, filename))
4931 return file;
4934 return NULL;
4937 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4939 MSIPACKAGE *package = param;
4940 LPWSTR driver, driver_path, ptr;
4941 WCHAR outpath[MAX_PATH];
4942 MSIFILE *driver_file, *setup_file;
4943 LPCWSTR desc;
4944 DWORD len, usage;
4945 UINT r = ERROR_SUCCESS;
4947 static const WCHAR driver_fmt[] = {
4948 'D','r','i','v','e','r','=','%','s',0};
4949 static const WCHAR setup_fmt[] = {
4950 'S','e','t','u','p','=','%','s',0};
4951 static const WCHAR usage_fmt[] = {
4952 'F','i','l','e','U','s','a','g','e','=','1',0};
4954 desc = MSI_RecordGetString(rec, 3);
4956 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4957 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4959 if (!driver_file)
4961 ERR("ODBC Driver entry not found!\n");
4962 return ERROR_FUNCTION_FAILED;
4965 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
4966 if (setup_file)
4967 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
4968 len += lstrlenW(usage_fmt) + 1;
4970 driver = msi_alloc(len * sizeof(WCHAR));
4971 if (!driver)
4972 return ERROR_OUTOFMEMORY;
4974 ptr = driver;
4975 lstrcpyW(ptr, desc);
4976 ptr += lstrlenW(ptr) + 1;
4978 sprintfW(ptr, driver_fmt, driver_file->FileName);
4979 ptr += lstrlenW(ptr) + 1;
4981 if (setup_file)
4983 sprintfW(ptr, setup_fmt, setup_file->FileName);
4984 ptr += lstrlenW(ptr) + 1;
4987 lstrcpyW(ptr, usage_fmt);
4988 ptr += lstrlenW(ptr) + 1;
4989 *ptr = '\0';
4991 driver_path = strdupW(driver_file->TargetPath);
4992 ptr = strrchrW(driver_path, '\\');
4993 if (ptr) *ptr = '\0';
4995 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4996 NULL, ODBC_INSTALL_COMPLETE, &usage))
4998 ERR("Failed to install SQL driver!\n");
4999 r = ERROR_FUNCTION_FAILED;
5002 msi_free(driver);
5003 msi_free(driver_path);
5005 return r;
5008 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5010 MSIPACKAGE *package = param;
5011 LPWSTR translator, translator_path, ptr;
5012 WCHAR outpath[MAX_PATH];
5013 MSIFILE *translator_file, *setup_file;
5014 LPCWSTR desc;
5015 DWORD len, usage;
5016 UINT r = ERROR_SUCCESS;
5018 static const WCHAR translator_fmt[] = {
5019 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5020 static const WCHAR setup_fmt[] = {
5021 'S','e','t','u','p','=','%','s',0};
5023 desc = MSI_RecordGetString(rec, 3);
5025 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5026 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5028 if (!translator_file)
5030 ERR("ODBC Translator entry not found!\n");
5031 return ERROR_FUNCTION_FAILED;
5034 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 1;
5035 if (setup_file)
5036 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5038 translator = msi_alloc(len * sizeof(WCHAR));
5039 if (!translator)
5040 return ERROR_OUTOFMEMORY;
5042 ptr = translator;
5043 lstrcpyW(ptr, desc);
5044 ptr += lstrlenW(ptr) + 1;
5046 sprintfW(ptr, translator_fmt, translator_file->FileName);
5047 ptr += lstrlenW(ptr) + 1;
5049 if (setup_file)
5051 sprintfW(ptr, setup_fmt, setup_file->FileName);
5052 ptr += lstrlenW(ptr) + 1;
5054 *ptr = '\0';
5056 translator_path = strdupW(translator_file->TargetPath);
5057 ptr = strrchrW(translator_path, '\\');
5058 if (ptr) *ptr = '\0';
5060 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5061 NULL, ODBC_INSTALL_COMPLETE, &usage))
5063 ERR("Failed to install SQL translator!\n");
5064 r = ERROR_FUNCTION_FAILED;
5067 msi_free(translator);
5068 msi_free(translator_path);
5070 return r;
5073 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5075 LPWSTR attrs;
5076 LPCWSTR desc, driver;
5077 WORD request = ODBC_ADD_SYS_DSN;
5078 INT registration;
5079 DWORD len;
5080 UINT r = ERROR_SUCCESS;
5082 static const WCHAR attrs_fmt[] = {
5083 'D','S','N','=','%','s',0 };
5085 desc = MSI_RecordGetString(rec, 3);
5086 driver = MSI_RecordGetString(rec, 4);
5087 registration = MSI_RecordGetInteger(rec, 5);
5089 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5090 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5092 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5093 attrs = msi_alloc(len * sizeof(WCHAR));
5094 if (!attrs)
5095 return ERROR_OUTOFMEMORY;
5097 len = sprintfW(attrs, attrs_fmt, desc);
5098 attrs[len + 1] = 0;
5100 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5102 ERR("Failed to install SQL data source!\n");
5103 r = ERROR_FUNCTION_FAILED;
5106 msi_free(attrs);
5108 return r;
5111 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5113 UINT rc;
5114 MSIQUERY *view;
5116 static const WCHAR driver_query[] = {
5117 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5118 'O','D','B','C','D','r','i','v','e','r',0 };
5120 static const WCHAR translator_query[] = {
5121 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5122 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5124 static const WCHAR source_query[] = {
5125 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5126 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5128 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5129 if (rc != ERROR_SUCCESS)
5130 return ERROR_SUCCESS;
5132 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5133 msiobj_release(&view->hdr);
5135 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5136 if (rc != ERROR_SUCCESS)
5137 return ERROR_SUCCESS;
5139 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5140 msiobj_release(&view->hdr);
5142 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5143 if (rc != ERROR_SUCCESS)
5144 return ERROR_SUCCESS;
5146 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5147 msiobj_release(&view->hdr);
5149 return rc;
5152 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5154 DWORD usage;
5155 LPCWSTR desc;
5157 desc = MSI_RecordGetString( rec, 3 );
5158 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5160 WARN("Failed to remove ODBC driver\n");
5162 else if (!usage)
5164 FIXME("Usage count reached 0\n");
5167 return ERROR_SUCCESS;
5170 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5172 DWORD usage;
5173 LPCWSTR desc;
5175 desc = MSI_RecordGetString( rec, 3 );
5176 if (!SQLRemoveTranslatorW( desc, &usage ))
5178 WARN("Failed to remove ODBC translator\n");
5180 else if (!usage)
5182 FIXME("Usage count reached 0\n");
5185 return ERROR_SUCCESS;
5188 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5190 LPWSTR attrs;
5191 LPCWSTR desc, driver;
5192 WORD request = ODBC_REMOVE_SYS_DSN;
5193 INT registration;
5194 DWORD len;
5196 static const WCHAR attrs_fmt[] = {
5197 'D','S','N','=','%','s',0 };
5199 desc = MSI_RecordGetString( rec, 3 );
5200 driver = MSI_RecordGetString( rec, 4 );
5201 registration = MSI_RecordGetInteger( rec, 5 );
5203 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5204 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5206 len = strlenW( attrs_fmt ) + strlenW( desc ) + 1 + 1;
5207 attrs = msi_alloc( len * sizeof(WCHAR) );
5208 if (!attrs)
5209 return ERROR_OUTOFMEMORY;
5211 FIXME("Use ODBCSourceAttribute table\n");
5213 len = sprintfW( attrs, attrs_fmt, desc );
5214 attrs[len + 1] = 0;
5216 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5218 WARN("Failed to remove ODBC data source\n");
5220 msi_free( attrs );
5222 return ERROR_SUCCESS;
5225 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5227 UINT rc;
5228 MSIQUERY *view;
5230 static const WCHAR driver_query[] = {
5231 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5232 'O','D','B','C','D','r','i','v','e','r',0 };
5234 static const WCHAR translator_query[] = {
5235 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5236 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5238 static const WCHAR source_query[] = {
5239 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5240 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5242 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5243 if (rc != ERROR_SUCCESS)
5244 return ERROR_SUCCESS;
5246 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5247 msiobj_release( &view->hdr );
5249 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5250 if (rc != ERROR_SUCCESS)
5251 return ERROR_SUCCESS;
5253 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5254 msiobj_release( &view->hdr );
5256 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5257 if (rc != ERROR_SUCCESS)
5258 return ERROR_SUCCESS;
5260 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5261 msiobj_release( &view->hdr );
5263 return rc;
5266 #define ENV_ACT_SETALWAYS 0x1
5267 #define ENV_ACT_SETABSENT 0x2
5268 #define ENV_ACT_REMOVE 0x4
5269 #define ENV_ACT_REMOVEMATCH 0x8
5271 #define ENV_MOD_MACHINE 0x20000000
5272 #define ENV_MOD_APPEND 0x40000000
5273 #define ENV_MOD_PREFIX 0x80000000
5274 #define ENV_MOD_MASK 0xC0000000
5276 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5278 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5280 LPCWSTR cptr = *name;
5282 static const WCHAR prefix[] = {'[','~',']',0};
5283 static const int prefix_len = 3;
5285 *flags = 0;
5286 while (*cptr)
5288 if (*cptr == '=')
5289 *flags |= ENV_ACT_SETALWAYS;
5290 else if (*cptr == '+')
5291 *flags |= ENV_ACT_SETABSENT;
5292 else if (*cptr == '-')
5293 *flags |= ENV_ACT_REMOVE;
5294 else if (*cptr == '!')
5295 *flags |= ENV_ACT_REMOVEMATCH;
5296 else if (*cptr == '*')
5297 *flags |= ENV_MOD_MACHINE;
5298 else
5299 break;
5301 cptr++;
5302 (*name)++;
5305 if (!*cptr)
5307 ERR("Missing environment variable\n");
5308 return ERROR_FUNCTION_FAILED;
5311 if (*value)
5313 LPCWSTR ptr = *value;
5314 if (!strncmpW(ptr, prefix, prefix_len))
5316 if (ptr[prefix_len] == szSemiColon[0])
5318 *flags |= ENV_MOD_APPEND;
5319 *value += lstrlenW(prefix);
5321 else
5323 *value = NULL;
5326 else if (lstrlenW(*value) >= prefix_len)
5328 ptr += lstrlenW(ptr) - prefix_len;
5329 if (!lstrcmpW(ptr, prefix))
5331 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5333 *flags |= ENV_MOD_PREFIX;
5334 /* the "[~]" will be removed by deformat_string */;
5336 else
5338 *value = NULL;
5344 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5345 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5346 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5347 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5349 ERR("Invalid flags: %08x\n", *flags);
5350 return ERROR_FUNCTION_FAILED;
5353 if (!*flags)
5354 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5356 return ERROR_SUCCESS;
5359 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5361 MSIPACKAGE *package = param;
5362 LPCWSTR name, value;
5363 LPWSTR data = NULL, newval = NULL;
5364 LPWSTR deformatted = NULL, ptr;
5365 DWORD flags, type, size;
5366 LONG res;
5367 HKEY env = NULL, root;
5368 LPCWSTR environment;
5370 static const WCHAR user_env[] =
5371 {'E','n','v','i','r','o','n','m','e','n','t',0};
5372 static const WCHAR machine_env[] =
5373 {'S','y','s','t','e','m','\\',
5374 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5375 'C','o','n','t','r','o','l','\\',
5376 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5377 'E','n','v','i','r','o','n','m','e','n','t',0};
5379 name = MSI_RecordGetString(rec, 2);
5380 value = MSI_RecordGetString(rec, 3);
5382 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5384 res = env_set_flags(&name, &value, &flags);
5385 if (res != ERROR_SUCCESS || !value)
5386 goto done;
5388 if (value && !deformat_string(package, value, &deformatted))
5390 res = ERROR_OUTOFMEMORY;
5391 goto done;
5394 value = deformatted;
5396 if (flags & ENV_MOD_MACHINE)
5398 environment = machine_env;
5399 root = HKEY_LOCAL_MACHINE;
5401 else
5403 environment = user_env;
5404 root = HKEY_CURRENT_USER;
5407 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5408 KEY_ALL_ACCESS, NULL, &env, NULL);
5409 if (res != ERROR_SUCCESS)
5410 goto done;
5412 if (flags & ENV_ACT_REMOVE)
5413 FIXME("Not removing environment variable on uninstall!\n");
5415 size = 0;
5416 type = REG_SZ;
5417 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5418 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5419 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5420 goto done;
5422 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5424 /* Nothing to do. */
5425 if (!value)
5427 res = ERROR_SUCCESS;
5428 goto done;
5431 /* If we are appending but the string was empty, strip ; */
5432 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5434 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5435 newval = strdupW(value);
5436 if (!newval)
5438 res = ERROR_OUTOFMEMORY;
5439 goto done;
5442 else
5444 /* Contrary to MSDN, +-variable to [~];path works */
5445 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5447 res = ERROR_SUCCESS;
5448 goto done;
5451 data = msi_alloc(size);
5452 if (!data)
5454 RegCloseKey(env);
5455 return ERROR_OUTOFMEMORY;
5458 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5459 if (res != ERROR_SUCCESS)
5460 goto done;
5462 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5464 res = RegDeleteKeyW(env, name);
5465 goto done;
5468 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5469 if (flags & ENV_MOD_MASK)
5471 DWORD mod_size;
5472 int multiplier = 0;
5473 if (flags & ENV_MOD_APPEND) multiplier++;
5474 if (flags & ENV_MOD_PREFIX) multiplier++;
5475 mod_size = lstrlenW(value) * multiplier;
5476 size += mod_size * sizeof(WCHAR);
5479 newval = msi_alloc(size);
5480 ptr = newval;
5481 if (!newval)
5483 res = ERROR_OUTOFMEMORY;
5484 goto done;
5487 if (flags & ENV_MOD_PREFIX)
5489 lstrcpyW(newval, value);
5490 ptr = newval + lstrlenW(value);
5493 lstrcpyW(ptr, data);
5495 if (flags & ENV_MOD_APPEND)
5497 lstrcatW(newval, value);
5500 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5501 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5503 done:
5504 if (env) RegCloseKey(env);
5505 msi_free(deformatted);
5506 msi_free(data);
5507 msi_free(newval);
5508 return res;
5511 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5513 UINT rc;
5514 MSIQUERY * view;
5515 static const WCHAR ExecSeqQuery[] =
5516 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5517 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5518 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5519 if (rc != ERROR_SUCCESS)
5520 return ERROR_SUCCESS;
5522 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5523 msiobj_release(&view->hdr);
5525 return rc;
5528 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5530 typedef struct
5532 struct list entry;
5533 LPWSTR sourcename;
5534 LPWSTR destname;
5535 LPWSTR source;
5536 LPWSTR dest;
5537 } FILE_LIST;
5539 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5541 BOOL ret;
5543 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5544 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5546 WARN("Source or dest is directory, not moving\n");
5547 return FALSE;
5550 if (options == msidbMoveFileOptionsMove)
5552 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5553 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5554 if (!ret)
5556 WARN("MoveFile failed: %d\n", GetLastError());
5557 return FALSE;
5560 else
5562 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5563 ret = CopyFileW(source, dest, FALSE);
5564 if (!ret)
5566 WARN("CopyFile failed: %d\n", GetLastError());
5567 return FALSE;
5571 return TRUE;
5574 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5576 LPWSTR path, ptr;
5577 DWORD dirlen, pathlen;
5579 ptr = strrchrW(wildcard, '\\');
5580 dirlen = ptr - wildcard + 1;
5582 pathlen = dirlen + lstrlenW(filename) + 1;
5583 path = msi_alloc(pathlen * sizeof(WCHAR));
5585 lstrcpynW(path, wildcard, dirlen + 1);
5586 lstrcatW(path, filename);
5588 return path;
5591 static void free_file_entry(FILE_LIST *file)
5593 msi_free(file->source);
5594 msi_free(file->dest);
5595 msi_free(file);
5598 static void free_list(FILE_LIST *list)
5600 while (!list_empty(&list->entry))
5602 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5604 list_remove(&file->entry);
5605 free_file_entry(file);
5609 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5611 FILE_LIST *new, *file;
5612 LPWSTR ptr, filename;
5613 DWORD size;
5615 new = msi_alloc_zero(sizeof(FILE_LIST));
5616 if (!new)
5617 return FALSE;
5619 new->source = strdupW(source);
5620 ptr = strrchrW(dest, '\\') + 1;
5621 filename = strrchrW(new->source, '\\') + 1;
5623 new->sourcename = filename;
5625 if (*ptr)
5626 new->destname = ptr;
5627 else
5628 new->destname = new->sourcename;
5630 size = (ptr - dest) + lstrlenW(filename) + 1;
5631 new->dest = msi_alloc(size * sizeof(WCHAR));
5632 if (!new->dest)
5634 free_file_entry(new);
5635 return FALSE;
5638 lstrcpynW(new->dest, dest, ptr - dest + 1);
5639 lstrcatW(new->dest, filename);
5641 if (list_empty(&files->entry))
5643 list_add_head(&files->entry, &new->entry);
5644 return TRUE;
5647 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5649 if (lstrcmpW(source, file->source) < 0)
5651 list_add_before(&file->entry, &new->entry);
5652 return TRUE;
5656 list_add_after(&file->entry, &new->entry);
5657 return TRUE;
5660 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5662 WIN32_FIND_DATAW wfd;
5663 HANDLE hfile;
5664 LPWSTR path;
5665 BOOL res;
5666 FILE_LIST files, *file;
5667 DWORD size;
5669 hfile = FindFirstFileW(source, &wfd);
5670 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5672 list_init(&files.entry);
5674 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5676 if (is_dot_dir(wfd.cFileName)) continue;
5678 path = wildcard_to_file(source, wfd.cFileName);
5679 if (!path)
5681 res = FALSE;
5682 goto done;
5685 add_wildcard(&files, path, dest);
5686 msi_free(path);
5689 /* no files match the wildcard */
5690 if (list_empty(&files.entry))
5691 goto done;
5693 /* only the first wildcard match gets renamed to dest */
5694 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5695 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5696 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5697 if (!file->dest)
5699 res = FALSE;
5700 goto done;
5703 /* file->dest may be shorter after the reallocation, so add a NULL
5704 * terminator. This is needed for the call to strrchrW, as there will no
5705 * longer be a NULL terminator within the bounds of the allocation in this case.
5707 file->dest[size - 1] = '\0';
5708 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5710 while (!list_empty(&files.entry))
5712 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5714 msi_move_file(file->source, file->dest, options);
5716 list_remove(&file->entry);
5717 free_file_entry(file);
5720 res = TRUE;
5722 done:
5723 free_list(&files);
5724 FindClose(hfile);
5725 return res;
5728 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5730 MSIPACKAGE *package = param;
5731 MSICOMPONENT *comp;
5732 LPCWSTR sourcename;
5733 LPWSTR destname = NULL;
5734 LPWSTR sourcedir = NULL, destdir = NULL;
5735 LPWSTR source = NULL, dest = NULL;
5736 int options;
5737 DWORD size;
5738 BOOL ret, wildcards;
5740 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5741 if (!comp || !comp->Enabled ||
5742 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5744 TRACE("Component not set for install, not moving file\n");
5745 return ERROR_SUCCESS;
5748 sourcename = MSI_RecordGetString(rec, 3);
5749 options = MSI_RecordGetInteger(rec, 7);
5751 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5752 if (!sourcedir)
5753 goto done;
5755 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5756 if (!destdir)
5757 goto done;
5759 if (!sourcename)
5761 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5762 goto done;
5764 source = strdupW(sourcedir);
5765 if (!source)
5766 goto done;
5768 else
5770 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5771 source = msi_alloc(size * sizeof(WCHAR));
5772 if (!source)
5773 goto done;
5775 lstrcpyW(source, sourcedir);
5776 if (source[lstrlenW(source) - 1] != '\\')
5777 lstrcatW(source, szBackSlash);
5778 lstrcatW(source, sourcename);
5781 wildcards = strchrW(source, '*') || strchrW(source, '?');
5783 if (MSI_RecordIsNull(rec, 4))
5785 if (!wildcards)
5787 destname = strdupW(sourcename);
5788 if (!destname)
5789 goto done;
5792 else
5794 destname = strdupW(MSI_RecordGetString(rec, 4));
5795 if (destname)
5796 reduce_to_longfilename(destname);
5799 size = 0;
5800 if (destname)
5801 size = lstrlenW(destname);
5803 size += lstrlenW(destdir) + 2;
5804 dest = msi_alloc(size * sizeof(WCHAR));
5805 if (!dest)
5806 goto done;
5808 lstrcpyW(dest, destdir);
5809 if (dest[lstrlenW(dest) - 1] != '\\')
5810 lstrcatW(dest, szBackSlash);
5812 if (destname)
5813 lstrcatW(dest, destname);
5815 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5817 ret = CreateDirectoryW(destdir, NULL);
5818 if (!ret)
5820 WARN("CreateDirectory failed: %d\n", GetLastError());
5821 return ERROR_SUCCESS;
5825 if (!wildcards)
5826 msi_move_file(source, dest, options);
5827 else
5828 move_files_wildcard(source, dest, options);
5830 done:
5831 msi_free(sourcedir);
5832 msi_free(destdir);
5833 msi_free(destname);
5834 msi_free(source);
5835 msi_free(dest);
5837 return ERROR_SUCCESS;
5840 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5842 UINT rc;
5843 MSIQUERY *view;
5845 static const WCHAR ExecSeqQuery[] =
5846 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5847 '`','M','o','v','e','F','i','l','e','`',0};
5849 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5850 if (rc != ERROR_SUCCESS)
5851 return ERROR_SUCCESS;
5853 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5854 msiobj_release(&view->hdr);
5856 return rc;
5859 typedef struct tagMSIASSEMBLY
5861 struct list entry;
5862 MSICOMPONENT *component;
5863 MSIFEATURE *feature;
5864 MSIFILE *file;
5865 LPWSTR manifest;
5866 LPWSTR application;
5867 DWORD attributes;
5868 BOOL installed;
5869 } MSIASSEMBLY;
5871 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5872 DWORD dwReserved);
5873 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5874 LPVOID pvReserved, HMODULE *phModDll);
5876 static BOOL init_functionpointers(void)
5878 HRESULT hr;
5879 HMODULE hfusion;
5880 HMODULE hmscoree;
5882 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5884 hmscoree = LoadLibraryA("mscoree.dll");
5885 if (!hmscoree)
5887 WARN("mscoree.dll not available\n");
5888 return FALSE;
5891 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5892 if (!pLoadLibraryShim)
5894 WARN("LoadLibraryShim not available\n");
5895 FreeLibrary(hmscoree);
5896 return FALSE;
5899 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5900 if (FAILED(hr))
5902 WARN("fusion.dll not available\n");
5903 FreeLibrary(hmscoree);
5904 return FALSE;
5907 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5909 FreeLibrary(hmscoree);
5910 return TRUE;
5913 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5914 LPWSTR path)
5916 IAssemblyCache *cache;
5917 HRESULT hr;
5918 UINT r = ERROR_FUNCTION_FAILED;
5920 TRACE("installing assembly: %s\n", debugstr_w(path));
5922 if (assembly->feature)
5923 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5925 if (assembly->manifest)
5926 FIXME("Manifest unhandled\n");
5928 if (assembly->application)
5930 FIXME("Assembly should be privately installed\n");
5931 return ERROR_SUCCESS;
5934 if (assembly->attributes == msidbAssemblyAttributesWin32)
5936 FIXME("Win32 assemblies not handled\n");
5937 return ERROR_SUCCESS;
5940 hr = pCreateAssemblyCache(&cache, 0);
5941 if (FAILED(hr))
5942 goto done;
5944 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5945 if (FAILED(hr))
5946 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5948 r = ERROR_SUCCESS;
5950 done:
5951 IAssemblyCache_Release(cache);
5952 return r;
5955 typedef struct tagASSEMBLY_LIST
5957 MSIPACKAGE *package;
5958 IAssemblyCache *cache;
5959 struct list *assemblies;
5960 } ASSEMBLY_LIST;
5962 typedef struct tagASSEMBLY_NAME
5964 LPWSTR name;
5965 LPWSTR version;
5966 LPWSTR culture;
5967 LPWSTR pubkeytoken;
5968 } ASSEMBLY_NAME;
5970 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5972 ASSEMBLY_NAME *asmname = param;
5973 LPCWSTR name = MSI_RecordGetString(rec, 2);
5974 LPWSTR val = msi_dup_record_field(rec, 3);
5976 static const WCHAR Name[] = {'N','a','m','e',0};
5977 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5978 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5979 static const WCHAR PublicKeyToken[] = {
5980 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5982 if (!strcmpiW(name, Name))
5983 asmname->name = val;
5984 else if (!strcmpiW(name, Version))
5985 asmname->version = val;
5986 else if (!strcmpiW(name, Culture))
5987 asmname->culture = val;
5988 else if (!strcmpiW(name, PublicKeyToken))
5989 asmname->pubkeytoken = val;
5990 else
5991 msi_free(val);
5993 return ERROR_SUCCESS;
5996 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5998 if (!*str)
6000 *size = lstrlenW(append) + 1;
6001 *str = msi_alloc((*size) * sizeof(WCHAR));
6002 lstrcpyW(*str, append);
6003 return;
6006 (*size) += lstrlenW(append);
6007 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6008 lstrcatW(*str, append);
6011 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6012 MSICOMPONENT *comp)
6014 ASSEMBLY_INFO asminfo;
6015 ASSEMBLY_NAME name;
6016 MSIQUERY *view;
6017 LPWSTR disp;
6018 DWORD size;
6019 BOOL found;
6020 UINT r;
6022 static const WCHAR separator[] = {',',' ',0};
6023 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6024 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6025 static const WCHAR PublicKeyToken[] = {
6026 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6027 static const WCHAR query[] = {
6028 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6029 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6030 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6031 '=','\'','%','s','\'',0};
6033 disp = NULL;
6034 found = FALSE;
6035 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6036 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6038 r = MSI_OpenQuery(db, &view, query, comp->Component);
6039 if (r != ERROR_SUCCESS)
6040 return ERROR_SUCCESS;
6042 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6043 msiobj_release(&view->hdr);
6045 if (!name.name)
6047 ERR("No assembly name specified!\n");
6048 goto done;
6051 append_str(&disp, &size, name.name);
6053 if (name.version)
6055 append_str(&disp, &size, separator);
6056 append_str(&disp, &size, Version);
6057 append_str(&disp, &size, name.version);
6060 if (name.culture)
6062 append_str(&disp, &size, separator);
6063 append_str(&disp, &size, Culture);
6064 append_str(&disp, &size, name.culture);
6067 if (name.pubkeytoken)
6069 append_str(&disp, &size, separator);
6070 append_str(&disp, &size, PublicKeyToken);
6071 append_str(&disp, &size, name.pubkeytoken);
6074 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6075 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6076 disp, &asminfo);
6077 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6079 done:
6080 msi_free(disp);
6081 msi_free(name.name);
6082 msi_free(name.version);
6083 msi_free(name.culture);
6084 msi_free(name.pubkeytoken);
6086 return found;
6089 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6091 ASSEMBLY_LIST *list = param;
6092 MSIASSEMBLY *assembly;
6094 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6095 if (!assembly)
6096 return ERROR_OUTOFMEMORY;
6098 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6100 if (!assembly->component || !assembly->component->Enabled ||
6101 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6103 TRACE("Component not set for install, not publishing assembly\n");
6104 msi_free(assembly);
6105 return ERROR_SUCCESS;
6108 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6109 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6111 if (!assembly->file)
6113 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6114 return ERROR_FUNCTION_FAILED;
6117 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6118 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6119 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6121 if (assembly->application)
6123 WCHAR version[24];
6124 DWORD size = sizeof(version)/sizeof(WCHAR);
6126 /* FIXME: we should probably check the manifest file here */
6128 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6129 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6131 assembly->installed = TRUE;
6134 else
6135 assembly->installed = check_assembly_installed(list->package->db,
6136 list->cache,
6137 assembly->component);
6139 list_add_head(list->assemblies, &assembly->entry);
6140 return ERROR_SUCCESS;
6143 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6145 IAssemblyCache *cache = NULL;
6146 ASSEMBLY_LIST list;
6147 MSIQUERY *view;
6148 HRESULT hr;
6149 UINT r;
6151 static const WCHAR query[] =
6152 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6153 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6155 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6156 if (r != ERROR_SUCCESS)
6157 return ERROR_SUCCESS;
6159 hr = pCreateAssemblyCache(&cache, 0);
6160 if (FAILED(hr))
6161 return ERROR_FUNCTION_FAILED;
6163 list.package = package;
6164 list.cache = cache;
6165 list.assemblies = assemblies;
6167 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6168 msiobj_release(&view->hdr);
6170 IAssemblyCache_Release(cache);
6172 return r;
6175 static void free_assemblies(struct list *assemblies)
6177 struct list *item, *cursor;
6179 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6181 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6183 list_remove(&assembly->entry);
6184 msi_free(assembly->application);
6185 msi_free(assembly->manifest);
6186 msi_free(assembly);
6190 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6192 MSIASSEMBLY *assembly;
6194 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6196 if (!lstrcmpW(assembly->file->File, file))
6198 *out = assembly;
6199 return TRUE;
6203 return FALSE;
6206 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6207 LPWSTR *path, DWORD *attrs, PVOID user)
6209 MSIASSEMBLY *assembly;
6210 WCHAR temppath[MAX_PATH];
6211 struct list *assemblies = user;
6212 UINT r;
6214 if (!find_assembly(assemblies, file, &assembly))
6215 return FALSE;
6217 GetTempPathW(MAX_PATH, temppath);
6218 PathAddBackslashW(temppath);
6219 lstrcatW(temppath, assembly->file->FileName);
6221 if (action == MSICABEXTRACT_BEGINEXTRACT)
6223 if (assembly->installed)
6224 return FALSE;
6226 *path = strdupW(temppath);
6227 *attrs = assembly->file->Attributes;
6229 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6231 assembly->installed = TRUE;
6233 r = install_assembly(package, assembly, temppath);
6234 if (r != ERROR_SUCCESS)
6235 ERR("Failed to install assembly\n");
6238 return TRUE;
6241 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6243 UINT r;
6244 struct list assemblies = LIST_INIT(assemblies);
6245 MSIASSEMBLY *assembly;
6246 MSIMEDIAINFO *mi;
6248 if (!init_functionpointers() || !pCreateAssemblyCache)
6249 return ERROR_FUNCTION_FAILED;
6251 r = load_assemblies(package, &assemblies);
6252 if (r != ERROR_SUCCESS)
6253 goto done;
6255 if (list_empty(&assemblies))
6256 goto done;
6258 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6259 if (!mi)
6261 r = ERROR_OUTOFMEMORY;
6262 goto done;
6265 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6267 if (assembly->installed && !mi->is_continuous)
6268 continue;
6270 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6271 (assembly->file->IsCompressed && !mi->is_extracted))
6273 MSICABDATA data;
6275 r = ready_media(package, assembly->file, mi);
6276 if (r != ERROR_SUCCESS)
6278 ERR("Failed to ready media\n");
6279 break;
6282 data.mi = mi;
6283 data.package = package;
6284 data.cb = installassembly_cb;
6285 data.user = &assemblies;
6287 if (assembly->file->IsCompressed &&
6288 !msi_cabextract(package, mi, &data))
6290 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6291 r = ERROR_FUNCTION_FAILED;
6292 break;
6296 if (!assembly->file->IsCompressed)
6298 LPWSTR source = resolve_file_source(package, assembly->file);
6300 r = install_assembly(package, assembly, source);
6301 if (r != ERROR_SUCCESS)
6302 ERR("Failed to install assembly\n");
6304 msi_free(source);
6307 /* FIXME: write Installer assembly reg values */
6310 done:
6311 free_assemblies(&assemblies);
6312 return r;
6315 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6317 LPWSTR key, template, id;
6318 UINT r = ERROR_SUCCESS;
6320 id = msi_dup_property( package, szProductID );
6321 if (id)
6323 msi_free( id );
6324 return ERROR_SUCCESS;
6326 template = msi_dup_property( package, szPIDTemplate );
6327 key = msi_dup_property( package, szPIDKEY );
6329 if (key && template)
6331 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6332 r = MSI_SetPropertyW( package, szProductID, key );
6334 msi_free( template );
6335 msi_free( key );
6336 return r;
6339 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6341 TRACE("\n");
6342 package->need_reboot = 1;
6343 return ERROR_SUCCESS;
6346 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6348 TRACE("%p\n", package);
6349 return ERROR_SUCCESS;
6352 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6354 FIXME("%p\n", package);
6355 return ERROR_SUCCESS;
6358 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6360 FIXME("%p\n", package);
6361 return ERROR_SUCCESS;
6364 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6365 LPCSTR action, LPCWSTR table )
6367 static const WCHAR query[] = {
6368 'S','E','L','E','C','T',' ','*',' ',
6369 'F','R','O','M',' ','`','%','s','`',0 };
6370 MSIQUERY *view = NULL;
6371 DWORD count = 0;
6372 UINT r;
6374 r = MSI_OpenQuery( package->db, &view, query, table );
6375 if (r == ERROR_SUCCESS)
6377 r = MSI_IterateRecords(view, &count, NULL, package);
6378 msiobj_release(&view->hdr);
6381 if (count)
6382 FIXME("%s -> %u ignored %s table values\n",
6383 action, count, debugstr_w(table));
6385 return ERROR_SUCCESS;
6388 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6390 static const WCHAR table[] =
6391 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6392 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6395 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6397 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6398 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6401 static UINT ACTION_BindImage( MSIPACKAGE *package )
6403 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6404 return msi_unimplemented_action_stub( package, "BindImage", table );
6407 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6409 static const WCHAR table[] = {
6410 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6411 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6414 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6416 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6417 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6420 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6422 static const WCHAR table[] = {
6423 'E','n','v','i','r','o','n','m','e','n','t',0 };
6424 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6427 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6429 static const WCHAR table[] = {
6430 'M','s','i','A','s','s','e','m','b','l','y',0 };
6431 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6434 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6436 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6437 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6440 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6442 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6443 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6446 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6448 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6449 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6452 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6454 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6455 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6458 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6460 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6461 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6464 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6466 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6467 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6470 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6472 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6473 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6476 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6478 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6479 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6482 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6484 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6485 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6488 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6490 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6491 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6494 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6496 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6497 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6500 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6502 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6503 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6506 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6508 static const WCHAR table[] = { 'M','I','M','E',0 };
6509 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6512 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6514 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6515 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6518 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6520 static const struct
6522 const WCHAR *action;
6523 UINT (*handler)(MSIPACKAGE *);
6525 StandardActions[] =
6527 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6528 { szAppSearch, ACTION_AppSearch },
6529 { szBindImage, ACTION_BindImage },
6530 { szCCPSearch, ACTION_CCPSearch },
6531 { szCostFinalize, ACTION_CostFinalize },
6532 { szCostInitialize, ACTION_CostInitialize },
6533 { szCreateFolders, ACTION_CreateFolders },
6534 { szCreateShortcuts, ACTION_CreateShortcuts },
6535 { szDeleteServices, ACTION_DeleteServices },
6536 { szDisableRollback, ACTION_DisableRollback },
6537 { szDuplicateFiles, ACTION_DuplicateFiles },
6538 { szExecuteAction, ACTION_ExecuteAction },
6539 { szFileCost, ACTION_FileCost },
6540 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6541 { szForceReboot, ACTION_ForceReboot },
6542 { szInstallAdminPackage, ACTION_InstallAdminPackage },
6543 { szInstallExecute, ACTION_InstallExecute },
6544 { szInstallExecuteAgain, ACTION_InstallExecute },
6545 { szInstallFiles, ACTION_InstallFiles},
6546 { szInstallFinalize, ACTION_InstallFinalize },
6547 { szInstallInitialize, ACTION_InstallInitialize },
6548 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6549 { szInstallValidate, ACTION_InstallValidate },
6550 { szIsolateComponents, ACTION_IsolateComponents },
6551 { szLaunchConditions, ACTION_LaunchConditions },
6552 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6553 { szMoveFiles, ACTION_MoveFiles },
6554 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6555 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6556 { szInstallODBC, ACTION_InstallODBC },
6557 { szInstallServices, ACTION_InstallServices },
6558 { szPatchFiles, ACTION_PatchFiles },
6559 { szProcessComponents, ACTION_ProcessComponents },
6560 { szPublishComponents, ACTION_PublishComponents },
6561 { szPublishFeatures, ACTION_PublishFeatures },
6562 { szPublishProduct, ACTION_PublishProduct },
6563 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6564 { szRegisterComPlus, ACTION_RegisterComPlus},
6565 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6566 { szRegisterFonts, ACTION_RegisterFonts },
6567 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6568 { szRegisterProduct, ACTION_RegisterProduct },
6569 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6570 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6571 { szRegisterUser, ACTION_RegisterUser },
6572 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6573 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6574 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6575 { szRemoveFiles, ACTION_RemoveFiles },
6576 { szRemoveFolders, ACTION_RemoveFolders },
6577 { szRemoveIniValues, ACTION_RemoveIniValues },
6578 { szRemoveODBC, ACTION_RemoveODBC },
6579 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6580 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6581 { szResolveSource, ACTION_ResolveSource },
6582 { szRMCCPSearch, ACTION_RMCCPSearch },
6583 { szScheduleReboot, ACTION_ScheduleReboot },
6584 { szSelfRegModules, ACTION_SelfRegModules },
6585 { szSelfUnregModules, ACTION_SelfUnregModules },
6586 { szSetODBCFolders, ACTION_SetODBCFolders },
6587 { szStartServices, ACTION_StartServices },
6588 { szStopServices, ACTION_StopServices },
6589 { szUnpublishComponents, ACTION_UnpublishComponents },
6590 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6591 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6592 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6593 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6594 { szUnregisterFonts, ACTION_UnregisterFonts },
6595 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6596 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6597 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6598 { szValidateProductID, ACTION_ValidateProductID },
6599 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6600 { szWriteIniValues, ACTION_WriteIniValues },
6601 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6602 { NULL, NULL },
6605 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6606 UINT* rc, BOOL force )
6608 BOOL ret = FALSE;
6609 BOOL run = force;
6610 int i;
6612 if (!run && !package->script->CurrentlyScripting)
6613 run = TRUE;
6615 if (!run)
6617 if (strcmpW(action,szInstallFinalize) == 0 ||
6618 strcmpW(action,szInstallExecute) == 0 ||
6619 strcmpW(action,szInstallExecuteAgain) == 0)
6620 run = TRUE;
6623 i = 0;
6624 while (StandardActions[i].action != NULL)
6626 if (strcmpW(StandardActions[i].action, action)==0)
6628 if (!run)
6630 ui_actioninfo(package, action, TRUE, 0);
6631 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6632 ui_actioninfo(package, action, FALSE, *rc);
6634 else
6636 ui_actionstart(package, action);
6637 if (StandardActions[i].handler)
6639 *rc = StandardActions[i].handler(package);
6641 else
6643 FIXME("unhandled standard action %s\n",debugstr_w(action));
6644 *rc = ERROR_SUCCESS;
6647 ret = TRUE;
6648 break;
6650 i++;
6652 return ret;
6655 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6657 UINT rc = ERROR_SUCCESS;
6658 BOOL handled;
6660 TRACE("Performing action (%s)\n", debugstr_w(action));
6662 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6664 if (!handled)
6665 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6667 if (!handled)
6669 WARN("unhandled msi action %s\n", debugstr_w(action));
6670 rc = ERROR_FUNCTION_NOT_CALLED;
6673 return rc;
6676 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6678 UINT rc = ERROR_SUCCESS;
6679 BOOL handled = FALSE;
6681 TRACE("Performing action (%s)\n", debugstr_w(action));
6683 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6685 if (!handled)
6686 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6688 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6689 handled = TRUE;
6691 if (!handled)
6693 WARN("unhandled msi action %s\n", debugstr_w(action));
6694 rc = ERROR_FUNCTION_NOT_CALLED;
6697 return rc;
6700 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6702 UINT rc = ERROR_SUCCESS;
6703 MSIRECORD *row;
6705 static const WCHAR ExecSeqQuery[] =
6706 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6707 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6708 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6709 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6710 static const WCHAR UISeqQuery[] =
6711 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6712 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6713 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6714 ' ', '=',' ','%','i',0};
6716 if (needs_ui_sequence(package))
6717 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6718 else
6719 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6721 if (row)
6723 LPCWSTR action, cond;
6725 TRACE("Running the actions\n");
6727 /* check conditions */
6728 cond = MSI_RecordGetString(row, 2);
6730 /* this is a hack to skip errors in the condition code */
6731 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6733 msiobj_release(&row->hdr);
6734 return ERROR_SUCCESS;
6737 action = MSI_RecordGetString(row, 1);
6738 if (!action)
6740 ERR("failed to fetch action\n");
6741 msiobj_release(&row->hdr);
6742 return ERROR_FUNCTION_FAILED;
6745 if (needs_ui_sequence(package))
6746 rc = ACTION_PerformUIAction(package, action, -1);
6747 else
6748 rc = ACTION_PerformAction(package, action, -1, FALSE);
6750 msiobj_release(&row->hdr);
6753 return rc;
6756 /****************************************************
6757 * TOP level entry points
6758 *****************************************************/
6760 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6761 LPCWSTR szCommandLine )
6763 UINT rc;
6764 BOOL ui_exists;
6766 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6767 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6769 MSI_SetPropertyW(package, szAction, szInstall);
6771 package->script->InWhatSequence = SEQUENCE_INSTALL;
6773 if (szPackagePath)
6775 LPWSTR p, dir;
6776 LPCWSTR file;
6778 dir = strdupW(szPackagePath);
6779 p = strrchrW(dir, '\\');
6780 if (p)
6782 *(++p) = 0;
6783 file = szPackagePath + (p - dir);
6785 else
6787 msi_free(dir);
6788 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6789 GetCurrentDirectoryW(MAX_PATH, dir);
6790 lstrcatW(dir, szBackSlash);
6791 file = szPackagePath;
6794 msi_free( package->PackagePath );
6795 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6796 if (!package->PackagePath)
6798 msi_free(dir);
6799 return ERROR_OUTOFMEMORY;
6802 lstrcpyW(package->PackagePath, dir);
6803 lstrcatW(package->PackagePath, file);
6804 msi_free(dir);
6806 msi_set_sourcedir_props(package, FALSE);
6809 msi_parse_command_line( package, szCommandLine, FALSE );
6811 msi_apply_transforms( package );
6812 msi_apply_patches( package );
6814 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6816 TRACE("setting reinstall property\n");
6817 MSI_SetPropertyW( package, szReinstall, szAll );
6820 /* properties may have been added by a transform */
6821 msi_clone_properties( package );
6822 msi_set_context( package );
6824 if (needs_ui_sequence( package))
6826 package->script->InWhatSequence |= SEQUENCE_UI;
6827 rc = ACTION_ProcessUISequence(package);
6828 ui_exists = ui_sequence_exists(package);
6829 if (rc == ERROR_SUCCESS || !ui_exists)
6831 package->script->InWhatSequence |= SEQUENCE_EXEC;
6832 rc = ACTION_ProcessExecSequence(package, ui_exists);
6835 else
6836 rc = ACTION_ProcessExecSequence(package, FALSE);
6838 package->script->CurrentlyScripting = FALSE;
6840 /* process the ending type action */
6841 if (rc == ERROR_SUCCESS)
6842 ACTION_PerformActionSequence(package, -1);
6843 else if (rc == ERROR_INSTALL_USEREXIT)
6844 ACTION_PerformActionSequence(package, -2);
6845 else if (rc == ERROR_INSTALL_SUSPEND)
6846 ACTION_PerformActionSequence(package, -4);
6847 else /* failed */
6848 ACTION_PerformActionSequence(package, -3);
6850 /* finish up running custom actions */
6851 ACTION_FinishCustomActions(package);
6853 if (rc == ERROR_SUCCESS && package->need_reboot)
6854 return ERROR_SUCCESS_REBOOT_REQUIRED;
6856 return rc;