msi: Implement the RemoveShortcuts standard action.
[wine.git] / dlls / msi / action.c
blob4ddfc829471e069766b2132e1f532d5175063852
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 WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3032 static const WCHAR szlnk[] = {'.','l','n','k',0};
3033 LPCWSTR directory, extension;
3034 LPWSTR link_folder, link_file, filename;
3036 directory = MSI_RecordGetString( row, 2 );
3037 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3039 /* may be needed because of a bug somewhere else */
3040 create_full_pathW( link_folder );
3042 filename = msi_dup_record_field( row, 3 );
3043 reduce_to_longfilename( filename );
3045 extension = strchrW( filename, '.' );
3046 if (!extension || strcmpiW( extension, szlnk ))
3048 int len = strlenW( filename );
3049 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3050 memcpy( filename + len, szlnk, sizeof(szlnk) );
3052 link_file = build_directory_name( 2, link_folder, filename );
3053 msi_free( link_folder );
3054 msi_free( filename );
3056 return link_file;
3059 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3061 MSIPACKAGE *package = param;
3062 LPWSTR link_file, deformated, path;
3063 LPCWSTR component, target;
3064 MSICOMPONENT *comp;
3065 IShellLinkW *sl = NULL;
3066 IPersistFile *pf = NULL;
3067 HRESULT res;
3069 component = MSI_RecordGetString(row, 4);
3070 comp = get_loaded_component(package, component);
3071 if (!comp)
3072 return ERROR_SUCCESS;
3074 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3076 TRACE("Skipping shortcut creation due to disabled component\n");
3078 comp->Action = comp->Installed;
3079 return ERROR_SUCCESS;
3081 comp->Action = INSTALLSTATE_LOCAL;
3083 ui_actiondata(package,szCreateShortcuts,row);
3085 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3086 &IID_IShellLinkW, (LPVOID *) &sl );
3088 if (FAILED( res ))
3090 ERR("CLSID_ShellLink not available\n");
3091 goto err;
3094 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3095 if (FAILED( res ))
3097 ERR("QueryInterface(IID_IPersistFile) failed\n");
3098 goto err;
3101 target = MSI_RecordGetString(row, 5);
3102 if (strchrW(target, '['))
3104 deformat_string(package, target, &deformated);
3105 IShellLinkW_SetPath(sl,deformated);
3106 msi_free(deformated);
3108 else
3110 FIXME("poorly handled shortcut format, advertised shortcut\n");
3111 IShellLinkW_SetPath(sl,comp->FullKeypath);
3114 if (!MSI_RecordIsNull(row,6))
3116 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3117 deformat_string(package, arguments, &deformated);
3118 IShellLinkW_SetArguments(sl,deformated);
3119 msi_free(deformated);
3122 if (!MSI_RecordIsNull(row,7))
3124 LPCWSTR description = MSI_RecordGetString(row, 7);
3125 IShellLinkW_SetDescription(sl, description);
3128 if (!MSI_RecordIsNull(row,8))
3129 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3131 if (!MSI_RecordIsNull(row,9))
3133 INT index;
3134 LPCWSTR icon = MSI_RecordGetString(row, 9);
3136 path = build_icon_path(package, icon);
3137 index = MSI_RecordGetInteger(row,10);
3139 /* no value means 0 */
3140 if (index == MSI_NULL_INTEGER)
3141 index = 0;
3143 IShellLinkW_SetIconLocation(sl, path, index);
3144 msi_free(path);
3147 if (!MSI_RecordIsNull(row,11))
3148 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3150 if (!MSI_RecordIsNull(row,12))
3152 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3153 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3154 if (path)
3155 IShellLinkW_SetWorkingDirectory(sl, path);
3156 msi_free(path);
3159 link_file = get_link_file(package, row);
3161 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3162 IPersistFile_Save(pf, link_file, FALSE);
3164 msi_free(link_file);
3166 err:
3167 if (pf)
3168 IPersistFile_Release( pf );
3169 if (sl)
3170 IShellLinkW_Release( sl );
3172 return ERROR_SUCCESS;
3175 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3177 UINT rc;
3178 HRESULT res;
3179 MSIQUERY * view;
3180 static const WCHAR Query[] =
3181 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3182 '`','S','h','o','r','t','c','u','t','`',0};
3184 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3185 if (rc != ERROR_SUCCESS)
3186 return ERROR_SUCCESS;
3188 res = CoInitialize( NULL );
3190 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3191 msiobj_release(&view->hdr);
3193 if (SUCCEEDED(res))
3194 CoUninitialize();
3196 return rc;
3199 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3201 MSIPACKAGE *package = param;
3202 LPWSTR link_file;
3203 LPCWSTR component;
3204 MSICOMPONENT *comp;
3206 component = MSI_RecordGetString( row, 4 );
3207 comp = get_loaded_component( package, component );
3208 if (!comp)
3209 return ERROR_SUCCESS;
3211 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT ))
3213 TRACE("Skipping, component not scheduled for uninstall\n");
3215 comp->Action = comp->Installed;
3216 return ERROR_SUCCESS;
3218 comp->Action = INSTALLSTATE_ABSENT;
3220 ui_actiondata( package, szRemoveShortcuts, row );
3222 link_file = get_link_file( package, row );
3224 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3225 if (!DeleteFileW( link_file ))
3227 WARN("Failed to remove shortcut file %u\n", GetLastError());
3229 msi_free( link_file );
3231 return ERROR_SUCCESS;
3234 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3236 UINT rc;
3237 MSIQUERY *view;
3238 static const WCHAR query[] =
3239 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3240 '`','S','h','o','r','t','c','u','t','`',0};
3242 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3243 if (rc != ERROR_SUCCESS)
3244 return ERROR_SUCCESS;
3246 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3247 msiobj_release( &view->hdr );
3249 return rc;
3252 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3254 MSIPACKAGE* package = param;
3255 HANDLE the_file;
3256 LPWSTR FilePath;
3257 LPCWSTR FileName;
3258 CHAR buffer[1024];
3259 DWORD sz;
3260 UINT rc;
3261 MSIRECORD *uirow;
3263 FileName = MSI_RecordGetString(row,1);
3264 if (!FileName)
3266 ERR("Unable to get FileName\n");
3267 return ERROR_SUCCESS;
3270 FilePath = build_icon_path(package,FileName);
3272 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3274 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3275 FILE_ATTRIBUTE_NORMAL, NULL);
3277 if (the_file == INVALID_HANDLE_VALUE)
3279 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3280 msi_free(FilePath);
3281 return ERROR_SUCCESS;
3286 DWORD write;
3287 sz = 1024;
3288 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3289 if (rc != ERROR_SUCCESS)
3291 ERR("Failed to get stream\n");
3292 CloseHandle(the_file);
3293 DeleteFileW(FilePath);
3294 break;
3296 WriteFile(the_file,buffer,sz,&write,NULL);
3297 } while (sz == 1024);
3299 msi_free(FilePath);
3301 CloseHandle(the_file);
3303 uirow = MSI_CreateRecord(1);
3304 MSI_RecordSetStringW(uirow,1,FileName);
3305 ui_actiondata(package,szPublishProduct,uirow);
3306 msiobj_release( &uirow->hdr );
3308 return ERROR_SUCCESS;
3311 static UINT msi_publish_icons(MSIPACKAGE *package)
3313 UINT r;
3314 MSIQUERY *view;
3316 static const WCHAR query[]= {
3317 'S','E','L','E','C','T',' ','*',' ',
3318 'F','R','O','M',' ','`','I','c','o','n','`',0};
3320 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3321 if (r == ERROR_SUCCESS)
3323 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3324 msiobj_release(&view->hdr);
3327 return ERROR_SUCCESS;
3330 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3332 UINT r;
3333 HKEY source;
3334 LPWSTR buffer;
3335 MSIMEDIADISK *disk;
3336 MSISOURCELISTINFO *info;
3338 r = RegCreateKeyW(hkey, szSourceList, &source);
3339 if (r != ERROR_SUCCESS)
3340 return r;
3342 RegCloseKey(source);
3344 buffer = strrchrW(package->PackagePath, '\\') + 1;
3345 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3346 package->Context, MSICODE_PRODUCT,
3347 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3348 if (r != ERROR_SUCCESS)
3349 return r;
3351 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3352 package->Context, MSICODE_PRODUCT,
3353 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3354 if (r != ERROR_SUCCESS)
3355 return r;
3357 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3358 package->Context, MSICODE_PRODUCT,
3359 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3360 if (r != ERROR_SUCCESS)
3361 return r;
3363 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3365 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3366 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3367 info->options, info->value);
3368 else
3369 MsiSourceListSetInfoW(package->ProductCode, NULL,
3370 info->context, info->options,
3371 info->property, info->value);
3374 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3376 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3377 disk->context, disk->options,
3378 disk->disk_id, disk->volume_label, disk->disk_prompt);
3381 return ERROR_SUCCESS;
3384 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3386 MSIHANDLE hdb, suminfo;
3387 WCHAR guids[MAX_PATH];
3388 WCHAR packcode[SQUISH_GUID_SIZE];
3389 LPWSTR buffer;
3390 LPWSTR ptr;
3391 DWORD langid;
3392 DWORD size;
3393 UINT r;
3395 static const WCHAR szProductLanguage[] =
3396 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3397 static const WCHAR szARPProductIcon[] =
3398 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3399 static const WCHAR szProductVersion[] =
3400 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3401 static const WCHAR szAssignment[] =
3402 {'A','s','s','i','g','n','m','e','n','t',0};
3403 static const WCHAR szAdvertiseFlags[] =
3404 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3405 static const WCHAR szClients[] =
3406 {'C','l','i','e','n','t','s',0};
3407 static const WCHAR szColon[] = {':',0};
3409 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3410 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3411 msi_free(buffer);
3413 langid = msi_get_property_int(package, szProductLanguage, 0);
3414 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3416 /* FIXME */
3417 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3419 buffer = msi_dup_property(package, szARPProductIcon);
3420 if (buffer)
3422 LPWSTR path = build_icon_path(package,buffer);
3423 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3424 msi_free(path);
3425 msi_free(buffer);
3428 buffer = msi_dup_property(package, szProductVersion);
3429 if (buffer)
3431 DWORD verdword = msi_version_str_to_dword(buffer);
3432 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3433 msi_free(buffer);
3436 msi_reg_set_val_dword(hkey, szAssignment, 0);
3437 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3438 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3439 msi_reg_set_val_str(hkey, szClients, szColon);
3441 hdb = alloc_msihandle(&package->db->hdr);
3442 if (!hdb)
3443 return ERROR_NOT_ENOUGH_MEMORY;
3445 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3446 MsiCloseHandle(hdb);
3447 if (r != ERROR_SUCCESS)
3448 goto done;
3450 size = MAX_PATH;
3451 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3452 NULL, guids, &size);
3453 if (r != ERROR_SUCCESS)
3454 goto done;
3456 ptr = strchrW(guids, ';');
3457 if (ptr) *ptr = 0;
3458 squash_guid(guids, packcode);
3459 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3461 done:
3462 MsiCloseHandle(suminfo);
3463 return ERROR_SUCCESS;
3466 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3468 UINT r;
3469 HKEY hkey;
3470 LPWSTR upgrade;
3471 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3473 static const WCHAR szUpgradeCode[] =
3474 {'U','p','g','r','a','d','e','C','o','d','e',0};
3476 upgrade = msi_dup_property(package, szUpgradeCode);
3477 if (!upgrade)
3478 return ERROR_SUCCESS;
3480 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3482 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3483 if (r != ERROR_SUCCESS)
3484 goto done;
3486 else
3488 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3489 if (r != ERROR_SUCCESS)
3490 goto done;
3493 squash_guid(package->ProductCode, squashed_pc);
3494 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3496 RegCloseKey(hkey);
3498 done:
3499 msi_free(upgrade);
3500 return r;
3503 static BOOL msi_check_publish(MSIPACKAGE *package)
3505 MSIFEATURE *feature;
3507 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3509 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3510 return TRUE;
3513 return FALSE;
3516 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3518 MSIFEATURE *feature;
3520 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3522 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3523 return FALSE;
3526 return TRUE;
3529 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3531 WCHAR patch_squashed[GUID_SIZE];
3532 HKEY patches;
3533 LONG res;
3534 UINT r = ERROR_FUNCTION_FAILED;
3536 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3537 &patches, NULL);
3538 if (res != ERROR_SUCCESS)
3539 return ERROR_FUNCTION_FAILED;
3541 squash_guid(package->patch->patchcode, patch_squashed);
3543 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3544 (const BYTE *)patch_squashed,
3545 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3546 if (res != ERROR_SUCCESS)
3547 goto done;
3549 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3550 (const BYTE *)package->patch->transforms,
3551 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3552 if (res == ERROR_SUCCESS)
3553 r = ERROR_SUCCESS;
3555 done:
3556 RegCloseKey(patches);
3557 return r;
3561 * 99% of the work done here is only done for
3562 * advertised installs. However this is where the
3563 * Icon table is processed and written out
3564 * so that is what I am going to do here.
3566 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3568 UINT rc;
3569 HKEY hukey=0;
3570 HKEY hudkey=0;
3572 /* FIXME: also need to publish if the product is in advertise mode */
3573 if (!msi_check_publish(package))
3574 return ERROR_SUCCESS;
3576 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3577 &hukey, TRUE);
3578 if (rc != ERROR_SUCCESS)
3579 goto end;
3581 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3582 NULL, &hudkey, TRUE);
3583 if (rc != ERROR_SUCCESS)
3584 goto end;
3586 rc = msi_publish_upgrade_code(package);
3587 if (rc != ERROR_SUCCESS)
3588 goto end;
3590 if (package->patch)
3592 rc = msi_publish_patch(package, hukey, hudkey);
3593 if (rc != ERROR_SUCCESS)
3594 goto end;
3597 rc = msi_publish_product_properties(package, hukey);
3598 if (rc != ERROR_SUCCESS)
3599 goto end;
3601 rc = msi_publish_sourcelist(package, hukey);
3602 if (rc != ERROR_SUCCESS)
3603 goto end;
3605 rc = msi_publish_icons(package);
3607 end:
3608 RegCloseKey(hukey);
3609 RegCloseKey(hudkey);
3611 return rc;
3614 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3616 MSIPACKAGE *package = param;
3617 LPCWSTR component, section, key, value, identifier, dirproperty;
3618 LPWSTR deformated_section, deformated_key, deformated_value;
3619 LPWSTR folder, filename, fullname = NULL;
3620 LPCWSTR filenameptr;
3621 MSIRECORD * uirow;
3622 INT action;
3623 MSICOMPONENT *comp;
3624 static const WCHAR szWindowsFolder[] =
3625 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3627 component = MSI_RecordGetString(row, 8);
3628 comp = get_loaded_component(package,component);
3630 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3632 TRACE("Skipping ini file due to disabled component %s\n",
3633 debugstr_w(component));
3635 comp->Action = comp->Installed;
3637 return ERROR_SUCCESS;
3640 comp->Action = INSTALLSTATE_LOCAL;
3642 identifier = MSI_RecordGetString(row,1);
3643 dirproperty = MSI_RecordGetString(row,3);
3644 section = MSI_RecordGetString(row,4);
3645 key = MSI_RecordGetString(row,5);
3646 value = MSI_RecordGetString(row,6);
3647 action = MSI_RecordGetInteger(row,7);
3649 deformat_string(package,section,&deformated_section);
3650 deformat_string(package,key,&deformated_key);
3651 deformat_string(package,value,&deformated_value);
3653 filename = msi_dup_record_field(row, 2);
3654 if (filename && (filenameptr = strchrW(filename, '|')))
3655 filenameptr++;
3656 else
3657 filenameptr = filename;
3659 if (dirproperty)
3661 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3662 if (!folder)
3663 folder = msi_dup_property( package, dirproperty );
3665 else
3666 folder = msi_dup_property( package, szWindowsFolder );
3668 if (!folder)
3670 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3671 goto cleanup;
3674 fullname = build_directory_name(2, folder, filenameptr);
3676 if (action == 0)
3678 TRACE("Adding value %s to section %s in %s\n",
3679 debugstr_w(deformated_key), debugstr_w(deformated_section),
3680 debugstr_w(fullname));
3681 WritePrivateProfileStringW(deformated_section, deformated_key,
3682 deformated_value, fullname);
3684 else if (action == 1)
3686 WCHAR returned[10];
3687 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3688 returned, 10, fullname);
3689 if (returned[0] == 0)
3691 TRACE("Adding value %s to section %s in %s\n",
3692 debugstr_w(deformated_key), debugstr_w(deformated_section),
3693 debugstr_w(fullname));
3695 WritePrivateProfileStringW(deformated_section, deformated_key,
3696 deformated_value, fullname);
3699 else if (action == 3)
3700 FIXME("Append to existing section not yet implemented\n");
3702 uirow = MSI_CreateRecord(4);
3703 MSI_RecordSetStringW(uirow,1,identifier);
3704 MSI_RecordSetStringW(uirow,2,deformated_section);
3705 MSI_RecordSetStringW(uirow,3,deformated_key);
3706 MSI_RecordSetStringW(uirow,4,deformated_value);
3707 ui_actiondata(package,szWriteIniValues,uirow);
3708 msiobj_release( &uirow->hdr );
3710 cleanup:
3711 msi_free(filename);
3712 msi_free(fullname);
3713 msi_free(folder);
3714 msi_free(deformated_key);
3715 msi_free(deformated_value);
3716 msi_free(deformated_section);
3717 return ERROR_SUCCESS;
3720 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3722 UINT rc;
3723 MSIQUERY * view;
3724 static const WCHAR ExecSeqQuery[] =
3725 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3726 '`','I','n','i','F','i','l','e','`',0};
3728 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3729 if (rc != ERROR_SUCCESS)
3731 TRACE("no IniFile table\n");
3732 return ERROR_SUCCESS;
3735 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3736 msiobj_release(&view->hdr);
3737 return rc;
3740 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3742 MSIPACKAGE *package = param;
3743 LPCWSTR filename;
3744 LPWSTR FullName;
3745 MSIFILE *file;
3746 DWORD len;
3747 static const WCHAR ExeStr[] =
3748 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3749 static const WCHAR close[] = {'\"',0};
3750 STARTUPINFOW si;
3751 PROCESS_INFORMATION info;
3752 BOOL brc;
3753 MSIRECORD *uirow;
3754 LPWSTR uipath, p;
3756 memset(&si,0,sizeof(STARTUPINFOW));
3758 filename = MSI_RecordGetString(row,1);
3759 file = get_loaded_file( package, filename );
3761 if (!file)
3763 ERR("Unable to find file id %s\n",debugstr_w(filename));
3764 return ERROR_SUCCESS;
3767 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3769 FullName = msi_alloc(len*sizeof(WCHAR));
3770 strcpyW(FullName,ExeStr);
3771 strcatW( FullName, file->TargetPath );
3772 strcatW(FullName,close);
3774 TRACE("Registering %s\n",debugstr_w(FullName));
3775 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3776 &si, &info);
3778 if (brc)
3780 CloseHandle(info.hThread);
3781 msi_dialog_check_messages(info.hProcess);
3782 CloseHandle(info.hProcess);
3785 msi_free(FullName);
3787 /* the UI chunk */
3788 uirow = MSI_CreateRecord( 2 );
3789 uipath = strdupW( file->TargetPath );
3790 p = strrchrW(uipath,'\\');
3791 if (p)
3792 p[0]=0;
3793 MSI_RecordSetStringW( uirow, 1, &p[1] );
3794 MSI_RecordSetStringW( uirow, 2, uipath);
3795 ui_actiondata( package, szSelfRegModules, uirow);
3796 msiobj_release( &uirow->hdr );
3797 msi_free( uipath );
3798 /* FIXME: call ui_progress? */
3800 return ERROR_SUCCESS;
3803 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3805 UINT rc;
3806 MSIQUERY * view;
3807 static const WCHAR ExecSeqQuery[] =
3808 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3809 '`','S','e','l','f','R','e','g','`',0};
3811 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3812 if (rc != ERROR_SUCCESS)
3814 TRACE("no SelfReg table\n");
3815 return ERROR_SUCCESS;
3818 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3819 msiobj_release(&view->hdr);
3821 return ERROR_SUCCESS;
3824 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
3826 static const WCHAR regsvr32[] =
3827 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"',0};
3828 static const WCHAR close[] = {'\"',0};
3829 MSIPACKAGE *package = param;
3830 LPCWSTR filename;
3831 LPWSTR cmdline;
3832 MSIFILE *file;
3833 DWORD len;
3834 STARTUPINFOW si;
3835 PROCESS_INFORMATION pi;
3836 BOOL ret;
3837 MSIRECORD *uirow;
3838 LPWSTR uipath, p;
3840 memset( &si, 0, sizeof(STARTUPINFOW) );
3842 filename = MSI_RecordGetString( row, 1 );
3843 file = get_loaded_file( package, filename );
3845 if (!file)
3847 ERR("Unable to find file id %s\n", debugstr_w(filename));
3848 return ERROR_SUCCESS;
3851 len = strlenW( regsvr32 ) + strlenW( file->TargetPath ) + 2;
3853 cmdline = msi_alloc( len * sizeof(WCHAR) );
3854 strcpyW( cmdline, regsvr32 );
3855 strcatW( cmdline, file->TargetPath );
3856 strcatW( cmdline, close );
3858 TRACE("Unregistering %s\n", debugstr_w(cmdline));
3860 ret = CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, c_colon, &si, &pi );
3861 if (ret)
3863 CloseHandle( pi.hThread );
3864 msi_dialog_check_messages( pi.hProcess );
3865 CloseHandle( pi.hProcess );
3868 msi_free( cmdline );
3870 uirow = MSI_CreateRecord( 2 );
3871 uipath = strdupW( file->TargetPath );
3872 if ((p = strrchrW( uipath, '\\' )))
3874 *p = 0;
3875 MSI_RecordSetStringW( uirow, 1, ++p );
3877 MSI_RecordSetStringW( uirow, 2, uipath );
3878 ui_actiondata( package, szSelfUnregModules, uirow );
3879 msiobj_release( &uirow->hdr );
3880 msi_free( uipath );
3881 /* FIXME call ui_progress? */
3883 return ERROR_SUCCESS;
3886 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
3888 UINT rc;
3889 MSIQUERY *view;
3890 static const WCHAR query[] =
3891 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3892 '`','S','e','l','f','R','e','g','`',0};
3894 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3895 if (rc != ERROR_SUCCESS)
3897 TRACE("no SelfReg table\n");
3898 return ERROR_SUCCESS;
3901 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
3902 msiobj_release( &view->hdr );
3904 return ERROR_SUCCESS;
3907 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3909 MSIFEATURE *feature;
3910 UINT rc;
3911 HKEY hkey;
3912 HKEY userdata = NULL;
3914 if (!msi_check_publish(package))
3915 return ERROR_SUCCESS;
3917 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3918 &hkey, TRUE);
3919 if (rc != ERROR_SUCCESS)
3920 goto end;
3922 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3923 &userdata, TRUE);
3924 if (rc != ERROR_SUCCESS)
3925 goto end;
3927 /* here the guids are base 85 encoded */
3928 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3930 ComponentList *cl;
3931 LPWSTR data = NULL;
3932 GUID clsid;
3933 INT size;
3934 BOOL absent = FALSE;
3935 MSIRECORD *uirow;
3937 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3938 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3939 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3940 absent = TRUE;
3942 size = 1;
3943 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3945 size += 21;
3947 if (feature->Feature_Parent)
3948 size += strlenW( feature->Feature_Parent )+2;
3950 data = msi_alloc(size * sizeof(WCHAR));
3952 data[0] = 0;
3953 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3955 MSICOMPONENT* component = cl->component;
3956 WCHAR buf[21];
3958 buf[0] = 0;
3959 if (component->ComponentId)
3961 TRACE("From %s\n",debugstr_w(component->ComponentId));
3962 CLSIDFromString(component->ComponentId, &clsid);
3963 encode_base85_guid(&clsid,buf);
3964 TRACE("to %s\n",debugstr_w(buf));
3965 strcatW(data,buf);
3969 if (feature->Feature_Parent)
3971 static const WCHAR sep[] = {'\2',0};
3972 strcatW(data,sep);
3973 strcatW(data,feature->Feature_Parent);
3976 msi_reg_set_val_str( userdata, feature->Feature, data );
3977 msi_free(data);
3979 size = 0;
3980 if (feature->Feature_Parent)
3981 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3982 if (!absent)
3984 size += sizeof(WCHAR);
3985 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3986 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
3988 else
3990 size += 2*sizeof(WCHAR);
3991 data = msi_alloc(size);
3992 data[0] = 0x6;
3993 data[1] = 0;
3994 if (feature->Feature_Parent)
3995 strcpyW( &data[1], feature->Feature_Parent );
3996 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3997 (LPBYTE)data,size);
3998 msi_free(data);
4001 /* the UI chunk */
4002 uirow = MSI_CreateRecord( 1 );
4003 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4004 ui_actiondata( package, szPublishFeatures, uirow);
4005 msiobj_release( &uirow->hdr );
4006 /* FIXME: call ui_progress? */
4009 end:
4010 RegCloseKey(hkey);
4011 RegCloseKey(userdata);
4012 return rc;
4015 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4017 UINT r;
4018 HKEY hkey;
4020 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4022 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4023 &hkey, FALSE);
4024 if (r == ERROR_SUCCESS)
4026 RegDeleteValueW(hkey, feature->Feature);
4027 RegCloseKey(hkey);
4030 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4031 &hkey, FALSE);
4032 if (r == ERROR_SUCCESS)
4034 RegDeleteValueW(hkey, feature->Feature);
4035 RegCloseKey(hkey);
4038 return ERROR_SUCCESS;
4041 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4043 MSIFEATURE *feature;
4045 if (!msi_check_unpublish(package))
4046 return ERROR_SUCCESS;
4048 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4050 msi_unpublish_feature(package, feature);
4053 return ERROR_SUCCESS;
4056 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4058 LPWSTR prop, val, key;
4059 SYSTEMTIME systime;
4060 DWORD size, langid;
4061 WCHAR date[9];
4062 LPWSTR buffer;
4064 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4065 static const WCHAR szWindowsInstaller[] =
4066 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4067 static const WCHAR modpath_fmt[] =
4068 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4069 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4070 static const WCHAR szModifyPath[] =
4071 {'M','o','d','i','f','y','P','a','t','h',0};
4072 static const WCHAR szUninstallString[] =
4073 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4074 static const WCHAR szEstimatedSize[] =
4075 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4076 static const WCHAR szProductLanguage[] =
4077 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4078 static const WCHAR szProductVersion[] =
4079 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4080 static const WCHAR szProductName[] =
4081 {'P','r','o','d','u','c','t','N','a','m','e',0};
4082 static const WCHAR szDisplayName[] =
4083 {'D','i','s','p','l','a','y','N','a','m','e',0};
4084 static const WCHAR szDisplayVersion[] =
4085 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4086 static const WCHAR szManufacturer[] =
4087 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4089 static const LPCSTR propval[] = {
4090 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4091 "ARPCONTACT", "Contact",
4092 "ARPCOMMENTS", "Comments",
4093 "ProductName", "DisplayName",
4094 "ProductVersion", "DisplayVersion",
4095 "ARPHELPLINK", "HelpLink",
4096 "ARPHELPTELEPHONE", "HelpTelephone",
4097 "ARPINSTALLLOCATION", "InstallLocation",
4098 "SourceDir", "InstallSource",
4099 "Manufacturer", "Publisher",
4100 "ARPREADME", "Readme",
4101 "ARPSIZE", "Size",
4102 "ARPURLINFOABOUT", "URLInfoAbout",
4103 "ARPURLUPDATEINFO", "URLUpdateInfo",
4104 NULL,
4106 const LPCSTR *p = propval;
4108 while (*p)
4110 prop = strdupAtoW(*p++);
4111 key = strdupAtoW(*p++);
4112 val = msi_dup_property(package, prop);
4113 msi_reg_set_val_str(hkey, key, val);
4114 msi_free(val);
4115 msi_free(key);
4116 msi_free(prop);
4119 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4121 size = deformat_string(package, modpath_fmt, &buffer);
4122 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4123 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4124 msi_free(buffer);
4126 /* FIXME: Write real Estimated Size when we have it */
4127 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4129 buffer = msi_dup_property(package, szProductName);
4130 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4131 msi_free(buffer);
4133 buffer = msi_dup_property(package, cszSourceDir);
4134 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4135 msi_free(buffer);
4137 buffer = msi_dup_property(package, szManufacturer);
4138 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4139 msi_free(buffer);
4141 GetLocalTime(&systime);
4142 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4143 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4145 langid = msi_get_property_int(package, szProductLanguage, 0);
4146 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4148 buffer = msi_dup_property(package, szProductVersion);
4149 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4150 if (buffer)
4152 DWORD verdword = msi_version_str_to_dword(buffer);
4154 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4155 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4156 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4157 msi_free(buffer);
4160 return ERROR_SUCCESS;
4163 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4165 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4166 LPWSTR upgrade_code;
4167 HKEY hkey, props;
4168 HKEY upgrade;
4169 UINT rc;
4171 static const WCHAR szUpgradeCode[] = {
4172 'U','p','g','r','a','d','e','C','o','d','e',0};
4174 /* FIXME: also need to publish if the product is in advertise mode */
4175 if (!msi_check_publish(package))
4176 return ERROR_SUCCESS;
4178 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4179 if (rc != ERROR_SUCCESS)
4180 return rc;
4182 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4183 NULL, &props, TRUE);
4184 if (rc != ERROR_SUCCESS)
4185 goto done;
4187 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4188 msi_free( package->db->localfile );
4189 package->db->localfile = NULL;
4191 rc = msi_publish_install_properties(package, hkey);
4192 if (rc != ERROR_SUCCESS)
4193 goto done;
4195 rc = msi_publish_install_properties(package, props);
4196 if (rc != ERROR_SUCCESS)
4197 goto done;
4199 upgrade_code = msi_dup_property(package, szUpgradeCode);
4200 if (upgrade_code)
4202 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4203 squash_guid(package->ProductCode, squashed_pc);
4204 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4205 RegCloseKey(upgrade);
4206 msi_free(upgrade_code);
4209 done:
4210 RegCloseKey(hkey);
4212 return ERROR_SUCCESS;
4215 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4217 return execute_script(package,INSTALL_SCRIPT);
4220 static UINT msi_unpublish_product(MSIPACKAGE *package)
4222 LPWSTR upgrade;
4223 LPWSTR remove = NULL;
4224 LPWSTR *features = NULL;
4225 BOOL full_uninstall = TRUE;
4226 MSIFEATURE *feature;
4228 static const WCHAR szUpgradeCode[] =
4229 {'U','p','g','r','a','d','e','C','o','d','e',0};
4231 remove = msi_dup_property(package, szRemove);
4232 if (!remove)
4233 return ERROR_SUCCESS;
4235 features = msi_split_string(remove, ',');
4236 if (!features)
4238 msi_free(remove);
4239 ERR("REMOVE feature list is empty!\n");
4240 return ERROR_FUNCTION_FAILED;
4243 if (!lstrcmpW(features[0], szAll))
4244 full_uninstall = TRUE;
4245 else
4247 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4249 if (feature->Action != INSTALLSTATE_ABSENT)
4250 full_uninstall = FALSE;
4254 if (!full_uninstall)
4255 goto done;
4257 MSIREG_DeleteProductKey(package->ProductCode);
4258 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4259 MSIREG_DeleteUninstallKey(package->ProductCode);
4261 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4263 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4264 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4266 else
4268 MSIREG_DeleteUserProductKey(package->ProductCode);
4269 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4272 upgrade = msi_dup_property(package, szUpgradeCode);
4273 if (upgrade)
4275 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4276 msi_free(upgrade);
4279 done:
4280 msi_free(remove);
4281 msi_free(features);
4282 return ERROR_SUCCESS;
4285 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4287 UINT rc;
4289 rc = msi_unpublish_product(package);
4290 if (rc != ERROR_SUCCESS)
4291 return rc;
4293 /* turn off scheduling */
4294 package->script->CurrentlyScripting= FALSE;
4296 /* first do the same as an InstallExecute */
4297 rc = ACTION_InstallExecute(package);
4298 if (rc != ERROR_SUCCESS)
4299 return rc;
4301 /* then handle Commit Actions */
4302 rc = execute_script(package,COMMIT_SCRIPT);
4304 return rc;
4307 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4309 static const WCHAR RunOnce[] = {
4310 'S','o','f','t','w','a','r','e','\\',
4311 'M','i','c','r','o','s','o','f','t','\\',
4312 'W','i','n','d','o','w','s','\\',
4313 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4314 'R','u','n','O','n','c','e',0};
4315 static const WCHAR InstallRunOnce[] = {
4316 'S','o','f','t','w','a','r','e','\\',
4317 'M','i','c','r','o','s','o','f','t','\\',
4318 'W','i','n','d','o','w','s','\\',
4319 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4320 'I','n','s','t','a','l','l','e','r','\\',
4321 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4323 static const WCHAR msiexec_fmt[] = {
4324 '%','s',
4325 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4326 '\"','%','s','\"',0};
4327 static const WCHAR install_fmt[] = {
4328 '/','I',' ','\"','%','s','\"',' ',
4329 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4330 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4331 WCHAR buffer[256], sysdir[MAX_PATH];
4332 HKEY hkey;
4333 WCHAR squished_pc[100];
4335 squash_guid(package->ProductCode,squished_pc);
4337 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4338 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4339 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4340 squished_pc);
4342 msi_reg_set_val_str( hkey, squished_pc, buffer );
4343 RegCloseKey(hkey);
4345 TRACE("Reboot command %s\n",debugstr_w(buffer));
4347 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4348 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4350 msi_reg_set_val_str( hkey, squished_pc, buffer );
4351 RegCloseKey(hkey);
4353 return ERROR_INSTALL_SUSPEND;
4356 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4358 DWORD attrib;
4359 UINT rc;
4362 * We are currently doing what should be done here in the top level Install
4363 * however for Administrative and uninstalls this step will be needed
4365 if (!package->PackagePath)
4366 return ERROR_SUCCESS;
4368 msi_set_sourcedir_props(package, TRUE);
4370 attrib = GetFileAttributesW(package->db->path);
4371 if (attrib == INVALID_FILE_ATTRIBUTES)
4373 LPWSTR prompt;
4374 LPWSTR msg;
4375 DWORD size = 0;
4377 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4378 package->Context, MSICODE_PRODUCT,
4379 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4380 if (rc == ERROR_MORE_DATA)
4382 prompt = msi_alloc(size * sizeof(WCHAR));
4383 MsiSourceListGetInfoW(package->ProductCode, NULL,
4384 package->Context, MSICODE_PRODUCT,
4385 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4387 else
4388 prompt = strdupW(package->db->path);
4390 msg = generate_error_string(package,1302,1,prompt);
4391 while(attrib == INVALID_FILE_ATTRIBUTES)
4393 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4394 if (rc == IDCANCEL)
4396 rc = ERROR_INSTALL_USEREXIT;
4397 break;
4399 attrib = GetFileAttributesW(package->db->path);
4401 msi_free(prompt);
4402 rc = ERROR_SUCCESS;
4404 else
4405 return ERROR_SUCCESS;
4407 return rc;
4410 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4412 HKEY hkey=0;
4413 LPWSTR buffer;
4414 LPWSTR productid;
4415 UINT rc,i;
4417 static const WCHAR szPropKeys[][80] =
4419 {'P','r','o','d','u','c','t','I','D',0},
4420 {'U','S','E','R','N','A','M','E',0},
4421 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4422 {0},
4425 static const WCHAR szRegKeys[][80] =
4427 {'P','r','o','d','u','c','t','I','D',0},
4428 {'R','e','g','O','w','n','e','r',0},
4429 {'R','e','g','C','o','m','p','a','n','y',0},
4430 {0},
4433 if (msi_check_unpublish(package))
4435 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4436 return ERROR_SUCCESS;
4439 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4440 if (!productid)
4441 return ERROR_SUCCESS;
4443 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4444 NULL, &hkey, TRUE);
4445 if (rc != ERROR_SUCCESS)
4446 goto end;
4448 for( i = 0; szPropKeys[i][0]; i++ )
4450 buffer = msi_dup_property( package, szPropKeys[i] );
4451 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4452 msi_free( buffer );
4455 end:
4456 msi_free(productid);
4457 RegCloseKey(hkey);
4459 /* FIXME: call ui_actiondata */
4461 return rc;
4465 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4467 UINT rc;
4469 package->script->InWhatSequence |= SEQUENCE_EXEC;
4470 rc = ACTION_ProcessExecSequence(package,FALSE);
4471 return rc;
4475 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4477 MSIPACKAGE *package = param;
4478 LPCWSTR compgroupid=NULL;
4479 LPCWSTR feature=NULL;
4480 LPCWSTR text = NULL;
4481 LPCWSTR qualifier = NULL;
4482 LPCWSTR component = NULL;
4483 LPWSTR advertise = NULL;
4484 LPWSTR output = NULL;
4485 HKEY hkey;
4486 UINT rc = ERROR_SUCCESS;
4487 MSICOMPONENT *comp;
4488 DWORD sz = 0;
4489 MSIRECORD *uirow;
4491 component = MSI_RecordGetString(rec,3);
4492 comp = get_loaded_component(package,component);
4494 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4495 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4496 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4498 TRACE("Skipping: Component %s not scheduled for install\n",
4499 debugstr_w(component));
4501 return ERROR_SUCCESS;
4504 compgroupid = MSI_RecordGetString(rec,1);
4505 qualifier = MSI_RecordGetString(rec,2);
4507 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4508 if (rc != ERROR_SUCCESS)
4509 goto end;
4511 text = MSI_RecordGetString(rec,4);
4512 feature = MSI_RecordGetString(rec,5);
4514 advertise = create_component_advertise_string(package, comp, feature);
4516 sz = strlenW(advertise);
4518 if (text)
4519 sz += lstrlenW(text);
4521 sz+=3;
4522 sz *= sizeof(WCHAR);
4524 output = msi_alloc_zero(sz);
4525 strcpyW(output,advertise);
4526 msi_free(advertise);
4528 if (text)
4529 strcatW(output,text);
4531 msi_reg_set_val_multi_str( hkey, qualifier, output );
4533 end:
4534 RegCloseKey(hkey);
4535 msi_free(output);
4537 /* the UI chunk */
4538 uirow = MSI_CreateRecord( 2 );
4539 MSI_RecordSetStringW( uirow, 1, compgroupid );
4540 MSI_RecordSetStringW( uirow, 2, qualifier);
4541 ui_actiondata( package, szPublishComponents, uirow);
4542 msiobj_release( &uirow->hdr );
4543 /* FIXME: call ui_progress? */
4545 return rc;
4549 * At present I am ignorning the advertised components part of this and only
4550 * focusing on the qualified component sets
4552 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4554 UINT rc;
4555 MSIQUERY * view;
4556 static const WCHAR ExecSeqQuery[] =
4557 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4558 '`','P','u','b','l','i','s','h',
4559 'C','o','m','p','o','n','e','n','t','`',0};
4561 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4562 if (rc != ERROR_SUCCESS)
4563 return ERROR_SUCCESS;
4565 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4566 msiobj_release(&view->hdr);
4568 return rc;
4571 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4573 MSIPACKAGE *package = param;
4574 MSIRECORD *row;
4575 MSIFILE *file;
4576 SC_HANDLE hscm, service = NULL;
4577 LPCWSTR comp, depends, pass;
4578 LPWSTR name = NULL, disp = NULL;
4579 LPCWSTR load_order, serv_name, key;
4580 DWORD serv_type, start_type;
4581 DWORD err_control;
4583 static const WCHAR query[] =
4584 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4585 '`','C','o','m','p','o','n','e','n','t','`',' ',
4586 'W','H','E','R','E',' ',
4587 '`','C','o','m','p','o','n','e','n','t','`',' ',
4588 '=','\'','%','s','\'',0};
4590 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4591 if (!hscm)
4593 ERR("Failed to open the SC Manager!\n");
4594 goto done;
4597 start_type = MSI_RecordGetInteger(rec, 5);
4598 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4599 goto done;
4601 depends = MSI_RecordGetString(rec, 8);
4602 if (depends && *depends)
4603 FIXME("Dependency list unhandled!\n");
4605 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4606 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4607 serv_type = MSI_RecordGetInteger(rec, 4);
4608 err_control = MSI_RecordGetInteger(rec, 6);
4609 load_order = MSI_RecordGetString(rec, 7);
4610 serv_name = MSI_RecordGetString(rec, 9);
4611 pass = MSI_RecordGetString(rec, 10);
4612 comp = MSI_RecordGetString(rec, 12);
4614 /* fetch the service path */
4615 row = MSI_QueryGetRecord(package->db, query, comp);
4616 if (!row)
4618 ERR("Control query failed!\n");
4619 goto done;
4622 key = MSI_RecordGetString(row, 6);
4624 file = get_loaded_file(package, key);
4625 msiobj_release(&row->hdr);
4626 if (!file)
4628 ERR("Failed to load the service file\n");
4629 goto done;
4632 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4633 start_type, err_control, file->TargetPath,
4634 load_order, NULL, NULL, serv_name, pass);
4635 if (!service)
4637 if (GetLastError() != ERROR_SERVICE_EXISTS)
4638 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4641 done:
4642 CloseServiceHandle(service);
4643 CloseServiceHandle(hscm);
4644 msi_free(name);
4645 msi_free(disp);
4647 return ERROR_SUCCESS;
4650 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4652 UINT rc;
4653 MSIQUERY * view;
4654 static const WCHAR ExecSeqQuery[] =
4655 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4656 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4658 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4659 if (rc != ERROR_SUCCESS)
4660 return ERROR_SUCCESS;
4662 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4663 msiobj_release(&view->hdr);
4665 return rc;
4668 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4669 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4671 LPCWSTR *vector, *temp_vector;
4672 LPWSTR p, q;
4673 DWORD sep_len;
4675 static const WCHAR separator[] = {'[','~',']',0};
4677 *numargs = 0;
4678 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4680 if (!args)
4681 return NULL;
4683 vector = msi_alloc(sizeof(LPWSTR));
4684 if (!vector)
4685 return NULL;
4687 p = args;
4690 (*numargs)++;
4691 vector[*numargs - 1] = p;
4693 if ((q = strstrW(p, separator)))
4695 *q = '\0';
4697 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4698 if (!temp_vector)
4700 msi_free(vector);
4701 return NULL;
4703 vector = temp_vector;
4705 p = q + sep_len;
4707 } while (q);
4709 return vector;
4712 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4714 MSIPACKAGE *package = param;
4715 MSICOMPONENT *comp;
4716 SC_HANDLE scm = NULL, service = NULL;
4717 LPCWSTR *vector = NULL;
4718 LPWSTR name, args;
4719 DWORD event, numargs;
4720 UINT r = ERROR_FUNCTION_FAILED;
4722 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4723 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4724 return ERROR_SUCCESS;
4726 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4727 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4728 event = MSI_RecordGetInteger(rec, 3);
4730 if (!(event & msidbServiceControlEventStart))
4732 r = ERROR_SUCCESS;
4733 goto done;
4736 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4737 if (!scm)
4739 ERR("Failed to open the service control manager\n");
4740 goto done;
4743 service = OpenServiceW(scm, name, SERVICE_START);
4744 if (!service)
4746 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
4747 goto done;
4750 vector = msi_service_args_to_vector(args, &numargs);
4752 if (!StartServiceW(service, numargs, vector) &&
4753 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
4755 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
4756 goto done;
4759 r = ERROR_SUCCESS;
4761 done:
4762 CloseServiceHandle(service);
4763 CloseServiceHandle(scm);
4765 msi_free(name);
4766 msi_free(args);
4767 msi_free(vector);
4768 return r;
4771 static UINT ACTION_StartServices( MSIPACKAGE *package )
4773 UINT rc;
4774 MSIQUERY *view;
4776 static const WCHAR query[] = {
4777 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4778 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4780 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4781 if (rc != ERROR_SUCCESS)
4782 return ERROR_SUCCESS;
4784 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4785 msiobj_release(&view->hdr);
4787 return rc;
4790 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4792 DWORD i, needed, count;
4793 ENUM_SERVICE_STATUSW *dependencies;
4794 SERVICE_STATUS ss;
4795 SC_HANDLE depserv;
4797 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4798 0, &needed, &count))
4799 return TRUE;
4801 if (GetLastError() != ERROR_MORE_DATA)
4802 return FALSE;
4804 dependencies = msi_alloc(needed);
4805 if (!dependencies)
4806 return FALSE;
4808 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4809 needed, &needed, &count))
4810 goto error;
4812 for (i = 0; i < count; i++)
4814 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4815 SERVICE_STOP | SERVICE_QUERY_STATUS);
4816 if (!depserv)
4817 goto error;
4819 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4820 goto error;
4823 return TRUE;
4825 error:
4826 msi_free(dependencies);
4827 return FALSE;
4830 static UINT stop_service( LPCWSTR name )
4832 SC_HANDLE scm = NULL, service = NULL;
4833 SERVICE_STATUS status;
4834 SERVICE_STATUS_PROCESS ssp;
4835 DWORD needed;
4837 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4838 if (!scm)
4840 WARN("Failed to open the SCM: %d\n", GetLastError());
4841 goto done;
4844 service = OpenServiceW(scm, name,
4845 SERVICE_STOP |
4846 SERVICE_QUERY_STATUS |
4847 SERVICE_ENUMERATE_DEPENDENTS);
4848 if (!service)
4850 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
4851 goto done;
4854 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4855 sizeof(SERVICE_STATUS_PROCESS), &needed))
4857 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
4858 goto done;
4861 if (ssp.dwCurrentState == SERVICE_STOPPED)
4862 goto done;
4864 stop_service_dependents(scm, service);
4866 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4867 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4869 done:
4870 CloseServiceHandle(service);
4871 CloseServiceHandle(scm);
4873 return ERROR_SUCCESS;
4876 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
4878 MSIPACKAGE *package = param;
4879 MSICOMPONENT *comp;
4880 LPWSTR name;
4881 DWORD event;
4883 event = MSI_RecordGetInteger( rec, 3 );
4884 if (!(event & msidbServiceControlEventStop))
4885 return ERROR_SUCCESS;
4887 comp = get_loaded_component( package, MSI_RecordGetString( rec, 6 ) );
4888 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4889 return ERROR_SUCCESS;
4891 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
4892 stop_service( name );
4893 msi_free( name );
4895 return ERROR_SUCCESS;
4898 static UINT ACTION_StopServices( MSIPACKAGE *package )
4900 UINT rc;
4901 MSIQUERY *view;
4903 static const WCHAR query[] = {
4904 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4905 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4907 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4908 if (rc != ERROR_SUCCESS)
4909 return ERROR_SUCCESS;
4911 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4912 msiobj_release(&view->hdr);
4914 return rc;
4917 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
4919 MSIPACKAGE *package = param;
4920 MSICOMPONENT *comp;
4921 LPWSTR name = NULL;
4922 DWORD event;
4923 SC_HANDLE scm = NULL, service = NULL;
4925 event = MSI_RecordGetInteger( rec, 3 );
4926 if (!(event & msidbServiceControlEventDelete))
4927 return ERROR_SUCCESS;
4929 comp = get_loaded_component( package, MSI_RecordGetString(rec, 6) );
4930 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4931 return ERROR_SUCCESS;
4933 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
4934 stop_service( name );
4936 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
4937 if (!scm)
4939 WARN("Failed to open the SCM: %d\n", GetLastError());
4940 goto done;
4943 service = OpenServiceW( scm, name, DELETE );
4944 if (!service)
4946 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
4947 goto done;
4950 if (!DeleteService( service ))
4951 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
4953 done:
4954 CloseServiceHandle( service );
4955 CloseServiceHandle( scm );
4956 msi_free( name );
4958 return ERROR_SUCCESS;
4961 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
4963 UINT rc;
4964 MSIQUERY *view;
4966 static const WCHAR query[] = {
4967 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4968 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4970 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4971 if (rc != ERROR_SUCCESS)
4972 return ERROR_SUCCESS;
4974 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
4975 msiobj_release( &view->hdr );
4977 return rc;
4980 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4982 MSIFILE *file;
4984 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4986 if (!lstrcmpW(file->File, filename))
4987 return file;
4990 return NULL;
4993 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4995 MSIPACKAGE *package = param;
4996 LPWSTR driver, driver_path, ptr;
4997 WCHAR outpath[MAX_PATH];
4998 MSIFILE *driver_file, *setup_file;
4999 LPCWSTR desc;
5000 DWORD len, usage;
5001 UINT r = ERROR_SUCCESS;
5003 static const WCHAR driver_fmt[] = {
5004 'D','r','i','v','e','r','=','%','s',0};
5005 static const WCHAR setup_fmt[] = {
5006 'S','e','t','u','p','=','%','s',0};
5007 static const WCHAR usage_fmt[] = {
5008 'F','i','l','e','U','s','a','g','e','=','1',0};
5010 desc = MSI_RecordGetString(rec, 3);
5012 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5013 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5015 if (!driver_file)
5017 ERR("ODBC Driver entry not found!\n");
5018 return ERROR_FUNCTION_FAILED;
5021 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5022 if (setup_file)
5023 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5024 len += lstrlenW(usage_fmt) + 1;
5026 driver = msi_alloc(len * sizeof(WCHAR));
5027 if (!driver)
5028 return ERROR_OUTOFMEMORY;
5030 ptr = driver;
5031 lstrcpyW(ptr, desc);
5032 ptr += lstrlenW(ptr) + 1;
5034 sprintfW(ptr, driver_fmt, driver_file->FileName);
5035 ptr += lstrlenW(ptr) + 1;
5037 if (setup_file)
5039 sprintfW(ptr, setup_fmt, setup_file->FileName);
5040 ptr += lstrlenW(ptr) + 1;
5043 lstrcpyW(ptr, usage_fmt);
5044 ptr += lstrlenW(ptr) + 1;
5045 *ptr = '\0';
5047 driver_path = strdupW(driver_file->TargetPath);
5048 ptr = strrchrW(driver_path, '\\');
5049 if (ptr) *ptr = '\0';
5051 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5052 NULL, ODBC_INSTALL_COMPLETE, &usage))
5054 ERR("Failed to install SQL driver!\n");
5055 r = ERROR_FUNCTION_FAILED;
5058 msi_free(driver);
5059 msi_free(driver_path);
5061 return r;
5064 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5066 MSIPACKAGE *package = param;
5067 LPWSTR translator, translator_path, ptr;
5068 WCHAR outpath[MAX_PATH];
5069 MSIFILE *translator_file, *setup_file;
5070 LPCWSTR desc;
5071 DWORD len, usage;
5072 UINT r = ERROR_SUCCESS;
5074 static const WCHAR translator_fmt[] = {
5075 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5076 static const WCHAR setup_fmt[] = {
5077 'S','e','t','u','p','=','%','s',0};
5079 desc = MSI_RecordGetString(rec, 3);
5081 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5082 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5084 if (!translator_file)
5086 ERR("ODBC Translator entry not found!\n");
5087 return ERROR_FUNCTION_FAILED;
5090 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 1;
5091 if (setup_file)
5092 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5094 translator = msi_alloc(len * sizeof(WCHAR));
5095 if (!translator)
5096 return ERROR_OUTOFMEMORY;
5098 ptr = translator;
5099 lstrcpyW(ptr, desc);
5100 ptr += lstrlenW(ptr) + 1;
5102 sprintfW(ptr, translator_fmt, translator_file->FileName);
5103 ptr += lstrlenW(ptr) + 1;
5105 if (setup_file)
5107 sprintfW(ptr, setup_fmt, setup_file->FileName);
5108 ptr += lstrlenW(ptr) + 1;
5110 *ptr = '\0';
5112 translator_path = strdupW(translator_file->TargetPath);
5113 ptr = strrchrW(translator_path, '\\');
5114 if (ptr) *ptr = '\0';
5116 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5117 NULL, ODBC_INSTALL_COMPLETE, &usage))
5119 ERR("Failed to install SQL translator!\n");
5120 r = ERROR_FUNCTION_FAILED;
5123 msi_free(translator);
5124 msi_free(translator_path);
5126 return r;
5129 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5131 LPWSTR attrs;
5132 LPCWSTR desc, driver;
5133 WORD request = ODBC_ADD_SYS_DSN;
5134 INT registration;
5135 DWORD len;
5136 UINT r = ERROR_SUCCESS;
5138 static const WCHAR attrs_fmt[] = {
5139 'D','S','N','=','%','s',0 };
5141 desc = MSI_RecordGetString(rec, 3);
5142 driver = MSI_RecordGetString(rec, 4);
5143 registration = MSI_RecordGetInteger(rec, 5);
5145 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5146 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5148 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5149 attrs = msi_alloc(len * sizeof(WCHAR));
5150 if (!attrs)
5151 return ERROR_OUTOFMEMORY;
5153 len = sprintfW(attrs, attrs_fmt, desc);
5154 attrs[len + 1] = 0;
5156 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5158 ERR("Failed to install SQL data source!\n");
5159 r = ERROR_FUNCTION_FAILED;
5162 msi_free(attrs);
5164 return r;
5167 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5169 UINT rc;
5170 MSIQUERY *view;
5172 static const WCHAR driver_query[] = {
5173 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5174 'O','D','B','C','D','r','i','v','e','r',0 };
5176 static const WCHAR translator_query[] = {
5177 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5178 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5180 static const WCHAR source_query[] = {
5181 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5182 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5184 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5185 if (rc != ERROR_SUCCESS)
5186 return ERROR_SUCCESS;
5188 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5189 msiobj_release(&view->hdr);
5191 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5192 if (rc != ERROR_SUCCESS)
5193 return ERROR_SUCCESS;
5195 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5196 msiobj_release(&view->hdr);
5198 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5199 if (rc != ERROR_SUCCESS)
5200 return ERROR_SUCCESS;
5202 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5203 msiobj_release(&view->hdr);
5205 return rc;
5208 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
5210 DWORD usage;
5211 LPCWSTR desc;
5213 desc = MSI_RecordGetString( rec, 3 );
5214 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
5216 WARN("Failed to remove ODBC driver\n");
5218 else if (!usage)
5220 FIXME("Usage count reached 0\n");
5223 return ERROR_SUCCESS;
5226 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
5228 DWORD usage;
5229 LPCWSTR desc;
5231 desc = MSI_RecordGetString( rec, 3 );
5232 if (!SQLRemoveTranslatorW( desc, &usage ))
5234 WARN("Failed to remove ODBC translator\n");
5236 else if (!usage)
5238 FIXME("Usage count reached 0\n");
5241 return ERROR_SUCCESS;
5244 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
5246 LPWSTR attrs;
5247 LPCWSTR desc, driver;
5248 WORD request = ODBC_REMOVE_SYS_DSN;
5249 INT registration;
5250 DWORD len;
5252 static const WCHAR attrs_fmt[] = {
5253 'D','S','N','=','%','s',0 };
5255 desc = MSI_RecordGetString( rec, 3 );
5256 driver = MSI_RecordGetString( rec, 4 );
5257 registration = MSI_RecordGetInteger( rec, 5 );
5259 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
5260 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
5262 len = strlenW( attrs_fmt ) + strlenW( desc ) + 1 + 1;
5263 attrs = msi_alloc( len * sizeof(WCHAR) );
5264 if (!attrs)
5265 return ERROR_OUTOFMEMORY;
5267 FIXME("Use ODBCSourceAttribute table\n");
5269 len = sprintfW( attrs, attrs_fmt, desc );
5270 attrs[len + 1] = 0;
5272 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
5274 WARN("Failed to remove ODBC data source\n");
5276 msi_free( attrs );
5278 return ERROR_SUCCESS;
5281 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
5283 UINT rc;
5284 MSIQUERY *view;
5286 static const WCHAR driver_query[] = {
5287 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5288 'O','D','B','C','D','r','i','v','e','r',0 };
5290 static const WCHAR translator_query[] = {
5291 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5292 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5294 static const WCHAR source_query[] = {
5295 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5296 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5298 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
5299 if (rc != ERROR_SUCCESS)
5300 return ERROR_SUCCESS;
5302 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
5303 msiobj_release( &view->hdr );
5305 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
5306 if (rc != ERROR_SUCCESS)
5307 return ERROR_SUCCESS;
5309 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
5310 msiobj_release( &view->hdr );
5312 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
5313 if (rc != ERROR_SUCCESS)
5314 return ERROR_SUCCESS;
5316 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
5317 msiobj_release( &view->hdr );
5319 return rc;
5322 #define ENV_ACT_SETALWAYS 0x1
5323 #define ENV_ACT_SETABSENT 0x2
5324 #define ENV_ACT_REMOVE 0x4
5325 #define ENV_ACT_REMOVEMATCH 0x8
5327 #define ENV_MOD_MACHINE 0x20000000
5328 #define ENV_MOD_APPEND 0x40000000
5329 #define ENV_MOD_PREFIX 0x80000000
5330 #define ENV_MOD_MASK 0xC0000000
5332 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5334 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5336 LPCWSTR cptr = *name;
5338 static const WCHAR prefix[] = {'[','~',']',0};
5339 static const int prefix_len = 3;
5341 *flags = 0;
5342 while (*cptr)
5344 if (*cptr == '=')
5345 *flags |= ENV_ACT_SETALWAYS;
5346 else if (*cptr == '+')
5347 *flags |= ENV_ACT_SETABSENT;
5348 else if (*cptr == '-')
5349 *flags |= ENV_ACT_REMOVE;
5350 else if (*cptr == '!')
5351 *flags |= ENV_ACT_REMOVEMATCH;
5352 else if (*cptr == '*')
5353 *flags |= ENV_MOD_MACHINE;
5354 else
5355 break;
5357 cptr++;
5358 (*name)++;
5361 if (!*cptr)
5363 ERR("Missing environment variable\n");
5364 return ERROR_FUNCTION_FAILED;
5367 if (*value)
5369 LPCWSTR ptr = *value;
5370 if (!strncmpW(ptr, prefix, prefix_len))
5372 if (ptr[prefix_len] == szSemiColon[0])
5374 *flags |= ENV_MOD_APPEND;
5375 *value += lstrlenW(prefix);
5377 else
5379 *value = NULL;
5382 else if (lstrlenW(*value) >= prefix_len)
5384 ptr += lstrlenW(ptr) - prefix_len;
5385 if (!lstrcmpW(ptr, prefix))
5387 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
5389 *flags |= ENV_MOD_PREFIX;
5390 /* the "[~]" will be removed by deformat_string */;
5392 else
5394 *value = NULL;
5400 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5401 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5402 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5403 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5405 ERR("Invalid flags: %08x\n", *flags);
5406 return ERROR_FUNCTION_FAILED;
5409 if (!*flags)
5410 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
5412 return ERROR_SUCCESS;
5415 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5417 MSIPACKAGE *package = param;
5418 LPCWSTR name, value;
5419 LPWSTR data = NULL, newval = NULL;
5420 LPWSTR deformatted = NULL, ptr;
5421 DWORD flags, type, size;
5422 LONG res;
5423 HKEY env = NULL, root;
5424 LPCWSTR environment;
5426 static const WCHAR user_env[] =
5427 {'E','n','v','i','r','o','n','m','e','n','t',0};
5428 static const WCHAR machine_env[] =
5429 {'S','y','s','t','e','m','\\',
5430 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5431 'C','o','n','t','r','o','l','\\',
5432 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5433 'E','n','v','i','r','o','n','m','e','n','t',0};
5435 name = MSI_RecordGetString(rec, 2);
5436 value = MSI_RecordGetString(rec, 3);
5438 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
5440 res = env_set_flags(&name, &value, &flags);
5441 if (res != ERROR_SUCCESS || !value)
5442 goto done;
5444 if (value && !deformat_string(package, value, &deformatted))
5446 res = ERROR_OUTOFMEMORY;
5447 goto done;
5450 value = deformatted;
5452 if (flags & ENV_MOD_MACHINE)
5454 environment = machine_env;
5455 root = HKEY_LOCAL_MACHINE;
5457 else
5459 environment = user_env;
5460 root = HKEY_CURRENT_USER;
5463 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5464 KEY_ALL_ACCESS, NULL, &env, NULL);
5465 if (res != ERROR_SUCCESS)
5466 goto done;
5468 if (flags & ENV_ACT_REMOVE)
5469 FIXME("Not removing environment variable on uninstall!\n");
5471 size = 0;
5472 type = REG_SZ;
5473 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5474 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5475 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5476 goto done;
5478 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
5480 /* Nothing to do. */
5481 if (!value)
5483 res = ERROR_SUCCESS;
5484 goto done;
5487 /* If we are appending but the string was empty, strip ; */
5488 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
5490 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5491 newval = strdupW(value);
5492 if (!newval)
5494 res = ERROR_OUTOFMEMORY;
5495 goto done;
5498 else
5500 /* Contrary to MSDN, +-variable to [~];path works */
5501 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
5503 res = ERROR_SUCCESS;
5504 goto done;
5507 data = msi_alloc(size);
5508 if (!data)
5510 RegCloseKey(env);
5511 return ERROR_OUTOFMEMORY;
5514 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5515 if (res != ERROR_SUCCESS)
5516 goto done;
5518 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5520 res = RegDeleteKeyW(env, name);
5521 goto done;
5524 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
5525 if (flags & ENV_MOD_MASK)
5527 DWORD mod_size;
5528 int multiplier = 0;
5529 if (flags & ENV_MOD_APPEND) multiplier++;
5530 if (flags & ENV_MOD_PREFIX) multiplier++;
5531 mod_size = lstrlenW(value) * multiplier;
5532 size += mod_size * sizeof(WCHAR);
5535 newval = msi_alloc(size);
5536 ptr = newval;
5537 if (!newval)
5539 res = ERROR_OUTOFMEMORY;
5540 goto done;
5543 if (flags & ENV_MOD_PREFIX)
5545 lstrcpyW(newval, value);
5546 ptr = newval + lstrlenW(value);
5549 lstrcpyW(ptr, data);
5551 if (flags & ENV_MOD_APPEND)
5553 lstrcatW(newval, value);
5556 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5557 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5559 done:
5560 if (env) RegCloseKey(env);
5561 msi_free(deformatted);
5562 msi_free(data);
5563 msi_free(newval);
5564 return res;
5567 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5569 UINT rc;
5570 MSIQUERY * view;
5571 static const WCHAR ExecSeqQuery[] =
5572 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5573 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5574 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5575 if (rc != ERROR_SUCCESS)
5576 return ERROR_SUCCESS;
5578 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5579 msiobj_release(&view->hdr);
5581 return rc;
5584 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5586 typedef struct
5588 struct list entry;
5589 LPWSTR sourcename;
5590 LPWSTR destname;
5591 LPWSTR source;
5592 LPWSTR dest;
5593 } FILE_LIST;
5595 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5597 BOOL ret;
5599 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5600 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5602 WARN("Source or dest is directory, not moving\n");
5603 return FALSE;
5606 if (options == msidbMoveFileOptionsMove)
5608 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5609 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5610 if (!ret)
5612 WARN("MoveFile failed: %d\n", GetLastError());
5613 return FALSE;
5616 else
5618 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5619 ret = CopyFileW(source, dest, FALSE);
5620 if (!ret)
5622 WARN("CopyFile failed: %d\n", GetLastError());
5623 return FALSE;
5627 return TRUE;
5630 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5632 LPWSTR path, ptr;
5633 DWORD dirlen, pathlen;
5635 ptr = strrchrW(wildcard, '\\');
5636 dirlen = ptr - wildcard + 1;
5638 pathlen = dirlen + lstrlenW(filename) + 1;
5639 path = msi_alloc(pathlen * sizeof(WCHAR));
5641 lstrcpynW(path, wildcard, dirlen + 1);
5642 lstrcatW(path, filename);
5644 return path;
5647 static void free_file_entry(FILE_LIST *file)
5649 msi_free(file->source);
5650 msi_free(file->dest);
5651 msi_free(file);
5654 static void free_list(FILE_LIST *list)
5656 while (!list_empty(&list->entry))
5658 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5660 list_remove(&file->entry);
5661 free_file_entry(file);
5665 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5667 FILE_LIST *new, *file;
5668 LPWSTR ptr, filename;
5669 DWORD size;
5671 new = msi_alloc_zero(sizeof(FILE_LIST));
5672 if (!new)
5673 return FALSE;
5675 new->source = strdupW(source);
5676 ptr = strrchrW(dest, '\\') + 1;
5677 filename = strrchrW(new->source, '\\') + 1;
5679 new->sourcename = filename;
5681 if (*ptr)
5682 new->destname = ptr;
5683 else
5684 new->destname = new->sourcename;
5686 size = (ptr - dest) + lstrlenW(filename) + 1;
5687 new->dest = msi_alloc(size * sizeof(WCHAR));
5688 if (!new->dest)
5690 free_file_entry(new);
5691 return FALSE;
5694 lstrcpynW(new->dest, dest, ptr - dest + 1);
5695 lstrcatW(new->dest, filename);
5697 if (list_empty(&files->entry))
5699 list_add_head(&files->entry, &new->entry);
5700 return TRUE;
5703 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5705 if (lstrcmpW(source, file->source) < 0)
5707 list_add_before(&file->entry, &new->entry);
5708 return TRUE;
5712 list_add_after(&file->entry, &new->entry);
5713 return TRUE;
5716 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5718 WIN32_FIND_DATAW wfd;
5719 HANDLE hfile;
5720 LPWSTR path;
5721 BOOL res;
5722 FILE_LIST files, *file;
5723 DWORD size;
5725 hfile = FindFirstFileW(source, &wfd);
5726 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5728 list_init(&files.entry);
5730 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5732 if (is_dot_dir(wfd.cFileName)) continue;
5734 path = wildcard_to_file(source, wfd.cFileName);
5735 if (!path)
5737 res = FALSE;
5738 goto done;
5741 add_wildcard(&files, path, dest);
5742 msi_free(path);
5745 /* no files match the wildcard */
5746 if (list_empty(&files.entry))
5747 goto done;
5749 /* only the first wildcard match gets renamed to dest */
5750 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5751 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5752 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5753 if (!file->dest)
5755 res = FALSE;
5756 goto done;
5759 /* file->dest may be shorter after the reallocation, so add a NULL
5760 * terminator. This is needed for the call to strrchrW, as there will no
5761 * longer be a NULL terminator within the bounds of the allocation in this case.
5763 file->dest[size - 1] = '\0';
5764 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5766 while (!list_empty(&files.entry))
5768 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5770 msi_move_file(file->source, file->dest, options);
5772 list_remove(&file->entry);
5773 free_file_entry(file);
5776 res = TRUE;
5778 done:
5779 free_list(&files);
5780 FindClose(hfile);
5781 return res;
5784 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5786 MSIPACKAGE *package = param;
5787 MSICOMPONENT *comp;
5788 LPCWSTR sourcename;
5789 LPWSTR destname = NULL;
5790 LPWSTR sourcedir = NULL, destdir = NULL;
5791 LPWSTR source = NULL, dest = NULL;
5792 int options;
5793 DWORD size;
5794 BOOL ret, wildcards;
5796 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5797 if (!comp || !comp->Enabled ||
5798 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5800 TRACE("Component not set for install, not moving file\n");
5801 return ERROR_SUCCESS;
5804 sourcename = MSI_RecordGetString(rec, 3);
5805 options = MSI_RecordGetInteger(rec, 7);
5807 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5808 if (!sourcedir)
5809 goto done;
5811 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5812 if (!destdir)
5813 goto done;
5815 if (!sourcename)
5817 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5818 goto done;
5820 source = strdupW(sourcedir);
5821 if (!source)
5822 goto done;
5824 else
5826 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5827 source = msi_alloc(size * sizeof(WCHAR));
5828 if (!source)
5829 goto done;
5831 lstrcpyW(source, sourcedir);
5832 if (source[lstrlenW(source) - 1] != '\\')
5833 lstrcatW(source, szBackSlash);
5834 lstrcatW(source, sourcename);
5837 wildcards = strchrW(source, '*') || strchrW(source, '?');
5839 if (MSI_RecordIsNull(rec, 4))
5841 if (!wildcards)
5843 destname = strdupW(sourcename);
5844 if (!destname)
5845 goto done;
5848 else
5850 destname = strdupW(MSI_RecordGetString(rec, 4));
5851 if (destname)
5852 reduce_to_longfilename(destname);
5855 size = 0;
5856 if (destname)
5857 size = lstrlenW(destname);
5859 size += lstrlenW(destdir) + 2;
5860 dest = msi_alloc(size * sizeof(WCHAR));
5861 if (!dest)
5862 goto done;
5864 lstrcpyW(dest, destdir);
5865 if (dest[lstrlenW(dest) - 1] != '\\')
5866 lstrcatW(dest, szBackSlash);
5868 if (destname)
5869 lstrcatW(dest, destname);
5871 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5873 ret = CreateDirectoryW(destdir, NULL);
5874 if (!ret)
5876 WARN("CreateDirectory failed: %d\n", GetLastError());
5877 return ERROR_SUCCESS;
5881 if (!wildcards)
5882 msi_move_file(source, dest, options);
5883 else
5884 move_files_wildcard(source, dest, options);
5886 done:
5887 msi_free(sourcedir);
5888 msi_free(destdir);
5889 msi_free(destname);
5890 msi_free(source);
5891 msi_free(dest);
5893 return ERROR_SUCCESS;
5896 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5898 UINT rc;
5899 MSIQUERY *view;
5901 static const WCHAR ExecSeqQuery[] =
5902 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5903 '`','M','o','v','e','F','i','l','e','`',0};
5905 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5906 if (rc != ERROR_SUCCESS)
5907 return ERROR_SUCCESS;
5909 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5910 msiobj_release(&view->hdr);
5912 return rc;
5915 typedef struct tagMSIASSEMBLY
5917 struct list entry;
5918 MSICOMPONENT *component;
5919 MSIFEATURE *feature;
5920 MSIFILE *file;
5921 LPWSTR manifest;
5922 LPWSTR application;
5923 DWORD attributes;
5924 BOOL installed;
5925 } MSIASSEMBLY;
5927 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5928 DWORD dwReserved);
5929 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5930 LPVOID pvReserved, HMODULE *phModDll);
5932 static BOOL init_functionpointers(void)
5934 HRESULT hr;
5935 HMODULE hfusion;
5936 HMODULE hmscoree;
5938 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5940 hmscoree = LoadLibraryA("mscoree.dll");
5941 if (!hmscoree)
5943 WARN("mscoree.dll not available\n");
5944 return FALSE;
5947 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5948 if (!pLoadLibraryShim)
5950 WARN("LoadLibraryShim not available\n");
5951 FreeLibrary(hmscoree);
5952 return FALSE;
5955 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5956 if (FAILED(hr))
5958 WARN("fusion.dll not available\n");
5959 FreeLibrary(hmscoree);
5960 return FALSE;
5963 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5965 FreeLibrary(hmscoree);
5966 return TRUE;
5969 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5970 LPWSTR path)
5972 IAssemblyCache *cache;
5973 HRESULT hr;
5974 UINT r = ERROR_FUNCTION_FAILED;
5976 TRACE("installing assembly: %s\n", debugstr_w(path));
5978 if (assembly->feature)
5979 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5981 if (assembly->manifest)
5982 FIXME("Manifest unhandled\n");
5984 if (assembly->application)
5986 FIXME("Assembly should be privately installed\n");
5987 return ERROR_SUCCESS;
5990 if (assembly->attributes == msidbAssemblyAttributesWin32)
5992 FIXME("Win32 assemblies not handled\n");
5993 return ERROR_SUCCESS;
5996 hr = pCreateAssemblyCache(&cache, 0);
5997 if (FAILED(hr))
5998 goto done;
6000 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
6001 if (FAILED(hr))
6002 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
6004 r = ERROR_SUCCESS;
6006 done:
6007 IAssemblyCache_Release(cache);
6008 return r;
6011 typedef struct tagASSEMBLY_LIST
6013 MSIPACKAGE *package;
6014 IAssemblyCache *cache;
6015 struct list *assemblies;
6016 } ASSEMBLY_LIST;
6018 typedef struct tagASSEMBLY_NAME
6020 LPWSTR name;
6021 LPWSTR version;
6022 LPWSTR culture;
6023 LPWSTR pubkeytoken;
6024 } ASSEMBLY_NAME;
6026 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
6028 ASSEMBLY_NAME *asmname = param;
6029 LPCWSTR name = MSI_RecordGetString(rec, 2);
6030 LPWSTR val = msi_dup_record_field(rec, 3);
6032 static const WCHAR Name[] = {'N','a','m','e',0};
6033 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
6034 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
6035 static const WCHAR PublicKeyToken[] = {
6036 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
6038 if (!strcmpiW(name, Name))
6039 asmname->name = val;
6040 else if (!strcmpiW(name, Version))
6041 asmname->version = val;
6042 else if (!strcmpiW(name, Culture))
6043 asmname->culture = val;
6044 else if (!strcmpiW(name, PublicKeyToken))
6045 asmname->pubkeytoken = val;
6046 else
6047 msi_free(val);
6049 return ERROR_SUCCESS;
6052 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
6054 if (!*str)
6056 *size = lstrlenW(append) + 1;
6057 *str = msi_alloc((*size) * sizeof(WCHAR));
6058 lstrcpyW(*str, append);
6059 return;
6062 (*size) += lstrlenW(append);
6063 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
6064 lstrcatW(*str, append);
6067 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
6068 MSICOMPONENT *comp)
6070 ASSEMBLY_INFO asminfo;
6071 ASSEMBLY_NAME name;
6072 MSIQUERY *view;
6073 LPWSTR disp;
6074 DWORD size;
6075 BOOL found;
6076 UINT r;
6078 static const WCHAR separator[] = {',',' ',0};
6079 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
6080 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
6081 static const WCHAR PublicKeyToken[] = {
6082 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
6083 static const WCHAR query[] = {
6084 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6085 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
6086 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
6087 '=','\'','%','s','\'',0};
6089 disp = NULL;
6090 found = FALSE;
6091 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
6092 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
6094 r = MSI_OpenQuery(db, &view, query, comp->Component);
6095 if (r != ERROR_SUCCESS)
6096 return ERROR_SUCCESS;
6098 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
6099 msiobj_release(&view->hdr);
6101 if (!name.name)
6103 ERR("No assembly name specified!\n");
6104 goto done;
6107 append_str(&disp, &size, name.name);
6109 if (name.version)
6111 append_str(&disp, &size, separator);
6112 append_str(&disp, &size, Version);
6113 append_str(&disp, &size, name.version);
6116 if (name.culture)
6118 append_str(&disp, &size, separator);
6119 append_str(&disp, &size, Culture);
6120 append_str(&disp, &size, name.culture);
6123 if (name.pubkeytoken)
6125 append_str(&disp, &size, separator);
6126 append_str(&disp, &size, PublicKeyToken);
6127 append_str(&disp, &size, name.pubkeytoken);
6130 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6131 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6132 disp, &asminfo);
6133 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6135 done:
6136 msi_free(disp);
6137 msi_free(name.name);
6138 msi_free(name.version);
6139 msi_free(name.culture);
6140 msi_free(name.pubkeytoken);
6142 return found;
6145 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6147 ASSEMBLY_LIST *list = param;
6148 MSIASSEMBLY *assembly;
6150 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6151 if (!assembly)
6152 return ERROR_OUTOFMEMORY;
6154 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6156 if (!assembly->component || !assembly->component->Enabled ||
6157 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6159 TRACE("Component not set for install, not publishing assembly\n");
6160 msi_free(assembly);
6161 return ERROR_SUCCESS;
6164 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6165 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6167 if (!assembly->file)
6169 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6170 return ERROR_FUNCTION_FAILED;
6173 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6174 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6175 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6177 if (assembly->application)
6179 WCHAR version[24];
6180 DWORD size = sizeof(version)/sizeof(WCHAR);
6182 /* FIXME: we should probably check the manifest file here */
6184 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6185 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6187 assembly->installed = TRUE;
6190 else
6191 assembly->installed = check_assembly_installed(list->package->db,
6192 list->cache,
6193 assembly->component);
6195 list_add_head(list->assemblies, &assembly->entry);
6196 return ERROR_SUCCESS;
6199 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6201 IAssemblyCache *cache = NULL;
6202 ASSEMBLY_LIST list;
6203 MSIQUERY *view;
6204 HRESULT hr;
6205 UINT r;
6207 static const WCHAR query[] =
6208 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6209 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6211 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6212 if (r != ERROR_SUCCESS)
6213 return ERROR_SUCCESS;
6215 hr = pCreateAssemblyCache(&cache, 0);
6216 if (FAILED(hr))
6217 return ERROR_FUNCTION_FAILED;
6219 list.package = package;
6220 list.cache = cache;
6221 list.assemblies = assemblies;
6223 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6224 msiobj_release(&view->hdr);
6226 IAssemblyCache_Release(cache);
6228 return r;
6231 static void free_assemblies(struct list *assemblies)
6233 struct list *item, *cursor;
6235 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6237 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6239 list_remove(&assembly->entry);
6240 msi_free(assembly->application);
6241 msi_free(assembly->manifest);
6242 msi_free(assembly);
6246 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6248 MSIASSEMBLY *assembly;
6250 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6252 if (!lstrcmpW(assembly->file->File, file))
6254 *out = assembly;
6255 return TRUE;
6259 return FALSE;
6262 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6263 LPWSTR *path, DWORD *attrs, PVOID user)
6265 MSIASSEMBLY *assembly;
6266 WCHAR temppath[MAX_PATH];
6267 struct list *assemblies = user;
6268 UINT r;
6270 if (!find_assembly(assemblies, file, &assembly))
6271 return FALSE;
6273 GetTempPathW(MAX_PATH, temppath);
6274 PathAddBackslashW(temppath);
6275 lstrcatW(temppath, assembly->file->FileName);
6277 if (action == MSICABEXTRACT_BEGINEXTRACT)
6279 if (assembly->installed)
6280 return FALSE;
6282 *path = strdupW(temppath);
6283 *attrs = assembly->file->Attributes;
6285 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6287 assembly->installed = TRUE;
6289 r = install_assembly(package, assembly, temppath);
6290 if (r != ERROR_SUCCESS)
6291 ERR("Failed to install assembly\n");
6294 return TRUE;
6297 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6299 UINT r;
6300 struct list assemblies = LIST_INIT(assemblies);
6301 MSIASSEMBLY *assembly;
6302 MSIMEDIAINFO *mi;
6304 if (!init_functionpointers() || !pCreateAssemblyCache)
6305 return ERROR_FUNCTION_FAILED;
6307 r = load_assemblies(package, &assemblies);
6308 if (r != ERROR_SUCCESS)
6309 goto done;
6311 if (list_empty(&assemblies))
6312 goto done;
6314 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6315 if (!mi)
6317 r = ERROR_OUTOFMEMORY;
6318 goto done;
6321 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6323 if (assembly->installed && !mi->is_continuous)
6324 continue;
6326 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6327 (assembly->file->IsCompressed && !mi->is_extracted))
6329 MSICABDATA data;
6331 r = ready_media(package, assembly->file, mi);
6332 if (r != ERROR_SUCCESS)
6334 ERR("Failed to ready media\n");
6335 break;
6338 data.mi = mi;
6339 data.package = package;
6340 data.cb = installassembly_cb;
6341 data.user = &assemblies;
6343 if (assembly->file->IsCompressed &&
6344 !msi_cabextract(package, mi, &data))
6346 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6347 r = ERROR_FUNCTION_FAILED;
6348 break;
6352 if (!assembly->file->IsCompressed)
6354 LPWSTR source = resolve_file_source(package, assembly->file);
6356 r = install_assembly(package, assembly, source);
6357 if (r != ERROR_SUCCESS)
6358 ERR("Failed to install assembly\n");
6360 msi_free(source);
6363 /* FIXME: write Installer assembly reg values */
6366 done:
6367 free_assemblies(&assemblies);
6368 return r;
6371 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6373 LPWSTR key, template, id;
6374 UINT r = ERROR_SUCCESS;
6376 id = msi_dup_property( package, szProductID );
6377 if (id)
6379 msi_free( id );
6380 return ERROR_SUCCESS;
6382 template = msi_dup_property( package, szPIDTemplate );
6383 key = msi_dup_property( package, szPIDKEY );
6385 if (key && template)
6387 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6388 r = MSI_SetPropertyW( package, szProductID, key );
6390 msi_free( template );
6391 msi_free( key );
6392 return r;
6395 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6397 TRACE("\n");
6398 package->need_reboot = 1;
6399 return ERROR_SUCCESS;
6402 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6404 TRACE("%p\n", package);
6405 return ERROR_SUCCESS;
6408 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6410 FIXME("%p\n", package);
6411 return ERROR_SUCCESS;
6414 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6416 FIXME("%p\n", package);
6417 return ERROR_SUCCESS;
6420 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6421 LPCSTR action, LPCWSTR table )
6423 static const WCHAR query[] = {
6424 'S','E','L','E','C','T',' ','*',' ',
6425 'F','R','O','M',' ','`','%','s','`',0 };
6426 MSIQUERY *view = NULL;
6427 DWORD count = 0;
6428 UINT r;
6430 r = MSI_OpenQuery( package->db, &view, query, table );
6431 if (r == ERROR_SUCCESS)
6433 r = MSI_IterateRecords(view, &count, NULL, package);
6434 msiobj_release(&view->hdr);
6437 if (count)
6438 FIXME("%s -> %u ignored %s table values\n",
6439 action, count, debugstr_w(table));
6441 return ERROR_SUCCESS;
6444 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6446 static const WCHAR table[] =
6447 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6448 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6451 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6453 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6454 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6457 static UINT ACTION_BindImage( MSIPACKAGE *package )
6459 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6460 return msi_unimplemented_action_stub( package, "BindImage", table );
6463 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6465 static const WCHAR table[] = {
6466 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6467 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6470 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6472 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6473 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6476 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6478 static const WCHAR table[] = {
6479 'E','n','v','i','r','o','n','m','e','n','t',0 };
6480 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6483 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6485 static const WCHAR table[] = {
6486 'M','s','i','A','s','s','e','m','b','l','y',0 };
6487 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6490 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6492 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6493 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6496 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6498 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6499 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6502 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6504 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6505 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6508 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6510 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6511 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6514 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6516 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6517 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6520 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6522 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6523 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6526 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6528 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6529 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6532 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6534 static const WCHAR table[] = { 'D','i','r','e','c','t','o','r','y',0 };
6535 return msi_unimplemented_action_stub( package, "SetODBCFolders", table );
6538 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6540 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6541 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6544 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6546 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6547 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6550 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6552 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6553 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6556 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6558 static const WCHAR table[] = { 'M','I','M','E',0 };
6559 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6562 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6564 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6565 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6568 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6570 static const struct
6572 const WCHAR *action;
6573 UINT (*handler)(MSIPACKAGE *);
6575 StandardActions[] =
6577 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6578 { szAppSearch, ACTION_AppSearch },
6579 { szBindImage, ACTION_BindImage },
6580 { szCCPSearch, ACTION_CCPSearch },
6581 { szCostFinalize, ACTION_CostFinalize },
6582 { szCostInitialize, ACTION_CostInitialize },
6583 { szCreateFolders, ACTION_CreateFolders },
6584 { szCreateShortcuts, ACTION_CreateShortcuts },
6585 { szDeleteServices, ACTION_DeleteServices },
6586 { szDisableRollback, ACTION_DisableRollback },
6587 { szDuplicateFiles, ACTION_DuplicateFiles },
6588 { szExecuteAction, ACTION_ExecuteAction },
6589 { szFileCost, ACTION_FileCost },
6590 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6591 { szForceReboot, ACTION_ForceReboot },
6592 { szInstallAdminPackage, ACTION_InstallAdminPackage },
6593 { szInstallExecute, ACTION_InstallExecute },
6594 { szInstallExecuteAgain, ACTION_InstallExecute },
6595 { szInstallFiles, ACTION_InstallFiles},
6596 { szInstallFinalize, ACTION_InstallFinalize },
6597 { szInstallInitialize, ACTION_InstallInitialize },
6598 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6599 { szInstallValidate, ACTION_InstallValidate },
6600 { szIsolateComponents, ACTION_IsolateComponents },
6601 { szLaunchConditions, ACTION_LaunchConditions },
6602 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6603 { szMoveFiles, ACTION_MoveFiles },
6604 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6605 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6606 { szInstallODBC, ACTION_InstallODBC },
6607 { szInstallServices, ACTION_InstallServices },
6608 { szPatchFiles, ACTION_PatchFiles },
6609 { szProcessComponents, ACTION_ProcessComponents },
6610 { szPublishComponents, ACTION_PublishComponents },
6611 { szPublishFeatures, ACTION_PublishFeatures },
6612 { szPublishProduct, ACTION_PublishProduct },
6613 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6614 { szRegisterComPlus, ACTION_RegisterComPlus},
6615 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6616 { szRegisterFonts, ACTION_RegisterFonts },
6617 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6618 { szRegisterProduct, ACTION_RegisterProduct },
6619 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6620 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6621 { szRegisterUser, ACTION_RegisterUser },
6622 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6623 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6624 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6625 { szRemoveFiles, ACTION_RemoveFiles },
6626 { szRemoveFolders, ACTION_RemoveFolders },
6627 { szRemoveIniValues, ACTION_RemoveIniValues },
6628 { szRemoveODBC, ACTION_RemoveODBC },
6629 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6630 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6631 { szResolveSource, ACTION_ResolveSource },
6632 { szRMCCPSearch, ACTION_RMCCPSearch },
6633 { szScheduleReboot, ACTION_ScheduleReboot },
6634 { szSelfRegModules, ACTION_SelfRegModules },
6635 { szSelfUnregModules, ACTION_SelfUnregModules },
6636 { szSetODBCFolders, ACTION_SetODBCFolders },
6637 { szStartServices, ACTION_StartServices },
6638 { szStopServices, ACTION_StopServices },
6639 { szUnpublishComponents, ACTION_UnpublishComponents },
6640 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6641 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6642 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6643 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6644 { szUnregisterFonts, ACTION_UnregisterFonts },
6645 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6646 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6647 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6648 { szValidateProductID, ACTION_ValidateProductID },
6649 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6650 { szWriteIniValues, ACTION_WriteIniValues },
6651 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6652 { NULL, NULL },
6655 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6656 UINT* rc, BOOL force )
6658 BOOL ret = FALSE;
6659 BOOL run = force;
6660 int i;
6662 if (!run && !package->script->CurrentlyScripting)
6663 run = TRUE;
6665 if (!run)
6667 if (strcmpW(action,szInstallFinalize) == 0 ||
6668 strcmpW(action,szInstallExecute) == 0 ||
6669 strcmpW(action,szInstallExecuteAgain) == 0)
6670 run = TRUE;
6673 i = 0;
6674 while (StandardActions[i].action != NULL)
6676 if (strcmpW(StandardActions[i].action, action)==0)
6678 if (!run)
6680 ui_actioninfo(package, action, TRUE, 0);
6681 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6682 ui_actioninfo(package, action, FALSE, *rc);
6684 else
6686 ui_actionstart(package, action);
6687 if (StandardActions[i].handler)
6689 *rc = StandardActions[i].handler(package);
6691 else
6693 FIXME("unhandled standard action %s\n",debugstr_w(action));
6694 *rc = ERROR_SUCCESS;
6697 ret = TRUE;
6698 break;
6700 i++;
6702 return ret;
6705 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
6707 UINT rc = ERROR_SUCCESS;
6708 BOOL handled;
6710 TRACE("Performing action (%s)\n", debugstr_w(action));
6712 handled = ACTION_HandleStandardAction(package, action, &rc, force);
6714 if (!handled)
6715 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
6717 if (!handled)
6719 WARN("unhandled msi action %s\n", debugstr_w(action));
6720 rc = ERROR_FUNCTION_NOT_CALLED;
6723 return rc;
6726 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
6728 UINT rc = ERROR_SUCCESS;
6729 BOOL handled = FALSE;
6731 TRACE("Performing action (%s)\n", debugstr_w(action));
6733 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
6735 if (!handled)
6736 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
6738 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
6739 handled = TRUE;
6741 if (!handled)
6743 WARN("unhandled msi action %s\n", debugstr_w(action));
6744 rc = ERROR_FUNCTION_NOT_CALLED;
6747 return rc;
6750 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
6752 UINT rc = ERROR_SUCCESS;
6753 MSIRECORD *row;
6755 static const WCHAR ExecSeqQuery[] =
6756 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6757 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
6758 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
6759 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
6760 static const WCHAR UISeqQuery[] =
6761 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6762 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
6763 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
6764 ' ', '=',' ','%','i',0};
6766 if (needs_ui_sequence(package))
6767 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
6768 else
6769 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
6771 if (row)
6773 LPCWSTR action, cond;
6775 TRACE("Running the actions\n");
6777 /* check conditions */
6778 cond = MSI_RecordGetString(row, 2);
6780 /* this is a hack to skip errors in the condition code */
6781 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
6783 msiobj_release(&row->hdr);
6784 return ERROR_SUCCESS;
6787 action = MSI_RecordGetString(row, 1);
6788 if (!action)
6790 ERR("failed to fetch action\n");
6791 msiobj_release(&row->hdr);
6792 return ERROR_FUNCTION_FAILED;
6795 if (needs_ui_sequence(package))
6796 rc = ACTION_PerformUIAction(package, action, -1);
6797 else
6798 rc = ACTION_PerformAction(package, action, -1, FALSE);
6800 msiobj_release(&row->hdr);
6803 return rc;
6806 /****************************************************
6807 * TOP level entry points
6808 *****************************************************/
6810 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
6811 LPCWSTR szCommandLine )
6813 UINT rc;
6814 BOOL ui_exists;
6816 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
6817 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
6819 MSI_SetPropertyW(package, szAction, szInstall);
6821 package->script->InWhatSequence = SEQUENCE_INSTALL;
6823 if (szPackagePath)
6825 LPWSTR p, dir;
6826 LPCWSTR file;
6828 dir = strdupW(szPackagePath);
6829 p = strrchrW(dir, '\\');
6830 if (p)
6832 *(++p) = 0;
6833 file = szPackagePath + (p - dir);
6835 else
6837 msi_free(dir);
6838 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
6839 GetCurrentDirectoryW(MAX_PATH, dir);
6840 lstrcatW(dir, szBackSlash);
6841 file = szPackagePath;
6844 msi_free( package->PackagePath );
6845 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
6846 if (!package->PackagePath)
6848 msi_free(dir);
6849 return ERROR_OUTOFMEMORY;
6852 lstrcpyW(package->PackagePath, dir);
6853 lstrcatW(package->PackagePath, file);
6854 msi_free(dir);
6856 msi_set_sourcedir_props(package, FALSE);
6859 msi_parse_command_line( package, szCommandLine, FALSE );
6861 msi_apply_transforms( package );
6862 msi_apply_patches( package );
6864 if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
6866 TRACE("setting reinstall property\n");
6867 MSI_SetPropertyW( package, szReinstall, szAll );
6870 /* properties may have been added by a transform */
6871 msi_clone_properties( package );
6872 msi_set_context( package );
6874 if (needs_ui_sequence( package))
6876 package->script->InWhatSequence |= SEQUENCE_UI;
6877 rc = ACTION_ProcessUISequence(package);
6878 ui_exists = ui_sequence_exists(package);
6879 if (rc == ERROR_SUCCESS || !ui_exists)
6881 package->script->InWhatSequence |= SEQUENCE_EXEC;
6882 rc = ACTION_ProcessExecSequence(package, ui_exists);
6885 else
6886 rc = ACTION_ProcessExecSequence(package, FALSE);
6888 package->script->CurrentlyScripting = FALSE;
6890 /* process the ending type action */
6891 if (rc == ERROR_SUCCESS)
6892 ACTION_PerformActionSequence(package, -1);
6893 else if (rc == ERROR_INSTALL_USEREXIT)
6894 ACTION_PerformActionSequence(package, -2);
6895 else if (rc == ERROR_INSTALL_SUSPEND)
6896 ACTION_PerformActionSequence(package, -4);
6897 else /* failed */
6898 ACTION_PerformActionSequence(package, -3);
6900 /* finish up running custom actions */
6901 ACTION_FinishCustomActions(package);
6903 if (rc == ERROR_SUCCESS && package->need_reboot)
6904 return ERROR_SUCCESS_REBOOT_REQUIRED;
6906 return rc;