msi: Don't use a temporary directory for local assemblies.
[wine.git] / dlls / msi / action.c
blob5328193a8489c06a3b6af422cfe51afffcd7280f
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 "shlwapi.h"
39 #include "wine/unicode.h"
40 #include "winver.h"
42 #define REG_PROGRESS_VALUE 13200
43 #define COMPONENT_PROGRESS_VALUE 24000
45 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 * consts and values used
50 static const WCHAR c_colon[] = {'C',':','\\',0};
52 static const WCHAR szCreateFolders[] =
53 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
54 static const WCHAR szCostFinalize[] =
55 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
56 static const WCHAR szWriteRegistryValues[] =
57 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
58 static const WCHAR szCostInitialize[] =
59 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szFileCost[] =
61 {'F','i','l','e','C','o','s','t',0};
62 static const WCHAR szInstallInitialize[] =
63 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
64 static const WCHAR szInstallValidate[] =
65 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
66 static const WCHAR szLaunchConditions[] =
67 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
68 static const WCHAR szProcessComponents[] =
69 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
70 static const WCHAR szRegisterTypeLibraries[] =
71 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
72 static const WCHAR szCreateShortcuts[] =
73 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
74 static const WCHAR szPublishProduct[] =
75 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
76 static const WCHAR szWriteIniValues[] =
77 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
78 static const WCHAR szSelfRegModules[] =
79 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
80 static const WCHAR szPublishFeatures[] =
81 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
82 static const WCHAR szRegisterProduct[] =
83 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
84 static const WCHAR szInstallExecute[] =
85 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
86 static const WCHAR szInstallExecuteAgain[] =
87 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
88 static const WCHAR szInstallFinalize[] =
89 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
90 static const WCHAR szForceReboot[] =
91 {'F','o','r','c','e','R','e','b','o','o','t',0};
92 static const WCHAR szResolveSource[] =
93 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
94 static const WCHAR szAllocateRegistrySpace[] =
95 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
96 static const WCHAR szBindImage[] =
97 {'B','i','n','d','I','m','a','g','e',0};
98 static const WCHAR szDeleteServices[] =
99 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
100 static const WCHAR szDisableRollback[] =
101 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
102 static const WCHAR szExecuteAction[] =
103 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
104 static const WCHAR szInstallAdminPackage[] =
105 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
106 static const WCHAR szInstallSFPCatalogFile[] =
107 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
108 static const WCHAR szIsolateComponents[] =
109 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
110 static const WCHAR szMigrateFeatureStates[] =
111 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
112 static const WCHAR szMsiUnpublishAssemblies[] =
113 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
114 static const WCHAR szInstallODBC[] =
115 {'I','n','s','t','a','l','l','O','D','B','C',0};
116 static const WCHAR szInstallServices[] =
117 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
118 static const WCHAR szPatchFiles[] =
119 {'P','a','t','c','h','F','i','l','e','s',0};
120 static const WCHAR szPublishComponents[] =
121 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
122 static const WCHAR szRegisterComPlus[] =
123 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
124 static const WCHAR szRegisterUser[] =
125 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
126 static const WCHAR szRemoveEnvironmentStrings[] =
127 {'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};
128 static const WCHAR szRemoveExistingProducts[] =
129 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
130 static const WCHAR szRemoveFolders[] =
131 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
132 static const WCHAR szRemoveIniValues[] =
133 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
134 static const WCHAR szRemoveODBC[] =
135 {'R','e','m','o','v','e','O','D','B','C',0};
136 static const WCHAR szRemoveRegistryValues[] =
137 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
138 static const WCHAR szRemoveShortcuts[] =
139 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
140 static const WCHAR szRMCCPSearch[] =
141 {'R','M','C','C','P','S','e','a','r','c','h',0};
142 static const WCHAR szScheduleReboot[] =
143 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
144 static const WCHAR szSelfUnregModules[] =
145 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
146 static const WCHAR szSetODBCFolders[] =
147 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
148 static const WCHAR szStartServices[] =
149 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
150 static const WCHAR szStopServices[] =
151 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
152 static const WCHAR szUnpublishComponents[] =
153 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
154 static const WCHAR szUnpublishFeatures[] =
155 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
156 static const WCHAR szUnregisterComPlus[] =
157 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
158 static const WCHAR szUnregisterTypeLibraries[] =
159 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
160 static const WCHAR szValidateProductID[] =
161 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
162 static const WCHAR szWriteEnvironmentStrings[] =
163 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
165 /********************************************************
166 * helper functions
167 ********************************************************/
169 void msi_feature_set_state( MSIPACKAGE *package, MSIFEATURE *feature, INSTALLSTATE state )
171 if (!package->ProductCode)
173 feature->ActionRequest = state;
174 feature->Action = state;
176 else if (state == INSTALLSTATE_ABSENT)
178 switch (feature->Installed)
180 case INSTALLSTATE_ABSENT:
181 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
182 feature->Action = INSTALLSTATE_UNKNOWN;
183 break;
184 default:
185 feature->ActionRequest = state;
186 feature->Action = state;
189 else if (state == INSTALLSTATE_SOURCE)
191 switch (feature->Installed)
193 case INSTALLSTATE_ABSENT:
194 case INSTALLSTATE_SOURCE:
195 feature->ActionRequest = state;
196 feature->Action = state;
197 break;
198 case INSTALLSTATE_LOCAL:
199 feature->ActionRequest = INSTALLSTATE_LOCAL;
200 feature->Action = INSTALLSTATE_LOCAL;
201 break;
202 default:
203 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
204 feature->Action = INSTALLSTATE_UNKNOWN;
207 else
209 feature->ActionRequest = state;
210 feature->Action = state;
214 void msi_component_set_state( MSIPACKAGE *package, MSICOMPONENT *comp, INSTALLSTATE state )
216 if (!package->ProductCode)
218 comp->ActionRequest = state;
219 comp->Action = state;
221 else if (state == INSTALLSTATE_ABSENT)
223 switch (comp->Installed)
225 case INSTALLSTATE_LOCAL:
226 case INSTALLSTATE_SOURCE:
227 case INSTALLSTATE_DEFAULT:
228 comp->ActionRequest = state;
229 comp->Action = state;
230 break;
231 default:
232 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
233 comp->Action = INSTALLSTATE_UNKNOWN;
236 else if (state == INSTALLSTATE_SOURCE)
238 if (comp->Installed == INSTALLSTATE_ABSENT ||
239 (comp->Installed == INSTALLSTATE_SOURCE && comp->hasLocalFeature))
241 comp->ActionRequest = state;
242 comp->Action = state;
244 else
246 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
247 comp->Action = INSTALLSTATE_UNKNOWN;
250 else
252 comp->ActionRequest = state;
253 comp->Action = state;
257 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
259 static const WCHAR Query_t[] =
260 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
261 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
262 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
263 ' ','\'','%','s','\'',0};
264 MSIRECORD * row;
266 row = MSI_QueryGetRecord( package->db, Query_t, action );
267 if (!row)
268 return;
269 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
270 msiobj_release(&row->hdr);
273 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
274 UINT rc)
276 MSIRECORD * row;
277 static const WCHAR template_s[]=
278 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
279 '%','s', '.',0};
280 static const WCHAR template_e[]=
281 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
282 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
283 '%','i','.',0};
284 static const WCHAR format[] =
285 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
286 WCHAR message[1024];
287 WCHAR timet[0x100];
289 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
290 if (start)
291 sprintfW(message,template_s,timet,action);
292 else
293 sprintfW(message,template_e,timet,action,rc);
295 row = MSI_CreateRecord(1);
296 MSI_RecordSetStringW(row,1,message);
298 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
299 msiobj_release(&row->hdr);
302 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
303 BOOL preserve_case )
305 LPCWSTR ptr,ptr2;
306 BOOL quote;
307 DWORD len;
308 LPWSTR prop = NULL, val = NULL;
310 if (!szCommandLine)
311 return ERROR_SUCCESS;
313 ptr = szCommandLine;
315 while (*ptr)
317 if (*ptr==' ')
319 ptr++;
320 continue;
323 TRACE("Looking at %s\n",debugstr_w(ptr));
325 ptr2 = strchrW(ptr,'=');
326 if (!ptr2)
328 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
329 break;
332 quote = FALSE;
334 len = ptr2-ptr;
335 prop = msi_alloc((len+1)*sizeof(WCHAR));
336 memcpy(prop,ptr,len*sizeof(WCHAR));
337 prop[len]=0;
339 if (!preserve_case)
340 struprW(prop);
342 ptr2++;
344 len = 0;
345 ptr = ptr2;
346 while (*ptr && (quote || (!quote && *ptr!=' ')))
348 if (*ptr == '"')
349 quote = !quote;
350 ptr++;
351 len++;
354 if (*ptr2=='"')
356 ptr2++;
357 len -= 2;
359 val = msi_alloc((len+1)*sizeof(WCHAR));
360 memcpy(val,ptr2,len*sizeof(WCHAR));
361 val[len] = 0;
363 if (lstrlenW(prop) > 0)
365 UINT r = msi_set_property( package->db, prop, val );
367 TRACE("Found commandline property (%s) = (%s)\n",
368 debugstr_w(prop), debugstr_w(val));
370 if (r == ERROR_SUCCESS && !strcmpW( prop, cszSourceDir ))
371 msi_reset_folders( package, TRUE );
373 msi_free(val);
374 msi_free(prop);
377 return ERROR_SUCCESS;
381 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
383 LPCWSTR pc;
384 LPWSTR p, *ret = NULL;
385 UINT count = 0;
387 if (!str)
388 return ret;
390 /* count the number of substrings */
391 for ( pc = str, count = 0; pc; count++ )
393 pc = strchrW( pc, sep );
394 if (pc)
395 pc++;
398 /* allocate space for an array of substring pointers and the substrings */
399 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
400 (lstrlenW(str)+1) * sizeof(WCHAR) );
401 if (!ret)
402 return ret;
404 /* copy the string and set the pointers */
405 p = (LPWSTR) &ret[count+1];
406 lstrcpyW( p, str );
407 for( count = 0; (ret[count] = p); count++ )
409 p = strchrW( p, sep );
410 if (p)
411 *p++ = 0;
414 return ret;
417 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
419 static const WCHAR szSystemLanguageID[] =
420 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
422 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
423 UINT ret = ERROR_FUNCTION_FAILED;
425 prod_code = msi_dup_property( package->db, szProductCode );
426 patch_product = msi_get_suminfo_product( patch );
428 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
430 if ( strstrW( patch_product, prod_code ) )
432 MSISUMMARYINFO *si;
433 const WCHAR *p;
435 si = MSI_GetSummaryInformationW( patch, 0 );
436 if (!si)
438 ERR("no summary information!\n");
439 goto end;
442 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
443 if (!template)
445 ERR("no template property!\n");
446 msiobj_release( &si->hdr );
447 goto end;
450 if (!template[0])
452 ret = ERROR_SUCCESS;
453 msiobj_release( &si->hdr );
454 goto end;
457 langid = msi_dup_property( package->db, szSystemLanguageID );
458 if (!langid)
460 msiobj_release( &si->hdr );
461 goto end;
464 p = strchrW( template, ';' );
465 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
467 TRACE("applicable transform\n");
468 ret = ERROR_SUCCESS;
471 /* FIXME: check platform */
473 msiobj_release( &si->hdr );
476 end:
477 msi_free( patch_product );
478 msi_free( prod_code );
479 msi_free( template );
480 msi_free( langid );
482 return ret;
485 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
486 MSIDATABASE *patch_db, LPCWSTR name )
488 UINT ret = ERROR_FUNCTION_FAILED;
489 IStorage *stg = NULL;
490 HRESULT r;
492 TRACE("%p %s\n", package, debugstr_w(name) );
494 if (*name++ != ':')
496 ERR("expected a colon in %s\n", debugstr_w(name));
497 return ERROR_FUNCTION_FAILED;
500 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
501 if (SUCCEEDED(r))
503 ret = msi_check_transform_applicable( package, stg );
504 if (ret == ERROR_SUCCESS)
505 msi_table_apply_transform( package->db, stg );
506 else
507 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
508 IStorage_Release( stg );
510 else
511 ERR("failed to open substorage %s\n", debugstr_w(name));
513 return ERROR_SUCCESS;
516 UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
518 LPWSTR guid_list, *guids, product_code;
519 UINT i, ret = ERROR_FUNCTION_FAILED;
521 product_code = msi_dup_property( package->db, szProductCode );
522 if (!product_code)
524 /* FIXME: the property ProductCode should be written into the DB somewhere */
525 ERR("no product code to check\n");
526 return ERROR_SUCCESS;
529 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
530 guids = msi_split_string( guid_list, ';' );
531 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
533 if (!strcmpW( guids[i], product_code ))
534 ret = ERROR_SUCCESS;
536 msi_free( guids );
537 msi_free( guid_list );
538 msi_free( product_code );
540 return ret;
543 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
545 MSIQUERY *view;
546 MSIRECORD *rec = NULL;
547 LPWSTR patch;
548 LPCWSTR prop;
549 UINT r;
551 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
552 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
553 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
554 '`','S','o','u','r','c','e','`',' ','I','S',' ',
555 'N','O','T',' ','N','U','L','L',0};
557 r = MSI_DatabaseOpenViewW(package->db, query, &view);
558 if (r != ERROR_SUCCESS)
559 return r;
561 r = MSI_ViewExecute(view, 0);
562 if (r != ERROR_SUCCESS)
563 goto done;
565 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
567 prop = MSI_RecordGetString(rec, 1);
568 patch = msi_dup_property(package->db, szPatch);
569 msi_set_property(package->db, prop, patch);
570 msi_free(patch);
573 done:
574 if (rec) msiobj_release(&rec->hdr);
575 msiobj_release(&view->hdr);
577 return r;
580 UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
582 MSIPATCHINFO *pi;
583 UINT r = ERROR_SUCCESS;
584 WCHAR *p;
586 pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
587 if (!pi)
588 return ERROR_OUTOFMEMORY;
590 pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
591 if (!pi->patchcode)
593 msi_free( pi );
594 return ERROR_OUTOFMEMORY;
597 p = pi->patchcode;
598 if (*p != '{')
600 msi_free( pi->patchcode );
601 msi_free( pi );
602 return ERROR_PATCH_PACKAGE_INVALID;
605 p = strchrW( p + 1, '}' );
606 if (!p)
608 msi_free( pi->patchcode );
609 msi_free( pi );
610 return ERROR_PATCH_PACKAGE_INVALID;
613 if (p[1])
615 FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
616 p[1] = 0;
619 TRACE("patch code %s\n", debugstr_w(pi->patchcode));
621 pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
622 if (!pi->transforms)
624 msi_free( pi->patchcode );
625 msi_free( pi );
626 return ERROR_OUTOFMEMORY;
629 *patch = pi;
630 return r;
633 UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
635 UINT i, r = ERROR_SUCCESS;
636 WCHAR **substorage;
638 /* apply substorage transforms */
639 substorage = msi_split_string( patch->transforms, ';' );
640 for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
641 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
643 msi_free( substorage );
644 if (r != ERROR_SUCCESS)
645 return r;
647 msi_set_media_source_prop( package );
650 * There might be a CAB file in the patch package,
651 * so append it to the list of storages to search for streams.
653 append_storage_to_db( package->db, patch_db->storage );
655 patch->state = MSIPATCHSTATE_APPLIED;
656 list_add_tail( &package->patches, &patch->entry );
657 return ERROR_SUCCESS;
660 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
662 static const WCHAR dotmsp[] = {'.','m','s','p',0};
663 MSIDATABASE *patch_db = NULL;
664 WCHAR localfile[MAX_PATH];
665 MSISUMMARYINFO *si;
666 MSIPATCHINFO *patch = NULL;
667 UINT r = ERROR_SUCCESS;
669 TRACE("%p %s\n", package, debugstr_w( file ) );
671 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
672 if ( r != ERROR_SUCCESS )
674 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
675 return r;
678 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
679 if (!si)
681 msiobj_release( &patch_db->hdr );
682 return ERROR_FUNCTION_FAILED;
685 r = msi_check_patch_applicable( package, si );
686 if (r != ERROR_SUCCESS)
688 TRACE("patch not applicable\n");
689 r = ERROR_SUCCESS;
690 goto done;
693 r = msi_parse_patch_summary( si, &patch );
694 if ( r != ERROR_SUCCESS )
695 goto done;
697 r = msi_get_local_package_name( localfile, dotmsp );
698 if ( r != ERROR_SUCCESS )
699 goto done;
701 TRACE("copying to local package %s\n", debugstr_w(localfile));
703 if (!CopyFileW( file, localfile, FALSE ))
705 ERR("Unable to copy package (%s -> %s) (error %u)\n",
706 debugstr_w(file), debugstr_w(localfile), GetLastError());
707 r = GetLastError();
708 goto done;
710 patch->localfile = strdupW( localfile );
712 r = msi_apply_patch_db( package, patch_db, patch );
713 if ( r != ERROR_SUCCESS )
714 WARN("patch failed to apply %u\n", r);
716 done:
717 msiobj_release( &si->hdr );
718 msiobj_release( &patch_db->hdr );
719 if (patch && r != ERROR_SUCCESS)
721 if (patch->localfile)
722 DeleteFileW( patch->localfile );
724 msi_free( patch->patchcode );
725 msi_free( patch->transforms );
726 msi_free( patch->localfile );
727 msi_free( patch );
729 return r;
732 /* get the PATCH property, and apply all the patches it specifies */
733 static UINT msi_apply_patches( MSIPACKAGE *package )
735 LPWSTR patch_list, *patches;
736 UINT i, r = ERROR_SUCCESS;
738 patch_list = msi_dup_property( package->db, szPatch );
740 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
742 patches = msi_split_string( patch_list, ';' );
743 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
744 r = msi_apply_patch_package( package, patches[i] );
746 msi_free( patches );
747 msi_free( patch_list );
749 return r;
752 static UINT msi_apply_transforms( MSIPACKAGE *package )
754 static const WCHAR szTransforms[] = {
755 'T','R','A','N','S','F','O','R','M','S',0 };
756 LPWSTR xform_list, *xforms;
757 UINT i, r = ERROR_SUCCESS;
759 xform_list = msi_dup_property( package->db, szTransforms );
760 xforms = msi_split_string( xform_list, ';' );
762 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
764 if (xforms[i][0] == ':')
765 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
766 else
767 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
770 msi_free( xforms );
771 msi_free( xform_list );
773 return r;
776 static BOOL ui_sequence_exists( MSIPACKAGE *package )
778 MSIQUERY *view;
779 UINT rc;
781 static const WCHAR ExecSeqQuery [] =
782 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
783 '`','I','n','s','t','a','l','l',
784 'U','I','S','e','q','u','e','n','c','e','`',
785 ' ','W','H','E','R','E',' ',
786 '`','S','e','q','u','e','n','c','e','`',' ',
787 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
788 '`','S','e','q','u','e','n','c','e','`',0};
790 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
791 if (rc == ERROR_SUCCESS)
793 msiobj_release(&view->hdr);
794 return TRUE;
797 return FALSE;
800 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
802 LPWSTR source, check;
804 if (msi_get_property_int( package->db, szInstalled, 0 ))
806 HKEY hkey;
808 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
809 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
810 RegCloseKey( hkey );
812 else
814 LPWSTR p, db;
815 DWORD len;
817 db = msi_dup_property( package->db, szOriginalDatabase );
818 if (!db)
819 return ERROR_OUTOFMEMORY;
821 p = strrchrW( db, '\\' );
822 if (!p)
824 p = strrchrW( db, '/' );
825 if (!p)
827 msi_free(db);
828 return ERROR_SUCCESS;
832 len = p - db + 2;
833 source = msi_alloc( len * sizeof(WCHAR) );
834 lstrcpynW( source, db, len );
835 msi_free( db );
838 check = msi_dup_property( package->db, cszSourceDir );
839 if (!check || replace)
841 UINT r = msi_set_property( package->db, cszSourceDir, source );
842 if (r == ERROR_SUCCESS)
843 msi_reset_folders( package, TRUE );
845 msi_free( check );
847 check = msi_dup_property( package->db, cszSOURCEDIR );
848 if (!check || replace)
849 msi_set_property( package->db, cszSOURCEDIR, source );
851 msi_free( check );
852 msi_free( source );
854 return ERROR_SUCCESS;
857 static BOOL needs_ui_sequence(MSIPACKAGE *package)
859 INT level = msi_get_property_int(package->db, szUILevel, 0);
860 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
863 UINT msi_set_context(MSIPACKAGE *package)
865 int num;
867 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
869 num = msi_get_property_int(package->db, szAllUsers, 0);
870 if (num == 1 || num == 2)
871 package->Context = MSIINSTALLCONTEXT_MACHINE;
873 return ERROR_SUCCESS;
876 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
878 UINT rc;
879 LPCWSTR cond, action;
880 MSIPACKAGE *package = param;
882 action = MSI_RecordGetString(row,1);
883 if (!action)
885 ERR("Error is retrieving action name\n");
886 return ERROR_FUNCTION_FAILED;
889 /* check conditions */
890 cond = MSI_RecordGetString(row,2);
892 /* this is a hack to skip errors in the condition code */
893 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
895 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
896 return ERROR_SUCCESS;
899 if (needs_ui_sequence(package))
900 rc = ACTION_PerformUIAction(package, action, -1);
901 else
902 rc = ACTION_PerformAction(package, action, -1);
904 msi_dialog_check_messages( NULL );
906 if (package->CurrentInstallState != ERROR_SUCCESS)
907 rc = package->CurrentInstallState;
909 if (rc == ERROR_FUNCTION_NOT_CALLED)
910 rc = ERROR_SUCCESS;
912 if (rc != ERROR_SUCCESS)
913 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
915 return rc;
918 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
920 MSIQUERY * view;
921 UINT r;
922 static const WCHAR query[] =
923 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
924 '`','%','s','`',
925 ' ','W','H','E','R','E',' ',
926 '`','S','e','q','u','e','n','c','e','`',' ',
927 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
928 '`','S','e','q','u','e','n','c','e','`',0};
930 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
932 r = MSI_OpenQuery( package->db, &view, query, szTable );
933 if (r == ERROR_SUCCESS)
935 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
936 msiobj_release(&view->hdr);
939 return r;
942 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
944 MSIQUERY * view;
945 UINT rc;
946 static const WCHAR ExecSeqQuery[] =
947 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
948 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
949 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
950 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
951 'O','R','D','E','R',' ', 'B','Y',' ',
952 '`','S','e','q','u','e','n','c','e','`',0 };
953 static const WCHAR IVQuery[] =
954 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
955 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
956 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
957 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
958 ' ','\'', 'I','n','s','t','a','l','l',
959 'V','a','l','i','d','a','t','e','\'', 0};
960 INT seq = 0;
962 if (package->script->ExecuteSequenceRun)
964 TRACE("Execute Sequence already Run\n");
965 return ERROR_SUCCESS;
968 package->script->ExecuteSequenceRun = TRUE;
970 /* get the sequence number */
971 if (UIran)
973 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
974 if( !row )
975 return ERROR_FUNCTION_FAILED;
976 seq = MSI_RecordGetInteger(row,1);
977 msiobj_release(&row->hdr);
980 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
981 if (rc == ERROR_SUCCESS)
983 TRACE("Running the actions\n");
985 msi_set_property(package->db, cszSourceDir, NULL);
987 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
988 msiobj_release(&view->hdr);
991 return rc;
994 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
996 MSIQUERY * view;
997 UINT rc;
998 static const WCHAR ExecSeqQuery [] =
999 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1000 '`','I','n','s','t','a','l','l',
1001 'U','I','S','e','q','u','e','n','c','e','`',
1002 ' ','W','H','E','R','E',' ',
1003 '`','S','e','q','u','e','n','c','e','`',' ',
1004 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1005 '`','S','e','q','u','e','n','c','e','`',0};
1007 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1008 if (rc == ERROR_SUCCESS)
1010 TRACE("Running the actions\n");
1012 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1013 msiobj_release(&view->hdr);
1016 return rc;
1019 /********************************************************
1020 * ACTION helper functions and functions that perform the actions
1021 *******************************************************/
1022 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1023 UINT* rc, UINT script, BOOL force )
1025 BOOL ret=FALSE;
1026 UINT arc;
1028 arc = ACTION_CustomAction(package, action, script, force);
1030 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1032 *rc = arc;
1033 ret = TRUE;
1035 return ret;
1039 * Actual Action Handlers
1042 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1044 MSIPACKAGE *package = param;
1045 LPCWSTR dir, component;
1046 LPWSTR full_path;
1047 MSIRECORD *uirow;
1048 MSIFOLDER *folder;
1049 MSICOMPONENT *comp;
1051 component = MSI_RecordGetString(row, 2);
1052 if (!component)
1053 return ERROR_SUCCESS;
1055 comp = get_loaded_component(package, component);
1056 if (!comp)
1057 return ERROR_SUCCESS;
1059 if (!comp->Enabled)
1061 TRACE("component is disabled\n");
1062 return ERROR_SUCCESS;
1065 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1067 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1068 comp->Action = comp->Installed;
1069 return ERROR_SUCCESS;
1071 comp->Action = INSTALLSTATE_LOCAL;
1073 dir = MSI_RecordGetString(row,1);
1074 if (!dir)
1076 ERR("Unable to get folder id\n");
1077 return ERROR_SUCCESS;
1080 uirow = MSI_CreateRecord(1);
1081 MSI_RecordSetStringW(uirow, 1, dir);
1082 ui_actiondata(package, szCreateFolders, uirow);
1083 msiobj_release(&uirow->hdr);
1085 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1086 if (!full_path)
1088 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1089 return ERROR_SUCCESS;
1092 TRACE("Folder is %s\n",debugstr_w(full_path));
1094 if (folder->State == 0)
1095 create_full_pathW(full_path);
1097 folder->State = 3;
1099 msi_free(full_path);
1100 return ERROR_SUCCESS;
1103 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1105 static const WCHAR ExecSeqQuery[] =
1106 {'S','E','L','E','C','T',' ',
1107 '`','D','i','r','e','c','t','o','r','y','_','`',
1108 ' ','F','R','O','M',' ',
1109 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1110 UINT rc;
1111 MSIQUERY *view;
1113 /* create all the empty folders specified in the CreateFolder table */
1114 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1115 if (rc != ERROR_SUCCESS)
1116 return ERROR_SUCCESS;
1118 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1119 msiobj_release(&view->hdr);
1121 return rc;
1124 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1126 MSIPACKAGE *package = param;
1127 LPCWSTR dir, component;
1128 LPWSTR full_path;
1129 MSIRECORD *uirow;
1130 MSIFOLDER *folder;
1131 MSICOMPONENT *comp;
1133 component = MSI_RecordGetString(row, 2);
1134 if (!component)
1135 return ERROR_SUCCESS;
1137 comp = get_loaded_component(package, component);
1138 if (!comp)
1139 return ERROR_SUCCESS;
1141 if (!comp->Enabled)
1143 TRACE("component is disabled\n");
1144 return ERROR_SUCCESS;
1147 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1149 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1150 comp->Action = comp->Installed;
1151 return ERROR_SUCCESS;
1153 comp->Action = INSTALLSTATE_ABSENT;
1155 dir = MSI_RecordGetString( row, 1 );
1156 if (!dir)
1158 ERR("Unable to get folder id\n");
1159 return ERROR_SUCCESS;
1162 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1163 if (!full_path)
1165 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1166 return ERROR_SUCCESS;
1169 TRACE("folder is %s\n", debugstr_w(full_path));
1171 uirow = MSI_CreateRecord( 1 );
1172 MSI_RecordSetStringW( uirow, 1, dir );
1173 ui_actiondata( package, szRemoveFolders, uirow );
1174 msiobj_release( &uirow->hdr );
1176 RemoveDirectoryW( full_path );
1177 folder->State = 0;
1179 msi_free( full_path );
1180 return ERROR_SUCCESS;
1183 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1185 static const WCHAR query[] =
1186 {'S','E','L','E','C','T',' ', '`','D','i','r','e','c','t','o','r','y','_','`',
1187 ' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1189 MSIQUERY *view;
1190 UINT rc;
1192 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1193 if (rc != ERROR_SUCCESS)
1194 return ERROR_SUCCESS;
1196 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1197 msiobj_release( &view->hdr );
1199 return rc;
1202 static UINT load_component( MSIRECORD *row, LPVOID param )
1204 MSIPACKAGE *package = param;
1205 MSICOMPONENT *comp;
1207 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1208 if (!comp)
1209 return ERROR_FUNCTION_FAILED;
1211 list_add_tail( &package->components, &comp->entry );
1213 /* fill in the data */
1214 comp->Component = msi_dup_record_field( row, 1 );
1216 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1218 comp->ComponentId = msi_dup_record_field( row, 2 );
1219 comp->Directory = msi_dup_record_field( row, 3 );
1220 comp->Attributes = MSI_RecordGetInteger(row,4);
1221 comp->Condition = msi_dup_record_field( row, 5 );
1222 comp->KeyPath = msi_dup_record_field( row, 6 );
1224 comp->Installed = INSTALLSTATE_UNKNOWN;
1225 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1227 comp->assembly = load_assembly( package, comp );
1228 return ERROR_SUCCESS;
1231 static UINT load_all_components( MSIPACKAGE *package )
1233 static const WCHAR query[] = {
1234 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1235 '`','C','o','m','p','o','n','e','n','t','`',0 };
1236 MSIQUERY *view;
1237 UINT r;
1239 if (!list_empty(&package->components))
1240 return ERROR_SUCCESS;
1242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1243 if (r != ERROR_SUCCESS)
1244 return r;
1246 r = MSI_IterateRecords(view, NULL, load_component, package);
1247 msiobj_release(&view->hdr);
1248 return r;
1251 typedef struct {
1252 MSIPACKAGE *package;
1253 MSIFEATURE *feature;
1254 } _ilfs;
1256 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1258 ComponentList *cl;
1260 cl = msi_alloc( sizeof (*cl) );
1261 if ( !cl )
1262 return ERROR_NOT_ENOUGH_MEMORY;
1263 cl->component = comp;
1264 list_add_tail( &feature->Components, &cl->entry );
1266 return ERROR_SUCCESS;
1269 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1271 FeatureList *fl;
1273 fl = msi_alloc( sizeof(*fl) );
1274 if ( !fl )
1275 return ERROR_NOT_ENOUGH_MEMORY;
1276 fl->feature = child;
1277 list_add_tail( &parent->Children, &fl->entry );
1279 return ERROR_SUCCESS;
1282 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1284 _ilfs* ilfs = param;
1285 LPCWSTR component;
1286 MSICOMPONENT *comp;
1288 component = MSI_RecordGetString(row,1);
1290 /* check to see if the component is already loaded */
1291 comp = get_loaded_component( ilfs->package, component );
1292 if (!comp)
1294 ERR("unknown component %s\n", debugstr_w(component));
1295 return ERROR_FUNCTION_FAILED;
1298 add_feature_component( ilfs->feature, comp );
1299 comp->Enabled = TRUE;
1301 return ERROR_SUCCESS;
1304 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1306 MSIFEATURE *feature;
1308 if ( !name )
1309 return NULL;
1311 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1313 if ( !strcmpW( feature->Feature, name ) )
1314 return feature;
1317 return NULL;
1320 static UINT load_feature(MSIRECORD * row, LPVOID param)
1322 MSIPACKAGE* package = param;
1323 MSIFEATURE* feature;
1324 static const WCHAR Query1[] =
1325 {'S','E','L','E','C','T',' ',
1326 '`','C','o','m','p','o','n','e','n','t','_','`',
1327 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1328 'C','o','m','p','o','n','e','n','t','s','`',' ',
1329 'W','H','E','R','E',' ',
1330 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1331 MSIQUERY * view;
1332 UINT rc;
1333 _ilfs ilfs;
1335 /* fill in the data */
1337 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1338 if (!feature)
1339 return ERROR_NOT_ENOUGH_MEMORY;
1341 list_init( &feature->Children );
1342 list_init( &feature->Components );
1344 feature->Feature = msi_dup_record_field( row, 1 );
1346 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1348 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1349 feature->Title = msi_dup_record_field( row, 3 );
1350 feature->Description = msi_dup_record_field( row, 4 );
1352 if (!MSI_RecordIsNull(row,5))
1353 feature->Display = MSI_RecordGetInteger(row,5);
1355 feature->Level= MSI_RecordGetInteger(row,6);
1356 feature->Directory = msi_dup_record_field( row, 7 );
1357 feature->Attributes = MSI_RecordGetInteger(row,8);
1359 feature->Installed = INSTALLSTATE_UNKNOWN;
1360 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1362 list_add_tail( &package->features, &feature->entry );
1364 /* load feature components */
1366 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1367 if (rc != ERROR_SUCCESS)
1368 return ERROR_SUCCESS;
1370 ilfs.package = package;
1371 ilfs.feature = feature;
1373 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1374 msiobj_release(&view->hdr);
1376 return ERROR_SUCCESS;
1379 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1381 MSIPACKAGE* package = param;
1382 MSIFEATURE *parent, *child;
1384 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1385 if (!child)
1386 return ERROR_FUNCTION_FAILED;
1388 if (!child->Feature_Parent)
1389 return ERROR_SUCCESS;
1391 parent = find_feature_by_name( package, child->Feature_Parent );
1392 if (!parent)
1393 return ERROR_FUNCTION_FAILED;
1395 add_feature_child( parent, child );
1396 return ERROR_SUCCESS;
1399 static UINT load_all_features( MSIPACKAGE *package )
1401 static const WCHAR query[] = {
1402 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1403 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1404 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1405 MSIQUERY *view;
1406 UINT r;
1408 if (!list_empty(&package->features))
1409 return ERROR_SUCCESS;
1411 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1412 if (r != ERROR_SUCCESS)
1413 return r;
1415 r = MSI_IterateRecords( view, NULL, load_feature, package );
1416 if (r != ERROR_SUCCESS)
1417 return r;
1419 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1420 msiobj_release( &view->hdr );
1422 return r;
1425 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1427 if (!p)
1428 return p;
1429 p = strchrW(p, ch);
1430 if (!p)
1431 return p;
1432 *p = 0;
1433 return p+1;
1436 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1438 static const WCHAR query[] = {
1439 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1440 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1441 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1442 MSIQUERY *view = NULL;
1443 MSIRECORD *row = NULL;
1444 UINT r;
1446 TRACE("%s\n", debugstr_w(file->File));
1448 r = MSI_OpenQuery(package->db, &view, query, file->File);
1449 if (r != ERROR_SUCCESS)
1450 goto done;
1452 r = MSI_ViewExecute(view, NULL);
1453 if (r != ERROR_SUCCESS)
1454 goto done;
1456 r = MSI_ViewFetch(view, &row);
1457 if (r != ERROR_SUCCESS)
1458 goto done;
1460 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1461 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1462 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1463 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1464 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1466 done:
1467 if (view) msiobj_release(&view->hdr);
1468 if (row) msiobj_release(&row->hdr);
1469 return r;
1472 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1474 MSIRECORD *row;
1475 static const WCHAR query[] = {
1476 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1477 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1478 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1480 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1481 if (!row)
1483 WARN("query failed\n");
1484 return ERROR_FUNCTION_FAILED;
1487 file->disk_id = MSI_RecordGetInteger( row, 1 );
1488 msiobj_release( &row->hdr );
1489 return ERROR_SUCCESS;
1492 static UINT load_file(MSIRECORD *row, LPVOID param)
1494 MSIPACKAGE* package = param;
1495 LPCWSTR component;
1496 MSIFILE *file;
1498 /* fill in the data */
1500 file = msi_alloc_zero( sizeof (MSIFILE) );
1501 if (!file)
1502 return ERROR_NOT_ENOUGH_MEMORY;
1504 file->File = msi_dup_record_field( row, 1 );
1506 component = MSI_RecordGetString( row, 2 );
1507 file->Component = get_loaded_component( package, component );
1509 if (!file->Component)
1511 WARN("Component not found: %s\n", debugstr_w(component));
1512 msi_free(file->File);
1513 msi_free(file);
1514 return ERROR_SUCCESS;
1517 file->FileName = msi_dup_record_field( row, 3 );
1518 reduce_to_longfilename( file->FileName );
1520 file->ShortName = msi_dup_record_field( row, 3 );
1521 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1523 file->FileSize = MSI_RecordGetInteger( row, 4 );
1524 file->Version = msi_dup_record_field( row, 5 );
1525 file->Language = msi_dup_record_field( row, 6 );
1526 file->Attributes = MSI_RecordGetInteger( row, 7 );
1527 file->Sequence = MSI_RecordGetInteger( row, 8 );
1529 file->state = msifs_invalid;
1531 /* if the compressed bits are not set in the file attributes,
1532 * then read the information from the package word count property
1534 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1536 file->IsCompressed = FALSE;
1538 else if (file->Attributes &
1539 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1541 file->IsCompressed = TRUE;
1543 else if (file->Attributes & msidbFileAttributesNoncompressed)
1545 file->IsCompressed = FALSE;
1547 else
1549 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1552 load_file_hash(package, file);
1553 load_file_disk_id(package, file);
1555 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1557 list_add_tail( &package->files, &file->entry );
1559 return ERROR_SUCCESS;
1562 static UINT load_all_files(MSIPACKAGE *package)
1564 MSIQUERY * view;
1565 UINT rc;
1566 static const WCHAR Query[] =
1567 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1568 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1569 '`','S','e','q','u','e','n','c','e','`', 0};
1571 if (!list_empty(&package->files))
1572 return ERROR_SUCCESS;
1574 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1575 if (rc != ERROR_SUCCESS)
1576 return ERROR_SUCCESS;
1578 rc = MSI_IterateRecords(view, NULL, load_file, package);
1579 msiobj_release(&view->hdr);
1581 return ERROR_SUCCESS;
1584 static UINT load_folder( MSIRECORD *row, LPVOID param )
1586 MSIPACKAGE *package = param;
1587 static WCHAR szEmpty[] = { 0 };
1588 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1589 MSIFOLDER *folder;
1591 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1592 if (!folder)
1593 return ERROR_NOT_ENOUGH_MEMORY;
1595 folder->Directory = msi_dup_record_field( row, 1 );
1597 TRACE("%s\n", debugstr_w(folder->Directory));
1599 p = msi_dup_record_field(row, 3);
1601 /* split src and target dir */
1602 tgt_short = p;
1603 src_short = folder_split_path( p, ':' );
1605 /* split the long and short paths */
1606 tgt_long = folder_split_path( tgt_short, '|' );
1607 src_long = folder_split_path( src_short, '|' );
1609 /* check for no-op dirs */
1610 if (tgt_short && !strcmpW( szDot, tgt_short ))
1611 tgt_short = szEmpty;
1612 if (src_short && !strcmpW( szDot, src_short ))
1613 src_short = szEmpty;
1615 if (!tgt_long)
1616 tgt_long = tgt_short;
1618 if (!src_short) {
1619 src_short = tgt_short;
1620 src_long = tgt_long;
1623 if (!src_long)
1624 src_long = src_short;
1626 /* FIXME: use the target short path too */
1627 folder->TargetDefault = strdupW(tgt_long);
1628 folder->SourceShortPath = strdupW(src_short);
1629 folder->SourceLongPath = strdupW(src_long);
1630 msi_free(p);
1632 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1633 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1634 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1636 folder->Parent = msi_dup_record_field( row, 2 );
1638 folder->Property = msi_dup_property( package->db, folder->Directory );
1640 list_add_tail( &package->folders, &folder->entry );
1642 TRACE("returning %p\n", folder);
1644 return ERROR_SUCCESS;
1647 static UINT load_all_folders( MSIPACKAGE *package )
1649 static const WCHAR query[] = {
1650 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1651 '`','D','i','r','e','c','t','o','r','y','`',0 };
1652 MSIQUERY *view;
1653 UINT r;
1655 if (!list_empty(&package->folders))
1656 return ERROR_SUCCESS;
1658 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1659 if (r != ERROR_SUCCESS)
1660 return r;
1662 r = MSI_IterateRecords(view, NULL, load_folder, package);
1663 msiobj_release(&view->hdr);
1664 return r;
1668 * I am not doing any of the costing functionality yet.
1669 * Mostly looking at doing the Component and Feature loading
1671 * The native MSI does A LOT of modification to tables here. Mostly adding
1672 * a lot of temporary columns to the Feature and Component tables.
1674 * note: Native msi also tracks the short filename. But I am only going to
1675 * track the long ones. Also looking at this directory table
1676 * it appears that the directory table does not get the parents
1677 * resolved base on property only based on their entries in the
1678 * directory table.
1680 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1682 static const WCHAR szCosting[] =
1683 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1685 msi_set_property( package->db, szCosting, szZero );
1686 msi_set_property( package->db, cszRootDrive, c_colon );
1688 load_all_folders( package );
1689 load_all_components( package );
1690 load_all_features( package );
1691 load_all_files( package );
1693 return ERROR_SUCCESS;
1696 static UINT execute_script(MSIPACKAGE *package, UINT script )
1698 UINT i;
1699 UINT rc = ERROR_SUCCESS;
1701 TRACE("Executing Script %i\n",script);
1703 if (!package->script)
1705 ERR("no script!\n");
1706 return ERROR_FUNCTION_FAILED;
1709 for (i = 0; i < package->script->ActionCount[script]; i++)
1711 LPWSTR action;
1712 action = package->script->Actions[script][i];
1713 ui_actionstart(package, action);
1714 TRACE("Executing Action (%s)\n",debugstr_w(action));
1715 rc = ACTION_PerformAction(package, action, script);
1716 if (rc != ERROR_SUCCESS)
1717 break;
1719 msi_free_action_script(package, script);
1720 return rc;
1723 static UINT ACTION_FileCost(MSIPACKAGE *package)
1725 return ERROR_SUCCESS;
1728 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1730 MSICOMPONENT *comp;
1731 INSTALLSTATE state;
1732 UINT r;
1734 state = MsiQueryProductStateW(package->ProductCode);
1736 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1738 if (!comp->ComponentId)
1739 continue;
1741 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1742 comp->Installed = INSTALLSTATE_ABSENT;
1743 else
1745 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1746 package->Context, comp->ComponentId,
1747 &comp->Installed);
1748 if (r != ERROR_SUCCESS)
1749 comp->Installed = INSTALLSTATE_ABSENT;
1754 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1756 MSIFEATURE *feature;
1757 INSTALLSTATE state;
1759 state = MsiQueryProductStateW(package->ProductCode);
1761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1763 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1764 feature->Installed = INSTALLSTATE_ABSENT;
1765 else
1767 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1768 feature->Feature);
1773 static BOOL process_state_property(MSIPACKAGE* package, int level,
1774 LPCWSTR property, INSTALLSTATE state)
1776 LPWSTR override;
1777 MSIFEATURE *feature;
1779 override = msi_dup_property( package->db, property );
1780 if (!override)
1781 return FALSE;
1783 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1785 if (strcmpW( property, szRemove ) &&
1786 (feature->Level <= 0 || feature->Level > level))
1787 continue;
1789 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1791 if (!strcmpiW( override, szAll ))
1792 msi_feature_set_state(package, feature, state);
1793 else
1795 LPWSTR ptr = override;
1796 LPWSTR ptr2 = strchrW(override,',');
1798 while (ptr)
1800 int len = ptr2 - ptr;
1802 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1803 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1805 msi_feature_set_state(package, feature, state);
1806 break;
1808 if (ptr2)
1810 ptr=ptr2+1;
1811 ptr2 = strchrW(ptr,',');
1813 else
1814 break;
1818 msi_free(override);
1820 return TRUE;
1823 static BOOL process_overrides( MSIPACKAGE *package, int level )
1825 static const WCHAR szAddLocal[] =
1826 {'A','D','D','L','O','C','A','L',0};
1827 static const WCHAR szAddSource[] =
1828 {'A','D','D','S','O','U','R','C','E',0};
1829 static const WCHAR szAdvertise[] =
1830 {'A','D','V','E','R','T','I','S','E',0};
1831 BOOL ret = FALSE;
1833 /* all these activation/deactivation things happen in order and things
1834 * later on the list override things earlier on the list.
1836 * 0 INSTALLLEVEL processing
1837 * 1 ADDLOCAL
1838 * 2 REMOVE
1839 * 3 ADDSOURCE
1840 * 4 ADDDEFAULT
1841 * 5 REINSTALL
1842 * 6 ADVERTISE
1843 * 7 COMPADDLOCAL
1844 * 8 COMPADDSOURCE
1845 * 9 FILEADDLOCAL
1846 * 10 FILEADDSOURCE
1847 * 11 FILEADDDEFAULT
1849 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1850 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1851 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1852 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1853 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1855 if (ret)
1856 msi_set_property( package->db, szPreselected, szOne );
1858 return ret;
1861 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1863 int level;
1864 static const WCHAR szlevel[] =
1865 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1866 MSICOMPONENT* component;
1867 MSIFEATURE *feature;
1869 TRACE("Checking Install Level\n");
1871 level = msi_get_property_int(package->db, szlevel, 1);
1873 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1875 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1877 BOOL feature_state = ((feature->Level > 0) &&
1878 (feature->Level <= level));
1880 if (feature_state && feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1882 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1883 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1884 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1885 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1886 else
1887 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1891 /* disable child features of unselected parent features */
1892 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1894 FeatureList *fl;
1896 if (feature->Level > 0 && feature->Level <= level)
1897 continue;
1899 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1900 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1903 else
1905 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1907 BOOL selected = feature->Level > 0 && feature->Level <= level;
1909 if (selected && feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1911 msi_feature_set_state(package, feature, feature->Installed);
1917 * now we want to enable or disable components based on feature
1919 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1921 ComponentList *cl;
1923 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1924 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1925 feature->ActionRequest, feature->Action);
1927 if (!feature->Level)
1928 continue;
1930 /* features with components that have compressed files are made local */
1931 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1933 if (cl->component->ForceLocalState &&
1934 feature->ActionRequest == INSTALLSTATE_SOURCE)
1936 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1937 break;
1941 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1943 component = cl->component;
1945 switch (feature->ActionRequest)
1947 case INSTALLSTATE_ABSENT:
1948 component->anyAbsent = 1;
1949 break;
1950 case INSTALLSTATE_ADVERTISED:
1951 component->hasAdvertiseFeature = 1;
1952 break;
1953 case INSTALLSTATE_SOURCE:
1954 component->hasSourceFeature = 1;
1955 break;
1956 case INSTALLSTATE_LOCAL:
1957 component->hasLocalFeature = 1;
1958 break;
1959 case INSTALLSTATE_DEFAULT:
1960 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1961 component->hasAdvertiseFeature = 1;
1962 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1963 component->hasSourceFeature = 1;
1964 else
1965 component->hasLocalFeature = 1;
1966 break;
1967 default:
1968 break;
1973 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1975 /* check if it's local or source */
1976 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1977 (component->hasLocalFeature || component->hasSourceFeature))
1979 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1980 !component->ForceLocalState)
1981 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1982 else
1983 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1984 continue;
1987 /* if any feature is local, the component must be local too */
1988 if (component->hasLocalFeature)
1990 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1991 continue;
1994 if (component->hasSourceFeature)
1996 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1997 continue;
2000 if (component->hasAdvertiseFeature)
2002 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2003 continue;
2006 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2007 if (component->anyAbsent)
2008 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2011 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2013 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2015 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2016 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2019 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2020 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2023 return ERROR_SUCCESS;
2026 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2028 MSIPACKAGE *package = param;
2029 LPCWSTR name;
2030 LPWSTR path;
2031 MSIFOLDER *f;
2033 name = MSI_RecordGetString(row,1);
2035 f = get_loaded_folder(package, name);
2036 if (!f) return ERROR_SUCCESS;
2038 /* reset the ResolvedTarget */
2039 msi_free(f->ResolvedTarget);
2040 f->ResolvedTarget = NULL;
2042 /* This helper function now does ALL the work */
2043 TRACE("Dir %s ...\n",debugstr_w(name));
2044 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2045 TRACE("resolves to %s\n",debugstr_w(path));
2046 msi_free(path);
2048 return ERROR_SUCCESS;
2051 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2053 MSIPACKAGE *package = param;
2054 LPCWSTR name;
2055 MSIFEATURE *feature;
2057 name = MSI_RecordGetString( row, 1 );
2059 feature = get_loaded_feature( package, name );
2060 if (!feature)
2061 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2062 else
2064 LPCWSTR Condition;
2065 Condition = MSI_RecordGetString(row,3);
2067 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2069 int level = MSI_RecordGetInteger(row,2);
2070 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2071 feature->Level = level;
2074 return ERROR_SUCCESS;
2077 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2079 static const WCHAR name[] = {'\\',0};
2080 VS_FIXEDFILEINFO *ptr, *ret;
2081 LPVOID version;
2082 DWORD versize, handle;
2083 UINT sz;
2085 TRACE("%s\n", debugstr_w(filename));
2087 versize = GetFileVersionInfoSizeW( filename, &handle );
2088 if (!versize)
2089 return NULL;
2091 version = msi_alloc( versize );
2092 if (!version)
2093 return NULL;
2095 GetFileVersionInfoW( filename, 0, versize, version );
2097 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2099 msi_free( version );
2100 return NULL;
2103 ret = msi_alloc( sz );
2104 memcpy( ret, ptr, sz );
2106 msi_free( version );
2107 return ret;
2110 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2112 DWORD ms, ls;
2114 msi_parse_version_string( version, &ms, &ls );
2116 if (fi->dwFileVersionMS > ms) return 1;
2117 else if (fi->dwFileVersionMS < ms) return -1;
2118 else if (fi->dwFileVersionLS > ls) return 1;
2119 else if (fi->dwFileVersionLS < ls) return -1;
2120 return 0;
2123 static DWORD get_disk_file_size( LPCWSTR filename )
2125 HANDLE file;
2126 DWORD size;
2128 TRACE("%s\n", debugstr_w(filename));
2130 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2131 if (file == INVALID_HANDLE_VALUE)
2132 return INVALID_FILE_SIZE;
2134 size = GetFileSize( file, NULL );
2135 CloseHandle( file );
2136 return size;
2139 static BOOL hash_matches( MSIFILE *file )
2141 UINT r;
2142 MSIFILEHASHINFO hash;
2144 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2145 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2146 if (r != ERROR_SUCCESS)
2147 return FALSE;
2149 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2152 static WCHAR *get_temp_dir( void )
2154 static UINT id;
2155 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2157 GetTempPathW( MAX_PATH, tmp );
2158 for (;;)
2160 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2161 if (CreateDirectoryW( dir, NULL )) break;
2163 return strdupW( dir );
2166 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2168 MSIASSEMBLY *assembly = file->Component->assembly;
2170 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2172 msi_free( file->TargetPath );
2173 if (assembly && !assembly->application)
2175 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2176 file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName );
2177 track_tempfile( package, file->TargetPath );
2179 else
2181 WCHAR *dir = resolve_folder( package, file->Component->Directory, FALSE, FALSE, TRUE, NULL );
2182 file->TargetPath = build_directory_name( 2, dir, file->FileName );
2183 msi_free( dir );
2186 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2189 static UINT set_file_install_states( MSIPACKAGE *package )
2191 VS_FIXEDFILEINFO *file_version;
2192 MSIFILE *file;
2194 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2196 MSICOMPONENT *comp = file->Component;
2197 DWORD file_size;
2199 if (!comp->Enabled) continue;
2201 if (file->IsCompressed)
2202 comp->ForceLocalState = TRUE;
2204 set_target_path( package, file );
2206 if ((comp->assembly && !comp->assembly->installed) ||
2207 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2209 file->state = msifs_missing;
2210 comp->Cost += file->FileSize;
2211 continue;
2213 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2215 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2216 HIWORD(file_version->dwFileVersionMS),
2217 LOWORD(file_version->dwFileVersionMS),
2218 HIWORD(file_version->dwFileVersionLS),
2219 LOWORD(file_version->dwFileVersionLS));
2221 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2223 file->state = msifs_overwrite;
2224 comp->Cost += file->FileSize;
2226 else
2228 TRACE("Destination file version equal or greater, not overwriting\n");
2229 file->state = msifs_present;
2231 msi_free( file_version );
2232 continue;
2234 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2236 file->state = msifs_overwrite;
2237 comp->Cost += file->FileSize - file_size;
2238 continue;
2240 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2242 TRACE("File hashes match, not overwriting\n");
2243 file->state = msifs_present;
2244 continue;
2246 file->state = msifs_overwrite;
2247 comp->Cost += file->FileSize - file_size;
2250 return ERROR_SUCCESS;
2254 * A lot is done in this function aside from just the costing.
2255 * The costing needs to be implemented at some point but for now I am going
2256 * to focus on the directory building
2259 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2261 static const WCHAR ExecSeqQuery[] =
2262 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2263 '`','D','i','r','e','c','t','o','r','y','`',0};
2264 static const WCHAR ConditionQuery[] =
2265 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2266 '`','C','o','n','d','i','t','i','o','n','`',0};
2267 static const WCHAR szCosting[] =
2268 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2269 static const WCHAR szlevel[] =
2270 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2271 static const WCHAR szOutOfDiskSpace[] =
2272 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2273 MSICOMPONENT *comp;
2274 UINT rc = ERROR_SUCCESS;
2275 MSIQUERY * view;
2276 LPWSTR level;
2278 TRACE("Building Directory properties\n");
2280 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2281 if (rc == ERROR_SUCCESS)
2283 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2284 package);
2285 msiobj_release(&view->hdr);
2288 /* read components states from the registry */
2289 ACTION_GetComponentInstallStates(package);
2290 ACTION_GetFeatureInstallStates(package);
2292 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2294 TRACE("Evaluating feature conditions\n");
2296 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2297 if (rc == ERROR_SUCCESS)
2299 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2300 msiobj_release( &view->hdr );
2303 TRACE("Evaluating component conditions\n");
2305 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2307 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2309 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2310 comp->Enabled = FALSE;
2312 else
2313 comp->Enabled = TRUE;
2316 TRACE("Calculating file install states\n");
2317 set_file_install_states( package );
2319 msi_set_property( package->db, szCosting, szOne );
2320 /* set default run level if not set */
2321 level = msi_dup_property( package->db, szlevel );
2322 if (!level)
2323 msi_set_property( package->db, szlevel, szOne );
2324 msi_free(level);
2326 /* FIXME: check volume disk space */
2327 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2329 return MSI_SetFeatureStates(package);
2332 /* OK this value is "interpreted" and then formatted based on the
2333 first few characters */
2334 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2335 DWORD *size)
2337 LPSTR data = NULL;
2339 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2341 if (value[1]=='x')
2343 LPWSTR ptr;
2344 CHAR byte[5];
2345 LPWSTR deformated = NULL;
2346 int count;
2348 deformat_string(package, &value[2], &deformated);
2350 /* binary value type */
2351 ptr = deformated;
2352 *type = REG_BINARY;
2353 if (strlenW(ptr)%2)
2354 *size = (strlenW(ptr)/2)+1;
2355 else
2356 *size = strlenW(ptr)/2;
2358 data = msi_alloc(*size);
2360 byte[0] = '0';
2361 byte[1] = 'x';
2362 byte[4] = 0;
2363 count = 0;
2364 /* if uneven pad with a zero in front */
2365 if (strlenW(ptr)%2)
2367 byte[2]= '0';
2368 byte[3]= *ptr;
2369 ptr++;
2370 data[count] = (BYTE)strtol(byte,NULL,0);
2371 count ++;
2372 TRACE("Uneven byte count\n");
2374 while (*ptr)
2376 byte[2]= *ptr;
2377 ptr++;
2378 byte[3]= *ptr;
2379 ptr++;
2380 data[count] = (BYTE)strtol(byte,NULL,0);
2381 count ++;
2383 msi_free(deformated);
2385 TRACE("Data %i bytes(%i)\n",*size,count);
2387 else
2389 LPWSTR deformated;
2390 LPWSTR p;
2391 DWORD d = 0;
2392 deformat_string(package, &value[1], &deformated);
2394 *type=REG_DWORD;
2395 *size = sizeof(DWORD);
2396 data = msi_alloc(*size);
2397 p = deformated;
2398 if (*p == '-')
2399 p++;
2400 while (*p)
2402 if ( (*p < '0') || (*p > '9') )
2403 break;
2404 d *= 10;
2405 d += (*p - '0');
2406 p++;
2408 if (deformated[0] == '-')
2409 d = -d;
2410 *(LPDWORD)data = d;
2411 TRACE("DWORD %i\n",*(LPDWORD)data);
2413 msi_free(deformated);
2416 else
2418 static const WCHAR szMulti[] = {'[','~',']',0};
2419 LPCWSTR ptr;
2420 *type=REG_SZ;
2422 if (value[0]=='#')
2424 if (value[1]=='%')
2426 ptr = &value[2];
2427 *type=REG_EXPAND_SZ;
2429 else
2430 ptr = &value[1];
2432 else
2433 ptr=value;
2435 if (strstrW(value, szMulti))
2436 *type = REG_MULTI_SZ;
2438 /* remove initial delimiter */
2439 if (!strncmpW(value, szMulti, 3))
2440 ptr = value + 3;
2442 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2444 /* add double NULL terminator */
2445 if (*type == REG_MULTI_SZ)
2447 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2448 data = msi_realloc_zero(data, *size);
2451 return data;
2454 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2456 const WCHAR *ret;
2458 switch (root)
2460 case -1:
2461 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2463 *root_key = HKEY_LOCAL_MACHINE;
2464 ret = szHLM;
2466 else
2468 *root_key = HKEY_CURRENT_USER;
2469 ret = szHCU;
2471 break;
2472 case 0:
2473 *root_key = HKEY_CLASSES_ROOT;
2474 ret = szHCR;
2475 break;
2476 case 1:
2477 *root_key = HKEY_CURRENT_USER;
2478 ret = szHCU;
2479 break;
2480 case 2:
2481 *root_key = HKEY_LOCAL_MACHINE;
2482 ret = szHLM;
2483 break;
2484 case 3:
2485 *root_key = HKEY_USERS;
2486 ret = szHU;
2487 break;
2488 default:
2489 ERR("Unknown root %i\n", root);
2490 return NULL;
2493 return ret;
2496 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2498 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2499 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2501 if (is_64bit && package->platform == PLATFORM_INTEL &&
2502 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2504 UINT size;
2505 WCHAR *path_32node;
2507 size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR);
2508 path_32node = msi_alloc( size );
2509 if (!path_32node)
2510 return NULL;
2512 memcpy( path_32node, path, len * sizeof(WCHAR) );
2513 path_32node[len] = 0;
2514 strcatW( path_32node, szWow6432Node );
2515 strcatW( path_32node, szBackSlash );
2516 strcatW( path_32node, path + len );
2517 return path_32node;
2520 return strdupW( path );
2523 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2525 MSIPACKAGE *package = param;
2526 LPSTR value_data = NULL;
2527 HKEY root_key, hkey;
2528 DWORD type,size;
2529 LPWSTR deformated, uikey, keypath;
2530 LPCWSTR szRoot, component, name, key, value;
2531 MSICOMPONENT *comp;
2532 MSIRECORD * uirow;
2533 INT root;
2534 BOOL check_first = FALSE;
2535 UINT rc;
2537 ui_progress(package,2,0,0,0);
2539 component = MSI_RecordGetString(row, 6);
2540 comp = get_loaded_component(package,component);
2541 if (!comp)
2542 return ERROR_SUCCESS;
2544 if (!comp->Enabled)
2546 TRACE("component is disabled\n");
2547 return ERROR_SUCCESS;
2550 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2552 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2553 comp->Action = comp->Installed;
2554 return ERROR_SUCCESS;
2556 comp->Action = INSTALLSTATE_LOCAL;
2558 name = MSI_RecordGetString(row, 4);
2559 if( MSI_RecordIsNull(row,5) && name )
2561 /* null values can have special meanings */
2562 if (name[0]=='-' && name[1] == 0)
2563 return ERROR_SUCCESS;
2564 else if ((name[0]=='+' && name[1] == 0) ||
2565 (name[0] == '*' && name[1] == 0))
2566 name = NULL;
2567 check_first = TRUE;
2570 root = MSI_RecordGetInteger(row,2);
2571 key = MSI_RecordGetString(row, 3);
2573 szRoot = get_root_key( package, root, &root_key );
2574 if (!szRoot)
2575 return ERROR_SUCCESS;
2577 deformat_string(package, key , &deformated);
2578 size = strlenW(deformated) + strlenW(szRoot) + 1;
2579 uikey = msi_alloc(size*sizeof(WCHAR));
2580 strcpyW(uikey,szRoot);
2581 strcatW(uikey,deformated);
2583 keypath = get_keypath( package, root_key, deformated );
2584 msi_free( deformated );
2585 if (RegCreateKeyW( root_key, keypath, &hkey ))
2587 ERR("Could not create key %s\n", debugstr_w(keypath));
2588 msi_free(uikey);
2589 return ERROR_SUCCESS;
2592 value = MSI_RecordGetString(row,5);
2593 if (value)
2594 value_data = parse_value(package, value, &type, &size);
2595 else
2597 value_data = (LPSTR)strdupW(szEmpty);
2598 size = sizeof(szEmpty);
2599 type = REG_SZ;
2602 deformat_string(package, name, &deformated);
2604 if (!check_first)
2606 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2607 debugstr_w(uikey));
2608 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2610 else
2612 DWORD sz = 0;
2613 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2614 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2616 TRACE("value %s of %s checked already exists\n",
2617 debugstr_w(deformated), debugstr_w(uikey));
2619 else
2621 TRACE("Checked and setting value %s of %s\n",
2622 debugstr_w(deformated), debugstr_w(uikey));
2623 if (deformated || size)
2624 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2627 RegCloseKey(hkey);
2629 uirow = MSI_CreateRecord(3);
2630 MSI_RecordSetStringW(uirow,2,deformated);
2631 MSI_RecordSetStringW(uirow,1,uikey);
2632 if (type == REG_SZ || type == REG_EXPAND_SZ)
2633 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2634 ui_actiondata(package,szWriteRegistryValues,uirow);
2635 msiobj_release( &uirow->hdr );
2637 msi_free(value_data);
2638 msi_free(deformated);
2639 msi_free(uikey);
2641 return ERROR_SUCCESS;
2644 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2646 UINT rc;
2647 MSIQUERY * view;
2648 static const WCHAR ExecSeqQuery[] =
2649 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2650 '`','R','e','g','i','s','t','r','y','`',0 };
2652 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2653 if (rc != ERROR_SUCCESS)
2654 return ERROR_SUCCESS;
2656 /* increment progress bar each time action data is sent */
2657 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2659 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2661 msiobj_release(&view->hdr);
2662 return rc;
2665 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2667 LONG res;
2668 HKEY hkey;
2669 DWORD num_subkeys, num_values;
2671 if (delete_key)
2673 if ((res = RegDeleteTreeW( hkey_root, key )))
2675 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2677 return;
2680 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2682 if ((res = RegDeleteValueW( hkey, value )))
2684 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2686 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2687 NULL, NULL, NULL, NULL );
2688 RegCloseKey( hkey );
2689 if (!res && !num_subkeys && !num_values)
2691 TRACE("Removing empty key %s\n", debugstr_w(key));
2692 RegDeleteKeyW( hkey_root, key );
2694 return;
2696 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2700 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2702 MSIPACKAGE *package = param;
2703 LPCWSTR component, name, key_str, root_key_str;
2704 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2705 MSICOMPONENT *comp;
2706 MSIRECORD *uirow;
2707 BOOL delete_key = FALSE;
2708 HKEY hkey_root;
2709 UINT size;
2710 INT root;
2712 ui_progress( package, 2, 0, 0, 0 );
2714 component = MSI_RecordGetString( row, 6 );
2715 comp = get_loaded_component( package, component );
2716 if (!comp)
2717 return ERROR_SUCCESS;
2719 if (!comp->Enabled)
2721 TRACE("component is disabled\n");
2722 return ERROR_SUCCESS;
2725 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2727 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2728 comp->Action = comp->Installed;
2729 return ERROR_SUCCESS;
2731 comp->Action = INSTALLSTATE_ABSENT;
2733 name = MSI_RecordGetString( row, 4 );
2734 if (MSI_RecordIsNull( row, 5 ) && name )
2736 if (name[0] == '+' && !name[1])
2737 return ERROR_SUCCESS;
2738 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2740 delete_key = TRUE;
2741 name = NULL;
2745 root = MSI_RecordGetInteger( row, 2 );
2746 key_str = MSI_RecordGetString( row, 3 );
2748 root_key_str = get_root_key( package, root, &hkey_root );
2749 if (!root_key_str)
2750 return ERROR_SUCCESS;
2752 deformat_string( package, key_str, &deformated_key );
2753 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2754 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2755 strcpyW( ui_key_str, root_key_str );
2756 strcatW( ui_key_str, deformated_key );
2758 deformat_string( package, name, &deformated_name );
2760 keypath = get_keypath( package, hkey_root, deformated_key );
2761 msi_free( deformated_key );
2762 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2763 msi_free( keypath );
2765 uirow = MSI_CreateRecord( 2 );
2766 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2767 MSI_RecordSetStringW( uirow, 2, deformated_name );
2769 ui_actiondata( package, szRemoveRegistryValues, uirow );
2770 msiobj_release( &uirow->hdr );
2772 msi_free( ui_key_str );
2773 msi_free( deformated_name );
2774 return ERROR_SUCCESS;
2777 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2779 MSIPACKAGE *package = param;
2780 LPCWSTR component, name, key_str, root_key_str;
2781 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2782 MSICOMPONENT *comp;
2783 MSIRECORD *uirow;
2784 BOOL delete_key = FALSE;
2785 HKEY hkey_root;
2786 UINT size;
2787 INT root;
2789 ui_progress( package, 2, 0, 0, 0 );
2791 component = MSI_RecordGetString( row, 5 );
2792 comp = get_loaded_component( package, component );
2793 if (!comp)
2794 return ERROR_SUCCESS;
2796 if (!comp->Enabled)
2798 TRACE("component is disabled\n");
2799 return ERROR_SUCCESS;
2802 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2804 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2805 comp->Action = comp->Installed;
2806 return ERROR_SUCCESS;
2808 comp->Action = INSTALLSTATE_LOCAL;
2810 if ((name = MSI_RecordGetString( row, 4 )))
2812 if (name[0] == '-' && !name[1])
2814 delete_key = TRUE;
2815 name = NULL;
2819 root = MSI_RecordGetInteger( row, 2 );
2820 key_str = MSI_RecordGetString( row, 3 );
2822 root_key_str = get_root_key( package, root, &hkey_root );
2823 if (!root_key_str)
2824 return ERROR_SUCCESS;
2826 deformat_string( package, key_str, &deformated_key );
2827 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2828 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2829 strcpyW( ui_key_str, root_key_str );
2830 strcatW( ui_key_str, deformated_key );
2832 deformat_string( package, name, &deformated_name );
2834 keypath = get_keypath( package, hkey_root, deformated_key );
2835 msi_free( deformated_key );
2836 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2837 msi_free( keypath );
2839 uirow = MSI_CreateRecord( 2 );
2840 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2841 MSI_RecordSetStringW( uirow, 2, deformated_name );
2843 ui_actiondata( package, szRemoveRegistryValues, uirow );
2844 msiobj_release( &uirow->hdr );
2846 msi_free( ui_key_str );
2847 msi_free( deformated_name );
2848 return ERROR_SUCCESS;
2851 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2853 UINT rc;
2854 MSIQUERY *view;
2855 static const WCHAR registry_query[] =
2856 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2857 '`','R','e','g','i','s','t','r','y','`',0 };
2858 static const WCHAR remove_registry_query[] =
2859 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2860 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2862 /* increment progress bar each time action data is sent */
2863 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2865 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2866 if (rc == ERROR_SUCCESS)
2868 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2869 msiobj_release( &view->hdr );
2870 if (rc != ERROR_SUCCESS)
2871 return rc;
2874 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2875 if (rc == ERROR_SUCCESS)
2877 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2878 msiobj_release( &view->hdr );
2879 if (rc != ERROR_SUCCESS)
2880 return rc;
2883 return ERROR_SUCCESS;
2886 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2888 package->script->CurrentlyScripting = TRUE;
2890 return ERROR_SUCCESS;
2894 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2896 MSICOMPONENT *comp;
2897 DWORD progress = 0;
2898 DWORD total = 0;
2899 static const WCHAR q1[]=
2900 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2901 '`','R','e','g','i','s','t','r','y','`',0};
2902 UINT rc;
2903 MSIQUERY * view;
2904 MSIFEATURE *feature;
2905 MSIFILE *file;
2907 TRACE("InstallValidate\n");
2909 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2910 if (rc == ERROR_SUCCESS)
2912 MSI_IterateRecords( view, &progress, NULL, package );
2913 msiobj_release( &view->hdr );
2914 total += progress * REG_PROGRESS_VALUE;
2917 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2918 total += COMPONENT_PROGRESS_VALUE;
2920 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2921 total += file->FileSize;
2923 ui_progress(package,0,total,0,0);
2925 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2927 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2928 debugstr_w(feature->Feature), feature->Installed,
2929 feature->ActionRequest, feature->Action);
2932 return ERROR_SUCCESS;
2935 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2937 MSIPACKAGE* package = param;
2938 LPCWSTR cond = NULL;
2939 LPCWSTR message = NULL;
2940 UINT r;
2942 static const WCHAR title[]=
2943 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2945 cond = MSI_RecordGetString(row,1);
2947 r = MSI_EvaluateConditionW(package,cond);
2948 if (r == MSICONDITION_FALSE)
2950 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2952 LPWSTR deformated;
2953 message = MSI_RecordGetString(row,2);
2954 deformat_string(package,message,&deformated);
2955 MessageBoxW(NULL,deformated,title,MB_OK);
2956 msi_free(deformated);
2959 return ERROR_INSTALL_FAILURE;
2962 return ERROR_SUCCESS;
2965 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2967 UINT rc;
2968 MSIQUERY * view = NULL;
2969 static const WCHAR ExecSeqQuery[] =
2970 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2971 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2973 TRACE("Checking launch conditions\n");
2975 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2976 if (rc != ERROR_SUCCESS)
2977 return ERROR_SUCCESS;
2979 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2980 msiobj_release(&view->hdr);
2982 return rc;
2985 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2988 if (!cmp->KeyPath)
2989 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2991 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2993 MSIRECORD * row = 0;
2994 UINT root,len;
2995 LPWSTR deformated,buffer,deformated_name;
2996 LPCWSTR key,name;
2997 static const WCHAR ExecSeqQuery[] =
2998 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2999 '`','R','e','g','i','s','t','r','y','`',' ',
3000 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3001 ' ','=',' ' ,'\'','%','s','\'',0 };
3002 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3003 static const WCHAR fmt2[]=
3004 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3006 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3007 if (!row)
3008 return NULL;
3010 root = MSI_RecordGetInteger(row,2);
3011 key = MSI_RecordGetString(row, 3);
3012 name = MSI_RecordGetString(row, 4);
3013 deformat_string(package, key , &deformated);
3014 deformat_string(package, name, &deformated_name);
3016 len = strlenW(deformated) + 6;
3017 if (deformated_name)
3018 len+=strlenW(deformated_name);
3020 buffer = msi_alloc( len *sizeof(WCHAR));
3022 if (deformated_name)
3023 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3024 else
3025 sprintfW(buffer,fmt,root,deformated);
3027 msi_free(deformated);
3028 msi_free(deformated_name);
3029 msiobj_release(&row->hdr);
3031 return buffer;
3033 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3035 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3036 return NULL;
3038 else
3040 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
3042 if (file)
3043 return strdupW( file->TargetPath );
3045 return NULL;
3048 static HKEY openSharedDLLsKey(void)
3050 HKEY hkey=0;
3051 static const WCHAR path[] =
3052 {'S','o','f','t','w','a','r','e','\\',
3053 'M','i','c','r','o','s','o','f','t','\\',
3054 'W','i','n','d','o','w','s','\\',
3055 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3056 'S','h','a','r','e','d','D','L','L','s',0};
3058 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3059 return hkey;
3062 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3064 HKEY hkey;
3065 DWORD count=0;
3066 DWORD type;
3067 DWORD sz = sizeof(count);
3068 DWORD rc;
3070 hkey = openSharedDLLsKey();
3071 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3072 if (rc != ERROR_SUCCESS)
3073 count = 0;
3074 RegCloseKey(hkey);
3075 return count;
3078 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3080 HKEY hkey;
3082 hkey = openSharedDLLsKey();
3083 if (count > 0)
3084 msi_reg_set_val_dword( hkey, path, count );
3085 else
3086 RegDeleteValueW(hkey,path);
3087 RegCloseKey(hkey);
3088 return count;
3092 * Return TRUE if the count should be written out and FALSE if not
3094 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3096 MSIFEATURE *feature;
3097 INT count = 0;
3098 BOOL write = FALSE;
3100 /* only refcount DLLs */
3101 if (comp->KeyPath == NULL ||
3102 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3103 comp->Attributes & msidbComponentAttributesODBCDataSource)
3104 write = FALSE;
3105 else
3107 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3108 write = (count > 0);
3110 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3111 write = TRUE;
3114 /* increment counts */
3115 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3117 ComponentList *cl;
3119 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3120 continue;
3122 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3124 if ( cl->component == comp )
3125 count++;
3129 /* decrement counts */
3130 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3132 ComponentList *cl;
3134 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3135 continue;
3137 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3139 if ( cl->component == comp )
3140 count--;
3144 /* ref count all the files in the component */
3145 if (write)
3147 MSIFILE *file;
3149 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3151 if (file->Component == comp)
3152 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3156 /* add a count for permanent */
3157 if (comp->Attributes & msidbComponentAttributesPermanent)
3158 count ++;
3160 comp->RefCount = count;
3162 if (write)
3163 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3166 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3168 WCHAR squished_pc[GUID_SIZE];
3169 WCHAR squished_cc[GUID_SIZE];
3170 UINT rc;
3171 MSICOMPONENT *comp;
3172 HKEY hkey;
3174 TRACE("\n");
3176 squash_guid(package->ProductCode,squished_pc);
3177 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3179 msi_set_sourcedir_props(package, FALSE);
3181 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3183 MSIRECORD * uirow;
3185 ui_progress(package,2,0,0,0);
3186 if (!comp->ComponentId)
3187 continue;
3189 squash_guid(comp->ComponentId,squished_cc);
3191 msi_free(comp->FullKeypath);
3192 comp->FullKeypath = resolve_keypath( package, comp );
3194 ACTION_RefCountComponent( package, comp );
3196 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3197 debugstr_w(comp->Component),
3198 debugstr_w(squished_cc),
3199 debugstr_w(comp->FullKeypath),
3200 comp->RefCount,
3201 comp->ActionRequest);
3203 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3204 comp->ActionRequest == INSTALLSTATE_SOURCE)
3206 if (!comp->FullKeypath)
3207 continue;
3209 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3210 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3211 &hkey, TRUE);
3212 else
3213 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3214 &hkey, TRUE);
3216 if (rc != ERROR_SUCCESS)
3217 continue;
3219 if (comp->Attributes & msidbComponentAttributesPermanent)
3221 static const WCHAR szPermKey[] =
3222 { '0','0','0','0','0','0','0','0','0','0','0','0',
3223 '0','0','0','0','0','0','0','0','0','0','0','0',
3224 '0','0','0','0','0','0','0','0',0 };
3226 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3229 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3230 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3231 else
3233 MSIFILE *file;
3234 MSIRECORD *row;
3235 LPWSTR ptr, ptr2;
3236 WCHAR source[MAX_PATH];
3237 WCHAR base[MAX_PATH];
3238 LPWSTR sourcepath;
3240 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3241 static const WCHAR query[] = {
3242 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3243 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3244 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3245 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3246 '`','D','i','s','k','I','d','`',0};
3248 file = get_loaded_file(package, comp->KeyPath);
3249 if (!file)
3250 continue;
3252 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3253 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3254 ptr2 = strrchrW(source, '\\') + 1;
3255 msiobj_release(&row->hdr);
3257 lstrcpyW(base, package->PackagePath);
3258 ptr = strrchrW(base, '\\');
3259 *(ptr + 1) = '\0';
3261 sourcepath = resolve_file_source(package, file);
3262 ptr = sourcepath + lstrlenW(base);
3263 lstrcpyW(ptr2, ptr);
3264 msi_free(sourcepath);
3266 msi_reg_set_val_str(hkey, squished_pc, source);
3268 RegCloseKey(hkey);
3270 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3272 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3273 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3274 else
3275 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3277 comp->Action = comp->ActionRequest;
3279 /* UI stuff */
3280 uirow = MSI_CreateRecord(3);
3281 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3282 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3283 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3284 ui_actiondata(package,szProcessComponents,uirow);
3285 msiobj_release( &uirow->hdr );
3288 return ERROR_SUCCESS;
3291 typedef struct {
3292 CLSID clsid;
3293 LPWSTR source;
3295 LPWSTR path;
3296 ITypeLib *ptLib;
3297 } typelib_struct;
3299 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3300 LPWSTR lpszName, LONG_PTR lParam)
3302 TLIBATTR *attr;
3303 typelib_struct *tl_struct = (typelib_struct*) lParam;
3304 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3305 int sz;
3306 HRESULT res;
3308 if (!IS_INTRESOURCE(lpszName))
3310 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3311 return TRUE;
3314 sz = strlenW(tl_struct->source)+4;
3315 sz *= sizeof(WCHAR);
3317 if ((INT_PTR)lpszName == 1)
3318 tl_struct->path = strdupW(tl_struct->source);
3319 else
3321 tl_struct->path = msi_alloc(sz);
3322 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3325 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3326 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3327 if (FAILED(res))
3329 msi_free(tl_struct->path);
3330 tl_struct->path = NULL;
3332 return TRUE;
3335 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3336 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3338 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3339 return FALSE;
3342 msi_free(tl_struct->path);
3343 tl_struct->path = NULL;
3345 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3346 ITypeLib_Release(tl_struct->ptLib);
3348 return TRUE;
3351 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3353 MSIPACKAGE* package = param;
3354 LPCWSTR component;
3355 MSICOMPONENT *comp;
3356 MSIFILE *file;
3357 typelib_struct tl_struct;
3358 ITypeLib *tlib;
3359 HMODULE module;
3360 HRESULT hr;
3362 component = MSI_RecordGetString(row,3);
3363 comp = get_loaded_component(package,component);
3364 if (!comp)
3365 return ERROR_SUCCESS;
3367 if (!comp->Enabled)
3369 TRACE("component is disabled\n");
3370 return ERROR_SUCCESS;
3373 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3375 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3376 comp->Action = comp->Installed;
3377 return ERROR_SUCCESS;
3379 comp->Action = INSTALLSTATE_LOCAL;
3381 if (!comp->KeyPath || !(file = get_loaded_file( package, comp->KeyPath )))
3383 TRACE("component has no key path\n");
3384 return ERROR_SUCCESS;
3386 ui_actiondata( package, szRegisterTypeLibraries, row );
3388 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3389 if (module)
3391 LPCWSTR guid;
3392 guid = MSI_RecordGetString(row,1);
3393 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3394 tl_struct.source = strdupW( file->TargetPath );
3395 tl_struct.path = NULL;
3397 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3398 (LONG_PTR)&tl_struct);
3400 if (tl_struct.path)
3402 LPWSTR help = NULL;
3403 LPCWSTR helpid;
3404 HRESULT res;
3406 helpid = MSI_RecordGetString(row,6);
3408 if (helpid)
3409 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3410 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3411 msi_free(help);
3413 if (FAILED(res))
3414 ERR("Failed to register type library %s\n",
3415 debugstr_w(tl_struct.path));
3416 else
3417 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3419 ITypeLib_Release(tl_struct.ptLib);
3420 msi_free(tl_struct.path);
3422 else
3423 ERR("Failed to load type library %s\n",
3424 debugstr_w(tl_struct.source));
3426 FreeLibrary(module);
3427 msi_free(tl_struct.source);
3429 else
3431 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3432 if (FAILED(hr))
3434 ERR("Failed to load type library: %08x\n", hr);
3435 return ERROR_INSTALL_FAILURE;
3438 ITypeLib_Release(tlib);
3441 return ERROR_SUCCESS;
3444 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3447 * OK this is a bit confusing.. I am given a _Component key and I believe
3448 * that the file that is being registered as a type library is the "key file
3449 * of that component" which I interpret to mean "The file in the KeyPath of
3450 * that component".
3452 UINT rc;
3453 MSIQUERY * view;
3454 static const WCHAR Query[] =
3455 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3456 '`','T','y','p','e','L','i','b','`',0};
3458 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3459 if (rc != ERROR_SUCCESS)
3460 return ERROR_SUCCESS;
3462 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3463 msiobj_release(&view->hdr);
3464 return rc;
3467 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3469 MSIPACKAGE *package = param;
3470 LPCWSTR component, guid;
3471 MSICOMPONENT *comp;
3472 GUID libid;
3473 UINT version;
3474 LCID language;
3475 SYSKIND syskind;
3476 HRESULT hr;
3478 component = MSI_RecordGetString( row, 3 );
3479 comp = get_loaded_component( package, component );
3480 if (!comp)
3481 return ERROR_SUCCESS;
3483 if (!comp->Enabled)
3485 TRACE("component is disabled\n");
3486 return ERROR_SUCCESS;
3489 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3491 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3492 comp->Action = comp->Installed;
3493 return ERROR_SUCCESS;
3495 comp->Action = INSTALLSTATE_ABSENT;
3497 ui_actiondata( package, szUnregisterTypeLibraries, row );
3499 guid = MSI_RecordGetString( row, 1 );
3500 CLSIDFromString( (LPCWSTR)guid, &libid );
3501 version = MSI_RecordGetInteger( row, 4 );
3502 language = MSI_RecordGetInteger( row, 2 );
3504 #ifdef _WIN64
3505 syskind = SYS_WIN64;
3506 #else
3507 syskind = SYS_WIN32;
3508 #endif
3510 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3511 if (FAILED(hr))
3513 WARN("Failed to unregister typelib: %08x\n", hr);
3516 return ERROR_SUCCESS;
3519 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3521 UINT rc;
3522 MSIQUERY *view;
3523 static const WCHAR query[] =
3524 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3525 '`','T','y','p','e','L','i','b','`',0};
3527 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3528 if (rc != ERROR_SUCCESS)
3529 return ERROR_SUCCESS;
3531 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3532 msiobj_release( &view->hdr );
3533 return rc;
3536 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3538 static const WCHAR szlnk[] = {'.','l','n','k',0};
3539 LPCWSTR directory, extension;
3540 LPWSTR link_folder, link_file, filename;
3542 directory = MSI_RecordGetString( row, 2 );
3543 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3545 /* may be needed because of a bug somewhere else */
3546 create_full_pathW( link_folder );
3548 filename = msi_dup_record_field( row, 3 );
3549 reduce_to_longfilename( filename );
3551 extension = strchrW( filename, '.' );
3552 if (!extension || strcmpiW( extension, szlnk ))
3554 int len = strlenW( filename );
3555 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3556 memcpy( filename + len, szlnk, sizeof(szlnk) );
3558 link_file = build_directory_name( 2, link_folder, filename );
3559 msi_free( link_folder );
3560 msi_free( filename );
3562 return link_file;
3565 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3567 MSIPACKAGE *package = param;
3568 LPWSTR link_file, deformated, path;
3569 LPCWSTR component, target;
3570 MSICOMPONENT *comp;
3571 IShellLinkW *sl = NULL;
3572 IPersistFile *pf = NULL;
3573 HRESULT res;
3575 component = MSI_RecordGetString(row, 4);
3576 comp = get_loaded_component(package, component);
3577 if (!comp)
3578 return ERROR_SUCCESS;
3580 if (!comp->Enabled)
3582 TRACE("component is disabled\n");
3583 return ERROR_SUCCESS;
3586 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3588 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3589 comp->Action = comp->Installed;
3590 return ERROR_SUCCESS;
3592 comp->Action = INSTALLSTATE_LOCAL;
3594 ui_actiondata(package,szCreateShortcuts,row);
3596 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3597 &IID_IShellLinkW, (LPVOID *) &sl );
3599 if (FAILED( res ))
3601 ERR("CLSID_ShellLink not available\n");
3602 goto err;
3605 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3606 if (FAILED( res ))
3608 ERR("QueryInterface(IID_IPersistFile) failed\n");
3609 goto err;
3612 target = MSI_RecordGetString(row, 5);
3613 if (strchrW(target, '['))
3615 deformat_string(package, target, &deformated);
3616 IShellLinkW_SetPath(sl,deformated);
3617 msi_free(deformated);
3619 else
3621 FIXME("poorly handled shortcut format, advertised shortcut\n");
3622 IShellLinkW_SetPath(sl,comp->FullKeypath);
3625 if (!MSI_RecordIsNull(row,6))
3627 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3628 deformat_string(package, arguments, &deformated);
3629 IShellLinkW_SetArguments(sl,deformated);
3630 msi_free(deformated);
3633 if (!MSI_RecordIsNull(row,7))
3635 LPCWSTR description = MSI_RecordGetString(row, 7);
3636 IShellLinkW_SetDescription(sl, description);
3639 if (!MSI_RecordIsNull(row,8))
3640 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3642 if (!MSI_RecordIsNull(row,9))
3644 INT index;
3645 LPCWSTR icon = MSI_RecordGetString(row, 9);
3647 path = build_icon_path(package, icon);
3648 index = MSI_RecordGetInteger(row,10);
3650 /* no value means 0 */
3651 if (index == MSI_NULL_INTEGER)
3652 index = 0;
3654 IShellLinkW_SetIconLocation(sl, path, index);
3655 msi_free(path);
3658 if (!MSI_RecordIsNull(row,11))
3659 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3661 if (!MSI_RecordIsNull(row,12))
3663 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3664 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3665 if (path)
3666 IShellLinkW_SetWorkingDirectory(sl, path);
3667 msi_free(path);
3670 link_file = get_link_file(package, row);
3672 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3673 IPersistFile_Save(pf, link_file, FALSE);
3675 msi_free(link_file);
3677 err:
3678 if (pf)
3679 IPersistFile_Release( pf );
3680 if (sl)
3681 IShellLinkW_Release( sl );
3683 return ERROR_SUCCESS;
3686 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3688 UINT rc;
3689 HRESULT res;
3690 MSIQUERY * view;
3691 static const WCHAR Query[] =
3692 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3693 '`','S','h','o','r','t','c','u','t','`',0};
3695 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3696 if (rc != ERROR_SUCCESS)
3697 return ERROR_SUCCESS;
3699 res = CoInitialize( NULL );
3701 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3702 msiobj_release(&view->hdr);
3704 if (SUCCEEDED(res))
3705 CoUninitialize();
3707 return rc;
3710 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3712 MSIPACKAGE *package = param;
3713 LPWSTR link_file;
3714 LPCWSTR component;
3715 MSICOMPONENT *comp;
3717 component = MSI_RecordGetString( row, 4 );
3718 comp = get_loaded_component( package, component );
3719 if (!comp)
3720 return ERROR_SUCCESS;
3722 if (!comp->Enabled)
3724 TRACE("component is disabled\n");
3725 return ERROR_SUCCESS;
3728 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3730 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3731 comp->Action = comp->Installed;
3732 return ERROR_SUCCESS;
3734 comp->Action = INSTALLSTATE_ABSENT;
3736 ui_actiondata( package, szRemoveShortcuts, row );
3738 link_file = get_link_file( package, row );
3740 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3741 if (!DeleteFileW( link_file ))
3743 WARN("Failed to remove shortcut file %u\n", GetLastError());
3745 msi_free( link_file );
3747 return ERROR_SUCCESS;
3750 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3752 UINT rc;
3753 MSIQUERY *view;
3754 static const WCHAR query[] =
3755 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3756 '`','S','h','o','r','t','c','u','t','`',0};
3758 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3759 if (rc != ERROR_SUCCESS)
3760 return ERROR_SUCCESS;
3762 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3763 msiobj_release( &view->hdr );
3765 return rc;
3768 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3770 MSIPACKAGE* package = param;
3771 HANDLE the_file;
3772 LPWSTR FilePath;
3773 LPCWSTR FileName;
3774 CHAR buffer[1024];
3775 DWORD sz;
3776 UINT rc;
3778 FileName = MSI_RecordGetString(row,1);
3779 if (!FileName)
3781 ERR("Unable to get FileName\n");
3782 return ERROR_SUCCESS;
3785 FilePath = build_icon_path(package,FileName);
3787 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3789 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3790 FILE_ATTRIBUTE_NORMAL, NULL);
3792 if (the_file == INVALID_HANDLE_VALUE)
3794 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3795 msi_free(FilePath);
3796 return ERROR_SUCCESS;
3801 DWORD write;
3802 sz = 1024;
3803 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3804 if (rc != ERROR_SUCCESS)
3806 ERR("Failed to get stream\n");
3807 CloseHandle(the_file);
3808 DeleteFileW(FilePath);
3809 break;
3811 WriteFile(the_file,buffer,sz,&write,NULL);
3812 } while (sz == 1024);
3814 msi_free(FilePath);
3815 CloseHandle(the_file);
3817 return ERROR_SUCCESS;
3820 static UINT msi_publish_icons(MSIPACKAGE *package)
3822 UINT r;
3823 MSIQUERY *view;
3825 static const WCHAR query[]= {
3826 'S','E','L','E','C','T',' ','*',' ',
3827 'F','R','O','M',' ','`','I','c','o','n','`',0};
3829 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3830 if (r == ERROR_SUCCESS)
3832 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3833 msiobj_release(&view->hdr);
3836 return ERROR_SUCCESS;
3839 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3841 UINT r;
3842 HKEY source;
3843 LPWSTR buffer;
3844 MSIMEDIADISK *disk;
3845 MSISOURCELISTINFO *info;
3847 r = RegCreateKeyW(hkey, szSourceList, &source);
3848 if (r != ERROR_SUCCESS)
3849 return r;
3851 RegCloseKey(source);
3853 buffer = strrchrW(package->PackagePath, '\\') + 1;
3854 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3855 package->Context, MSICODE_PRODUCT,
3856 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3857 if (r != ERROR_SUCCESS)
3858 return r;
3860 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3861 package->Context, MSICODE_PRODUCT,
3862 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3863 if (r != ERROR_SUCCESS)
3864 return r;
3866 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3867 package->Context, MSICODE_PRODUCT,
3868 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3869 if (r != ERROR_SUCCESS)
3870 return r;
3872 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3874 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3875 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3876 info->options, info->value);
3877 else
3878 MsiSourceListSetInfoW(package->ProductCode, NULL,
3879 info->context, info->options,
3880 info->property, info->value);
3883 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3885 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3886 disk->context, disk->options,
3887 disk->disk_id, disk->volume_label, disk->disk_prompt);
3890 return ERROR_SUCCESS;
3893 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3895 MSIHANDLE hdb, suminfo;
3896 WCHAR guids[MAX_PATH];
3897 WCHAR packcode[SQUISH_GUID_SIZE];
3898 LPWSTR buffer;
3899 LPWSTR ptr;
3900 DWORD langid;
3901 DWORD size;
3902 UINT r;
3904 static const WCHAR szProductLanguage[] =
3905 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3906 static const WCHAR szARPProductIcon[] =
3907 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3908 static const WCHAR szProductVersion[] =
3909 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3910 static const WCHAR szAssignment[] =
3911 {'A','s','s','i','g','n','m','e','n','t',0};
3912 static const WCHAR szAdvertiseFlags[] =
3913 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3914 static const WCHAR szClients[] =
3915 {'C','l','i','e','n','t','s',0};
3916 static const WCHAR szColon[] = {':',0};
3918 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3919 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3920 msi_free(buffer);
3922 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3923 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3925 /* FIXME */
3926 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3928 buffer = msi_dup_property(package->db, szARPProductIcon);
3929 if (buffer)
3931 LPWSTR path = build_icon_path(package,buffer);
3932 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3933 msi_free(path);
3934 msi_free(buffer);
3937 buffer = msi_dup_property(package->db, szProductVersion);
3938 if (buffer)
3940 DWORD verdword = msi_version_str_to_dword(buffer);
3941 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3942 msi_free(buffer);
3945 msi_reg_set_val_dword(hkey, szAssignment, 0);
3946 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3947 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3948 msi_reg_set_val_str(hkey, szClients, szColon);
3950 hdb = alloc_msihandle(&package->db->hdr);
3951 if (!hdb)
3952 return ERROR_NOT_ENOUGH_MEMORY;
3954 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3955 MsiCloseHandle(hdb);
3956 if (r != ERROR_SUCCESS)
3957 goto done;
3959 size = MAX_PATH;
3960 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3961 NULL, guids, &size);
3962 if (r != ERROR_SUCCESS)
3963 goto done;
3965 ptr = strchrW(guids, ';');
3966 if (ptr) *ptr = 0;
3967 squash_guid(guids, packcode);
3968 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3970 done:
3971 MsiCloseHandle(suminfo);
3972 return ERROR_SUCCESS;
3975 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3977 UINT r;
3978 HKEY hkey;
3979 LPWSTR upgrade;
3980 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3982 upgrade = msi_dup_property(package->db, szUpgradeCode);
3983 if (!upgrade)
3984 return ERROR_SUCCESS;
3986 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3988 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3989 if (r != ERROR_SUCCESS)
3990 goto done;
3992 else
3994 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3995 if (r != ERROR_SUCCESS)
3996 goto done;
3999 squash_guid(package->ProductCode, squashed_pc);
4000 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4002 RegCloseKey(hkey);
4004 done:
4005 msi_free(upgrade);
4006 return r;
4009 static BOOL msi_check_publish(MSIPACKAGE *package)
4011 MSIFEATURE *feature;
4013 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4015 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4016 return TRUE;
4019 return FALSE;
4022 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4024 MSIFEATURE *feature;
4026 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4028 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4029 return FALSE;
4032 return TRUE;
4035 static UINT msi_publish_patches( MSIPACKAGE *package )
4037 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4038 WCHAR patch_squashed[GUID_SIZE];
4039 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4040 LONG res;
4041 MSIPATCHINFO *patch;
4042 UINT r;
4043 WCHAR *p, *all_patches = NULL;
4044 DWORD len = 0;
4046 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4047 if (r != ERROR_SUCCESS)
4048 return ERROR_FUNCTION_FAILED;
4050 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4051 if (res != ERROR_SUCCESS)
4053 r = ERROR_FUNCTION_FAILED;
4054 goto done;
4057 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4058 if (r != ERROR_SUCCESS)
4059 goto done;
4061 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4063 squash_guid( patch->patchcode, patch_squashed );
4064 len += strlenW( patch_squashed ) + 1;
4067 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4068 if (!all_patches)
4069 goto done;
4071 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4073 HKEY patch_key;
4075 squash_guid( patch->patchcode, p );
4076 p += strlenW( p ) + 1;
4078 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4079 (const BYTE *)patch->transforms,
4080 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4081 if (res != ERROR_SUCCESS)
4082 goto done;
4084 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4085 if (r != ERROR_SUCCESS)
4086 goto done;
4088 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4089 (const BYTE *)patch->localfile,
4090 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4091 RegCloseKey( patch_key );
4092 if (res != ERROR_SUCCESS)
4093 goto done;
4095 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4096 if (res != ERROR_SUCCESS)
4097 goto done;
4099 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4100 RegCloseKey( patch_key );
4101 if (res != ERROR_SUCCESS)
4102 goto done;
4105 all_patches[len] = 0;
4106 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4107 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4108 if (res != ERROR_SUCCESS)
4109 goto done;
4111 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4112 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4113 if (res != ERROR_SUCCESS)
4114 r = ERROR_FUNCTION_FAILED;
4116 done:
4117 RegCloseKey( product_patches_key );
4118 RegCloseKey( patches_key );
4119 RegCloseKey( product_key );
4120 msi_free( all_patches );
4121 return r;
4125 * 99% of the work done here is only done for
4126 * advertised installs. However this is where the
4127 * Icon table is processed and written out
4128 * so that is what I am going to do here.
4130 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4132 UINT rc;
4133 HKEY hukey = NULL, hudkey = NULL;
4134 MSIRECORD *uirow;
4136 if (!list_empty(&package->patches))
4138 rc = msi_publish_patches(package);
4139 if (rc != ERROR_SUCCESS)
4140 goto end;
4143 /* FIXME: also need to publish if the product is in advertise mode */
4144 if (!msi_check_publish(package))
4145 return ERROR_SUCCESS;
4147 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4148 &hukey, TRUE);
4149 if (rc != ERROR_SUCCESS)
4150 goto end;
4152 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4153 NULL, &hudkey, TRUE);
4154 if (rc != ERROR_SUCCESS)
4155 goto end;
4157 rc = msi_publish_upgrade_code(package);
4158 if (rc != ERROR_SUCCESS)
4159 goto end;
4161 rc = msi_publish_product_properties(package, hukey);
4162 if (rc != ERROR_SUCCESS)
4163 goto end;
4165 rc = msi_publish_sourcelist(package, hukey);
4166 if (rc != ERROR_SUCCESS)
4167 goto end;
4169 rc = msi_publish_icons(package);
4171 end:
4172 uirow = MSI_CreateRecord( 1 );
4173 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4174 ui_actiondata( package, szPublishProduct, uirow );
4175 msiobj_release( &uirow->hdr );
4177 RegCloseKey(hukey);
4178 RegCloseKey(hudkey);
4180 return rc;
4183 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4185 WCHAR *filename, *ptr, *folder, *ret;
4186 const WCHAR *dirprop;
4188 filename = msi_dup_record_field( row, 2 );
4189 if (filename && (ptr = strchrW( filename, '|' )))
4190 ptr++;
4191 else
4192 ptr = filename;
4194 dirprop = MSI_RecordGetString( row, 3 );
4195 if (dirprop)
4197 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
4198 if (!folder)
4199 folder = msi_dup_property( package->db, dirprop );
4201 else
4202 folder = msi_dup_property( package->db, szWindowsFolder );
4204 if (!folder)
4206 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4207 msi_free( filename );
4208 return NULL;
4211 ret = build_directory_name( 2, folder, ptr );
4213 msi_free( filename );
4214 msi_free( folder );
4215 return ret;
4218 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4220 MSIPACKAGE *package = param;
4221 LPCWSTR component, section, key, value, identifier;
4222 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4223 MSIRECORD * uirow;
4224 INT action;
4225 MSICOMPONENT *comp;
4227 component = MSI_RecordGetString(row, 8);
4228 comp = get_loaded_component(package,component);
4229 if (!comp)
4230 return ERROR_SUCCESS;
4232 if (!comp->Enabled)
4234 TRACE("component is disabled\n");
4235 return ERROR_SUCCESS;
4238 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4240 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4241 comp->Action = comp->Installed;
4242 return ERROR_SUCCESS;
4244 comp->Action = INSTALLSTATE_LOCAL;
4246 identifier = MSI_RecordGetString(row,1);
4247 section = MSI_RecordGetString(row,4);
4248 key = MSI_RecordGetString(row,5);
4249 value = MSI_RecordGetString(row,6);
4250 action = MSI_RecordGetInteger(row,7);
4252 deformat_string(package,section,&deformated_section);
4253 deformat_string(package,key,&deformated_key);
4254 deformat_string(package,value,&deformated_value);
4256 fullname = get_ini_file_name(package, row);
4258 if (action == 0)
4260 TRACE("Adding value %s to section %s in %s\n",
4261 debugstr_w(deformated_key), debugstr_w(deformated_section),
4262 debugstr_w(fullname));
4263 WritePrivateProfileStringW(deformated_section, deformated_key,
4264 deformated_value, fullname);
4266 else if (action == 1)
4268 WCHAR returned[10];
4269 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4270 returned, 10, fullname);
4271 if (returned[0] == 0)
4273 TRACE("Adding value %s to section %s in %s\n",
4274 debugstr_w(deformated_key), debugstr_w(deformated_section),
4275 debugstr_w(fullname));
4277 WritePrivateProfileStringW(deformated_section, deformated_key,
4278 deformated_value, fullname);
4281 else if (action == 3)
4282 FIXME("Append to existing section not yet implemented\n");
4284 uirow = MSI_CreateRecord(4);
4285 MSI_RecordSetStringW(uirow,1,identifier);
4286 MSI_RecordSetStringW(uirow,2,deformated_section);
4287 MSI_RecordSetStringW(uirow,3,deformated_key);
4288 MSI_RecordSetStringW(uirow,4,deformated_value);
4289 ui_actiondata(package,szWriteIniValues,uirow);
4290 msiobj_release( &uirow->hdr );
4292 msi_free(fullname);
4293 msi_free(deformated_key);
4294 msi_free(deformated_value);
4295 msi_free(deformated_section);
4296 return ERROR_SUCCESS;
4299 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4301 UINT rc;
4302 MSIQUERY * view;
4303 static const WCHAR ExecSeqQuery[] =
4304 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4305 '`','I','n','i','F','i','l','e','`',0};
4307 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4308 if (rc != ERROR_SUCCESS)
4310 TRACE("no IniFile table\n");
4311 return ERROR_SUCCESS;
4314 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4315 msiobj_release(&view->hdr);
4316 return rc;
4319 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4321 MSIPACKAGE *package = param;
4322 LPCWSTR component, section, key, value, identifier;
4323 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4324 MSICOMPONENT *comp;
4325 MSIRECORD *uirow;
4326 INT action;
4328 component = MSI_RecordGetString( row, 8 );
4329 comp = get_loaded_component( package, component );
4330 if (!comp)
4331 return ERROR_SUCCESS;
4333 if (!comp->Enabled)
4335 TRACE("component is disabled\n");
4336 return ERROR_SUCCESS;
4339 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4341 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4342 comp->Action = comp->Installed;
4343 return ERROR_SUCCESS;
4345 comp->Action = INSTALLSTATE_ABSENT;
4347 identifier = MSI_RecordGetString( row, 1 );
4348 section = MSI_RecordGetString( row, 4 );
4349 key = MSI_RecordGetString( row, 5 );
4350 value = MSI_RecordGetString( row, 6 );
4351 action = MSI_RecordGetInteger( row, 7 );
4353 deformat_string( package, section, &deformated_section );
4354 deformat_string( package, key, &deformated_key );
4355 deformat_string( package, value, &deformated_value );
4357 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4359 filename = get_ini_file_name( package, row );
4361 TRACE("Removing key %s from section %s in %s\n",
4362 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4364 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4366 WARN("Unable to remove key %u\n", GetLastError());
4368 msi_free( filename );
4370 else
4371 FIXME("Unsupported action %d\n", action);
4374 uirow = MSI_CreateRecord( 4 );
4375 MSI_RecordSetStringW( uirow, 1, identifier );
4376 MSI_RecordSetStringW( uirow, 2, deformated_section );
4377 MSI_RecordSetStringW( uirow, 3, deformated_key );
4378 MSI_RecordSetStringW( uirow, 4, deformated_value );
4379 ui_actiondata( package, szRemoveIniValues, uirow );
4380 msiobj_release( &uirow->hdr );
4382 msi_free( deformated_key );
4383 msi_free( deformated_value );
4384 msi_free( deformated_section );
4385 return ERROR_SUCCESS;
4388 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4390 MSIPACKAGE *package = param;
4391 LPCWSTR component, section, key, value, identifier;
4392 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4393 MSICOMPONENT *comp;
4394 MSIRECORD *uirow;
4395 INT action;
4397 component = MSI_RecordGetString( row, 8 );
4398 comp = get_loaded_component( package, component );
4399 if (!comp)
4400 return ERROR_SUCCESS;
4402 if (!comp->Enabled)
4404 TRACE("component is disabled\n");
4405 return ERROR_SUCCESS;
4408 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4410 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4411 comp->Action = comp->Installed;
4412 return ERROR_SUCCESS;
4414 comp->Action = INSTALLSTATE_LOCAL;
4416 identifier = MSI_RecordGetString( row, 1 );
4417 section = MSI_RecordGetString( row, 4 );
4418 key = MSI_RecordGetString( row, 5 );
4419 value = MSI_RecordGetString( row, 6 );
4420 action = MSI_RecordGetInteger( row, 7 );
4422 deformat_string( package, section, &deformated_section );
4423 deformat_string( package, key, &deformated_key );
4424 deformat_string( package, value, &deformated_value );
4426 if (action == msidbIniFileActionRemoveLine)
4428 filename = get_ini_file_name( package, row );
4430 TRACE("Removing key %s from section %s in %s\n",
4431 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4433 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4435 WARN("Unable to remove key %u\n", GetLastError());
4437 msi_free( filename );
4439 else
4440 FIXME("Unsupported action %d\n", action);
4442 uirow = MSI_CreateRecord( 4 );
4443 MSI_RecordSetStringW( uirow, 1, identifier );
4444 MSI_RecordSetStringW( uirow, 2, deformated_section );
4445 MSI_RecordSetStringW( uirow, 3, deformated_key );
4446 MSI_RecordSetStringW( uirow, 4, deformated_value );
4447 ui_actiondata( package, szRemoveIniValues, uirow );
4448 msiobj_release( &uirow->hdr );
4450 msi_free( deformated_key );
4451 msi_free( deformated_value );
4452 msi_free( deformated_section );
4453 return ERROR_SUCCESS;
4456 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4458 UINT rc;
4459 MSIQUERY *view;
4460 static const WCHAR query[] =
4461 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4462 '`','I','n','i','F','i','l','e','`',0};
4463 static const WCHAR remove_query[] =
4464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4465 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4467 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4468 if (rc == ERROR_SUCCESS)
4470 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4471 msiobj_release( &view->hdr );
4472 if (rc != ERROR_SUCCESS)
4473 return rc;
4476 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4477 if (rc == ERROR_SUCCESS)
4479 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4480 msiobj_release( &view->hdr );
4481 if (rc != ERROR_SUCCESS)
4482 return rc;
4485 return ERROR_SUCCESS;
4488 static void register_dll( const WCHAR *dll, BOOL unregister )
4490 HMODULE hmod;
4492 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4493 if (hmod)
4495 HRESULT (WINAPI *func_ptr)( void );
4496 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4498 func_ptr = (void *)GetProcAddress( hmod, func );
4499 if (func_ptr)
4501 HRESULT hr = func_ptr();
4502 if (FAILED( hr ))
4503 WARN("failed to register dll 0x%08x\n", hr);
4505 else
4506 WARN("entry point %s not found\n", func);
4507 FreeLibrary( hmod );
4508 return;
4510 WARN("failed to load library %u\n", GetLastError());
4513 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4515 MSIPACKAGE *package = param;
4516 LPCWSTR filename;
4517 MSIFILE *file;
4518 MSIRECORD *uirow;
4520 filename = MSI_RecordGetString(row,1);
4521 file = get_loaded_file( package, filename );
4523 if (!file)
4525 ERR("Unable to find file id %s\n",debugstr_w(filename));
4526 return ERROR_SUCCESS;
4529 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4531 register_dll( file->TargetPath, FALSE );
4533 uirow = MSI_CreateRecord( 2 );
4534 MSI_RecordSetStringW( uirow, 1, filename );
4535 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4536 ui_actiondata( package, szSelfRegModules, uirow );
4537 msiobj_release( &uirow->hdr );
4539 return ERROR_SUCCESS;
4542 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4544 UINT rc;
4545 MSIQUERY * view;
4546 static const WCHAR ExecSeqQuery[] =
4547 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4548 '`','S','e','l','f','R','e','g','`',0};
4550 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4551 if (rc != ERROR_SUCCESS)
4553 TRACE("no SelfReg table\n");
4554 return ERROR_SUCCESS;
4557 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4558 msiobj_release(&view->hdr);
4560 return ERROR_SUCCESS;
4563 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4565 MSIPACKAGE *package = param;
4566 LPCWSTR filename;
4567 MSIFILE *file;
4568 MSIRECORD *uirow;
4570 filename = MSI_RecordGetString( row, 1 );
4571 file = get_loaded_file( package, filename );
4573 if (!file)
4575 ERR("Unable to find file id %s\n", debugstr_w(filename));
4576 return ERROR_SUCCESS;
4579 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4581 register_dll( file->TargetPath, TRUE );
4583 uirow = MSI_CreateRecord( 2 );
4584 MSI_RecordSetStringW( uirow, 1, filename );
4585 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4586 ui_actiondata( package, szSelfUnregModules, uirow );
4587 msiobj_release( &uirow->hdr );
4589 return ERROR_SUCCESS;
4592 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4594 UINT rc;
4595 MSIQUERY *view;
4596 static const WCHAR query[] =
4597 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4598 '`','S','e','l','f','R','e','g','`',0};
4600 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4601 if (rc != ERROR_SUCCESS)
4603 TRACE("no SelfReg table\n");
4604 return ERROR_SUCCESS;
4607 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4608 msiobj_release( &view->hdr );
4610 return ERROR_SUCCESS;
4613 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4615 MSIFEATURE *feature;
4616 UINT rc;
4617 HKEY hkey = NULL, userdata = NULL;
4619 if (!msi_check_publish(package))
4620 return ERROR_SUCCESS;
4622 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4623 &hkey, TRUE);
4624 if (rc != ERROR_SUCCESS)
4625 goto end;
4627 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4628 &userdata, TRUE);
4629 if (rc != ERROR_SUCCESS)
4630 goto end;
4632 /* here the guids are base 85 encoded */
4633 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4635 ComponentList *cl;
4636 LPWSTR data = NULL;
4637 GUID clsid;
4638 INT size;
4639 BOOL absent = FALSE;
4640 MSIRECORD *uirow;
4642 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4643 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4644 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4646 size = 1;
4647 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4649 size += 21;
4651 if (feature->Feature_Parent)
4652 size += strlenW( feature->Feature_Parent )+2;
4654 data = msi_alloc(size * sizeof(WCHAR));
4656 data[0] = 0;
4657 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4659 MSICOMPONENT* component = cl->component;
4660 WCHAR buf[21];
4662 buf[0] = 0;
4663 if (component->ComponentId)
4665 TRACE("From %s\n",debugstr_w(component->ComponentId));
4666 CLSIDFromString(component->ComponentId, &clsid);
4667 encode_base85_guid(&clsid,buf);
4668 TRACE("to %s\n",debugstr_w(buf));
4669 strcatW(data,buf);
4673 if (feature->Feature_Parent)
4675 static const WCHAR sep[] = {'\2',0};
4676 strcatW(data,sep);
4677 strcatW(data,feature->Feature_Parent);
4680 msi_reg_set_val_str( userdata, feature->Feature, data );
4681 msi_free(data);
4683 size = 0;
4684 if (feature->Feature_Parent)
4685 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4686 if (!absent)
4688 size += sizeof(WCHAR);
4689 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4690 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4692 else
4694 size += 2*sizeof(WCHAR);
4695 data = msi_alloc(size);
4696 data[0] = 0x6;
4697 data[1] = 0;
4698 if (feature->Feature_Parent)
4699 strcpyW( &data[1], feature->Feature_Parent );
4700 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4701 (LPBYTE)data,size);
4702 msi_free(data);
4705 /* the UI chunk */
4706 uirow = MSI_CreateRecord( 1 );
4707 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4708 ui_actiondata( package, szPublishFeatures, uirow);
4709 msiobj_release( &uirow->hdr );
4710 /* FIXME: call ui_progress? */
4713 end:
4714 RegCloseKey(hkey);
4715 RegCloseKey(userdata);
4716 return rc;
4719 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4721 UINT r;
4722 HKEY hkey;
4723 MSIRECORD *uirow;
4725 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4727 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4728 &hkey, FALSE);
4729 if (r == ERROR_SUCCESS)
4731 RegDeleteValueW(hkey, feature->Feature);
4732 RegCloseKey(hkey);
4735 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4736 &hkey, FALSE);
4737 if (r == ERROR_SUCCESS)
4739 RegDeleteValueW(hkey, feature->Feature);
4740 RegCloseKey(hkey);
4743 uirow = MSI_CreateRecord( 1 );
4744 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4745 ui_actiondata( package, szUnpublishFeatures, uirow );
4746 msiobj_release( &uirow->hdr );
4748 return ERROR_SUCCESS;
4751 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4753 MSIFEATURE *feature;
4755 if (!msi_check_unpublish(package))
4756 return ERROR_SUCCESS;
4758 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4760 msi_unpublish_feature(package, feature);
4763 return ERROR_SUCCESS;
4766 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4768 SYSTEMTIME systime;
4769 DWORD size, langid;
4770 WCHAR date[9], *val, *buffer;
4771 const WCHAR *prop, *key;
4773 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4774 static const WCHAR szWindowsInstaller[] =
4775 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4776 static const WCHAR modpath_fmt[] =
4777 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4778 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4779 static const WCHAR szModifyPath[] =
4780 {'M','o','d','i','f','y','P','a','t','h',0};
4781 static const WCHAR szUninstallString[] =
4782 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4783 static const WCHAR szEstimatedSize[] =
4784 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4785 static const WCHAR szProductLanguage[] =
4786 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4787 static const WCHAR szProductVersion[] =
4788 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4789 static const WCHAR szDisplayVersion[] =
4790 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4791 static const WCHAR szInstallSource[] =
4792 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4793 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4794 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4795 static const WCHAR szAuthorizedCDFPrefix[] =
4796 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4797 static const WCHAR szARPCONTACT[] =
4798 {'A','R','P','C','O','N','T','A','C','T',0};
4799 static const WCHAR szContact[] =
4800 {'C','o','n','t','a','c','t',0};
4801 static const WCHAR szARPCOMMENTS[] =
4802 {'A','R','P','C','O','M','M','E','N','T','S',0};
4803 static const WCHAR szComments[] =
4804 {'C','o','m','m','e','n','t','s',0};
4805 static const WCHAR szProductName[] =
4806 {'P','r','o','d','u','c','t','N','a','m','e',0};
4807 static const WCHAR szDisplayName[] =
4808 {'D','i','s','p','l','a','y','N','a','m','e',0};
4809 static const WCHAR szARPHELPLINK[] =
4810 {'A','R','P','H','E','L','P','L','I','N','K',0};
4811 static const WCHAR szHelpLink[] =
4812 {'H','e','l','p','L','i','n','k',0};
4813 static const WCHAR szARPHELPTELEPHONE[] =
4814 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4815 static const WCHAR szHelpTelephone[] =
4816 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4817 static const WCHAR szARPINSTALLLOCATION[] =
4818 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4819 static const WCHAR szInstallLocation[] =
4820 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4821 static const WCHAR szManufacturer[] =
4822 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4823 static const WCHAR szPublisher[] =
4824 {'P','u','b','l','i','s','h','e','r',0};
4825 static const WCHAR szARPREADME[] =
4826 {'A','R','P','R','E','A','D','M','E',0};
4827 static const WCHAR szReadme[] =
4828 {'R','e','a','d','M','e',0};
4829 static const WCHAR szARPSIZE[] =
4830 {'A','R','P','S','I','Z','E',0};
4831 static const WCHAR szSize[] =
4832 {'S','i','z','e',0};
4833 static const WCHAR szARPURLINFOABOUT[] =
4834 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4835 static const WCHAR szURLInfoAbout[] =
4836 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4837 static const WCHAR szARPURLUPDATEINFO[] =
4838 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4839 static const WCHAR szURLUpdateInfo[] =
4840 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4842 static const WCHAR *propval[] = {
4843 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4844 szARPCONTACT, szContact,
4845 szARPCOMMENTS, szComments,
4846 szProductName, szDisplayName,
4847 szARPHELPLINK, szHelpLink,
4848 szARPHELPTELEPHONE, szHelpTelephone,
4849 szARPINSTALLLOCATION, szInstallLocation,
4850 cszSourceDir, szInstallSource,
4851 szManufacturer, szPublisher,
4852 szARPREADME, szReadme,
4853 szARPSIZE, szSize,
4854 szARPURLINFOABOUT, szURLInfoAbout,
4855 szARPURLUPDATEINFO, szURLUpdateInfo,
4856 NULL
4858 const WCHAR **p = propval;
4860 while (*p)
4862 prop = *p++;
4863 key = *p++;
4864 val = msi_dup_property(package->db, prop);
4865 msi_reg_set_val_str(hkey, key, val);
4866 msi_free(val);
4869 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4871 size = deformat_string(package, modpath_fmt, &buffer);
4872 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4873 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4874 msi_free(buffer);
4876 /* FIXME: Write real Estimated Size when we have it */
4877 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4879 GetLocalTime(&systime);
4880 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4881 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4883 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4884 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4886 buffer = msi_dup_property(package->db, szProductVersion);
4887 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4888 if (buffer)
4890 DWORD verdword = msi_version_str_to_dword(buffer);
4892 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4893 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4894 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4895 msi_free(buffer);
4898 return ERROR_SUCCESS;
4901 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4903 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4904 MSIRECORD *uirow;
4905 LPWSTR upgrade_code;
4906 HKEY hkey, props;
4907 HKEY upgrade;
4908 UINT rc;
4910 /* FIXME: also need to publish if the product is in advertise mode */
4911 if (!msi_check_publish(package))
4912 return ERROR_SUCCESS;
4914 rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE);
4915 if (rc != ERROR_SUCCESS)
4916 return rc;
4918 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4919 NULL, &props, TRUE);
4920 if (rc != ERROR_SUCCESS)
4921 goto done;
4923 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4924 msi_free( package->db->localfile );
4925 package->db->localfile = NULL;
4927 rc = msi_publish_install_properties(package, hkey);
4928 if (rc != ERROR_SUCCESS)
4929 goto done;
4931 rc = msi_publish_install_properties(package, props);
4932 if (rc != ERROR_SUCCESS)
4933 goto done;
4935 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4936 if (upgrade_code)
4938 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4939 squash_guid(package->ProductCode, squashed_pc);
4940 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4941 RegCloseKey(upgrade);
4942 msi_free(upgrade_code);
4945 done:
4946 uirow = MSI_CreateRecord( 1 );
4947 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4948 ui_actiondata( package, szRegisterProduct, uirow );
4949 msiobj_release( &uirow->hdr );
4951 RegCloseKey(hkey);
4952 return ERROR_SUCCESS;
4955 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4957 return execute_script(package,INSTALL_SCRIPT);
4960 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4962 WCHAR *upgrade, **features;
4963 BOOL full_uninstall = TRUE;
4964 MSIFEATURE *feature;
4965 MSIPATCHINFO *patch;
4967 static const WCHAR szUpgradeCode[] =
4968 {'U','p','g','r','a','d','e','C','o','d','e',0};
4970 features = msi_split_string(remove, ',');
4971 if (!features)
4973 ERR("REMOVE feature list is empty!\n");
4974 return ERROR_FUNCTION_FAILED;
4977 if (!strcmpW( features[0], szAll ))
4978 full_uninstall = TRUE;
4979 else
4981 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4983 if (feature->Action != INSTALLSTATE_ABSENT)
4984 full_uninstall = FALSE;
4987 msi_free(features);
4989 if (!full_uninstall)
4990 return ERROR_SUCCESS;
4992 MSIREG_DeleteProductKey(package->ProductCode);
4993 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4994 MSIREG_DeleteUninstallKey(package);
4996 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4998 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4999 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5001 else
5003 MSIREG_DeleteUserProductKey(package->ProductCode);
5004 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5007 upgrade = msi_dup_property(package->db, szUpgradeCode);
5008 if (upgrade)
5010 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5011 msi_free(upgrade);
5014 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5016 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5019 return ERROR_SUCCESS;
5022 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5024 UINT rc;
5025 WCHAR *remove;
5027 /* turn off scheduling */
5028 package->script->CurrentlyScripting= FALSE;
5030 /* first do the same as an InstallExecute */
5031 rc = ACTION_InstallExecute(package);
5032 if (rc != ERROR_SUCCESS)
5033 return rc;
5035 /* then handle Commit Actions */
5036 rc = execute_script(package,COMMIT_SCRIPT);
5037 if (rc != ERROR_SUCCESS)
5038 return rc;
5040 remove = msi_dup_property(package->db, szRemove);
5041 if (remove)
5042 rc = msi_unpublish_product(package, remove);
5044 msi_free(remove);
5045 return rc;
5048 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5050 static const WCHAR RunOnce[] = {
5051 'S','o','f','t','w','a','r','e','\\',
5052 'M','i','c','r','o','s','o','f','t','\\',
5053 'W','i','n','d','o','w','s','\\',
5054 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5055 'R','u','n','O','n','c','e',0};
5056 static const WCHAR InstallRunOnce[] = {
5057 'S','o','f','t','w','a','r','e','\\',
5058 'M','i','c','r','o','s','o','f','t','\\',
5059 'W','i','n','d','o','w','s','\\',
5060 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5061 'I','n','s','t','a','l','l','e','r','\\',
5062 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5064 static const WCHAR msiexec_fmt[] = {
5065 '%','s',
5066 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5067 '\"','%','s','\"',0};
5068 static const WCHAR install_fmt[] = {
5069 '/','I',' ','\"','%','s','\"',' ',
5070 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5071 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5072 WCHAR buffer[256], sysdir[MAX_PATH];
5073 HKEY hkey;
5074 WCHAR squished_pc[100];
5076 squash_guid(package->ProductCode,squished_pc);
5078 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5079 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5080 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5081 squished_pc);
5083 msi_reg_set_val_str( hkey, squished_pc, buffer );
5084 RegCloseKey(hkey);
5086 TRACE("Reboot command %s\n",debugstr_w(buffer));
5088 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5089 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5091 msi_reg_set_val_str( hkey, squished_pc, buffer );
5092 RegCloseKey(hkey);
5094 return ERROR_INSTALL_SUSPEND;
5097 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5099 DWORD attrib;
5100 UINT rc;
5103 * We are currently doing what should be done here in the top level Install
5104 * however for Administrative and uninstalls this step will be needed
5106 if (!package->PackagePath)
5107 return ERROR_SUCCESS;
5109 msi_set_sourcedir_props(package, TRUE);
5111 attrib = GetFileAttributesW(package->db->path);
5112 if (attrib == INVALID_FILE_ATTRIBUTES)
5114 LPWSTR prompt;
5115 LPWSTR msg;
5116 DWORD size = 0;
5118 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5119 package->Context, MSICODE_PRODUCT,
5120 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5121 if (rc == ERROR_MORE_DATA)
5123 prompt = msi_alloc(size * sizeof(WCHAR));
5124 MsiSourceListGetInfoW(package->ProductCode, NULL,
5125 package->Context, MSICODE_PRODUCT,
5126 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5128 else
5129 prompt = strdupW(package->db->path);
5131 msg = generate_error_string(package,1302,1,prompt);
5132 while(attrib == INVALID_FILE_ATTRIBUTES)
5134 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5135 if (rc == IDCANCEL)
5137 rc = ERROR_INSTALL_USEREXIT;
5138 break;
5140 attrib = GetFileAttributesW(package->db->path);
5142 msi_free(prompt);
5143 rc = ERROR_SUCCESS;
5145 else
5146 return ERROR_SUCCESS;
5148 return rc;
5151 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5153 HKEY hkey = 0;
5154 LPWSTR buffer, productid = NULL;
5155 UINT i, rc = ERROR_SUCCESS;
5156 MSIRECORD *uirow;
5158 static const WCHAR szPropKeys[][80] =
5160 {'P','r','o','d','u','c','t','I','D',0},
5161 {'U','S','E','R','N','A','M','E',0},
5162 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5163 {0},
5166 static const WCHAR szRegKeys[][80] =
5168 {'P','r','o','d','u','c','t','I','D',0},
5169 {'R','e','g','O','w','n','e','r',0},
5170 {'R','e','g','C','o','m','p','a','n','y',0},
5171 {0},
5174 if (msi_check_unpublish(package))
5176 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5177 goto end;
5180 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5181 if (!productid)
5182 goto end;
5184 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5185 NULL, &hkey, TRUE);
5186 if (rc != ERROR_SUCCESS)
5187 goto end;
5189 for( i = 0; szPropKeys[i][0]; i++ )
5191 buffer = msi_dup_property( package->db, szPropKeys[i] );
5192 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5193 msi_free( buffer );
5196 end:
5197 uirow = MSI_CreateRecord( 1 );
5198 MSI_RecordSetStringW( uirow, 1, productid );
5199 ui_actiondata( package, szRegisterUser, uirow );
5200 msiobj_release( &uirow->hdr );
5202 msi_free(productid);
5203 RegCloseKey(hkey);
5204 return rc;
5208 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5210 UINT rc;
5212 package->script->InWhatSequence |= SEQUENCE_EXEC;
5213 rc = ACTION_ProcessExecSequence(package,FALSE);
5214 return rc;
5218 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5220 MSIPACKAGE *package = param;
5221 LPCWSTR compgroupid, component, feature, qualifier, text;
5222 LPWSTR advertise = NULL, output = NULL;
5223 HKEY hkey = NULL;
5224 UINT rc;
5225 MSICOMPONENT *comp;
5226 MSIFEATURE *feat;
5227 DWORD sz;
5228 MSIRECORD *uirow;
5230 feature = MSI_RecordGetString(rec, 5);
5231 feat = get_loaded_feature(package, feature);
5232 if (!feat)
5233 return ERROR_SUCCESS;
5235 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5236 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5237 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5239 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5240 feat->Action = feat->Installed;
5241 return ERROR_SUCCESS;
5244 component = MSI_RecordGetString(rec, 3);
5245 comp = get_loaded_component(package, component);
5246 if (!comp)
5247 return ERROR_SUCCESS;
5249 compgroupid = MSI_RecordGetString(rec,1);
5250 qualifier = MSI_RecordGetString(rec,2);
5252 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5253 if (rc != ERROR_SUCCESS)
5254 goto end;
5256 text = MSI_RecordGetString(rec,4);
5257 advertise = create_component_advertise_string(package, comp, feature);
5259 sz = strlenW(advertise);
5261 if (text)
5262 sz += lstrlenW(text);
5264 sz+=3;
5265 sz *= sizeof(WCHAR);
5267 output = msi_alloc_zero(sz);
5268 strcpyW(output,advertise);
5269 msi_free(advertise);
5271 if (text)
5272 strcatW(output,text);
5274 msi_reg_set_val_multi_str( hkey, qualifier, output );
5276 end:
5277 RegCloseKey(hkey);
5278 msi_free(output);
5280 /* the UI chunk */
5281 uirow = MSI_CreateRecord( 2 );
5282 MSI_RecordSetStringW( uirow, 1, compgroupid );
5283 MSI_RecordSetStringW( uirow, 2, qualifier);
5284 ui_actiondata( package, szPublishComponents, uirow);
5285 msiobj_release( &uirow->hdr );
5286 /* FIXME: call ui_progress? */
5288 return rc;
5292 * At present I am ignorning the advertised components part of this and only
5293 * focusing on the qualified component sets
5295 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5297 UINT rc;
5298 MSIQUERY * view;
5299 static const WCHAR ExecSeqQuery[] =
5300 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5301 '`','P','u','b','l','i','s','h',
5302 'C','o','m','p','o','n','e','n','t','`',0};
5304 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5305 if (rc != ERROR_SUCCESS)
5306 return ERROR_SUCCESS;
5308 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5309 msiobj_release(&view->hdr);
5311 return rc;
5314 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5316 static const WCHAR szInstallerComponents[] = {
5317 'S','o','f','t','w','a','r','e','\\',
5318 'M','i','c','r','o','s','o','f','t','\\',
5319 'I','n','s','t','a','l','l','e','r','\\',
5320 'C','o','m','p','o','n','e','n','t','s','\\',0};
5322 MSIPACKAGE *package = param;
5323 LPCWSTR compgroupid, component, feature, qualifier;
5324 MSICOMPONENT *comp;
5325 MSIFEATURE *feat;
5326 MSIRECORD *uirow;
5327 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5328 LONG res;
5330 feature = MSI_RecordGetString( rec, 5 );
5331 feat = get_loaded_feature( package, feature );
5332 if (!feat)
5333 return ERROR_SUCCESS;
5335 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5337 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5338 feat->Action = feat->Installed;
5339 return ERROR_SUCCESS;
5342 component = MSI_RecordGetString( rec, 3 );
5343 comp = get_loaded_component( package, component );
5344 if (!comp)
5345 return ERROR_SUCCESS;
5347 compgroupid = MSI_RecordGetString( rec, 1 );
5348 qualifier = MSI_RecordGetString( rec, 2 );
5350 squash_guid( compgroupid, squashed );
5351 strcpyW( keypath, szInstallerComponents );
5352 strcatW( keypath, squashed );
5354 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5355 if (res != ERROR_SUCCESS)
5357 WARN("Unable to delete component key %d\n", res);
5360 uirow = MSI_CreateRecord( 2 );
5361 MSI_RecordSetStringW( uirow, 1, compgroupid );
5362 MSI_RecordSetStringW( uirow, 2, qualifier );
5363 ui_actiondata( package, szUnpublishComponents, uirow );
5364 msiobj_release( &uirow->hdr );
5366 return ERROR_SUCCESS;
5369 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5371 UINT rc;
5372 MSIQUERY *view;
5373 static const WCHAR query[] =
5374 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5375 '`','P','u','b','l','i','s','h',
5376 'C','o','m','p','o','n','e','n','t','`',0};
5378 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5379 if (rc != ERROR_SUCCESS)
5380 return ERROR_SUCCESS;
5382 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5383 msiobj_release( &view->hdr );
5385 return rc;
5388 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5390 MSIPACKAGE *package = param;
5391 MSIRECORD *row;
5392 MSIFILE *file;
5393 SC_HANDLE hscm, service = NULL;
5394 LPCWSTR comp, key;
5395 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5396 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5397 DWORD serv_type, start_type, err_control;
5398 SERVICE_DESCRIPTIONW sd = {NULL};
5400 static const WCHAR query[] =
5401 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5402 '`','C','o','m','p','o','n','e','n','t','`',' ',
5403 'W','H','E','R','E',' ',
5404 '`','C','o','m','p','o','n','e','n','t','`',' ',
5405 '=','\'','%','s','\'',0};
5407 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5408 if (!hscm)
5410 ERR("Failed to open the SC Manager!\n");
5411 goto done;
5414 start_type = MSI_RecordGetInteger(rec, 5);
5415 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5416 goto done;
5418 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5419 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5420 serv_type = MSI_RecordGetInteger(rec, 4);
5421 err_control = MSI_RecordGetInteger(rec, 6);
5422 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5423 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5424 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5425 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5426 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5427 comp = MSI_RecordGetString(rec, 12);
5428 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5430 /* fetch the service path */
5431 row = MSI_QueryGetRecord(package->db, query, comp);
5432 if (!row)
5434 ERR("Control query failed!\n");
5435 goto done;
5437 key = MSI_RecordGetString(row, 6);
5439 file = get_loaded_file(package, key);
5440 msiobj_release(&row->hdr);
5441 if (!file)
5443 ERR("Failed to load the service file\n");
5444 goto done;
5447 if (!args || !args[0]) image_path = file->TargetPath;
5448 else
5450 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5451 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5452 return ERROR_OUTOFMEMORY;
5454 strcpyW(image_path, file->TargetPath);
5455 strcatW(image_path, szSpace);
5456 strcatW(image_path, args);
5458 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5459 start_type, err_control, image_path, load_order,
5460 NULL, depends, serv_name, pass);
5462 if (!service)
5464 if (GetLastError() != ERROR_SERVICE_EXISTS)
5465 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5467 else if (sd.lpDescription)
5469 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5470 WARN("failed to set service description %u\n", GetLastError());
5473 if (image_path != file->TargetPath) msi_free(image_path);
5474 done:
5475 CloseServiceHandle(service);
5476 CloseServiceHandle(hscm);
5477 msi_free(name);
5478 msi_free(disp);
5479 msi_free(sd.lpDescription);
5480 msi_free(load_order);
5481 msi_free(serv_name);
5482 msi_free(pass);
5483 msi_free(depends);
5484 msi_free(args);
5486 return ERROR_SUCCESS;
5489 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5491 UINT rc;
5492 MSIQUERY * view;
5493 static const WCHAR ExecSeqQuery[] =
5494 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5495 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5497 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5498 if (rc != ERROR_SUCCESS)
5499 return ERROR_SUCCESS;
5501 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5502 msiobj_release(&view->hdr);
5504 return rc;
5507 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5508 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5510 LPCWSTR *vector, *temp_vector;
5511 LPWSTR p, q;
5512 DWORD sep_len;
5514 static const WCHAR separator[] = {'[','~',']',0};
5516 *numargs = 0;
5517 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5519 if (!args)
5520 return NULL;
5522 vector = msi_alloc(sizeof(LPWSTR));
5523 if (!vector)
5524 return NULL;
5526 p = args;
5529 (*numargs)++;
5530 vector[*numargs - 1] = p;
5532 if ((q = strstrW(p, separator)))
5534 *q = '\0';
5536 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5537 if (!temp_vector)
5539 msi_free(vector);
5540 return NULL;
5542 vector = temp_vector;
5544 p = q + sep_len;
5546 } while (q);
5548 return vector;
5551 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5553 MSIPACKAGE *package = param;
5554 MSICOMPONENT *comp;
5555 MSIRECORD *uirow;
5556 SC_HANDLE scm = NULL, service = NULL;
5557 LPCWSTR component, *vector = NULL;
5558 LPWSTR name, args, display_name = NULL;
5559 DWORD event, numargs, len;
5560 UINT r = ERROR_FUNCTION_FAILED;
5562 component = MSI_RecordGetString(rec, 6);
5563 comp = get_loaded_component(package, component);
5564 if (!comp)
5565 return ERROR_SUCCESS;
5567 if (!comp->Enabled)
5569 TRACE("component is disabled\n");
5570 return ERROR_SUCCESS;
5573 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5575 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5576 comp->Action = comp->Installed;
5577 return ERROR_SUCCESS;
5579 comp->Action = INSTALLSTATE_LOCAL;
5581 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5582 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5583 event = MSI_RecordGetInteger(rec, 3);
5585 if (!(event & msidbServiceControlEventStart))
5587 r = ERROR_SUCCESS;
5588 goto done;
5591 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5592 if (!scm)
5594 ERR("Failed to open the service control manager\n");
5595 goto done;
5598 len = 0;
5599 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5600 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5602 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5603 GetServiceDisplayNameW( scm, name, display_name, &len );
5606 service = OpenServiceW(scm, name, SERVICE_START);
5607 if (!service)
5609 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5610 goto done;
5613 vector = msi_service_args_to_vector(args, &numargs);
5615 if (!StartServiceW(service, numargs, vector) &&
5616 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5618 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5619 goto done;
5622 r = ERROR_SUCCESS;
5624 done:
5625 uirow = MSI_CreateRecord( 2 );
5626 MSI_RecordSetStringW( uirow, 1, display_name );
5627 MSI_RecordSetStringW( uirow, 2, name );
5628 ui_actiondata( package, szStartServices, uirow );
5629 msiobj_release( &uirow->hdr );
5631 CloseServiceHandle(service);
5632 CloseServiceHandle(scm);
5634 msi_free(name);
5635 msi_free(args);
5636 msi_free(vector);
5637 msi_free(display_name);
5638 return r;
5641 static UINT ACTION_StartServices( MSIPACKAGE *package )
5643 UINT rc;
5644 MSIQUERY *view;
5646 static const WCHAR query[] = {
5647 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5648 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5650 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5651 if (rc != ERROR_SUCCESS)
5652 return ERROR_SUCCESS;
5654 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5655 msiobj_release(&view->hdr);
5657 return rc;
5660 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5662 DWORD i, needed, count;
5663 ENUM_SERVICE_STATUSW *dependencies;
5664 SERVICE_STATUS ss;
5665 SC_HANDLE depserv;
5667 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5668 0, &needed, &count))
5669 return TRUE;
5671 if (GetLastError() != ERROR_MORE_DATA)
5672 return FALSE;
5674 dependencies = msi_alloc(needed);
5675 if (!dependencies)
5676 return FALSE;
5678 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5679 needed, &needed, &count))
5680 goto error;
5682 for (i = 0; i < count; i++)
5684 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5685 SERVICE_STOP | SERVICE_QUERY_STATUS);
5686 if (!depserv)
5687 goto error;
5689 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5690 goto error;
5693 return TRUE;
5695 error:
5696 msi_free(dependencies);
5697 return FALSE;
5700 static UINT stop_service( LPCWSTR name )
5702 SC_HANDLE scm = NULL, service = NULL;
5703 SERVICE_STATUS status;
5704 SERVICE_STATUS_PROCESS ssp;
5705 DWORD needed;
5707 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5708 if (!scm)
5710 WARN("Failed to open the SCM: %d\n", GetLastError());
5711 goto done;
5714 service = OpenServiceW(scm, name,
5715 SERVICE_STOP |
5716 SERVICE_QUERY_STATUS |
5717 SERVICE_ENUMERATE_DEPENDENTS);
5718 if (!service)
5720 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5721 goto done;
5724 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5725 sizeof(SERVICE_STATUS_PROCESS), &needed))
5727 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5728 goto done;
5731 if (ssp.dwCurrentState == SERVICE_STOPPED)
5732 goto done;
5734 stop_service_dependents(scm, service);
5736 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5737 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5739 done:
5740 CloseServiceHandle(service);
5741 CloseServiceHandle(scm);
5743 return ERROR_SUCCESS;
5746 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5748 MSIPACKAGE *package = param;
5749 MSICOMPONENT *comp;
5750 MSIRECORD *uirow;
5751 LPCWSTR component;
5752 LPWSTR name = NULL, display_name = NULL;
5753 DWORD event, len;
5754 SC_HANDLE scm;
5756 event = MSI_RecordGetInteger( rec, 3 );
5757 if (!(event & msidbServiceControlEventStop))
5758 return ERROR_SUCCESS;
5760 component = MSI_RecordGetString( rec, 6 );
5761 comp = get_loaded_component( package, component );
5762 if (!comp)
5763 return ERROR_SUCCESS;
5765 if (!comp->Enabled)
5767 TRACE("component is disabled\n");
5768 return ERROR_SUCCESS;
5771 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5773 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5774 comp->Action = comp->Installed;
5775 return ERROR_SUCCESS;
5777 comp->Action = INSTALLSTATE_ABSENT;
5779 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5780 if (!scm)
5782 ERR("Failed to open the service control manager\n");
5783 goto done;
5786 len = 0;
5787 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5788 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5790 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5791 GetServiceDisplayNameW( scm, name, display_name, &len );
5793 CloseServiceHandle( scm );
5795 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5796 stop_service( name );
5798 done:
5799 uirow = MSI_CreateRecord( 2 );
5800 MSI_RecordSetStringW( uirow, 1, display_name );
5801 MSI_RecordSetStringW( uirow, 2, name );
5802 ui_actiondata( package, szStopServices, uirow );
5803 msiobj_release( &uirow->hdr );
5805 msi_free( name );
5806 msi_free( display_name );
5807 return ERROR_SUCCESS;
5810 static UINT ACTION_StopServices( MSIPACKAGE *package )
5812 UINT rc;
5813 MSIQUERY *view;
5815 static const WCHAR query[] = {
5816 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5817 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5819 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5820 if (rc != ERROR_SUCCESS)
5821 return ERROR_SUCCESS;
5823 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5824 msiobj_release(&view->hdr);
5826 return rc;
5829 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5831 MSIPACKAGE *package = param;
5832 MSICOMPONENT *comp;
5833 MSIRECORD *uirow;
5834 LPCWSTR component;
5835 LPWSTR name = NULL, display_name = NULL;
5836 DWORD event, len;
5837 SC_HANDLE scm = NULL, service = NULL;
5839 event = MSI_RecordGetInteger( rec, 3 );
5840 if (!(event & msidbServiceControlEventDelete))
5841 return ERROR_SUCCESS;
5843 component = MSI_RecordGetString(rec, 6);
5844 comp = get_loaded_component(package, component);
5845 if (!comp)
5846 return ERROR_SUCCESS;
5848 if (!comp->Enabled)
5850 TRACE("component is disabled\n");
5851 return ERROR_SUCCESS;
5854 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5856 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5857 comp->Action = comp->Installed;
5858 return ERROR_SUCCESS;
5860 comp->Action = INSTALLSTATE_ABSENT;
5862 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5863 stop_service( name );
5865 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5866 if (!scm)
5868 WARN("Failed to open the SCM: %d\n", GetLastError());
5869 goto done;
5872 len = 0;
5873 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5874 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5876 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5877 GetServiceDisplayNameW( scm, name, display_name, &len );
5880 service = OpenServiceW( scm, name, DELETE );
5881 if (!service)
5883 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5884 goto done;
5887 if (!DeleteService( service ))
5888 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5890 done:
5891 uirow = MSI_CreateRecord( 2 );
5892 MSI_RecordSetStringW( uirow, 1, display_name );
5893 MSI_RecordSetStringW( uirow, 2, name );
5894 ui_actiondata( package, szDeleteServices, uirow );
5895 msiobj_release( &uirow->hdr );
5897 CloseServiceHandle( service );
5898 CloseServiceHandle( scm );
5899 msi_free( name );
5900 msi_free( display_name );
5902 return ERROR_SUCCESS;
5905 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5907 UINT rc;
5908 MSIQUERY *view;
5910 static const WCHAR query[] = {
5911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5912 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5914 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5915 if (rc != ERROR_SUCCESS)
5916 return ERROR_SUCCESS;
5918 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5919 msiobj_release( &view->hdr );
5921 return rc;
5924 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5926 MSIPACKAGE *package = param;
5927 LPWSTR driver, driver_path, ptr;
5928 WCHAR outpath[MAX_PATH];
5929 MSIFILE *driver_file = NULL, *setup_file = NULL;
5930 MSIRECORD *uirow;
5931 LPCWSTR desc, file_key;
5932 DWORD len, usage;
5933 UINT r = ERROR_SUCCESS;
5935 static const WCHAR driver_fmt[] = {
5936 'D','r','i','v','e','r','=','%','s',0};
5937 static const WCHAR setup_fmt[] = {
5938 'S','e','t','u','p','=','%','s',0};
5939 static const WCHAR usage_fmt[] = {
5940 'F','i','l','e','U','s','a','g','e','=','1',0};
5942 desc = MSI_RecordGetString(rec, 3);
5944 file_key = MSI_RecordGetString( rec, 4 );
5945 if (file_key) driver_file = get_loaded_file( package, file_key );
5947 file_key = MSI_RecordGetString( rec, 5 );
5948 if (file_key) setup_file = get_loaded_file( package, file_key );
5950 if (!driver_file)
5952 ERR("ODBC Driver entry not found!\n");
5953 return ERROR_FUNCTION_FAILED;
5956 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5957 if (setup_file)
5958 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5959 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5961 driver = msi_alloc(len * sizeof(WCHAR));
5962 if (!driver)
5963 return ERROR_OUTOFMEMORY;
5965 ptr = driver;
5966 lstrcpyW(ptr, desc);
5967 ptr += lstrlenW(ptr) + 1;
5969 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5970 ptr += len + 1;
5972 if (setup_file)
5974 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5975 ptr += len + 1;
5978 lstrcpyW(ptr, usage_fmt);
5979 ptr += lstrlenW(ptr) + 1;
5980 *ptr = '\0';
5982 driver_path = strdupW(driver_file->TargetPath);
5983 ptr = strrchrW(driver_path, '\\');
5984 if (ptr) *ptr = '\0';
5986 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5987 NULL, ODBC_INSTALL_COMPLETE, &usage))
5989 ERR("Failed to install SQL driver!\n");
5990 r = ERROR_FUNCTION_FAILED;
5993 uirow = MSI_CreateRecord( 5 );
5994 MSI_RecordSetStringW( uirow, 1, desc );
5995 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
5996 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
5997 ui_actiondata( package, szInstallODBC, uirow );
5998 msiobj_release( &uirow->hdr );
6000 msi_free(driver);
6001 msi_free(driver_path);
6003 return r;
6006 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6008 MSIPACKAGE *package = param;
6009 LPWSTR translator, translator_path, ptr;
6010 WCHAR outpath[MAX_PATH];
6011 MSIFILE *translator_file = NULL, *setup_file = NULL;
6012 MSIRECORD *uirow;
6013 LPCWSTR desc, file_key;
6014 DWORD len, usage;
6015 UINT r = ERROR_SUCCESS;
6017 static const WCHAR translator_fmt[] = {
6018 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6019 static const WCHAR setup_fmt[] = {
6020 'S','e','t','u','p','=','%','s',0};
6022 desc = MSI_RecordGetString(rec, 3);
6024 file_key = MSI_RecordGetString( rec, 4 );
6025 if (file_key) translator_file = get_loaded_file( package, file_key );
6027 file_key = MSI_RecordGetString( rec, 5 );
6028 if (file_key) setup_file = get_loaded_file( package, file_key );
6030 if (!translator_file)
6032 ERR("ODBC Translator entry not found!\n");
6033 return ERROR_FUNCTION_FAILED;
6036 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6037 if (setup_file)
6038 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6040 translator = msi_alloc(len * sizeof(WCHAR));
6041 if (!translator)
6042 return ERROR_OUTOFMEMORY;
6044 ptr = translator;
6045 lstrcpyW(ptr, desc);
6046 ptr += lstrlenW(ptr) + 1;
6048 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6049 ptr += len + 1;
6051 if (setup_file)
6053 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6054 ptr += len + 1;
6056 *ptr = '\0';
6058 translator_path = strdupW(translator_file->TargetPath);
6059 ptr = strrchrW(translator_path, '\\');
6060 if (ptr) *ptr = '\0';
6062 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6063 NULL, ODBC_INSTALL_COMPLETE, &usage))
6065 ERR("Failed to install SQL translator!\n");
6066 r = ERROR_FUNCTION_FAILED;
6069 uirow = MSI_CreateRecord( 5 );
6070 MSI_RecordSetStringW( uirow, 1, desc );
6071 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6072 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6073 ui_actiondata( package, szInstallODBC, uirow );
6074 msiobj_release( &uirow->hdr );
6076 msi_free(translator);
6077 msi_free(translator_path);
6079 return r;
6082 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6084 MSIPACKAGE *package = param;
6085 LPWSTR attrs;
6086 LPCWSTR desc, driver;
6087 WORD request = ODBC_ADD_SYS_DSN;
6088 INT registration;
6089 DWORD len;
6090 UINT r = ERROR_SUCCESS;
6091 MSIRECORD *uirow;
6093 static const WCHAR attrs_fmt[] = {
6094 'D','S','N','=','%','s',0 };
6096 desc = MSI_RecordGetString(rec, 3);
6097 driver = MSI_RecordGetString(rec, 4);
6098 registration = MSI_RecordGetInteger(rec, 5);
6100 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6101 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6103 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6104 attrs = msi_alloc(len * sizeof(WCHAR));
6105 if (!attrs)
6106 return ERROR_OUTOFMEMORY;
6108 len = sprintfW(attrs, attrs_fmt, desc);
6109 attrs[len + 1] = 0;
6111 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6113 ERR("Failed to install SQL data source!\n");
6114 r = ERROR_FUNCTION_FAILED;
6117 uirow = MSI_CreateRecord( 5 );
6118 MSI_RecordSetStringW( uirow, 1, desc );
6119 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6120 MSI_RecordSetInteger( uirow, 3, request );
6121 ui_actiondata( package, szInstallODBC, uirow );
6122 msiobj_release( &uirow->hdr );
6124 msi_free(attrs);
6126 return r;
6129 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6131 UINT rc;
6132 MSIQUERY *view;
6134 static const WCHAR driver_query[] = {
6135 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6136 'O','D','B','C','D','r','i','v','e','r',0 };
6138 static const WCHAR translator_query[] = {
6139 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6140 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6142 static const WCHAR source_query[] = {
6143 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6144 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6146 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6147 if (rc != ERROR_SUCCESS)
6148 return ERROR_SUCCESS;
6150 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6151 msiobj_release(&view->hdr);
6153 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6154 if (rc != ERROR_SUCCESS)
6155 return ERROR_SUCCESS;
6157 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6158 msiobj_release(&view->hdr);
6160 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6161 if (rc != ERROR_SUCCESS)
6162 return ERROR_SUCCESS;
6164 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6165 msiobj_release(&view->hdr);
6167 return rc;
6170 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6172 MSIPACKAGE *package = param;
6173 MSIRECORD *uirow;
6174 DWORD usage;
6175 LPCWSTR desc;
6177 desc = MSI_RecordGetString( rec, 3 );
6178 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6180 WARN("Failed to remove ODBC driver\n");
6182 else if (!usage)
6184 FIXME("Usage count reached 0\n");
6187 uirow = MSI_CreateRecord( 2 );
6188 MSI_RecordSetStringW( uirow, 1, desc );
6189 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6190 ui_actiondata( package, szRemoveODBC, uirow );
6191 msiobj_release( &uirow->hdr );
6193 return ERROR_SUCCESS;
6196 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6198 MSIPACKAGE *package = param;
6199 MSIRECORD *uirow;
6200 DWORD usage;
6201 LPCWSTR desc;
6203 desc = MSI_RecordGetString( rec, 3 );
6204 if (!SQLRemoveTranslatorW( desc, &usage ))
6206 WARN("Failed to remove ODBC translator\n");
6208 else if (!usage)
6210 FIXME("Usage count reached 0\n");
6213 uirow = MSI_CreateRecord( 2 );
6214 MSI_RecordSetStringW( uirow, 1, desc );
6215 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6216 ui_actiondata( package, szRemoveODBC, uirow );
6217 msiobj_release( &uirow->hdr );
6219 return ERROR_SUCCESS;
6222 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6224 MSIPACKAGE *package = param;
6225 MSIRECORD *uirow;
6226 LPWSTR attrs;
6227 LPCWSTR desc, driver;
6228 WORD request = ODBC_REMOVE_SYS_DSN;
6229 INT registration;
6230 DWORD len;
6232 static const WCHAR attrs_fmt[] = {
6233 'D','S','N','=','%','s',0 };
6235 desc = MSI_RecordGetString( rec, 3 );
6236 driver = MSI_RecordGetString( rec, 4 );
6237 registration = MSI_RecordGetInteger( rec, 5 );
6239 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6240 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6242 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6243 attrs = msi_alloc( len * sizeof(WCHAR) );
6244 if (!attrs)
6245 return ERROR_OUTOFMEMORY;
6247 FIXME("Use ODBCSourceAttribute table\n");
6249 len = sprintfW( attrs, attrs_fmt, desc );
6250 attrs[len + 1] = 0;
6252 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6254 WARN("Failed to remove ODBC data source\n");
6256 msi_free( attrs );
6258 uirow = MSI_CreateRecord( 3 );
6259 MSI_RecordSetStringW( uirow, 1, desc );
6260 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6261 MSI_RecordSetInteger( uirow, 3, request );
6262 ui_actiondata( package, szRemoveODBC, uirow );
6263 msiobj_release( &uirow->hdr );
6265 return ERROR_SUCCESS;
6268 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6270 UINT rc;
6271 MSIQUERY *view;
6273 static const WCHAR driver_query[] = {
6274 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6275 'O','D','B','C','D','r','i','v','e','r',0 };
6277 static const WCHAR translator_query[] = {
6278 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6279 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6281 static const WCHAR source_query[] = {
6282 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6283 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6285 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6286 if (rc != ERROR_SUCCESS)
6287 return ERROR_SUCCESS;
6289 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6290 msiobj_release( &view->hdr );
6292 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6293 if (rc != ERROR_SUCCESS)
6294 return ERROR_SUCCESS;
6296 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6297 msiobj_release( &view->hdr );
6299 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6300 if (rc != ERROR_SUCCESS)
6301 return ERROR_SUCCESS;
6303 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6304 msiobj_release( &view->hdr );
6306 return rc;
6309 #define ENV_ACT_SETALWAYS 0x1
6310 #define ENV_ACT_SETABSENT 0x2
6311 #define ENV_ACT_REMOVE 0x4
6312 #define ENV_ACT_REMOVEMATCH 0x8
6314 #define ENV_MOD_MACHINE 0x20000000
6315 #define ENV_MOD_APPEND 0x40000000
6316 #define ENV_MOD_PREFIX 0x80000000
6317 #define ENV_MOD_MASK 0xC0000000
6319 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6321 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6323 LPCWSTR cptr = *name;
6325 static const WCHAR prefix[] = {'[','~',']',0};
6326 static const int prefix_len = 3;
6328 *flags = 0;
6329 while (*cptr)
6331 if (*cptr == '=')
6332 *flags |= ENV_ACT_SETALWAYS;
6333 else if (*cptr == '+')
6334 *flags |= ENV_ACT_SETABSENT;
6335 else if (*cptr == '-')
6336 *flags |= ENV_ACT_REMOVE;
6337 else if (*cptr == '!')
6338 *flags |= ENV_ACT_REMOVEMATCH;
6339 else if (*cptr == '*')
6340 *flags |= ENV_MOD_MACHINE;
6341 else
6342 break;
6344 cptr++;
6345 (*name)++;
6348 if (!*cptr)
6350 ERR("Missing environment variable\n");
6351 return ERROR_FUNCTION_FAILED;
6354 if (*value)
6356 LPCWSTR ptr = *value;
6357 if (!strncmpW(ptr, prefix, prefix_len))
6359 if (ptr[prefix_len] == szSemiColon[0])
6361 *flags |= ENV_MOD_APPEND;
6362 *value += lstrlenW(prefix);
6364 else
6366 *value = NULL;
6369 else if (lstrlenW(*value) >= prefix_len)
6371 ptr += lstrlenW(ptr) - prefix_len;
6372 if (!strcmpW( ptr, prefix ))
6374 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6376 *flags |= ENV_MOD_PREFIX;
6377 /* the "[~]" will be removed by deformat_string */;
6379 else
6381 *value = NULL;
6387 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6388 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6389 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6390 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6392 ERR("Invalid flags: %08x\n", *flags);
6393 return ERROR_FUNCTION_FAILED;
6396 if (!*flags)
6397 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6399 return ERROR_SUCCESS;
6402 static UINT open_env_key( DWORD flags, HKEY *key )
6404 static const WCHAR user_env[] =
6405 {'E','n','v','i','r','o','n','m','e','n','t',0};
6406 static const WCHAR machine_env[] =
6407 {'S','y','s','t','e','m','\\',
6408 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6409 'C','o','n','t','r','o','l','\\',
6410 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6411 'E','n','v','i','r','o','n','m','e','n','t',0};
6412 const WCHAR *env;
6413 HKEY root;
6414 LONG res;
6416 if (flags & ENV_MOD_MACHINE)
6418 env = machine_env;
6419 root = HKEY_LOCAL_MACHINE;
6421 else
6423 env = user_env;
6424 root = HKEY_CURRENT_USER;
6427 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6428 if (res != ERROR_SUCCESS)
6430 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6431 return ERROR_FUNCTION_FAILED;
6434 return ERROR_SUCCESS;
6437 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6439 MSIPACKAGE *package = param;
6440 LPCWSTR name, value, component;
6441 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6442 DWORD flags, type, size;
6443 UINT res;
6444 HKEY env = NULL;
6445 MSICOMPONENT *comp;
6446 MSIRECORD *uirow;
6447 int action = 0;
6449 component = MSI_RecordGetString(rec, 4);
6450 comp = get_loaded_component(package, component);
6451 if (!comp)
6452 return ERROR_SUCCESS;
6454 if (!comp->Enabled)
6456 TRACE("component is disabled\n");
6457 return ERROR_SUCCESS;
6460 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6462 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6463 comp->Action = comp->Installed;
6464 return ERROR_SUCCESS;
6466 comp->Action = INSTALLSTATE_LOCAL;
6468 name = MSI_RecordGetString(rec, 2);
6469 value = MSI_RecordGetString(rec, 3);
6471 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6473 res = env_parse_flags(&name, &value, &flags);
6474 if (res != ERROR_SUCCESS || !value)
6475 goto done;
6477 if (value && !deformat_string(package, value, &deformatted))
6479 res = ERROR_OUTOFMEMORY;
6480 goto done;
6483 value = deformatted;
6485 res = open_env_key( flags, &env );
6486 if (res != ERROR_SUCCESS)
6487 goto done;
6489 if (flags & ENV_MOD_MACHINE)
6490 action |= 0x20000000;
6492 size = 0;
6493 type = REG_SZ;
6494 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6495 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6496 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6497 goto done;
6499 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6501 action = 0x2;
6503 /* Nothing to do. */
6504 if (!value)
6506 res = ERROR_SUCCESS;
6507 goto done;
6510 /* If we are appending but the string was empty, strip ; */
6511 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6513 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6514 newval = strdupW(value);
6515 if (!newval)
6517 res = ERROR_OUTOFMEMORY;
6518 goto done;
6521 else
6523 action = 0x1;
6525 /* Contrary to MSDN, +-variable to [~];path works */
6526 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6528 res = ERROR_SUCCESS;
6529 goto done;
6532 data = msi_alloc(size);
6533 if (!data)
6535 RegCloseKey(env);
6536 return ERROR_OUTOFMEMORY;
6539 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6540 if (res != ERROR_SUCCESS)
6541 goto done;
6543 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6545 action = 0x4;
6546 res = RegDeleteValueW(env, name);
6547 if (res != ERROR_SUCCESS)
6548 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6549 goto done;
6552 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6553 if (flags & ENV_MOD_MASK)
6555 DWORD mod_size;
6556 int multiplier = 0;
6557 if (flags & ENV_MOD_APPEND) multiplier++;
6558 if (flags & ENV_MOD_PREFIX) multiplier++;
6559 mod_size = lstrlenW(value) * multiplier;
6560 size += mod_size * sizeof(WCHAR);
6563 newval = msi_alloc(size);
6564 ptr = newval;
6565 if (!newval)
6567 res = ERROR_OUTOFMEMORY;
6568 goto done;
6571 if (flags & ENV_MOD_PREFIX)
6573 lstrcpyW(newval, value);
6574 ptr = newval + lstrlenW(value);
6575 action |= 0x80000000;
6578 lstrcpyW(ptr, data);
6580 if (flags & ENV_MOD_APPEND)
6582 lstrcatW(newval, value);
6583 action |= 0x40000000;
6586 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6587 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6588 if (res)
6590 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6593 done:
6594 uirow = MSI_CreateRecord( 3 );
6595 MSI_RecordSetStringW( uirow, 1, name );
6596 MSI_RecordSetStringW( uirow, 2, newval );
6597 MSI_RecordSetInteger( uirow, 3, action );
6598 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6599 msiobj_release( &uirow->hdr );
6601 if (env) RegCloseKey(env);
6602 msi_free(deformatted);
6603 msi_free(data);
6604 msi_free(newval);
6605 return res;
6608 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6610 UINT rc;
6611 MSIQUERY * view;
6612 static const WCHAR ExecSeqQuery[] =
6613 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6614 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6615 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6616 if (rc != ERROR_SUCCESS)
6617 return ERROR_SUCCESS;
6619 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6620 msiobj_release(&view->hdr);
6622 return rc;
6625 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6627 MSIPACKAGE *package = param;
6628 LPCWSTR name, value, component;
6629 LPWSTR deformatted = NULL;
6630 DWORD flags;
6631 HKEY env;
6632 MSICOMPONENT *comp;
6633 MSIRECORD *uirow;
6634 int action = 0;
6635 LONG res;
6636 UINT r;
6638 component = MSI_RecordGetString( rec, 4 );
6639 comp = get_loaded_component( package, component );
6640 if (!comp)
6641 return ERROR_SUCCESS;
6643 if (!comp->Enabled)
6645 TRACE("component is disabled\n");
6646 return ERROR_SUCCESS;
6649 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6651 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6652 comp->Action = comp->Installed;
6653 return ERROR_SUCCESS;
6655 comp->Action = INSTALLSTATE_ABSENT;
6657 name = MSI_RecordGetString( rec, 2 );
6658 value = MSI_RecordGetString( rec, 3 );
6660 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6662 r = env_parse_flags( &name, &value, &flags );
6663 if (r != ERROR_SUCCESS)
6664 return r;
6666 if (!(flags & ENV_ACT_REMOVE))
6668 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6669 return ERROR_SUCCESS;
6672 if (value && !deformat_string( package, value, &deformatted ))
6673 return ERROR_OUTOFMEMORY;
6675 value = deformatted;
6677 r = open_env_key( flags, &env );
6678 if (r != ERROR_SUCCESS)
6680 r = ERROR_SUCCESS;
6681 goto done;
6684 if (flags & ENV_MOD_MACHINE)
6685 action |= 0x20000000;
6687 TRACE("Removing %s\n", debugstr_w(name));
6689 res = RegDeleteValueW( env, name );
6690 if (res != ERROR_SUCCESS)
6692 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6693 r = ERROR_SUCCESS;
6696 done:
6697 uirow = MSI_CreateRecord( 3 );
6698 MSI_RecordSetStringW( uirow, 1, name );
6699 MSI_RecordSetStringW( uirow, 2, value );
6700 MSI_RecordSetInteger( uirow, 3, action );
6701 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6702 msiobj_release( &uirow->hdr );
6704 if (env) RegCloseKey( env );
6705 msi_free( deformatted );
6706 return r;
6709 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6711 UINT rc;
6712 MSIQUERY *view;
6713 static const WCHAR query[] =
6714 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6715 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6717 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6718 if (rc != ERROR_SUCCESS)
6719 return ERROR_SUCCESS;
6721 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6722 msiobj_release( &view->hdr );
6724 return rc;
6727 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6729 LPWSTR key, template, id;
6730 UINT r = ERROR_SUCCESS;
6732 id = msi_dup_property( package->db, szProductID );
6733 if (id)
6735 msi_free( id );
6736 return ERROR_SUCCESS;
6738 template = msi_dup_property( package->db, szPIDTemplate );
6739 key = msi_dup_property( package->db, szPIDKEY );
6741 if (key && template)
6743 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6744 r = msi_set_property( package->db, szProductID, key );
6746 msi_free( template );
6747 msi_free( key );
6748 return r;
6751 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6753 TRACE("\n");
6754 package->need_reboot = 1;
6755 return ERROR_SUCCESS;
6758 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6760 static const WCHAR szAvailableFreeReg[] =
6761 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6762 MSIRECORD *uirow;
6763 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6765 TRACE("%p %d kilobytes\n", package, space);
6767 uirow = MSI_CreateRecord( 1 );
6768 MSI_RecordSetInteger( uirow, 1, space );
6769 ui_actiondata( package, szAllocateRegistrySpace, uirow );
6770 msiobj_release( &uirow->hdr );
6772 return ERROR_SUCCESS;
6775 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6777 FIXME("%p\n", package);
6778 return ERROR_SUCCESS;
6781 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6783 FIXME("%p\n", package);
6784 return ERROR_SUCCESS;
6787 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6789 UINT r, count;
6790 MSIQUERY *view;
6792 static const WCHAR driver_query[] = {
6793 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6794 'O','D','B','C','D','r','i','v','e','r',0 };
6796 static const WCHAR translator_query[] = {
6797 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6798 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6800 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6801 if (r == ERROR_SUCCESS)
6803 count = 0;
6804 r = MSI_IterateRecords( view, &count, NULL, package );
6805 msiobj_release( &view->hdr );
6806 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6809 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6810 if (r == ERROR_SUCCESS)
6812 count = 0;
6813 r = MSI_IterateRecords( view, &count, NULL, package );
6814 msiobj_release( &view->hdr );
6815 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6818 return ERROR_SUCCESS;
6821 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6822 LPCSTR action, LPCWSTR table )
6824 static const WCHAR query[] = {
6825 'S','E','L','E','C','T',' ','*',' ',
6826 'F','R','O','M',' ','`','%','s','`',0 };
6827 MSIQUERY *view = NULL;
6828 DWORD count = 0;
6829 UINT r;
6831 r = MSI_OpenQuery( package->db, &view, query, table );
6832 if (r == ERROR_SUCCESS)
6834 r = MSI_IterateRecords(view, &count, NULL, package);
6835 msiobj_release(&view->hdr);
6838 if (count)
6839 FIXME("%s -> %u ignored %s table values\n",
6840 action, count, debugstr_w(table));
6842 return ERROR_SUCCESS;
6845 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6847 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6848 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6851 static UINT ACTION_BindImage( MSIPACKAGE *package )
6853 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6854 return msi_unimplemented_action_stub( package, "BindImage", table );
6857 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6859 static const WCHAR table[] = {
6860 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
6861 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6864 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6866 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6867 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6870 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6872 static const WCHAR table[] = {
6873 'M','s','i','A','s','s','e','m','b','l','y',0 };
6874 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6877 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6879 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6880 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6883 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6885 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6886 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6889 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6891 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6892 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6895 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6897 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6898 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6901 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6903 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6904 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6907 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
6909 static const struct
6911 const WCHAR *action;
6912 UINT (*handler)(MSIPACKAGE *);
6914 StandardActions[] =
6916 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6917 { szAppSearch, ACTION_AppSearch },
6918 { szBindImage, ACTION_BindImage },
6919 { szCCPSearch, ACTION_CCPSearch },
6920 { szCostFinalize, ACTION_CostFinalize },
6921 { szCostInitialize, ACTION_CostInitialize },
6922 { szCreateFolders, ACTION_CreateFolders },
6923 { szCreateShortcuts, ACTION_CreateShortcuts },
6924 { szDeleteServices, ACTION_DeleteServices },
6925 { szDisableRollback, ACTION_DisableRollback },
6926 { szDuplicateFiles, ACTION_DuplicateFiles },
6927 { szExecuteAction, ACTION_ExecuteAction },
6928 { szFileCost, ACTION_FileCost },
6929 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6930 { szForceReboot, ACTION_ForceReboot },
6931 { szInstallAdminPackage, ACTION_InstallAdminPackage },
6932 { szInstallExecute, ACTION_InstallExecute },
6933 { szInstallExecuteAgain, ACTION_InstallExecute },
6934 { szInstallFiles, ACTION_InstallFiles},
6935 { szInstallFinalize, ACTION_InstallFinalize },
6936 { szInstallInitialize, ACTION_InstallInitialize },
6937 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6938 { szInstallValidate, ACTION_InstallValidate },
6939 { szIsolateComponents, ACTION_IsolateComponents },
6940 { szLaunchConditions, ACTION_LaunchConditions },
6941 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6942 { szMoveFiles, ACTION_MoveFiles },
6943 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6944 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6945 { szInstallODBC, ACTION_InstallODBC },
6946 { szInstallServices, ACTION_InstallServices },
6947 { szPatchFiles, ACTION_PatchFiles },
6948 { szProcessComponents, ACTION_ProcessComponents },
6949 { szPublishComponents, ACTION_PublishComponents },
6950 { szPublishFeatures, ACTION_PublishFeatures },
6951 { szPublishProduct, ACTION_PublishProduct },
6952 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6953 { szRegisterComPlus, ACTION_RegisterComPlus},
6954 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6955 { szRegisterFonts, ACTION_RegisterFonts },
6956 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6957 { szRegisterProduct, ACTION_RegisterProduct },
6958 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6959 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6960 { szRegisterUser, ACTION_RegisterUser },
6961 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6962 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6963 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6964 { szRemoveFiles, ACTION_RemoveFiles },
6965 { szRemoveFolders, ACTION_RemoveFolders },
6966 { szRemoveIniValues, ACTION_RemoveIniValues },
6967 { szRemoveODBC, ACTION_RemoveODBC },
6968 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6969 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6970 { szResolveSource, ACTION_ResolveSource },
6971 { szRMCCPSearch, ACTION_RMCCPSearch },
6972 { szScheduleReboot, ACTION_ScheduleReboot },
6973 { szSelfRegModules, ACTION_SelfRegModules },
6974 { szSelfUnregModules, ACTION_SelfUnregModules },
6975 { szSetODBCFolders, ACTION_SetODBCFolders },
6976 { szStartServices, ACTION_StartServices },
6977 { szStopServices, ACTION_StopServices },
6978 { szUnpublishComponents, ACTION_UnpublishComponents },
6979 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6980 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6981 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6982 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6983 { szUnregisterFonts, ACTION_UnregisterFonts },
6984 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6985 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6986 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6987 { szValidateProductID, ACTION_ValidateProductID },
6988 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6989 { szWriteIniValues, ACTION_WriteIniValues },
6990 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6991 { NULL, NULL },
6994 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
6996 BOOL ret = FALSE;
6997 UINT i;
6999 i = 0;
7000 while (StandardActions[i].action != NULL)
7002 if (!strcmpW( StandardActions[i].action, action ))
7004 ui_actionstart( package, action );
7005 if (StandardActions[i].handler)
7007 ui_actioninfo( package, action, TRUE, 0 );
7008 *rc = StandardActions[i].handler( package );
7009 ui_actioninfo( package, action, FALSE, *rc );
7011 else
7013 FIXME("unhandled standard action %s\n", debugstr_w(action));
7014 *rc = ERROR_SUCCESS;
7016 ret = TRUE;
7017 break;
7019 i++;
7021 return ret;
7024 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7026 UINT rc = ERROR_SUCCESS;
7027 BOOL handled;
7029 TRACE("Performing action (%s)\n", debugstr_w(action));
7031 handled = ACTION_HandleStandardAction(package, action, &rc);
7033 if (!handled)
7034 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7036 if (!handled)
7038 WARN("unhandled msi action %s\n", debugstr_w(action));
7039 rc = ERROR_FUNCTION_NOT_CALLED;
7042 return rc;
7045 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7047 UINT rc = ERROR_SUCCESS;
7048 BOOL handled = FALSE;
7050 TRACE("Performing action (%s)\n", debugstr_w(action));
7052 handled = ACTION_HandleStandardAction(package, action, &rc);
7054 if (!handled)
7055 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7057 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7058 handled = TRUE;
7060 if (!handled)
7062 WARN("unhandled msi action %s\n", debugstr_w(action));
7063 rc = ERROR_FUNCTION_NOT_CALLED;
7066 return rc;
7069 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7071 UINT rc = ERROR_SUCCESS;
7072 MSIRECORD *row;
7074 static const WCHAR ExecSeqQuery[] =
7075 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7076 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7077 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7078 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7079 static const WCHAR UISeqQuery[] =
7080 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7081 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7082 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7083 ' ', '=',' ','%','i',0};
7085 if (needs_ui_sequence(package))
7086 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7087 else
7088 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7090 if (row)
7092 LPCWSTR action, cond;
7094 TRACE("Running the actions\n");
7096 /* check conditions */
7097 cond = MSI_RecordGetString(row, 2);
7099 /* this is a hack to skip errors in the condition code */
7100 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7102 msiobj_release(&row->hdr);
7103 return ERROR_SUCCESS;
7106 action = MSI_RecordGetString(row, 1);
7107 if (!action)
7109 ERR("failed to fetch action\n");
7110 msiobj_release(&row->hdr);
7111 return ERROR_FUNCTION_FAILED;
7114 if (needs_ui_sequence(package))
7115 rc = ACTION_PerformUIAction(package, action, -1);
7116 else
7117 rc = ACTION_PerformAction(package, action, -1);
7119 msiobj_release(&row->hdr);
7122 return rc;
7125 /****************************************************
7126 * TOP level entry points
7127 *****************************************************/
7129 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7130 LPCWSTR szCommandLine )
7132 UINT rc;
7133 BOOL ui_exists;
7135 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7136 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7138 msi_set_property( package->db, szAction, szInstall );
7140 package->script->InWhatSequence = SEQUENCE_INSTALL;
7142 if (szPackagePath)
7144 LPWSTR p, dir;
7145 LPCWSTR file;
7147 dir = strdupW(szPackagePath);
7148 p = strrchrW(dir, '\\');
7149 if (p)
7151 *(++p) = 0;
7152 file = szPackagePath + (p - dir);
7154 else
7156 msi_free(dir);
7157 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7158 GetCurrentDirectoryW(MAX_PATH, dir);
7159 lstrcatW(dir, szBackSlash);
7160 file = szPackagePath;
7163 msi_free( package->PackagePath );
7164 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7165 if (!package->PackagePath)
7167 msi_free(dir);
7168 return ERROR_OUTOFMEMORY;
7171 lstrcpyW(package->PackagePath, dir);
7172 lstrcatW(package->PackagePath, file);
7173 msi_free(dir);
7175 msi_set_sourcedir_props(package, FALSE);
7178 msi_parse_command_line( package, szCommandLine, FALSE );
7180 msi_apply_transforms( package );
7181 msi_apply_patches( package );
7183 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7185 TRACE("setting reinstall property\n");
7186 msi_set_property( package->db, szReinstall, szAll );
7189 /* properties may have been added by a transform */
7190 msi_clone_properties( package );
7192 msi_parse_command_line( package, szCommandLine, FALSE );
7193 msi_adjust_privilege_properties( package );
7194 msi_set_context( package );
7196 if (needs_ui_sequence( package))
7198 package->script->InWhatSequence |= SEQUENCE_UI;
7199 rc = ACTION_ProcessUISequence(package);
7200 ui_exists = ui_sequence_exists(package);
7201 if (rc == ERROR_SUCCESS || !ui_exists)
7203 package->script->InWhatSequence |= SEQUENCE_EXEC;
7204 rc = ACTION_ProcessExecSequence(package, ui_exists);
7207 else
7208 rc = ACTION_ProcessExecSequence(package, FALSE);
7210 package->script->CurrentlyScripting = FALSE;
7212 /* process the ending type action */
7213 if (rc == ERROR_SUCCESS)
7214 ACTION_PerformActionSequence(package, -1);
7215 else if (rc == ERROR_INSTALL_USEREXIT)
7216 ACTION_PerformActionSequence(package, -2);
7217 else if (rc == ERROR_INSTALL_SUSPEND)
7218 ACTION_PerformActionSequence(package, -4);
7219 else /* failed */
7220 ACTION_PerformActionSequence(package, -3);
7222 /* finish up running custom actions */
7223 ACTION_FinishCustomActions(package);
7225 if (rc == ERROR_SUCCESS && package->need_reboot)
7226 return ERROR_SUCCESS_REBOOT_REQUIRED;
7228 return rc;