msi: Don't try to install or remove ODBC drivers if the component is disabled.
[wine.git] / dlls / msi / action.c
blob2cccdc9d2109e9ce3bc51a5ac1b72521646421f6
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
768 WCHAR *transform;
770 if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
771 else
773 WCHAR *p = strrchrW( package->PackagePath, '\\' );
774 DWORD len = p - package->PackagePath + 1;
776 if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
778 msi_free( xforms );
779 msi_free( xform_list );
780 return ERROR_OUTOFMEMORY;
782 memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
783 memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
785 r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
786 if (transform != xforms[i]) msi_free( transform );
790 msi_free( xforms );
791 msi_free( xform_list );
793 return r;
796 static BOOL ui_sequence_exists( MSIPACKAGE *package )
798 MSIQUERY *view;
799 UINT rc;
801 static const WCHAR ExecSeqQuery [] =
802 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
803 '`','I','n','s','t','a','l','l',
804 'U','I','S','e','q','u','e','n','c','e','`',
805 ' ','W','H','E','R','E',' ',
806 '`','S','e','q','u','e','n','c','e','`',' ',
807 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
808 '`','S','e','q','u','e','n','c','e','`',0};
810 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
811 if (rc == ERROR_SUCCESS)
813 msiobj_release(&view->hdr);
814 return TRUE;
817 return FALSE;
820 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
822 LPWSTR source, check;
824 if (msi_get_property_int( package->db, szInstalled, 0 ))
826 HKEY hkey;
828 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
829 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
830 RegCloseKey( hkey );
832 else
834 LPWSTR p, db;
835 DWORD len;
837 db = msi_dup_property( package->db, szOriginalDatabase );
838 if (!db)
839 return ERROR_OUTOFMEMORY;
841 p = strrchrW( db, '\\' );
842 if (!p)
844 p = strrchrW( db, '/' );
845 if (!p)
847 msi_free(db);
848 return ERROR_SUCCESS;
852 len = p - db + 2;
853 source = msi_alloc( len * sizeof(WCHAR) );
854 lstrcpynW( source, db, len );
855 msi_free( db );
858 check = msi_dup_property( package->db, cszSourceDir );
859 if (!check || replace)
861 UINT r = msi_set_property( package->db, cszSourceDir, source );
862 if (r == ERROR_SUCCESS)
863 msi_reset_folders( package, TRUE );
865 msi_free( check );
867 check = msi_dup_property( package->db, cszSOURCEDIR );
868 if (!check || replace)
869 msi_set_property( package->db, cszSOURCEDIR, source );
871 msi_free( check );
872 msi_free( source );
874 return ERROR_SUCCESS;
877 static BOOL needs_ui_sequence(MSIPACKAGE *package)
879 INT level = msi_get_property_int(package->db, szUILevel, 0);
880 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
883 UINT msi_set_context(MSIPACKAGE *package)
885 int num;
887 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
889 num = msi_get_property_int(package->db, szAllUsers, 0);
890 if (num == 1 || num == 2)
891 package->Context = MSIINSTALLCONTEXT_MACHINE;
893 return ERROR_SUCCESS;
896 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
898 UINT rc;
899 LPCWSTR cond, action;
900 MSIPACKAGE *package = param;
902 action = MSI_RecordGetString(row,1);
903 if (!action)
905 ERR("Error is retrieving action name\n");
906 return ERROR_FUNCTION_FAILED;
909 /* check conditions */
910 cond = MSI_RecordGetString(row,2);
912 /* this is a hack to skip errors in the condition code */
913 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
915 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
916 return ERROR_SUCCESS;
919 if (needs_ui_sequence(package))
920 rc = ACTION_PerformUIAction(package, action, -1);
921 else
922 rc = ACTION_PerformAction(package, action, -1);
924 msi_dialog_check_messages( NULL );
926 if (package->CurrentInstallState != ERROR_SUCCESS)
927 rc = package->CurrentInstallState;
929 if (rc == ERROR_FUNCTION_NOT_CALLED)
930 rc = ERROR_SUCCESS;
932 if (rc != ERROR_SUCCESS)
933 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
935 return rc;
938 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
940 MSIQUERY * view;
941 UINT r;
942 static const WCHAR query[] =
943 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
944 '`','%','s','`',
945 ' ','W','H','E','R','E',' ',
946 '`','S','e','q','u','e','n','c','e','`',' ',
947 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
948 '`','S','e','q','u','e','n','c','e','`',0};
950 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
952 r = MSI_OpenQuery( package->db, &view, query, szTable );
953 if (r == ERROR_SUCCESS)
955 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
956 msiobj_release(&view->hdr);
959 return r;
962 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
964 MSIQUERY * view;
965 UINT rc;
966 static const WCHAR ExecSeqQuery[] =
967 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
968 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
969 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
970 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
971 'O','R','D','E','R',' ', 'B','Y',' ',
972 '`','S','e','q','u','e','n','c','e','`',0 };
973 static const WCHAR IVQuery[] =
974 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
975 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
976 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
977 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
978 ' ','\'', 'I','n','s','t','a','l','l',
979 'V','a','l','i','d','a','t','e','\'', 0};
980 INT seq = 0;
982 if (package->script->ExecuteSequenceRun)
984 TRACE("Execute Sequence already Run\n");
985 return ERROR_SUCCESS;
988 package->script->ExecuteSequenceRun = TRUE;
990 /* get the sequence number */
991 if (UIran)
993 MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
994 if( !row )
995 return ERROR_FUNCTION_FAILED;
996 seq = MSI_RecordGetInteger(row,1);
997 msiobj_release(&row->hdr);
1000 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1001 if (rc == ERROR_SUCCESS)
1003 TRACE("Running the actions\n");
1005 msi_set_property(package->db, cszSourceDir, NULL);
1007 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1008 msiobj_release(&view->hdr);
1011 return rc;
1014 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1016 MSIQUERY * view;
1017 UINT rc;
1018 static const WCHAR ExecSeqQuery [] =
1019 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1020 '`','I','n','s','t','a','l','l',
1021 'U','I','S','e','q','u','e','n','c','e','`',
1022 ' ','W','H','E','R','E',' ',
1023 '`','S','e','q','u','e','n','c','e','`',' ',
1024 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1025 '`','S','e','q','u','e','n','c','e','`',0};
1027 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1028 if (rc == ERROR_SUCCESS)
1030 TRACE("Running the actions\n");
1032 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
1033 msiobj_release(&view->hdr);
1036 return rc;
1039 /********************************************************
1040 * ACTION helper functions and functions that perform the actions
1041 *******************************************************/
1042 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1043 UINT* rc, UINT script, BOOL force )
1045 BOOL ret=FALSE;
1046 UINT arc;
1048 arc = ACTION_CustomAction(package, action, script, force);
1050 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1052 *rc = arc;
1053 ret = TRUE;
1055 return ret;
1059 * Actual Action Handlers
1062 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1064 MSIPACKAGE *package = param;
1065 LPCWSTR dir, component;
1066 LPWSTR full_path;
1067 MSIRECORD *uirow;
1068 MSIFOLDER *folder;
1069 MSICOMPONENT *comp;
1071 component = MSI_RecordGetString(row, 2);
1072 if (!component)
1073 return ERROR_SUCCESS;
1075 comp = get_loaded_component(package, component);
1076 if (!comp)
1077 return ERROR_SUCCESS;
1079 if (!comp->Enabled)
1081 TRACE("component is disabled\n");
1082 return ERROR_SUCCESS;
1085 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
1087 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
1088 comp->Action = comp->Installed;
1089 return ERROR_SUCCESS;
1091 comp->Action = INSTALLSTATE_LOCAL;
1093 dir = MSI_RecordGetString(row,1);
1094 if (!dir)
1096 ERR("Unable to get folder id\n");
1097 return ERROR_SUCCESS;
1100 uirow = MSI_CreateRecord(1);
1101 MSI_RecordSetStringW(uirow, 1, dir);
1102 ui_actiondata(package, szCreateFolders, uirow);
1103 msiobj_release(&uirow->hdr);
1105 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1106 if (!full_path)
1108 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1109 return ERROR_SUCCESS;
1112 TRACE("Folder is %s\n",debugstr_w(full_path));
1114 if (folder->State == 0)
1115 create_full_pathW(full_path);
1117 folder->State = 3;
1119 msi_free(full_path);
1120 return ERROR_SUCCESS;
1123 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1125 static const WCHAR query[] =
1126 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1127 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1128 UINT rc;
1129 MSIQUERY *view;
1131 /* create all the empty folders specified in the CreateFolder table */
1132 rc = MSI_DatabaseOpenViewW(package->db, query, &view );
1133 if (rc != ERROR_SUCCESS)
1134 return ERROR_SUCCESS;
1136 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1137 msiobj_release(&view->hdr);
1139 return rc;
1142 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
1144 MSIPACKAGE *package = param;
1145 LPCWSTR dir, component;
1146 LPWSTR full_path;
1147 MSIRECORD *uirow;
1148 MSIFOLDER *folder;
1149 MSICOMPONENT *comp;
1151 component = MSI_RecordGetString(row, 2);
1152 if (!component)
1153 return ERROR_SUCCESS;
1155 comp = get_loaded_component(package, component);
1156 if (!comp)
1157 return ERROR_SUCCESS;
1159 if (!comp->Enabled)
1161 TRACE("component is disabled\n");
1162 return ERROR_SUCCESS;
1165 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
1167 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
1168 comp->Action = comp->Installed;
1169 return ERROR_SUCCESS;
1171 comp->Action = INSTALLSTATE_ABSENT;
1173 dir = MSI_RecordGetString( row, 1 );
1174 if (!dir)
1176 ERR("Unable to get folder id\n");
1177 return ERROR_SUCCESS;
1180 full_path = resolve_folder( package, dir, FALSE, FALSE, TRUE, &folder );
1181 if (!full_path)
1183 ERR("Unable to resolve folder id %s\n", debugstr_w(dir));
1184 return ERROR_SUCCESS;
1187 TRACE("folder is %s\n", debugstr_w(full_path));
1189 uirow = MSI_CreateRecord( 1 );
1190 MSI_RecordSetStringW( uirow, 1, dir );
1191 ui_actiondata( package, szRemoveFolders, uirow );
1192 msiobj_release( &uirow->hdr );
1194 RemoveDirectoryW( full_path );
1195 folder->State = 0;
1197 msi_free( full_path );
1198 return ERROR_SUCCESS;
1201 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
1203 static const WCHAR query[] =
1204 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1205 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
1207 MSIQUERY *view;
1208 UINT rc;
1210 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
1211 if (rc != ERROR_SUCCESS)
1212 return ERROR_SUCCESS;
1214 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
1215 msiobj_release( &view->hdr );
1217 return rc;
1220 static UINT load_component( MSIRECORD *row, LPVOID param )
1222 MSIPACKAGE *package = param;
1223 MSICOMPONENT *comp;
1225 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1226 if (!comp)
1227 return ERROR_FUNCTION_FAILED;
1229 list_add_tail( &package->components, &comp->entry );
1231 /* fill in the data */
1232 comp->Component = msi_dup_record_field( row, 1 );
1234 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1236 comp->ComponentId = msi_dup_record_field( row, 2 );
1237 comp->Directory = msi_dup_record_field( row, 3 );
1238 comp->Attributes = MSI_RecordGetInteger(row,4);
1239 comp->Condition = msi_dup_record_field( row, 5 );
1240 comp->KeyPath = msi_dup_record_field( row, 6 );
1242 comp->Installed = INSTALLSTATE_UNKNOWN;
1243 comp->Action = INSTALLSTATE_UNKNOWN;
1244 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
1246 comp->assembly = load_assembly( package, comp );
1247 return ERROR_SUCCESS;
1250 static UINT load_all_components( MSIPACKAGE *package )
1252 static const WCHAR query[] = {
1253 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1254 '`','C','o','m','p','o','n','e','n','t','`',0 };
1255 MSIQUERY *view;
1256 UINT r;
1258 if (!list_empty(&package->components))
1259 return ERROR_SUCCESS;
1261 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1262 if (r != ERROR_SUCCESS)
1263 return r;
1265 r = MSI_IterateRecords(view, NULL, load_component, package);
1266 msiobj_release(&view->hdr);
1267 return r;
1270 typedef struct {
1271 MSIPACKAGE *package;
1272 MSIFEATURE *feature;
1273 } _ilfs;
1275 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1277 ComponentList *cl;
1279 cl = msi_alloc( sizeof (*cl) );
1280 if ( !cl )
1281 return ERROR_NOT_ENOUGH_MEMORY;
1282 cl->component = comp;
1283 list_add_tail( &feature->Components, &cl->entry );
1285 return ERROR_SUCCESS;
1288 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1290 FeatureList *fl;
1292 fl = msi_alloc( sizeof(*fl) );
1293 if ( !fl )
1294 return ERROR_NOT_ENOUGH_MEMORY;
1295 fl->feature = child;
1296 list_add_tail( &parent->Children, &fl->entry );
1298 return ERROR_SUCCESS;
1301 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1303 _ilfs* ilfs = param;
1304 LPCWSTR component;
1305 MSICOMPONENT *comp;
1307 component = MSI_RecordGetString(row,1);
1309 /* check to see if the component is already loaded */
1310 comp = get_loaded_component( ilfs->package, component );
1311 if (!comp)
1313 ERR("unknown component %s\n", debugstr_w(component));
1314 return ERROR_FUNCTION_FAILED;
1317 add_feature_component( ilfs->feature, comp );
1318 comp->Enabled = TRUE;
1320 return ERROR_SUCCESS;
1323 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1325 MSIFEATURE *feature;
1327 if ( !name )
1328 return NULL;
1330 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1332 if ( !strcmpW( feature->Feature, name ) )
1333 return feature;
1336 return NULL;
1339 static UINT load_feature(MSIRECORD * row, LPVOID param)
1341 MSIPACKAGE* package = param;
1342 MSIFEATURE* feature;
1343 static const WCHAR Query1[] =
1344 {'S','E','L','E','C','T',' ',
1345 '`','C','o','m','p','o','n','e','n','t','_','`',
1346 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1347 'C','o','m','p','o','n','e','n','t','s','`',' ',
1348 'W','H','E','R','E',' ',
1349 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1350 MSIQUERY * view;
1351 UINT rc;
1352 _ilfs ilfs;
1354 /* fill in the data */
1356 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1357 if (!feature)
1358 return ERROR_NOT_ENOUGH_MEMORY;
1360 list_init( &feature->Children );
1361 list_init( &feature->Components );
1363 feature->Feature = msi_dup_record_field( row, 1 );
1365 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1367 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1368 feature->Title = msi_dup_record_field( row, 3 );
1369 feature->Description = msi_dup_record_field( row, 4 );
1371 if (!MSI_RecordIsNull(row,5))
1372 feature->Display = MSI_RecordGetInteger(row,5);
1374 feature->Level= MSI_RecordGetInteger(row,6);
1375 feature->Directory = msi_dup_record_field( row, 7 );
1376 feature->Attributes = MSI_RecordGetInteger(row,8);
1378 feature->Installed = INSTALLSTATE_UNKNOWN;
1379 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1381 list_add_tail( &package->features, &feature->entry );
1383 /* load feature components */
1385 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1386 if (rc != ERROR_SUCCESS)
1387 return ERROR_SUCCESS;
1389 ilfs.package = package;
1390 ilfs.feature = feature;
1392 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1393 msiobj_release(&view->hdr);
1395 return ERROR_SUCCESS;
1398 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1400 MSIPACKAGE* package = param;
1401 MSIFEATURE *parent, *child;
1403 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1404 if (!child)
1405 return ERROR_FUNCTION_FAILED;
1407 if (!child->Feature_Parent)
1408 return ERROR_SUCCESS;
1410 parent = find_feature_by_name( package, child->Feature_Parent );
1411 if (!parent)
1412 return ERROR_FUNCTION_FAILED;
1414 add_feature_child( parent, child );
1415 return ERROR_SUCCESS;
1418 static UINT load_all_features( MSIPACKAGE *package )
1420 static const WCHAR query[] = {
1421 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1422 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1423 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1424 MSIQUERY *view;
1425 UINT r;
1427 if (!list_empty(&package->features))
1428 return ERROR_SUCCESS;
1430 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1431 if (r != ERROR_SUCCESS)
1432 return r;
1434 r = MSI_IterateRecords( view, NULL, load_feature, package );
1435 if (r != ERROR_SUCCESS)
1436 return r;
1438 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1439 msiobj_release( &view->hdr );
1441 return r;
1444 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1446 if (!p)
1447 return p;
1448 p = strchrW(p, ch);
1449 if (!p)
1450 return p;
1451 *p = 0;
1452 return p+1;
1455 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1457 static const WCHAR query[] = {
1458 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1459 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1460 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1461 MSIQUERY *view = NULL;
1462 MSIRECORD *row = NULL;
1463 UINT r;
1465 TRACE("%s\n", debugstr_w(file->File));
1467 r = MSI_OpenQuery(package->db, &view, query, file->File);
1468 if (r != ERROR_SUCCESS)
1469 goto done;
1471 r = MSI_ViewExecute(view, NULL);
1472 if (r != ERROR_SUCCESS)
1473 goto done;
1475 r = MSI_ViewFetch(view, &row);
1476 if (r != ERROR_SUCCESS)
1477 goto done;
1479 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1480 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1481 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1482 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1483 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1485 done:
1486 if (view) msiobj_release(&view->hdr);
1487 if (row) msiobj_release(&row->hdr);
1488 return r;
1491 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1493 MSIRECORD *row;
1494 static const WCHAR query[] = {
1495 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1496 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1497 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1499 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1500 if (!row)
1502 WARN("query failed\n");
1503 return ERROR_FUNCTION_FAILED;
1506 file->disk_id = MSI_RecordGetInteger( row, 1 );
1507 msiobj_release( &row->hdr );
1508 return ERROR_SUCCESS;
1511 static UINT load_file(MSIRECORD *row, LPVOID param)
1513 MSIPACKAGE* package = param;
1514 LPCWSTR component;
1515 MSIFILE *file;
1517 /* fill in the data */
1519 file = msi_alloc_zero( sizeof (MSIFILE) );
1520 if (!file)
1521 return ERROR_NOT_ENOUGH_MEMORY;
1523 file->File = msi_dup_record_field( row, 1 );
1525 component = MSI_RecordGetString( row, 2 );
1526 file->Component = get_loaded_component( package, component );
1528 if (!file->Component)
1530 WARN("Component not found: %s\n", debugstr_w(component));
1531 msi_free(file->File);
1532 msi_free(file);
1533 return ERROR_SUCCESS;
1536 file->FileName = msi_dup_record_field( row, 3 );
1537 reduce_to_longfilename( file->FileName );
1539 file->ShortName = msi_dup_record_field( row, 3 );
1540 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1542 file->FileSize = MSI_RecordGetInteger( row, 4 );
1543 file->Version = msi_dup_record_field( row, 5 );
1544 file->Language = msi_dup_record_field( row, 6 );
1545 file->Attributes = MSI_RecordGetInteger( row, 7 );
1546 file->Sequence = MSI_RecordGetInteger( row, 8 );
1548 file->state = msifs_invalid;
1550 /* if the compressed bits are not set in the file attributes,
1551 * then read the information from the package word count property
1553 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1555 file->IsCompressed = FALSE;
1557 else if (file->Attributes &
1558 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1560 file->IsCompressed = TRUE;
1562 else if (file->Attributes & msidbFileAttributesNoncompressed)
1564 file->IsCompressed = FALSE;
1566 else
1568 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1571 load_file_hash(package, file);
1572 load_file_disk_id(package, file);
1574 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1576 list_add_tail( &package->files, &file->entry );
1578 return ERROR_SUCCESS;
1581 static UINT load_all_files(MSIPACKAGE *package)
1583 MSIQUERY * view;
1584 UINT rc;
1585 static const WCHAR Query[] =
1586 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1587 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1588 '`','S','e','q','u','e','n','c','e','`', 0};
1590 if (!list_empty(&package->files))
1591 return ERROR_SUCCESS;
1593 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1594 if (rc != ERROR_SUCCESS)
1595 return ERROR_SUCCESS;
1597 rc = MSI_IterateRecords(view, NULL, load_file, package);
1598 msiobj_release(&view->hdr);
1600 return ERROR_SUCCESS;
1603 static UINT load_folder( MSIRECORD *row, LPVOID param )
1605 MSIPACKAGE *package = param;
1606 static WCHAR szEmpty[] = { 0 };
1607 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1608 MSIFOLDER *folder;
1610 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1611 if (!folder)
1612 return ERROR_NOT_ENOUGH_MEMORY;
1614 folder->Directory = msi_dup_record_field( row, 1 );
1616 TRACE("%s\n", debugstr_w(folder->Directory));
1618 p = msi_dup_record_field(row, 3);
1620 /* split src and target dir */
1621 tgt_short = p;
1622 src_short = folder_split_path( p, ':' );
1624 /* split the long and short paths */
1625 tgt_long = folder_split_path( tgt_short, '|' );
1626 src_long = folder_split_path( src_short, '|' );
1628 /* check for no-op dirs */
1629 if (tgt_short && !strcmpW( szDot, tgt_short ))
1630 tgt_short = szEmpty;
1631 if (src_short && !strcmpW( szDot, src_short ))
1632 src_short = szEmpty;
1634 if (!tgt_long)
1635 tgt_long = tgt_short;
1637 if (!src_short) {
1638 src_short = tgt_short;
1639 src_long = tgt_long;
1642 if (!src_long)
1643 src_long = src_short;
1645 /* FIXME: use the target short path too */
1646 folder->TargetDefault = strdupW(tgt_long);
1647 folder->SourceShortPath = strdupW(src_short);
1648 folder->SourceLongPath = strdupW(src_long);
1649 msi_free(p);
1651 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1652 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1653 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1655 folder->Parent = msi_dup_record_field( row, 2 );
1657 folder->Property = msi_dup_property( package->db, folder->Directory );
1659 list_add_tail( &package->folders, &folder->entry );
1661 TRACE("returning %p\n", folder);
1663 return ERROR_SUCCESS;
1666 static UINT load_all_folders( MSIPACKAGE *package )
1668 static const WCHAR query[] = {
1669 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1670 '`','D','i','r','e','c','t','o','r','y','`',0 };
1671 MSIQUERY *view;
1672 UINT r;
1674 if (!list_empty(&package->folders))
1675 return ERROR_SUCCESS;
1677 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1678 if (r != ERROR_SUCCESS)
1679 return r;
1681 r = MSI_IterateRecords(view, NULL, load_folder, package);
1682 msiobj_release(&view->hdr);
1683 return r;
1687 * I am not doing any of the costing functionality yet.
1688 * Mostly looking at doing the Component and Feature loading
1690 * The native MSI does A LOT of modification to tables here. Mostly adding
1691 * a lot of temporary columns to the Feature and Component tables.
1693 * note: Native msi also tracks the short filename. But I am only going to
1694 * track the long ones. Also looking at this directory table
1695 * it appears that the directory table does not get the parents
1696 * resolved base on property only based on their entries in the
1697 * directory table.
1699 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1701 static const WCHAR szCosting[] =
1702 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1704 msi_set_property( package->db, szCosting, szZero );
1705 msi_set_property( package->db, cszRootDrive, c_colon );
1707 load_all_folders( package );
1708 load_all_components( package );
1709 load_all_features( package );
1710 load_all_files( package );
1712 return ERROR_SUCCESS;
1715 static UINT execute_script(MSIPACKAGE *package, UINT script )
1717 UINT i;
1718 UINT rc = ERROR_SUCCESS;
1720 TRACE("Executing Script %i\n",script);
1722 if (!package->script)
1724 ERR("no script!\n");
1725 return ERROR_FUNCTION_FAILED;
1728 for (i = 0; i < package->script->ActionCount[script]; i++)
1730 LPWSTR action;
1731 action = package->script->Actions[script][i];
1732 ui_actionstart(package, action);
1733 TRACE("Executing Action (%s)\n",debugstr_w(action));
1734 rc = ACTION_PerformAction(package, action, script);
1735 if (rc != ERROR_SUCCESS)
1736 break;
1738 msi_free_action_script(package, script);
1739 return rc;
1742 static UINT ACTION_FileCost(MSIPACKAGE *package)
1744 return ERROR_SUCCESS;
1747 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1749 MSICOMPONENT *comp;
1750 INSTALLSTATE state;
1751 UINT r;
1753 state = MsiQueryProductStateW(package->ProductCode);
1755 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1757 if (!comp->Enabled || !comp->ComponentId)
1758 continue;
1760 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1761 comp->Installed = INSTALLSTATE_ABSENT;
1762 else
1764 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1765 package->Context, comp->ComponentId,
1766 &comp->Installed);
1767 if (r != ERROR_SUCCESS)
1768 comp->Installed = INSTALLSTATE_ABSENT;
1773 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1775 MSIFEATURE *feature;
1776 INSTALLSTATE state;
1778 state = MsiQueryProductStateW(package->ProductCode);
1780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1782 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1783 feature->Installed = INSTALLSTATE_ABSENT;
1784 else
1786 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1787 feature->Feature);
1792 static BOOL process_state_property(MSIPACKAGE* package, int level,
1793 LPCWSTR property, INSTALLSTATE state)
1795 LPWSTR override;
1796 MSIFEATURE *feature;
1798 override = msi_dup_property( package->db, property );
1799 if (!override)
1800 return FALSE;
1802 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1804 if (strcmpW( property, szRemove ) &&
1805 (feature->Level <= 0 || feature->Level > level))
1806 continue;
1808 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1810 if (!strcmpiW( override, szAll ))
1811 msi_feature_set_state(package, feature, state);
1812 else
1814 LPWSTR ptr = override;
1815 LPWSTR ptr2 = strchrW(override,',');
1817 while (ptr)
1819 int len = ptr2 - ptr;
1821 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1822 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1824 msi_feature_set_state(package, feature, state);
1825 break;
1827 if (ptr2)
1829 ptr=ptr2+1;
1830 ptr2 = strchrW(ptr,',');
1832 else
1833 break;
1837 msi_free(override);
1839 return TRUE;
1842 static BOOL process_overrides( MSIPACKAGE *package, int level )
1844 static const WCHAR szAddLocal[] =
1845 {'A','D','D','L','O','C','A','L',0};
1846 static const WCHAR szAddSource[] =
1847 {'A','D','D','S','O','U','R','C','E',0};
1848 static const WCHAR szAdvertise[] =
1849 {'A','D','V','E','R','T','I','S','E',0};
1850 BOOL ret = FALSE;
1852 /* all these activation/deactivation things happen in order and things
1853 * later on the list override things earlier on the list.
1855 * 0 INSTALLLEVEL processing
1856 * 1 ADDLOCAL
1857 * 2 REMOVE
1858 * 3 ADDSOURCE
1859 * 4 ADDDEFAULT
1860 * 5 REINSTALL
1861 * 6 ADVERTISE
1862 * 7 COMPADDLOCAL
1863 * 8 COMPADDSOURCE
1864 * 9 FILEADDLOCAL
1865 * 10 FILEADDSOURCE
1866 * 11 FILEADDDEFAULT
1868 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1869 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1870 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1871 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1872 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1874 if (ret)
1875 msi_set_property( package->db, szPreselected, szOne );
1877 return ret;
1880 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1882 int level;
1883 static const WCHAR szlevel[] =
1884 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1885 MSICOMPONENT* component;
1886 MSIFEATURE *feature;
1888 TRACE("Checking Install Level\n");
1890 level = msi_get_property_int(package->db, szlevel, 1);
1892 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1894 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1896 BOOL feature_state = ((feature->Level > 0) &&
1897 (feature->Level <= level));
1899 if (feature_state && feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1901 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1902 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1903 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1904 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1905 else
1906 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1910 /* disable child features of unselected parent features */
1911 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1913 FeatureList *fl;
1915 if (feature->Level > 0 && feature->Level <= level)
1916 continue;
1918 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1919 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1922 else
1924 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1926 BOOL selected = feature->Level > 0 && feature->Level <= level;
1928 if (selected && feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1930 msi_feature_set_state(package, feature, feature->Installed);
1936 * now we want to enable or disable components based on feature
1938 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1940 ComponentList *cl;
1942 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1943 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1944 feature->ActionRequest, feature->Action);
1946 if (!feature->Level)
1947 continue;
1949 /* features with components that have compressed files are made local */
1950 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1952 if (!cl->component->Enabled) continue;
1954 if (cl->component->ForceLocalState &&
1955 feature->ActionRequest == INSTALLSTATE_SOURCE)
1957 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1958 break;
1962 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1964 component = cl->component;
1966 if (!component->Enabled) continue;
1968 switch (feature->ActionRequest)
1970 case INSTALLSTATE_ABSENT:
1971 component->anyAbsent = 1;
1972 break;
1973 case INSTALLSTATE_ADVERTISED:
1974 component->hasAdvertiseFeature = 1;
1975 break;
1976 case INSTALLSTATE_SOURCE:
1977 component->hasSourceFeature = 1;
1978 break;
1979 case INSTALLSTATE_LOCAL:
1980 component->hasLocalFeature = 1;
1981 break;
1982 case INSTALLSTATE_DEFAULT:
1983 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1984 component->hasAdvertiseFeature = 1;
1985 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1986 component->hasSourceFeature = 1;
1987 else
1988 component->hasLocalFeature = 1;
1989 break;
1990 default:
1991 break;
1996 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1998 if (!component->Enabled) continue;
2000 /* check if it's local or source */
2001 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2002 (component->hasLocalFeature || component->hasSourceFeature))
2004 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2005 !component->ForceLocalState)
2006 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2007 else
2008 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2009 continue;
2012 /* if any feature is local, the component must be local too */
2013 if (component->hasLocalFeature)
2015 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2016 continue;
2019 if (component->hasSourceFeature)
2021 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2022 continue;
2025 if (component->hasAdvertiseFeature)
2027 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2028 continue;
2031 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2032 if (component->anyAbsent)
2033 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2036 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2038 if (!component->Enabled) continue;
2040 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2042 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2043 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2046 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
2047 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2050 return ERROR_SUCCESS;
2053 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2055 MSIPACKAGE *package = param;
2056 LPCWSTR name;
2057 LPWSTR path;
2058 MSIFOLDER *f;
2060 name = MSI_RecordGetString(row,1);
2062 f = get_loaded_folder(package, name);
2063 if (!f) return ERROR_SUCCESS;
2065 /* reset the ResolvedTarget */
2066 msi_free(f->ResolvedTarget);
2067 f->ResolvedTarget = NULL;
2069 /* This helper function now does ALL the work */
2070 TRACE("Dir %s ...\n",debugstr_w(name));
2071 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2072 TRACE("resolves to %s\n",debugstr_w(path));
2073 msi_free(path);
2075 return ERROR_SUCCESS;
2078 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2080 MSIPACKAGE *package = param;
2081 LPCWSTR name;
2082 MSIFEATURE *feature;
2084 name = MSI_RecordGetString( row, 1 );
2086 feature = get_loaded_feature( package, name );
2087 if (!feature)
2088 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2089 else
2091 LPCWSTR Condition;
2092 Condition = MSI_RecordGetString(row,3);
2094 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2096 int level = MSI_RecordGetInteger(row,2);
2097 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2098 feature->Level = level;
2101 return ERROR_SUCCESS;
2104 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2106 static const WCHAR name[] = {'\\',0};
2107 VS_FIXEDFILEINFO *ptr, *ret;
2108 LPVOID version;
2109 DWORD versize, handle;
2110 UINT sz;
2112 TRACE("%s\n", debugstr_w(filename));
2114 versize = GetFileVersionInfoSizeW( filename, &handle );
2115 if (!versize)
2116 return NULL;
2118 version = msi_alloc( versize );
2119 if (!version)
2120 return NULL;
2122 GetFileVersionInfoW( filename, 0, versize, version );
2124 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2126 msi_free( version );
2127 return NULL;
2130 ret = msi_alloc( sz );
2131 memcpy( ret, ptr, sz );
2133 msi_free( version );
2134 return ret;
2137 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2139 DWORD ms, ls;
2141 msi_parse_version_string( version, &ms, &ls );
2143 if (fi->dwFileVersionMS > ms) return 1;
2144 else if (fi->dwFileVersionMS < ms) return -1;
2145 else if (fi->dwFileVersionLS > ls) return 1;
2146 else if (fi->dwFileVersionLS < ls) return -1;
2147 return 0;
2150 static DWORD get_disk_file_size( LPCWSTR filename )
2152 HANDLE file;
2153 DWORD size;
2155 TRACE("%s\n", debugstr_w(filename));
2157 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2158 if (file == INVALID_HANDLE_VALUE)
2159 return INVALID_FILE_SIZE;
2161 size = GetFileSize( file, NULL );
2162 CloseHandle( file );
2163 return size;
2166 static BOOL hash_matches( MSIFILE *file )
2168 UINT r;
2169 MSIFILEHASHINFO hash;
2171 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2172 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2173 if (r != ERROR_SUCCESS)
2174 return FALSE;
2176 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2179 static WCHAR *get_temp_dir( void )
2181 static UINT id;
2182 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2184 GetTempPathW( MAX_PATH, tmp );
2185 for (;;)
2187 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2188 if (CreateDirectoryW( dir, NULL )) break;
2190 return strdupW( dir );
2193 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2195 MSIASSEMBLY *assembly = file->Component->assembly;
2197 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2199 msi_free( file->TargetPath );
2200 if (assembly && !assembly->application)
2202 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2203 file->TargetPath = build_directory_name( 2, assembly->tempdir, file->FileName );
2204 track_tempfile( package, file->TargetPath );
2206 else
2208 WCHAR *dir = resolve_folder( package, file->Component->Directory, FALSE, FALSE, TRUE, NULL );
2209 file->TargetPath = build_directory_name( 2, dir, file->FileName );
2210 msi_free( dir );
2213 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2216 static UINT set_file_install_states( MSIPACKAGE *package )
2218 VS_FIXEDFILEINFO *file_version;
2219 MSIFILE *file;
2221 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2223 MSICOMPONENT *comp = file->Component;
2224 DWORD file_size;
2226 if (!comp->Enabled) continue;
2228 if (file->IsCompressed)
2229 comp->ForceLocalState = TRUE;
2231 set_target_path( package, file );
2233 if ((comp->assembly && !comp->assembly->installed) ||
2234 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2236 file->state = msifs_missing;
2237 comp->Cost += file->FileSize;
2238 continue;
2240 if (file->Version && (file_version = msi_get_disk_file_version( file->TargetPath )))
2242 TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
2243 HIWORD(file_version->dwFileVersionMS),
2244 LOWORD(file_version->dwFileVersionMS),
2245 HIWORD(file_version->dwFileVersionLS),
2246 LOWORD(file_version->dwFileVersionLS));
2248 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2250 file->state = msifs_overwrite;
2251 comp->Cost += file->FileSize;
2253 else
2255 TRACE("Destination file version equal or greater, not overwriting\n");
2256 file->state = msifs_present;
2258 msi_free( file_version );
2259 continue;
2261 if ((file_size = get_disk_file_size( file->TargetPath )) != file->FileSize)
2263 file->state = msifs_overwrite;
2264 comp->Cost += file->FileSize - file_size;
2265 continue;
2267 if (file->hash.dwFileHashInfoSize && hash_matches( file ))
2269 TRACE("File hashes match, not overwriting\n");
2270 file->state = msifs_present;
2271 continue;
2273 file->state = msifs_overwrite;
2274 comp->Cost += file->FileSize - file_size;
2277 return ERROR_SUCCESS;
2281 * A lot is done in this function aside from just the costing.
2282 * The costing needs to be implemented at some point but for now I am going
2283 * to focus on the directory building
2286 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2288 static const WCHAR ExecSeqQuery[] =
2289 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2290 '`','D','i','r','e','c','t','o','r','y','`',0};
2291 static const WCHAR ConditionQuery[] =
2292 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2293 '`','C','o','n','d','i','t','i','o','n','`',0};
2294 static const WCHAR szCosting[] =
2295 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2296 static const WCHAR szlevel[] =
2297 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2298 static const WCHAR szOutOfDiskSpace[] =
2299 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2300 MSICOMPONENT *comp;
2301 UINT rc = ERROR_SUCCESS;
2302 MSIQUERY * view;
2303 LPWSTR level;
2305 TRACE("Building Directory properties\n");
2307 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2308 if (rc == ERROR_SUCCESS)
2310 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2311 package);
2312 msiobj_release(&view->hdr);
2315 TRACE("Evaluating component conditions\n");
2316 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2318 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2320 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2321 comp->Enabled = FALSE;
2323 else
2324 comp->Enabled = TRUE;
2327 /* read components states from the registry */
2328 ACTION_GetComponentInstallStates(package);
2329 ACTION_GetFeatureInstallStates(package);
2331 if (!process_overrides( package, msi_get_property_int( package->db, szlevel, 1 ) ))
2333 TRACE("Evaluating feature conditions\n");
2335 rc = MSI_DatabaseOpenViewW( package->db, ConditionQuery, &view );
2336 if (rc == ERROR_SUCCESS)
2338 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2339 msiobj_release( &view->hdr );
2343 TRACE("Calculating file install states\n");
2344 set_file_install_states( package );
2346 msi_set_property( package->db, szCosting, szOne );
2347 /* set default run level if not set */
2348 level = msi_dup_property( package->db, szlevel );
2349 if (!level)
2350 msi_set_property( package->db, szlevel, szOne );
2351 msi_free(level);
2353 /* FIXME: check volume disk space */
2354 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2356 return MSI_SetFeatureStates(package);
2359 /* OK this value is "interpreted" and then formatted based on the
2360 first few characters */
2361 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2362 DWORD *size)
2364 LPSTR data = NULL;
2366 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2368 if (value[1]=='x')
2370 LPWSTR ptr;
2371 CHAR byte[5];
2372 LPWSTR deformated = NULL;
2373 int count;
2375 deformat_string(package, &value[2], &deformated);
2377 /* binary value type */
2378 ptr = deformated;
2379 *type = REG_BINARY;
2380 if (strlenW(ptr)%2)
2381 *size = (strlenW(ptr)/2)+1;
2382 else
2383 *size = strlenW(ptr)/2;
2385 data = msi_alloc(*size);
2387 byte[0] = '0';
2388 byte[1] = 'x';
2389 byte[4] = 0;
2390 count = 0;
2391 /* if uneven pad with a zero in front */
2392 if (strlenW(ptr)%2)
2394 byte[2]= '0';
2395 byte[3]= *ptr;
2396 ptr++;
2397 data[count] = (BYTE)strtol(byte,NULL,0);
2398 count ++;
2399 TRACE("Uneven byte count\n");
2401 while (*ptr)
2403 byte[2]= *ptr;
2404 ptr++;
2405 byte[3]= *ptr;
2406 ptr++;
2407 data[count] = (BYTE)strtol(byte,NULL,0);
2408 count ++;
2410 msi_free(deformated);
2412 TRACE("Data %i bytes(%i)\n",*size,count);
2414 else
2416 LPWSTR deformated;
2417 LPWSTR p;
2418 DWORD d = 0;
2419 deformat_string(package, &value[1], &deformated);
2421 *type=REG_DWORD;
2422 *size = sizeof(DWORD);
2423 data = msi_alloc(*size);
2424 p = deformated;
2425 if (*p == '-')
2426 p++;
2427 while (*p)
2429 if ( (*p < '0') || (*p > '9') )
2430 break;
2431 d *= 10;
2432 d += (*p - '0');
2433 p++;
2435 if (deformated[0] == '-')
2436 d = -d;
2437 *(LPDWORD)data = d;
2438 TRACE("DWORD %i\n",*(LPDWORD)data);
2440 msi_free(deformated);
2443 else
2445 static const WCHAR szMulti[] = {'[','~',']',0};
2446 LPCWSTR ptr;
2447 *type=REG_SZ;
2449 if (value[0]=='#')
2451 if (value[1]=='%')
2453 ptr = &value[2];
2454 *type=REG_EXPAND_SZ;
2456 else
2457 ptr = &value[1];
2459 else
2460 ptr=value;
2462 if (strstrW(value, szMulti))
2463 *type = REG_MULTI_SZ;
2465 /* remove initial delimiter */
2466 if (!strncmpW(value, szMulti, 3))
2467 ptr = value + 3;
2469 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2471 /* add double NULL terminator */
2472 if (*type == REG_MULTI_SZ)
2474 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2475 data = msi_realloc_zero(data, *size);
2478 return data;
2481 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2483 const WCHAR *ret;
2485 switch (root)
2487 case -1:
2488 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2490 *root_key = HKEY_LOCAL_MACHINE;
2491 ret = szHLM;
2493 else
2495 *root_key = HKEY_CURRENT_USER;
2496 ret = szHCU;
2498 break;
2499 case 0:
2500 *root_key = HKEY_CLASSES_ROOT;
2501 ret = szHCR;
2502 break;
2503 case 1:
2504 *root_key = HKEY_CURRENT_USER;
2505 ret = szHCU;
2506 break;
2507 case 2:
2508 *root_key = HKEY_LOCAL_MACHINE;
2509 ret = szHLM;
2510 break;
2511 case 3:
2512 *root_key = HKEY_USERS;
2513 ret = szHU;
2514 break;
2515 default:
2516 ERR("Unknown root %i\n", root);
2517 return NULL;
2520 return ret;
2523 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2525 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2526 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2528 if (is_64bit && package->platform == PLATFORM_INTEL &&
2529 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2531 UINT size;
2532 WCHAR *path_32node;
2534 size = (strlenW( path ) + strlenW( szWow6432Node ) + 1) * sizeof(WCHAR);
2535 path_32node = msi_alloc( size );
2536 if (!path_32node)
2537 return NULL;
2539 memcpy( path_32node, path, len * sizeof(WCHAR) );
2540 path_32node[len] = 0;
2541 strcatW( path_32node, szWow6432Node );
2542 strcatW( path_32node, szBackSlash );
2543 strcatW( path_32node, path + len );
2544 return path_32node;
2547 return strdupW( path );
2550 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2552 MSIPACKAGE *package = param;
2553 LPSTR value_data = NULL;
2554 HKEY root_key, hkey;
2555 DWORD type,size;
2556 LPWSTR deformated, uikey, keypath;
2557 LPCWSTR szRoot, component, name, key, value;
2558 MSICOMPONENT *comp;
2559 MSIRECORD * uirow;
2560 INT root;
2561 BOOL check_first = FALSE;
2562 UINT rc;
2564 ui_progress(package,2,0,0,0);
2566 component = MSI_RecordGetString(row, 6);
2567 comp = get_loaded_component(package,component);
2568 if (!comp)
2569 return ERROR_SUCCESS;
2571 if (!comp->Enabled)
2573 TRACE("component is disabled\n");
2574 return ERROR_SUCCESS;
2577 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2579 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2580 comp->Action = comp->Installed;
2581 return ERROR_SUCCESS;
2583 comp->Action = INSTALLSTATE_LOCAL;
2585 name = MSI_RecordGetString(row, 4);
2586 if( MSI_RecordIsNull(row,5) && name )
2588 /* null values can have special meanings */
2589 if (name[0]=='-' && name[1] == 0)
2590 return ERROR_SUCCESS;
2591 else if ((name[0]=='+' && name[1] == 0) ||
2592 (name[0] == '*' && name[1] == 0))
2593 name = NULL;
2594 check_first = TRUE;
2597 root = MSI_RecordGetInteger(row,2);
2598 key = MSI_RecordGetString(row, 3);
2600 szRoot = get_root_key( package, root, &root_key );
2601 if (!szRoot)
2602 return ERROR_SUCCESS;
2604 deformat_string(package, key , &deformated);
2605 size = strlenW(deformated) + strlenW(szRoot) + 1;
2606 uikey = msi_alloc(size*sizeof(WCHAR));
2607 strcpyW(uikey,szRoot);
2608 strcatW(uikey,deformated);
2610 keypath = get_keypath( package, root_key, deformated );
2611 msi_free( deformated );
2612 if (RegCreateKeyW( root_key, keypath, &hkey ))
2614 ERR("Could not create key %s\n", debugstr_w(keypath));
2615 msi_free(uikey);
2616 return ERROR_SUCCESS;
2619 value = MSI_RecordGetString(row,5);
2620 if (value)
2621 value_data = parse_value(package, value, &type, &size);
2622 else
2624 value_data = (LPSTR)strdupW(szEmpty);
2625 size = sizeof(szEmpty);
2626 type = REG_SZ;
2629 deformat_string(package, name, &deformated);
2631 if (!check_first)
2633 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2634 debugstr_w(uikey));
2635 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2637 else
2639 DWORD sz = 0;
2640 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2641 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2643 TRACE("value %s of %s checked already exists\n",
2644 debugstr_w(deformated), debugstr_w(uikey));
2646 else
2648 TRACE("Checked and setting value %s of %s\n",
2649 debugstr_w(deformated), debugstr_w(uikey));
2650 if (deformated || size)
2651 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2654 RegCloseKey(hkey);
2656 uirow = MSI_CreateRecord(3);
2657 MSI_RecordSetStringW(uirow,2,deformated);
2658 MSI_RecordSetStringW(uirow,1,uikey);
2659 if (type == REG_SZ || type == REG_EXPAND_SZ)
2660 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2661 ui_actiondata(package,szWriteRegistryValues,uirow);
2662 msiobj_release( &uirow->hdr );
2664 msi_free(value_data);
2665 msi_free(deformated);
2666 msi_free(uikey);
2668 return ERROR_SUCCESS;
2671 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2673 UINT rc;
2674 MSIQUERY * view;
2675 static const WCHAR ExecSeqQuery[] =
2676 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2677 '`','R','e','g','i','s','t','r','y','`',0 };
2679 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2680 if (rc != ERROR_SUCCESS)
2681 return ERROR_SUCCESS;
2683 /* increment progress bar each time action data is sent */
2684 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2686 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2688 msiobj_release(&view->hdr);
2689 return rc;
2692 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2694 LONG res;
2695 HKEY hkey;
2696 DWORD num_subkeys, num_values;
2698 if (delete_key)
2700 if ((res = RegDeleteTreeW( hkey_root, key )))
2702 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2704 return;
2707 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2709 if ((res = RegDeleteValueW( hkey, value )))
2711 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2713 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2714 NULL, NULL, NULL, NULL );
2715 RegCloseKey( hkey );
2716 if (!res && !num_subkeys && !num_values)
2718 TRACE("Removing empty key %s\n", debugstr_w(key));
2719 RegDeleteKeyW( hkey_root, key );
2721 return;
2723 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2727 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2729 MSIPACKAGE *package = param;
2730 LPCWSTR component, name, key_str, root_key_str;
2731 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2732 MSICOMPONENT *comp;
2733 MSIRECORD *uirow;
2734 BOOL delete_key = FALSE;
2735 HKEY hkey_root;
2736 UINT size;
2737 INT root;
2739 ui_progress( package, 2, 0, 0, 0 );
2741 component = MSI_RecordGetString( row, 6 );
2742 comp = get_loaded_component( package, component );
2743 if (!comp)
2744 return ERROR_SUCCESS;
2746 if (!comp->Enabled)
2748 TRACE("component is disabled\n");
2749 return ERROR_SUCCESS;
2752 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
2754 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
2755 comp->Action = comp->Installed;
2756 return ERROR_SUCCESS;
2758 comp->Action = INSTALLSTATE_ABSENT;
2760 name = MSI_RecordGetString( row, 4 );
2761 if (MSI_RecordIsNull( row, 5 ) && name )
2763 if (name[0] == '+' && !name[1])
2764 return ERROR_SUCCESS;
2765 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2767 delete_key = TRUE;
2768 name = NULL;
2772 root = MSI_RecordGetInteger( row, 2 );
2773 key_str = MSI_RecordGetString( row, 3 );
2775 root_key_str = get_root_key( package, root, &hkey_root );
2776 if (!root_key_str)
2777 return ERROR_SUCCESS;
2779 deformat_string( package, key_str, &deformated_key );
2780 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2781 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2782 strcpyW( ui_key_str, root_key_str );
2783 strcatW( ui_key_str, deformated_key );
2785 deformat_string( package, name, &deformated_name );
2787 keypath = get_keypath( package, hkey_root, deformated_key );
2788 msi_free( deformated_key );
2789 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2790 msi_free( keypath );
2792 uirow = MSI_CreateRecord( 2 );
2793 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2794 MSI_RecordSetStringW( uirow, 2, deformated_name );
2796 ui_actiondata( package, szRemoveRegistryValues, uirow );
2797 msiobj_release( &uirow->hdr );
2799 msi_free( ui_key_str );
2800 msi_free( deformated_name );
2801 return ERROR_SUCCESS;
2804 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2806 MSIPACKAGE *package = param;
2807 LPCWSTR component, name, key_str, root_key_str;
2808 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2809 MSICOMPONENT *comp;
2810 MSIRECORD *uirow;
2811 BOOL delete_key = FALSE;
2812 HKEY hkey_root;
2813 UINT size;
2814 INT root;
2816 ui_progress( package, 2, 0, 0, 0 );
2818 component = MSI_RecordGetString( row, 5 );
2819 comp = get_loaded_component( package, component );
2820 if (!comp)
2821 return ERROR_SUCCESS;
2823 if (!comp->Enabled)
2825 TRACE("component is disabled\n");
2826 return ERROR_SUCCESS;
2829 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
2831 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
2832 comp->Action = comp->Installed;
2833 return ERROR_SUCCESS;
2835 comp->Action = INSTALLSTATE_LOCAL;
2837 if ((name = MSI_RecordGetString( row, 4 )))
2839 if (name[0] == '-' && !name[1])
2841 delete_key = TRUE;
2842 name = NULL;
2846 root = MSI_RecordGetInteger( row, 2 );
2847 key_str = MSI_RecordGetString( row, 3 );
2849 root_key_str = get_root_key( package, root, &hkey_root );
2850 if (!root_key_str)
2851 return ERROR_SUCCESS;
2853 deformat_string( package, key_str, &deformated_key );
2854 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2855 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2856 strcpyW( ui_key_str, root_key_str );
2857 strcatW( ui_key_str, deformated_key );
2859 deformat_string( package, name, &deformated_name );
2861 keypath = get_keypath( package, hkey_root, deformated_key );
2862 msi_free( deformated_key );
2863 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2864 msi_free( keypath );
2866 uirow = MSI_CreateRecord( 2 );
2867 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2868 MSI_RecordSetStringW( uirow, 2, deformated_name );
2870 ui_actiondata( package, szRemoveRegistryValues, uirow );
2871 msiobj_release( &uirow->hdr );
2873 msi_free( ui_key_str );
2874 msi_free( deformated_name );
2875 return ERROR_SUCCESS;
2878 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2880 UINT rc;
2881 MSIQUERY *view;
2882 static const WCHAR registry_query[] =
2883 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2884 '`','R','e','g','i','s','t','r','y','`',0 };
2885 static const WCHAR remove_registry_query[] =
2886 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2887 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0 };
2889 /* increment progress bar each time action data is sent */
2890 ui_progress( package, 1, REG_PROGRESS_VALUE, 1, 0 );
2892 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2893 if (rc == ERROR_SUCCESS)
2895 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2896 msiobj_release( &view->hdr );
2897 if (rc != ERROR_SUCCESS)
2898 return rc;
2901 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2902 if (rc == ERROR_SUCCESS)
2904 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2905 msiobj_release( &view->hdr );
2906 if (rc != ERROR_SUCCESS)
2907 return rc;
2910 return ERROR_SUCCESS;
2913 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2915 package->script->CurrentlyScripting = TRUE;
2917 return ERROR_SUCCESS;
2921 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2923 MSICOMPONENT *comp;
2924 DWORD progress = 0;
2925 DWORD total = 0;
2926 static const WCHAR q1[]=
2927 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2928 '`','R','e','g','i','s','t','r','y','`',0};
2929 UINT rc;
2930 MSIQUERY * view;
2931 MSIFEATURE *feature;
2932 MSIFILE *file;
2934 TRACE("InstallValidate\n");
2936 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2937 if (rc == ERROR_SUCCESS)
2939 MSI_IterateRecords( view, &progress, NULL, package );
2940 msiobj_release( &view->hdr );
2941 total += progress * REG_PROGRESS_VALUE;
2944 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2945 total += COMPONENT_PROGRESS_VALUE;
2947 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2948 total += file->FileSize;
2950 ui_progress(package,0,total,0,0);
2952 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2954 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2955 debugstr_w(feature->Feature), feature->Installed,
2956 feature->ActionRequest, feature->Action);
2959 return ERROR_SUCCESS;
2962 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2964 MSIPACKAGE* package = param;
2965 LPCWSTR cond = NULL;
2966 LPCWSTR message = NULL;
2967 UINT r;
2969 static const WCHAR title[]=
2970 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2972 cond = MSI_RecordGetString(row,1);
2974 r = MSI_EvaluateConditionW(package,cond);
2975 if (r == MSICONDITION_FALSE)
2977 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2979 LPWSTR deformated;
2980 message = MSI_RecordGetString(row,2);
2981 deformat_string(package,message,&deformated);
2982 MessageBoxW(NULL,deformated,title,MB_OK);
2983 msi_free(deformated);
2986 return ERROR_INSTALL_FAILURE;
2989 return ERROR_SUCCESS;
2992 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2994 UINT rc;
2995 MSIQUERY * view = NULL;
2996 static const WCHAR ExecSeqQuery[] =
2997 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2998 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3000 TRACE("Checking launch conditions\n");
3002 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3003 if (rc != ERROR_SUCCESS)
3004 return ERROR_SUCCESS;
3006 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3007 msiobj_release(&view->hdr);
3009 return rc;
3012 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3015 if (!cmp->KeyPath)
3016 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
3018 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3020 MSIRECORD * row = 0;
3021 UINT root,len;
3022 LPWSTR deformated,buffer,deformated_name;
3023 LPCWSTR key,name;
3024 static const WCHAR ExecSeqQuery[] =
3025 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3026 '`','R','e','g','i','s','t','r','y','`',' ',
3027 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
3028 ' ','=',' ' ,'\'','%','s','\'',0 };
3029 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
3030 static const WCHAR fmt2[]=
3031 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3033 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
3034 if (!row)
3035 return NULL;
3037 root = MSI_RecordGetInteger(row,2);
3038 key = MSI_RecordGetString(row, 3);
3039 name = MSI_RecordGetString(row, 4);
3040 deformat_string(package, key , &deformated);
3041 deformat_string(package, name, &deformated_name);
3043 len = strlenW(deformated) + 6;
3044 if (deformated_name)
3045 len+=strlenW(deformated_name);
3047 buffer = msi_alloc( len *sizeof(WCHAR));
3049 if (deformated_name)
3050 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3051 else
3052 sprintfW(buffer,fmt,root,deformated);
3054 msi_free(deformated);
3055 msi_free(deformated_name);
3056 msiobj_release(&row->hdr);
3058 return buffer;
3060 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3062 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3063 return NULL;
3065 else
3067 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
3069 if (file)
3070 return strdupW( file->TargetPath );
3072 return NULL;
3075 static HKEY openSharedDLLsKey(void)
3077 HKEY hkey=0;
3078 static const WCHAR path[] =
3079 {'S','o','f','t','w','a','r','e','\\',
3080 'M','i','c','r','o','s','o','f','t','\\',
3081 'W','i','n','d','o','w','s','\\',
3082 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3083 'S','h','a','r','e','d','D','L','L','s',0};
3085 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3086 return hkey;
3089 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3091 HKEY hkey;
3092 DWORD count=0;
3093 DWORD type;
3094 DWORD sz = sizeof(count);
3095 DWORD rc;
3097 hkey = openSharedDLLsKey();
3098 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3099 if (rc != ERROR_SUCCESS)
3100 count = 0;
3101 RegCloseKey(hkey);
3102 return count;
3105 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3107 HKEY hkey;
3109 hkey = openSharedDLLsKey();
3110 if (count > 0)
3111 msi_reg_set_val_dword( hkey, path, count );
3112 else
3113 RegDeleteValueW(hkey,path);
3114 RegCloseKey(hkey);
3115 return count;
3119 * Return TRUE if the count should be written out and FALSE if not
3121 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3123 MSIFEATURE *feature;
3124 INT count = 0;
3125 BOOL write = FALSE;
3127 /* only refcount DLLs */
3128 if (comp->KeyPath == NULL ||
3129 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3130 comp->Attributes & msidbComponentAttributesODBCDataSource)
3131 write = FALSE;
3132 else
3134 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3135 write = (count > 0);
3137 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3138 write = TRUE;
3141 /* increment counts */
3142 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3144 ComponentList *cl;
3146 if (feature->ActionRequest != INSTALLSTATE_LOCAL)
3147 continue;
3149 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3151 if ( cl->component == comp )
3152 count++;
3156 /* decrement counts */
3157 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3159 ComponentList *cl;
3161 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3162 continue;
3164 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3166 if ( cl->component == comp )
3167 count--;
3171 /* ref count all the files in the component */
3172 if (write)
3174 MSIFILE *file;
3176 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3178 if (file->Component == comp)
3179 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3183 /* add a count for permanent */
3184 if (comp->Attributes & msidbComponentAttributesPermanent)
3185 count ++;
3187 comp->RefCount = count;
3189 if (write)
3190 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3193 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3195 WCHAR squished_pc[GUID_SIZE];
3196 WCHAR squished_cc[GUID_SIZE];
3197 UINT rc;
3198 MSICOMPONENT *comp;
3199 HKEY hkey;
3201 TRACE("\n");
3203 squash_guid(package->ProductCode,squished_pc);
3204 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
3206 msi_set_sourcedir_props(package, FALSE);
3208 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3210 MSIRECORD * uirow;
3212 ui_progress(package,2,0,0,0);
3213 if (!comp->ComponentId)
3214 continue;
3216 squash_guid(comp->ComponentId,squished_cc);
3218 msi_free(comp->FullKeypath);
3219 comp->FullKeypath = resolve_keypath( package, comp );
3221 ACTION_RefCountComponent( package, comp );
3223 TRACE("Component %s (%s), Keypath=%s, RefCount=%i Request=%u\n",
3224 debugstr_w(comp->Component),
3225 debugstr_w(squished_cc),
3226 debugstr_w(comp->FullKeypath),
3227 comp->RefCount,
3228 comp->ActionRequest);
3230 if (comp->ActionRequest == INSTALLSTATE_LOCAL ||
3231 comp->ActionRequest == INSTALLSTATE_SOURCE)
3233 if (!comp->FullKeypath)
3234 continue;
3236 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3237 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
3238 &hkey, TRUE);
3239 else
3240 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
3241 &hkey, TRUE);
3243 if (rc != ERROR_SUCCESS)
3244 continue;
3246 if (comp->Attributes & msidbComponentAttributesPermanent)
3248 static const WCHAR szPermKey[] =
3249 { '0','0','0','0','0','0','0','0','0','0','0','0',
3250 '0','0','0','0','0','0','0','0','0','0','0','0',
3251 '0','0','0','0','0','0','0','0',0 };
3253 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3256 if (comp->ActionRequest == INSTALLSTATE_LOCAL)
3257 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3258 else
3260 MSIFILE *file;
3261 MSIRECORD *row;
3262 LPWSTR ptr, ptr2;
3263 WCHAR source[MAX_PATH];
3264 WCHAR base[MAX_PATH];
3265 LPWSTR sourcepath;
3267 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3268 static const WCHAR query[] = {
3269 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3270 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3271 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3272 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3273 '`','D','i','s','k','I','d','`',0};
3275 if (!comp->KeyPath || !(file = get_loaded_file(package, comp->KeyPath)))
3276 continue;
3278 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3279 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3280 ptr2 = strrchrW(source, '\\') + 1;
3281 msiobj_release(&row->hdr);
3283 lstrcpyW(base, package->PackagePath);
3284 ptr = strrchrW(base, '\\');
3285 *(ptr + 1) = '\0';
3287 sourcepath = resolve_file_source(package, file);
3288 ptr = sourcepath + lstrlenW(base);
3289 lstrcpyW(ptr2, ptr);
3290 msi_free(sourcepath);
3292 msi_reg_set_val_str(hkey, squished_pc, source);
3294 RegCloseKey(hkey);
3296 else if (comp->ActionRequest == INSTALLSTATE_ABSENT)
3298 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3299 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3300 else
3301 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3303 comp->Action = comp->ActionRequest;
3305 /* UI stuff */
3306 uirow = MSI_CreateRecord(3);
3307 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3308 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3309 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3310 ui_actiondata(package,szProcessComponents,uirow);
3311 msiobj_release( &uirow->hdr );
3314 return ERROR_SUCCESS;
3317 typedef struct {
3318 CLSID clsid;
3319 LPWSTR source;
3321 LPWSTR path;
3322 ITypeLib *ptLib;
3323 } typelib_struct;
3325 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3326 LPWSTR lpszName, LONG_PTR lParam)
3328 TLIBATTR *attr;
3329 typelib_struct *tl_struct = (typelib_struct*) lParam;
3330 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3331 int sz;
3332 HRESULT res;
3334 if (!IS_INTRESOURCE(lpszName))
3336 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3337 return TRUE;
3340 sz = strlenW(tl_struct->source)+4;
3341 sz *= sizeof(WCHAR);
3343 if ((INT_PTR)lpszName == 1)
3344 tl_struct->path = strdupW(tl_struct->source);
3345 else
3347 tl_struct->path = msi_alloc(sz);
3348 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3351 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3352 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3353 if (FAILED(res))
3355 msi_free(tl_struct->path);
3356 tl_struct->path = NULL;
3358 return TRUE;
3361 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3362 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3364 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3365 return FALSE;
3368 msi_free(tl_struct->path);
3369 tl_struct->path = NULL;
3371 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3372 ITypeLib_Release(tl_struct->ptLib);
3374 return TRUE;
3377 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3379 MSIPACKAGE* package = param;
3380 LPCWSTR component;
3381 MSICOMPONENT *comp;
3382 MSIFILE *file;
3383 typelib_struct tl_struct;
3384 ITypeLib *tlib;
3385 HMODULE module;
3386 HRESULT hr;
3388 component = MSI_RecordGetString(row,3);
3389 comp = get_loaded_component(package,component);
3390 if (!comp)
3391 return ERROR_SUCCESS;
3393 if (!comp->Enabled)
3395 TRACE("component is disabled\n");
3396 return ERROR_SUCCESS;
3399 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3401 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
3402 comp->Action = comp->Installed;
3403 return ERROR_SUCCESS;
3405 comp->Action = INSTALLSTATE_LOCAL;
3407 if (!comp->KeyPath || !(file = get_loaded_file( package, comp->KeyPath )))
3409 TRACE("component has no key path\n");
3410 return ERROR_SUCCESS;
3412 ui_actiondata( package, szRegisterTypeLibraries, row );
3414 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3415 if (module)
3417 LPCWSTR guid;
3418 guid = MSI_RecordGetString(row,1);
3419 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3420 tl_struct.source = strdupW( file->TargetPath );
3421 tl_struct.path = NULL;
3423 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3424 (LONG_PTR)&tl_struct);
3426 if (tl_struct.path)
3428 LPWSTR help = NULL;
3429 LPCWSTR helpid;
3430 HRESULT res;
3432 helpid = MSI_RecordGetString(row,6);
3434 if (helpid)
3435 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3436 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3437 msi_free(help);
3439 if (FAILED(res))
3440 ERR("Failed to register type library %s\n",
3441 debugstr_w(tl_struct.path));
3442 else
3443 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3445 ITypeLib_Release(tl_struct.ptLib);
3446 msi_free(tl_struct.path);
3448 else
3449 ERR("Failed to load type library %s\n",
3450 debugstr_w(tl_struct.source));
3452 FreeLibrary(module);
3453 msi_free(tl_struct.source);
3455 else
3457 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3458 if (FAILED(hr))
3460 ERR("Failed to load type library: %08x\n", hr);
3461 return ERROR_INSTALL_FAILURE;
3464 ITypeLib_Release(tlib);
3467 return ERROR_SUCCESS;
3470 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3473 * OK this is a bit confusing.. I am given a _Component key and I believe
3474 * that the file that is being registered as a type library is the "key file
3475 * of that component" which I interpret to mean "The file in the KeyPath of
3476 * that component".
3478 UINT rc;
3479 MSIQUERY * view;
3480 static const WCHAR Query[] =
3481 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3482 '`','T','y','p','e','L','i','b','`',0};
3484 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3485 if (rc != ERROR_SUCCESS)
3486 return ERROR_SUCCESS;
3488 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3489 msiobj_release(&view->hdr);
3490 return rc;
3493 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3495 MSIPACKAGE *package = param;
3496 LPCWSTR component, guid;
3497 MSICOMPONENT *comp;
3498 GUID libid;
3499 UINT version;
3500 LCID language;
3501 SYSKIND syskind;
3502 HRESULT hr;
3504 component = MSI_RecordGetString( row, 3 );
3505 comp = get_loaded_component( package, component );
3506 if (!comp)
3507 return ERROR_SUCCESS;
3509 if (!comp->Enabled)
3511 TRACE("component is disabled\n");
3512 return ERROR_SUCCESS;
3515 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3517 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3518 comp->Action = comp->Installed;
3519 return ERROR_SUCCESS;
3521 comp->Action = INSTALLSTATE_ABSENT;
3523 ui_actiondata( package, szUnregisterTypeLibraries, row );
3525 guid = MSI_RecordGetString( row, 1 );
3526 CLSIDFromString( (LPCWSTR)guid, &libid );
3527 version = MSI_RecordGetInteger( row, 4 );
3528 language = MSI_RecordGetInteger( row, 2 );
3530 #ifdef _WIN64
3531 syskind = SYS_WIN64;
3532 #else
3533 syskind = SYS_WIN32;
3534 #endif
3536 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3537 if (FAILED(hr))
3539 WARN("Failed to unregister typelib: %08x\n", hr);
3542 return ERROR_SUCCESS;
3545 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3547 UINT rc;
3548 MSIQUERY *view;
3549 static const WCHAR query[] =
3550 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3551 '`','T','y','p','e','L','i','b','`',0};
3553 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3554 if (rc != ERROR_SUCCESS)
3555 return ERROR_SUCCESS;
3557 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3558 msiobj_release( &view->hdr );
3559 return rc;
3562 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3564 static const WCHAR szlnk[] = {'.','l','n','k',0};
3565 LPCWSTR directory, extension;
3566 LPWSTR link_folder, link_file, filename;
3568 directory = MSI_RecordGetString( row, 2 );
3569 link_folder = resolve_folder( package, directory, FALSE, FALSE, TRUE, NULL );
3571 /* may be needed because of a bug somewhere else */
3572 create_full_pathW( link_folder );
3574 filename = msi_dup_record_field( row, 3 );
3575 reduce_to_longfilename( filename );
3577 extension = strchrW( filename, '.' );
3578 if (!extension || strcmpiW( extension, szlnk ))
3580 int len = strlenW( filename );
3581 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3582 memcpy( filename + len, szlnk, sizeof(szlnk) );
3584 link_file = build_directory_name( 2, link_folder, filename );
3585 msi_free( link_folder );
3586 msi_free( filename );
3588 return link_file;
3591 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3593 MSIPACKAGE *package = param;
3594 LPWSTR link_file, deformated, path;
3595 LPCWSTR component, target;
3596 MSICOMPONENT *comp;
3597 IShellLinkW *sl = NULL;
3598 IPersistFile *pf = NULL;
3599 HRESULT res;
3601 component = MSI_RecordGetString(row, 4);
3602 comp = get_loaded_component(package, component);
3603 if (!comp)
3604 return ERROR_SUCCESS;
3606 if (!comp->Enabled)
3608 TRACE("component is disabled\n");
3609 return ERROR_SUCCESS;
3612 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
3614 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
3615 comp->Action = comp->Installed;
3616 return ERROR_SUCCESS;
3618 comp->Action = INSTALLSTATE_LOCAL;
3620 ui_actiondata(package,szCreateShortcuts,row);
3622 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3623 &IID_IShellLinkW, (LPVOID *) &sl );
3625 if (FAILED( res ))
3627 ERR("CLSID_ShellLink not available\n");
3628 goto err;
3631 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3632 if (FAILED( res ))
3634 ERR("QueryInterface(IID_IPersistFile) failed\n");
3635 goto err;
3638 target = MSI_RecordGetString(row, 5);
3639 if (strchrW(target, '['))
3641 deformat_string(package, target, &deformated);
3642 IShellLinkW_SetPath(sl,deformated);
3643 msi_free(deformated);
3645 else
3647 FIXME("poorly handled shortcut format, advertised shortcut\n");
3648 IShellLinkW_SetPath(sl,comp->FullKeypath);
3651 if (!MSI_RecordIsNull(row,6))
3653 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3654 deformat_string(package, arguments, &deformated);
3655 IShellLinkW_SetArguments(sl,deformated);
3656 msi_free(deformated);
3659 if (!MSI_RecordIsNull(row,7))
3661 LPCWSTR description = MSI_RecordGetString(row, 7);
3662 IShellLinkW_SetDescription(sl, description);
3665 if (!MSI_RecordIsNull(row,8))
3666 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3668 if (!MSI_RecordIsNull(row,9))
3670 INT index;
3671 LPCWSTR icon = MSI_RecordGetString(row, 9);
3673 path = build_icon_path(package, icon);
3674 index = MSI_RecordGetInteger(row,10);
3676 /* no value means 0 */
3677 if (index == MSI_NULL_INTEGER)
3678 index = 0;
3680 IShellLinkW_SetIconLocation(sl, path, index);
3681 msi_free(path);
3684 if (!MSI_RecordIsNull(row,11))
3685 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3687 if (!MSI_RecordIsNull(row,12))
3689 LPCWSTR wkdir = MSI_RecordGetString(row, 12);
3690 path = resolve_folder(package, wkdir, FALSE, FALSE, TRUE, NULL);
3691 if (path)
3692 IShellLinkW_SetWorkingDirectory(sl, path);
3693 msi_free(path);
3696 link_file = get_link_file(package, row);
3698 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3699 IPersistFile_Save(pf, link_file, FALSE);
3701 msi_free(link_file);
3703 err:
3704 if (pf)
3705 IPersistFile_Release( pf );
3706 if (sl)
3707 IShellLinkW_Release( sl );
3709 return ERROR_SUCCESS;
3712 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3714 UINT rc;
3715 HRESULT res;
3716 MSIQUERY * view;
3717 static const WCHAR Query[] =
3718 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3719 '`','S','h','o','r','t','c','u','t','`',0};
3721 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3722 if (rc != ERROR_SUCCESS)
3723 return ERROR_SUCCESS;
3725 res = CoInitialize( NULL );
3727 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3728 msiobj_release(&view->hdr);
3730 if (SUCCEEDED(res))
3731 CoUninitialize();
3733 return rc;
3736 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3738 MSIPACKAGE *package = param;
3739 LPWSTR link_file;
3740 LPCWSTR component;
3741 MSICOMPONENT *comp;
3743 component = MSI_RecordGetString( row, 4 );
3744 comp = get_loaded_component( package, component );
3745 if (!comp)
3746 return ERROR_SUCCESS;
3748 if (!comp->Enabled)
3750 TRACE("component is disabled\n");
3751 return ERROR_SUCCESS;
3754 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
3756 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
3757 comp->Action = comp->Installed;
3758 return ERROR_SUCCESS;
3760 comp->Action = INSTALLSTATE_ABSENT;
3762 ui_actiondata( package, szRemoveShortcuts, row );
3764 link_file = get_link_file( package, row );
3766 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3767 if (!DeleteFileW( link_file ))
3769 WARN("Failed to remove shortcut file %u\n", GetLastError());
3771 msi_free( link_file );
3773 return ERROR_SUCCESS;
3776 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3778 UINT rc;
3779 MSIQUERY *view;
3780 static const WCHAR query[] =
3781 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3782 '`','S','h','o','r','t','c','u','t','`',0};
3784 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3785 if (rc != ERROR_SUCCESS)
3786 return ERROR_SUCCESS;
3788 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3789 msiobj_release( &view->hdr );
3791 return rc;
3794 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3796 MSIPACKAGE* package = param;
3797 HANDLE the_file;
3798 LPWSTR FilePath;
3799 LPCWSTR FileName;
3800 CHAR buffer[1024];
3801 DWORD sz;
3802 UINT rc;
3804 FileName = MSI_RecordGetString(row,1);
3805 if (!FileName)
3807 ERR("Unable to get FileName\n");
3808 return ERROR_SUCCESS;
3811 FilePath = build_icon_path(package,FileName);
3813 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3815 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3816 FILE_ATTRIBUTE_NORMAL, NULL);
3818 if (the_file == INVALID_HANDLE_VALUE)
3820 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3821 msi_free(FilePath);
3822 return ERROR_SUCCESS;
3827 DWORD write;
3828 sz = 1024;
3829 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3830 if (rc != ERROR_SUCCESS)
3832 ERR("Failed to get stream\n");
3833 CloseHandle(the_file);
3834 DeleteFileW(FilePath);
3835 break;
3837 WriteFile(the_file,buffer,sz,&write,NULL);
3838 } while (sz == 1024);
3840 msi_free(FilePath);
3841 CloseHandle(the_file);
3843 return ERROR_SUCCESS;
3846 static UINT msi_publish_icons(MSIPACKAGE *package)
3848 UINT r;
3849 MSIQUERY *view;
3851 static const WCHAR query[]= {
3852 'S','E','L','E','C','T',' ','*',' ',
3853 'F','R','O','M',' ','`','I','c','o','n','`',0};
3855 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3856 if (r == ERROR_SUCCESS)
3858 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3859 msiobj_release(&view->hdr);
3862 return ERROR_SUCCESS;
3865 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3867 UINT r;
3868 HKEY source;
3869 LPWSTR buffer;
3870 MSIMEDIADISK *disk;
3871 MSISOURCELISTINFO *info;
3873 r = RegCreateKeyW(hkey, szSourceList, &source);
3874 if (r != ERROR_SUCCESS)
3875 return r;
3877 RegCloseKey(source);
3879 buffer = strrchrW(package->PackagePath, '\\') + 1;
3880 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3881 package->Context, MSICODE_PRODUCT,
3882 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3883 if (r != ERROR_SUCCESS)
3884 return r;
3886 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3887 package->Context, MSICODE_PRODUCT,
3888 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3889 if (r != ERROR_SUCCESS)
3890 return r;
3892 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3893 package->Context, MSICODE_PRODUCT,
3894 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3895 if (r != ERROR_SUCCESS)
3896 return r;
3898 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3900 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3901 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3902 info->options, info->value);
3903 else
3904 MsiSourceListSetInfoW(package->ProductCode, NULL,
3905 info->context, info->options,
3906 info->property, info->value);
3909 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3911 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3912 disk->context, disk->options,
3913 disk->disk_id, disk->volume_label, disk->disk_prompt);
3916 return ERROR_SUCCESS;
3919 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3921 MSIHANDLE hdb, suminfo;
3922 WCHAR guids[MAX_PATH];
3923 WCHAR packcode[SQUISH_GUID_SIZE];
3924 LPWSTR buffer;
3925 LPWSTR ptr;
3926 DWORD langid;
3927 DWORD size;
3928 UINT r;
3930 static const WCHAR szProductLanguage[] =
3931 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3932 static const WCHAR szARPProductIcon[] =
3933 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3934 static const WCHAR szProductVersion[] =
3935 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3936 static const WCHAR szAssignment[] =
3937 {'A','s','s','i','g','n','m','e','n','t',0};
3938 static const WCHAR szAdvertiseFlags[] =
3939 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3940 static const WCHAR szClients[] =
3941 {'C','l','i','e','n','t','s',0};
3942 static const WCHAR szColon[] = {':',0};
3944 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3945 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3946 msi_free(buffer);
3948 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3949 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3951 /* FIXME */
3952 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3954 buffer = msi_dup_property(package->db, szARPProductIcon);
3955 if (buffer)
3957 LPWSTR path = build_icon_path(package,buffer);
3958 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3959 msi_free(path);
3960 msi_free(buffer);
3963 buffer = msi_dup_property(package->db, szProductVersion);
3964 if (buffer)
3966 DWORD verdword = msi_version_str_to_dword(buffer);
3967 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3968 msi_free(buffer);
3971 msi_reg_set_val_dword(hkey, szAssignment, 0);
3972 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3973 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3974 msi_reg_set_val_str(hkey, szClients, szColon);
3976 hdb = alloc_msihandle(&package->db->hdr);
3977 if (!hdb)
3978 return ERROR_NOT_ENOUGH_MEMORY;
3980 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3981 MsiCloseHandle(hdb);
3982 if (r != ERROR_SUCCESS)
3983 goto done;
3985 size = MAX_PATH;
3986 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3987 NULL, guids, &size);
3988 if (r != ERROR_SUCCESS)
3989 goto done;
3991 ptr = strchrW(guids, ';');
3992 if (ptr) *ptr = 0;
3993 squash_guid(guids, packcode);
3994 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3996 done:
3997 MsiCloseHandle(suminfo);
3998 return ERROR_SUCCESS;
4001 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4003 UINT r;
4004 HKEY hkey;
4005 LPWSTR upgrade;
4006 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4008 upgrade = msi_dup_property(package->db, szUpgradeCode);
4009 if (!upgrade)
4010 return ERROR_SUCCESS;
4012 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4014 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4015 if (r != ERROR_SUCCESS)
4016 goto done;
4018 else
4020 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4021 if (r != ERROR_SUCCESS)
4022 goto done;
4025 squash_guid(package->ProductCode, squashed_pc);
4026 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4028 RegCloseKey(hkey);
4030 done:
4031 msi_free(upgrade);
4032 return r;
4035 static BOOL msi_check_publish(MSIPACKAGE *package)
4037 MSIFEATURE *feature;
4039 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4041 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
4042 return TRUE;
4045 return FALSE;
4048 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4050 MSIFEATURE *feature;
4052 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4054 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
4055 return FALSE;
4058 return TRUE;
4061 static UINT msi_publish_patches( MSIPACKAGE *package )
4063 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4064 WCHAR patch_squashed[GUID_SIZE];
4065 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4066 LONG res;
4067 MSIPATCHINFO *patch;
4068 UINT r;
4069 WCHAR *p, *all_patches = NULL;
4070 DWORD len = 0;
4072 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4073 if (r != ERROR_SUCCESS)
4074 return ERROR_FUNCTION_FAILED;
4076 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4077 if (res != ERROR_SUCCESS)
4079 r = ERROR_FUNCTION_FAILED;
4080 goto done;
4083 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4084 if (r != ERROR_SUCCESS)
4085 goto done;
4087 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4089 squash_guid( patch->patchcode, patch_squashed );
4090 len += strlenW( patch_squashed ) + 1;
4093 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4094 if (!all_patches)
4095 goto done;
4097 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4099 HKEY patch_key;
4101 squash_guid( patch->patchcode, p );
4102 p += strlenW( p ) + 1;
4104 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4105 (const BYTE *)patch->transforms,
4106 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4107 if (res != ERROR_SUCCESS)
4108 goto done;
4110 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4111 if (r != ERROR_SUCCESS)
4112 goto done;
4114 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ,
4115 (const BYTE *)patch->localfile,
4116 (strlenW(patch->localfile) + 1) * sizeof(WCHAR) );
4117 RegCloseKey( patch_key );
4118 if (res != ERROR_SUCCESS)
4119 goto done;
4121 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4122 if (res != ERROR_SUCCESS)
4123 goto done;
4125 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4126 RegCloseKey( patch_key );
4127 if (res != ERROR_SUCCESS)
4128 goto done;
4131 all_patches[len] = 0;
4132 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4133 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4134 if (res != ERROR_SUCCESS)
4135 goto done;
4137 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4138 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4139 if (res != ERROR_SUCCESS)
4140 r = ERROR_FUNCTION_FAILED;
4142 done:
4143 RegCloseKey( product_patches_key );
4144 RegCloseKey( patches_key );
4145 RegCloseKey( product_key );
4146 msi_free( all_patches );
4147 return r;
4151 * 99% of the work done here is only done for
4152 * advertised installs. However this is where the
4153 * Icon table is processed and written out
4154 * so that is what I am going to do here.
4156 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4158 UINT rc;
4159 HKEY hukey = NULL, hudkey = NULL;
4160 MSIRECORD *uirow;
4162 if (!list_empty(&package->patches))
4164 rc = msi_publish_patches(package);
4165 if (rc != ERROR_SUCCESS)
4166 goto end;
4169 /* FIXME: also need to publish if the product is in advertise mode */
4170 if (!msi_check_publish(package))
4171 return ERROR_SUCCESS;
4173 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4174 &hukey, TRUE);
4175 if (rc != ERROR_SUCCESS)
4176 goto end;
4178 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4179 NULL, &hudkey, TRUE);
4180 if (rc != ERROR_SUCCESS)
4181 goto end;
4183 rc = msi_publish_upgrade_code(package);
4184 if (rc != ERROR_SUCCESS)
4185 goto end;
4187 rc = msi_publish_product_properties(package, hukey);
4188 if (rc != ERROR_SUCCESS)
4189 goto end;
4191 rc = msi_publish_sourcelist(package, hukey);
4192 if (rc != ERROR_SUCCESS)
4193 goto end;
4195 rc = msi_publish_icons(package);
4197 end:
4198 uirow = MSI_CreateRecord( 1 );
4199 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4200 ui_actiondata( package, szPublishProduct, uirow );
4201 msiobj_release( &uirow->hdr );
4203 RegCloseKey(hukey);
4204 RegCloseKey(hudkey);
4206 return rc;
4209 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4211 WCHAR *filename, *ptr, *folder, *ret;
4212 const WCHAR *dirprop;
4214 filename = msi_dup_record_field( row, 2 );
4215 if (filename && (ptr = strchrW( filename, '|' )))
4216 ptr++;
4217 else
4218 ptr = filename;
4220 dirprop = MSI_RecordGetString( row, 3 );
4221 if (dirprop)
4223 folder = resolve_folder( package, dirprop, FALSE, FALSE, TRUE, NULL );
4224 if (!folder)
4225 folder = msi_dup_property( package->db, dirprop );
4227 else
4228 folder = msi_dup_property( package->db, szWindowsFolder );
4230 if (!folder)
4232 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4233 msi_free( filename );
4234 return NULL;
4237 ret = build_directory_name( 2, folder, ptr );
4239 msi_free( filename );
4240 msi_free( folder );
4241 return ret;
4244 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4246 MSIPACKAGE *package = param;
4247 LPCWSTR component, section, key, value, identifier;
4248 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4249 MSIRECORD * uirow;
4250 INT action;
4251 MSICOMPONENT *comp;
4253 component = MSI_RecordGetString(row, 8);
4254 comp = get_loaded_component(package,component);
4255 if (!comp)
4256 return ERROR_SUCCESS;
4258 if (!comp->Enabled)
4260 TRACE("component is disabled\n");
4261 return ERROR_SUCCESS;
4264 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4266 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4267 comp->Action = comp->Installed;
4268 return ERROR_SUCCESS;
4270 comp->Action = INSTALLSTATE_LOCAL;
4272 identifier = MSI_RecordGetString(row,1);
4273 section = MSI_RecordGetString(row,4);
4274 key = MSI_RecordGetString(row,5);
4275 value = MSI_RecordGetString(row,6);
4276 action = MSI_RecordGetInteger(row,7);
4278 deformat_string(package,section,&deformated_section);
4279 deformat_string(package,key,&deformated_key);
4280 deformat_string(package,value,&deformated_value);
4282 fullname = get_ini_file_name(package, row);
4284 if (action == 0)
4286 TRACE("Adding value %s to section %s in %s\n",
4287 debugstr_w(deformated_key), debugstr_w(deformated_section),
4288 debugstr_w(fullname));
4289 WritePrivateProfileStringW(deformated_section, deformated_key,
4290 deformated_value, fullname);
4292 else if (action == 1)
4294 WCHAR returned[10];
4295 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4296 returned, 10, fullname);
4297 if (returned[0] == 0)
4299 TRACE("Adding value %s to section %s in %s\n",
4300 debugstr_w(deformated_key), debugstr_w(deformated_section),
4301 debugstr_w(fullname));
4303 WritePrivateProfileStringW(deformated_section, deformated_key,
4304 deformated_value, fullname);
4307 else if (action == 3)
4308 FIXME("Append to existing section not yet implemented\n");
4310 uirow = MSI_CreateRecord(4);
4311 MSI_RecordSetStringW(uirow,1,identifier);
4312 MSI_RecordSetStringW(uirow,2,deformated_section);
4313 MSI_RecordSetStringW(uirow,3,deformated_key);
4314 MSI_RecordSetStringW(uirow,4,deformated_value);
4315 ui_actiondata(package,szWriteIniValues,uirow);
4316 msiobj_release( &uirow->hdr );
4318 msi_free(fullname);
4319 msi_free(deformated_key);
4320 msi_free(deformated_value);
4321 msi_free(deformated_section);
4322 return ERROR_SUCCESS;
4325 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4327 UINT rc;
4328 MSIQUERY * view;
4329 static const WCHAR ExecSeqQuery[] =
4330 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4331 '`','I','n','i','F','i','l','e','`',0};
4333 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4334 if (rc != ERROR_SUCCESS)
4336 TRACE("no IniFile table\n");
4337 return ERROR_SUCCESS;
4340 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4341 msiobj_release(&view->hdr);
4342 return rc;
4345 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4347 MSIPACKAGE *package = param;
4348 LPCWSTR component, section, key, value, identifier;
4349 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4350 MSICOMPONENT *comp;
4351 MSIRECORD *uirow;
4352 INT action;
4354 component = MSI_RecordGetString( row, 8 );
4355 comp = get_loaded_component( package, component );
4356 if (!comp)
4357 return ERROR_SUCCESS;
4359 if (!comp->Enabled)
4361 TRACE("component is disabled\n");
4362 return ERROR_SUCCESS;
4365 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
4367 TRACE("Component not scheduled for removal %s\n", debugstr_w(component));
4368 comp->Action = comp->Installed;
4369 return ERROR_SUCCESS;
4371 comp->Action = INSTALLSTATE_ABSENT;
4373 identifier = MSI_RecordGetString( row, 1 );
4374 section = MSI_RecordGetString( row, 4 );
4375 key = MSI_RecordGetString( row, 5 );
4376 value = MSI_RecordGetString( row, 6 );
4377 action = MSI_RecordGetInteger( row, 7 );
4379 deformat_string( package, section, &deformated_section );
4380 deformat_string( package, key, &deformated_key );
4381 deformat_string( package, value, &deformated_value );
4383 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4385 filename = get_ini_file_name( package, row );
4387 TRACE("Removing key %s from section %s in %s\n",
4388 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4390 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4392 WARN("Unable to remove key %u\n", GetLastError());
4394 msi_free( filename );
4396 else
4397 FIXME("Unsupported action %d\n", action);
4400 uirow = MSI_CreateRecord( 4 );
4401 MSI_RecordSetStringW( uirow, 1, identifier );
4402 MSI_RecordSetStringW( uirow, 2, deformated_section );
4403 MSI_RecordSetStringW( uirow, 3, deformated_key );
4404 MSI_RecordSetStringW( uirow, 4, deformated_value );
4405 ui_actiondata( package, szRemoveIniValues, uirow );
4406 msiobj_release( &uirow->hdr );
4408 msi_free( deformated_key );
4409 msi_free( deformated_value );
4410 msi_free( deformated_section );
4411 return ERROR_SUCCESS;
4414 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4416 MSIPACKAGE *package = param;
4417 LPCWSTR component, section, key, value, identifier;
4418 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4419 MSICOMPONENT *comp;
4420 MSIRECORD *uirow;
4421 INT action;
4423 component = MSI_RecordGetString( row, 8 );
4424 comp = get_loaded_component( package, component );
4425 if (!comp)
4426 return ERROR_SUCCESS;
4428 if (!comp->Enabled)
4430 TRACE("component is disabled\n");
4431 return ERROR_SUCCESS;
4434 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
4436 TRACE("Component not scheduled for installation %s\n", debugstr_w(component));
4437 comp->Action = comp->Installed;
4438 return ERROR_SUCCESS;
4440 comp->Action = INSTALLSTATE_LOCAL;
4442 identifier = MSI_RecordGetString( row, 1 );
4443 section = MSI_RecordGetString( row, 4 );
4444 key = MSI_RecordGetString( row, 5 );
4445 value = MSI_RecordGetString( row, 6 );
4446 action = MSI_RecordGetInteger( row, 7 );
4448 deformat_string( package, section, &deformated_section );
4449 deformat_string( package, key, &deformated_key );
4450 deformat_string( package, value, &deformated_value );
4452 if (action == msidbIniFileActionRemoveLine)
4454 filename = get_ini_file_name( package, row );
4456 TRACE("Removing key %s from section %s in %s\n",
4457 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4459 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4461 WARN("Unable to remove key %u\n", GetLastError());
4463 msi_free( filename );
4465 else
4466 FIXME("Unsupported action %d\n", action);
4468 uirow = MSI_CreateRecord( 4 );
4469 MSI_RecordSetStringW( uirow, 1, identifier );
4470 MSI_RecordSetStringW( uirow, 2, deformated_section );
4471 MSI_RecordSetStringW( uirow, 3, deformated_key );
4472 MSI_RecordSetStringW( uirow, 4, deformated_value );
4473 ui_actiondata( package, szRemoveIniValues, uirow );
4474 msiobj_release( &uirow->hdr );
4476 msi_free( deformated_key );
4477 msi_free( deformated_value );
4478 msi_free( deformated_section );
4479 return ERROR_SUCCESS;
4482 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4484 UINT rc;
4485 MSIQUERY *view;
4486 static const WCHAR query[] =
4487 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4488 '`','I','n','i','F','i','l','e','`',0};
4489 static const WCHAR remove_query[] =
4490 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4491 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4493 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4494 if (rc == ERROR_SUCCESS)
4496 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4497 msiobj_release( &view->hdr );
4498 if (rc != ERROR_SUCCESS)
4499 return rc;
4502 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4503 if (rc == ERROR_SUCCESS)
4505 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4506 msiobj_release( &view->hdr );
4507 if (rc != ERROR_SUCCESS)
4508 return rc;
4511 return ERROR_SUCCESS;
4514 static void register_dll( const WCHAR *dll, BOOL unregister )
4516 HMODULE hmod;
4518 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4519 if (hmod)
4521 HRESULT (WINAPI *func_ptr)( void );
4522 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4524 func_ptr = (void *)GetProcAddress( hmod, func );
4525 if (func_ptr)
4527 HRESULT hr = func_ptr();
4528 if (FAILED( hr ))
4529 WARN("failed to register dll 0x%08x\n", hr);
4531 else
4532 WARN("entry point %s not found\n", func);
4533 FreeLibrary( hmod );
4534 return;
4536 WARN("failed to load library %u\n", GetLastError());
4539 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4541 MSIPACKAGE *package = param;
4542 LPCWSTR filename;
4543 MSIFILE *file;
4544 MSIRECORD *uirow;
4546 filename = MSI_RecordGetString(row,1);
4547 file = get_loaded_file( package, filename );
4549 if (!file)
4551 ERR("Unable to find file id %s\n",debugstr_w(filename));
4552 return ERROR_SUCCESS;
4555 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4557 register_dll( file->TargetPath, FALSE );
4559 uirow = MSI_CreateRecord( 2 );
4560 MSI_RecordSetStringW( uirow, 1, filename );
4561 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4562 ui_actiondata( package, szSelfRegModules, uirow );
4563 msiobj_release( &uirow->hdr );
4565 return ERROR_SUCCESS;
4568 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4570 UINT rc;
4571 MSIQUERY * view;
4572 static const WCHAR ExecSeqQuery[] =
4573 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4574 '`','S','e','l','f','R','e','g','`',0};
4576 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4577 if (rc != ERROR_SUCCESS)
4579 TRACE("no SelfReg table\n");
4580 return ERROR_SUCCESS;
4583 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4584 msiobj_release(&view->hdr);
4586 return ERROR_SUCCESS;
4589 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4591 MSIPACKAGE *package = param;
4592 LPCWSTR filename;
4593 MSIFILE *file;
4594 MSIRECORD *uirow;
4596 filename = MSI_RecordGetString( row, 1 );
4597 file = get_loaded_file( package, filename );
4599 if (!file)
4601 ERR("Unable to find file id %s\n", debugstr_w(filename));
4602 return ERROR_SUCCESS;
4605 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4607 register_dll( file->TargetPath, TRUE );
4609 uirow = MSI_CreateRecord( 2 );
4610 MSI_RecordSetStringW( uirow, 1, filename );
4611 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4612 ui_actiondata( package, szSelfUnregModules, uirow );
4613 msiobj_release( &uirow->hdr );
4615 return ERROR_SUCCESS;
4618 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4620 UINT rc;
4621 MSIQUERY *view;
4622 static const WCHAR query[] =
4623 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4624 '`','S','e','l','f','R','e','g','`',0};
4626 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4627 if (rc != ERROR_SUCCESS)
4629 TRACE("no SelfReg table\n");
4630 return ERROR_SUCCESS;
4633 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4634 msiobj_release( &view->hdr );
4636 return ERROR_SUCCESS;
4639 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4641 MSIFEATURE *feature;
4642 UINT rc;
4643 HKEY hkey = NULL, userdata = NULL;
4645 if (!msi_check_publish(package))
4646 return ERROR_SUCCESS;
4648 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4649 &hkey, TRUE);
4650 if (rc != ERROR_SUCCESS)
4651 goto end;
4653 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4654 &userdata, TRUE);
4655 if (rc != ERROR_SUCCESS)
4656 goto end;
4658 /* here the guids are base 85 encoded */
4659 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4661 ComponentList *cl;
4662 LPWSTR data = NULL;
4663 GUID clsid;
4664 INT size;
4665 BOOL absent = FALSE;
4666 MSIRECORD *uirow;
4668 if (feature->ActionRequest != INSTALLSTATE_LOCAL &&
4669 feature->ActionRequest != INSTALLSTATE_SOURCE &&
4670 feature->ActionRequest != INSTALLSTATE_ADVERTISED) absent = TRUE;
4672 size = 1;
4673 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4675 size += 21;
4677 if (feature->Feature_Parent)
4678 size += strlenW( feature->Feature_Parent )+2;
4680 data = msi_alloc(size * sizeof(WCHAR));
4682 data[0] = 0;
4683 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4685 MSICOMPONENT* component = cl->component;
4686 WCHAR buf[21];
4688 buf[0] = 0;
4689 if (component->ComponentId)
4691 TRACE("From %s\n",debugstr_w(component->ComponentId));
4692 CLSIDFromString(component->ComponentId, &clsid);
4693 encode_base85_guid(&clsid,buf);
4694 TRACE("to %s\n",debugstr_w(buf));
4695 strcatW(data,buf);
4699 if (feature->Feature_Parent)
4701 static const WCHAR sep[] = {'\2',0};
4702 strcatW(data,sep);
4703 strcatW(data,feature->Feature_Parent);
4706 msi_reg_set_val_str( userdata, feature->Feature, data );
4707 msi_free(data);
4709 size = 0;
4710 if (feature->Feature_Parent)
4711 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4712 if (!absent)
4714 size += sizeof(WCHAR);
4715 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4716 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4718 else
4720 size += 2*sizeof(WCHAR);
4721 data = msi_alloc(size);
4722 data[0] = 0x6;
4723 data[1] = 0;
4724 if (feature->Feature_Parent)
4725 strcpyW( &data[1], feature->Feature_Parent );
4726 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4727 (LPBYTE)data,size);
4728 msi_free(data);
4731 /* the UI chunk */
4732 uirow = MSI_CreateRecord( 1 );
4733 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4734 ui_actiondata( package, szPublishFeatures, uirow);
4735 msiobj_release( &uirow->hdr );
4736 /* FIXME: call ui_progress? */
4739 end:
4740 RegCloseKey(hkey);
4741 RegCloseKey(userdata);
4742 return rc;
4745 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4747 UINT r;
4748 HKEY hkey;
4749 MSIRECORD *uirow;
4751 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4753 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4754 &hkey, FALSE);
4755 if (r == ERROR_SUCCESS)
4757 RegDeleteValueW(hkey, feature->Feature);
4758 RegCloseKey(hkey);
4761 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4762 &hkey, FALSE);
4763 if (r == ERROR_SUCCESS)
4765 RegDeleteValueW(hkey, feature->Feature);
4766 RegCloseKey(hkey);
4769 uirow = MSI_CreateRecord( 1 );
4770 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4771 ui_actiondata( package, szUnpublishFeatures, uirow );
4772 msiobj_release( &uirow->hdr );
4774 return ERROR_SUCCESS;
4777 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4779 MSIFEATURE *feature;
4781 if (!msi_check_unpublish(package))
4782 return ERROR_SUCCESS;
4784 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4786 msi_unpublish_feature(package, feature);
4789 return ERROR_SUCCESS;
4792 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4794 SYSTEMTIME systime;
4795 DWORD size, langid;
4796 WCHAR date[9], *val, *buffer;
4797 const WCHAR *prop, *key;
4799 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4800 static const WCHAR szWindowsInstaller[] =
4801 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4802 static const WCHAR modpath_fmt[] =
4803 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4804 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4805 static const WCHAR szModifyPath[] =
4806 {'M','o','d','i','f','y','P','a','t','h',0};
4807 static const WCHAR szUninstallString[] =
4808 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4809 static const WCHAR szEstimatedSize[] =
4810 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4811 static const WCHAR szProductLanguage[] =
4812 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4813 static const WCHAR szProductVersion[] =
4814 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4815 static const WCHAR szDisplayVersion[] =
4816 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4817 static const WCHAR szInstallSource[] =
4818 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4819 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4820 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4821 static const WCHAR szAuthorizedCDFPrefix[] =
4822 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4823 static const WCHAR szARPCONTACT[] =
4824 {'A','R','P','C','O','N','T','A','C','T',0};
4825 static const WCHAR szContact[] =
4826 {'C','o','n','t','a','c','t',0};
4827 static const WCHAR szARPCOMMENTS[] =
4828 {'A','R','P','C','O','M','M','E','N','T','S',0};
4829 static const WCHAR szComments[] =
4830 {'C','o','m','m','e','n','t','s',0};
4831 static const WCHAR szProductName[] =
4832 {'P','r','o','d','u','c','t','N','a','m','e',0};
4833 static const WCHAR szDisplayName[] =
4834 {'D','i','s','p','l','a','y','N','a','m','e',0};
4835 static const WCHAR szARPHELPLINK[] =
4836 {'A','R','P','H','E','L','P','L','I','N','K',0};
4837 static const WCHAR szHelpLink[] =
4838 {'H','e','l','p','L','i','n','k',0};
4839 static const WCHAR szARPHELPTELEPHONE[] =
4840 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4841 static const WCHAR szHelpTelephone[] =
4842 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4843 static const WCHAR szARPINSTALLLOCATION[] =
4844 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4845 static const WCHAR szInstallLocation[] =
4846 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4847 static const WCHAR szManufacturer[] =
4848 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4849 static const WCHAR szPublisher[] =
4850 {'P','u','b','l','i','s','h','e','r',0};
4851 static const WCHAR szARPREADME[] =
4852 {'A','R','P','R','E','A','D','M','E',0};
4853 static const WCHAR szReadme[] =
4854 {'R','e','a','d','M','e',0};
4855 static const WCHAR szARPSIZE[] =
4856 {'A','R','P','S','I','Z','E',0};
4857 static const WCHAR szSize[] =
4858 {'S','i','z','e',0};
4859 static const WCHAR szARPURLINFOABOUT[] =
4860 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4861 static const WCHAR szURLInfoAbout[] =
4862 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4863 static const WCHAR szARPURLUPDATEINFO[] =
4864 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4865 static const WCHAR szURLUpdateInfo[] =
4866 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4868 static const WCHAR *propval[] = {
4869 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4870 szARPCONTACT, szContact,
4871 szARPCOMMENTS, szComments,
4872 szProductName, szDisplayName,
4873 szARPHELPLINK, szHelpLink,
4874 szARPHELPTELEPHONE, szHelpTelephone,
4875 szARPINSTALLLOCATION, szInstallLocation,
4876 cszSourceDir, szInstallSource,
4877 szManufacturer, szPublisher,
4878 szARPREADME, szReadme,
4879 szARPSIZE, szSize,
4880 szARPURLINFOABOUT, szURLInfoAbout,
4881 szARPURLUPDATEINFO, szURLUpdateInfo,
4882 NULL
4884 const WCHAR **p = propval;
4886 while (*p)
4888 prop = *p++;
4889 key = *p++;
4890 val = msi_dup_property(package->db, prop);
4891 msi_reg_set_val_str(hkey, key, val);
4892 msi_free(val);
4895 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4897 size = deformat_string(package, modpath_fmt, &buffer);
4898 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4899 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4900 msi_free(buffer);
4902 /* FIXME: Write real Estimated Size when we have it */
4903 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4905 GetLocalTime(&systime);
4906 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4907 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4909 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4910 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4912 buffer = msi_dup_property(package->db, szProductVersion);
4913 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4914 if (buffer)
4916 DWORD verdword = msi_version_str_to_dword(buffer);
4918 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4919 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4921 msi_free(buffer);
4924 return ERROR_SUCCESS;
4927 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4929 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4930 MSIRECORD *uirow;
4931 LPWSTR upgrade_code;
4932 HKEY hkey, props;
4933 HKEY upgrade;
4934 UINT rc;
4936 /* FIXME: also need to publish if the product is in advertise mode */
4937 if (!msi_check_publish(package))
4938 return ERROR_SUCCESS;
4940 rc = MSIREG_OpenUninstallKey(package, &hkey, TRUE);
4941 if (rc != ERROR_SUCCESS)
4942 return rc;
4944 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4945 NULL, &props, TRUE);
4946 if (rc != ERROR_SUCCESS)
4947 goto done;
4949 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
4950 msi_free( package->db->localfile );
4951 package->db->localfile = NULL;
4953 rc = msi_publish_install_properties(package, hkey);
4954 if (rc != ERROR_SUCCESS)
4955 goto done;
4957 rc = msi_publish_install_properties(package, props);
4958 if (rc != ERROR_SUCCESS)
4959 goto done;
4961 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4962 if (upgrade_code)
4964 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4965 squash_guid(package->ProductCode, squashed_pc);
4966 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4967 RegCloseKey(upgrade);
4968 msi_free(upgrade_code);
4971 done:
4972 uirow = MSI_CreateRecord( 1 );
4973 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4974 ui_actiondata( package, szRegisterProduct, uirow );
4975 msiobj_release( &uirow->hdr );
4977 RegCloseKey(hkey);
4978 return ERROR_SUCCESS;
4981 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4983 return execute_script(package,INSTALL_SCRIPT);
4986 static UINT msi_unpublish_product(MSIPACKAGE *package, WCHAR *remove)
4988 WCHAR *upgrade, **features;
4989 BOOL full_uninstall = TRUE;
4990 MSIFEATURE *feature;
4991 MSIPATCHINFO *patch;
4993 static const WCHAR szUpgradeCode[] =
4994 {'U','p','g','r','a','d','e','C','o','d','e',0};
4996 features = msi_split_string(remove, ',');
4997 if (!features)
4999 ERR("REMOVE feature list is empty!\n");
5000 return ERROR_FUNCTION_FAILED;
5003 if (!strcmpW( features[0], szAll ))
5004 full_uninstall = TRUE;
5005 else
5007 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5009 if (feature->Action != INSTALLSTATE_ABSENT)
5010 full_uninstall = FALSE;
5013 msi_free(features);
5015 if (!full_uninstall)
5016 return ERROR_SUCCESS;
5018 MSIREG_DeleteProductKey(package->ProductCode);
5019 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5020 MSIREG_DeleteUninstallKey(package);
5022 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
5024 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5025 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5027 else
5029 MSIREG_DeleteUserProductKey(package->ProductCode);
5030 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5033 upgrade = msi_dup_property(package->db, szUpgradeCode);
5034 if (upgrade)
5036 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5037 msi_free(upgrade);
5040 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5042 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5045 return ERROR_SUCCESS;
5048 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5050 UINT rc;
5051 WCHAR *remove;
5053 /* turn off scheduling */
5054 package->script->CurrentlyScripting= FALSE;
5056 /* first do the same as an InstallExecute */
5057 rc = ACTION_InstallExecute(package);
5058 if (rc != ERROR_SUCCESS)
5059 return rc;
5061 /* then handle Commit Actions */
5062 rc = execute_script(package,COMMIT_SCRIPT);
5063 if (rc != ERROR_SUCCESS)
5064 return rc;
5066 remove = msi_dup_property(package->db, szRemove);
5067 if (remove)
5068 rc = msi_unpublish_product(package, remove);
5070 msi_free(remove);
5071 return rc;
5074 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5076 static const WCHAR RunOnce[] = {
5077 'S','o','f','t','w','a','r','e','\\',
5078 'M','i','c','r','o','s','o','f','t','\\',
5079 'W','i','n','d','o','w','s','\\',
5080 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5081 'R','u','n','O','n','c','e',0};
5082 static const WCHAR InstallRunOnce[] = {
5083 'S','o','f','t','w','a','r','e','\\',
5084 'M','i','c','r','o','s','o','f','t','\\',
5085 'W','i','n','d','o','w','s','\\',
5086 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5087 'I','n','s','t','a','l','l','e','r','\\',
5088 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5090 static const WCHAR msiexec_fmt[] = {
5091 '%','s',
5092 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5093 '\"','%','s','\"',0};
5094 static const WCHAR install_fmt[] = {
5095 '/','I',' ','\"','%','s','\"',' ',
5096 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5097 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5098 WCHAR buffer[256], sysdir[MAX_PATH];
5099 HKEY hkey;
5100 WCHAR squished_pc[100];
5102 squash_guid(package->ProductCode,squished_pc);
5104 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5105 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5106 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5107 squished_pc);
5109 msi_reg_set_val_str( hkey, squished_pc, buffer );
5110 RegCloseKey(hkey);
5112 TRACE("Reboot command %s\n",debugstr_w(buffer));
5114 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5115 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5117 msi_reg_set_val_str( hkey, squished_pc, buffer );
5118 RegCloseKey(hkey);
5120 return ERROR_INSTALL_SUSPEND;
5123 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5125 DWORD attrib;
5126 UINT rc;
5129 * We are currently doing what should be done here in the top level Install
5130 * however for Administrative and uninstalls this step will be needed
5132 if (!package->PackagePath)
5133 return ERROR_SUCCESS;
5135 msi_set_sourcedir_props(package, TRUE);
5137 attrib = GetFileAttributesW(package->db->path);
5138 if (attrib == INVALID_FILE_ATTRIBUTES)
5140 LPWSTR prompt;
5141 LPWSTR msg;
5142 DWORD size = 0;
5144 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5145 package->Context, MSICODE_PRODUCT,
5146 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5147 if (rc == ERROR_MORE_DATA)
5149 prompt = msi_alloc(size * sizeof(WCHAR));
5150 MsiSourceListGetInfoW(package->ProductCode, NULL,
5151 package->Context, MSICODE_PRODUCT,
5152 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5154 else
5155 prompt = strdupW(package->db->path);
5157 msg = generate_error_string(package,1302,1,prompt);
5158 while(attrib == INVALID_FILE_ATTRIBUTES)
5160 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5161 if (rc == IDCANCEL)
5163 rc = ERROR_INSTALL_USEREXIT;
5164 break;
5166 attrib = GetFileAttributesW(package->db->path);
5168 msi_free(prompt);
5169 rc = ERROR_SUCCESS;
5171 else
5172 return ERROR_SUCCESS;
5174 return rc;
5177 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5179 HKEY hkey = 0;
5180 LPWSTR buffer, productid = NULL;
5181 UINT i, rc = ERROR_SUCCESS;
5182 MSIRECORD *uirow;
5184 static const WCHAR szPropKeys[][80] =
5186 {'P','r','o','d','u','c','t','I','D',0},
5187 {'U','S','E','R','N','A','M','E',0},
5188 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5189 {0},
5192 static const WCHAR szRegKeys[][80] =
5194 {'P','r','o','d','u','c','t','I','D',0},
5195 {'R','e','g','O','w','n','e','r',0},
5196 {'R','e','g','C','o','m','p','a','n','y',0},
5197 {0},
5200 if (msi_check_unpublish(package))
5202 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5203 goto end;
5206 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5207 if (!productid)
5208 goto end;
5210 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5211 NULL, &hkey, TRUE);
5212 if (rc != ERROR_SUCCESS)
5213 goto end;
5215 for( i = 0; szPropKeys[i][0]; i++ )
5217 buffer = msi_dup_property( package->db, szPropKeys[i] );
5218 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5219 msi_free( buffer );
5222 end:
5223 uirow = MSI_CreateRecord( 1 );
5224 MSI_RecordSetStringW( uirow, 1, productid );
5225 ui_actiondata( package, szRegisterUser, uirow );
5226 msiobj_release( &uirow->hdr );
5228 msi_free(productid);
5229 RegCloseKey(hkey);
5230 return rc;
5234 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5236 UINT rc;
5238 package->script->InWhatSequence |= SEQUENCE_EXEC;
5239 rc = ACTION_ProcessExecSequence(package,FALSE);
5240 return rc;
5244 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5246 MSIPACKAGE *package = param;
5247 LPCWSTR compgroupid, component, feature, qualifier, text;
5248 LPWSTR advertise = NULL, output = NULL;
5249 HKEY hkey = NULL;
5250 UINT rc;
5251 MSICOMPONENT *comp;
5252 MSIFEATURE *feat;
5253 DWORD sz;
5254 MSIRECORD *uirow;
5256 feature = MSI_RecordGetString(rec, 5);
5257 feat = get_loaded_feature(package, feature);
5258 if (!feat)
5259 return ERROR_SUCCESS;
5261 if (feat->ActionRequest != INSTALLSTATE_LOCAL &&
5262 feat->ActionRequest != INSTALLSTATE_SOURCE &&
5263 feat->ActionRequest != INSTALLSTATE_ADVERTISED)
5265 TRACE("Feature %s not scheduled for installation\n", debugstr_w(feature));
5266 feat->Action = feat->Installed;
5267 return ERROR_SUCCESS;
5270 component = MSI_RecordGetString(rec, 3);
5271 comp = get_loaded_component(package, component);
5272 if (!comp)
5273 return ERROR_SUCCESS;
5275 compgroupid = MSI_RecordGetString(rec,1);
5276 qualifier = MSI_RecordGetString(rec,2);
5278 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5279 if (rc != ERROR_SUCCESS)
5280 goto end;
5282 text = MSI_RecordGetString(rec,4);
5283 advertise = create_component_advertise_string(package, comp, feature);
5285 sz = strlenW(advertise);
5287 if (text)
5288 sz += lstrlenW(text);
5290 sz+=3;
5291 sz *= sizeof(WCHAR);
5293 output = msi_alloc_zero(sz);
5294 strcpyW(output,advertise);
5295 msi_free(advertise);
5297 if (text)
5298 strcatW(output,text);
5300 msi_reg_set_val_multi_str( hkey, qualifier, output );
5302 end:
5303 RegCloseKey(hkey);
5304 msi_free(output);
5306 /* the UI chunk */
5307 uirow = MSI_CreateRecord( 2 );
5308 MSI_RecordSetStringW( uirow, 1, compgroupid );
5309 MSI_RecordSetStringW( uirow, 2, qualifier);
5310 ui_actiondata( package, szPublishComponents, uirow);
5311 msiobj_release( &uirow->hdr );
5312 /* FIXME: call ui_progress? */
5314 return rc;
5318 * At present I am ignorning the advertised components part of this and only
5319 * focusing on the qualified component sets
5321 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5323 UINT rc;
5324 MSIQUERY * view;
5325 static const WCHAR ExecSeqQuery[] =
5326 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5327 '`','P','u','b','l','i','s','h',
5328 'C','o','m','p','o','n','e','n','t','`',0};
5330 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5331 if (rc != ERROR_SUCCESS)
5332 return ERROR_SUCCESS;
5334 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5335 msiobj_release(&view->hdr);
5337 return rc;
5340 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5342 static const WCHAR szInstallerComponents[] = {
5343 'S','o','f','t','w','a','r','e','\\',
5344 'M','i','c','r','o','s','o','f','t','\\',
5345 'I','n','s','t','a','l','l','e','r','\\',
5346 'C','o','m','p','o','n','e','n','t','s','\\',0};
5348 MSIPACKAGE *package = param;
5349 LPCWSTR compgroupid, component, feature, qualifier;
5350 MSICOMPONENT *comp;
5351 MSIFEATURE *feat;
5352 MSIRECORD *uirow;
5353 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5354 LONG res;
5356 feature = MSI_RecordGetString( rec, 5 );
5357 feat = get_loaded_feature( package, feature );
5358 if (!feat)
5359 return ERROR_SUCCESS;
5361 if (feat->ActionRequest != INSTALLSTATE_ABSENT)
5363 TRACE("Feature %s not scheduled for removal\n", debugstr_w(feature));
5364 feat->Action = feat->Installed;
5365 return ERROR_SUCCESS;
5368 component = MSI_RecordGetString( rec, 3 );
5369 comp = get_loaded_component( package, component );
5370 if (!comp)
5371 return ERROR_SUCCESS;
5373 compgroupid = MSI_RecordGetString( rec, 1 );
5374 qualifier = MSI_RecordGetString( rec, 2 );
5376 squash_guid( compgroupid, squashed );
5377 strcpyW( keypath, szInstallerComponents );
5378 strcatW( keypath, squashed );
5380 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5381 if (res != ERROR_SUCCESS)
5383 WARN("Unable to delete component key %d\n", res);
5386 uirow = MSI_CreateRecord( 2 );
5387 MSI_RecordSetStringW( uirow, 1, compgroupid );
5388 MSI_RecordSetStringW( uirow, 2, qualifier );
5389 ui_actiondata( package, szUnpublishComponents, uirow );
5390 msiobj_release( &uirow->hdr );
5392 return ERROR_SUCCESS;
5395 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5397 UINT rc;
5398 MSIQUERY *view;
5399 static const WCHAR query[] =
5400 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5401 '`','P','u','b','l','i','s','h',
5402 'C','o','m','p','o','n','e','n','t','`',0};
5404 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5405 if (rc != ERROR_SUCCESS)
5406 return ERROR_SUCCESS;
5408 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5409 msiobj_release( &view->hdr );
5411 return rc;
5414 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5416 MSIPACKAGE *package = param;
5417 MSIRECORD *row;
5418 MSIFILE *file;
5419 SC_HANDLE hscm, service = NULL;
5420 LPCWSTR comp, key;
5421 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5422 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5423 DWORD serv_type, start_type, err_control;
5424 SERVICE_DESCRIPTIONW sd = {NULL};
5426 static const WCHAR query[] =
5427 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
5428 '`','C','o','m','p','o','n','e','n','t','`',' ',
5429 'W','H','E','R','E',' ',
5430 '`','C','o','m','p','o','n','e','n','t','`',' ',
5431 '=','\'','%','s','\'',0};
5433 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5434 if (!hscm)
5436 ERR("Failed to open the SC Manager!\n");
5437 goto done;
5440 start_type = MSI_RecordGetInteger(rec, 5);
5441 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5442 goto done;
5444 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5445 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5446 serv_type = MSI_RecordGetInteger(rec, 4);
5447 err_control = MSI_RecordGetInteger(rec, 6);
5448 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5449 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5450 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5451 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5452 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5453 comp = MSI_RecordGetString(rec, 12);
5454 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5456 /* fetch the service path */
5457 row = MSI_QueryGetRecord(package->db, query, comp);
5458 if (!row)
5460 ERR("Control query failed!\n");
5461 goto done;
5463 key = MSI_RecordGetString(row, 6);
5465 file = get_loaded_file(package, key);
5466 msiobj_release(&row->hdr);
5467 if (!file)
5469 ERR("Failed to load the service file\n");
5470 goto done;
5473 if (!args || !args[0]) image_path = file->TargetPath;
5474 else
5476 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5477 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5478 return ERROR_OUTOFMEMORY;
5480 strcpyW(image_path, file->TargetPath);
5481 strcatW(image_path, szSpace);
5482 strcatW(image_path, args);
5484 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5485 start_type, err_control, image_path, load_order,
5486 NULL, depends, serv_name, pass);
5488 if (!service)
5490 if (GetLastError() != ERROR_SERVICE_EXISTS)
5491 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5493 else if (sd.lpDescription)
5495 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5496 WARN("failed to set service description %u\n", GetLastError());
5499 if (image_path != file->TargetPath) msi_free(image_path);
5500 done:
5501 CloseServiceHandle(service);
5502 CloseServiceHandle(hscm);
5503 msi_free(name);
5504 msi_free(disp);
5505 msi_free(sd.lpDescription);
5506 msi_free(load_order);
5507 msi_free(serv_name);
5508 msi_free(pass);
5509 msi_free(depends);
5510 msi_free(args);
5512 return ERROR_SUCCESS;
5515 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5517 UINT rc;
5518 MSIQUERY * view;
5519 static const WCHAR ExecSeqQuery[] =
5520 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5521 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5523 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5524 if (rc != ERROR_SUCCESS)
5525 return ERROR_SUCCESS;
5527 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5528 msiobj_release(&view->hdr);
5530 return rc;
5533 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5534 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5536 LPCWSTR *vector, *temp_vector;
5537 LPWSTR p, q;
5538 DWORD sep_len;
5540 static const WCHAR separator[] = {'[','~',']',0};
5542 *numargs = 0;
5543 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5545 if (!args)
5546 return NULL;
5548 vector = msi_alloc(sizeof(LPWSTR));
5549 if (!vector)
5550 return NULL;
5552 p = args;
5555 (*numargs)++;
5556 vector[*numargs - 1] = p;
5558 if ((q = strstrW(p, separator)))
5560 *q = '\0';
5562 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5563 if (!temp_vector)
5565 msi_free(vector);
5566 return NULL;
5568 vector = temp_vector;
5570 p = q + sep_len;
5572 } while (q);
5574 return vector;
5577 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5579 MSIPACKAGE *package = param;
5580 MSICOMPONENT *comp;
5581 MSIRECORD *uirow;
5582 SC_HANDLE scm = NULL, service = NULL;
5583 LPCWSTR component, *vector = NULL;
5584 LPWSTR name, args, display_name = NULL;
5585 DWORD event, numargs, len;
5586 UINT r = ERROR_FUNCTION_FAILED;
5588 component = MSI_RecordGetString(rec, 6);
5589 comp = get_loaded_component(package, component);
5590 if (!comp)
5591 return ERROR_SUCCESS;
5593 if (!comp->Enabled)
5595 TRACE("component is disabled\n");
5596 return ERROR_SUCCESS;
5599 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
5601 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
5602 comp->Action = comp->Installed;
5603 return ERROR_SUCCESS;
5605 comp->Action = INSTALLSTATE_LOCAL;
5607 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5608 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5609 event = MSI_RecordGetInteger(rec, 3);
5611 if (!(event & msidbServiceControlEventStart))
5613 r = ERROR_SUCCESS;
5614 goto done;
5617 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5618 if (!scm)
5620 ERR("Failed to open the service control manager\n");
5621 goto done;
5624 len = 0;
5625 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5626 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5628 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5629 GetServiceDisplayNameW( scm, name, display_name, &len );
5632 service = OpenServiceW(scm, name, SERVICE_START);
5633 if (!service)
5635 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5636 goto done;
5639 vector = msi_service_args_to_vector(args, &numargs);
5641 if (!StartServiceW(service, numargs, vector) &&
5642 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5644 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5645 goto done;
5648 r = ERROR_SUCCESS;
5650 done:
5651 uirow = MSI_CreateRecord( 2 );
5652 MSI_RecordSetStringW( uirow, 1, display_name );
5653 MSI_RecordSetStringW( uirow, 2, name );
5654 ui_actiondata( package, szStartServices, uirow );
5655 msiobj_release( &uirow->hdr );
5657 CloseServiceHandle(service);
5658 CloseServiceHandle(scm);
5660 msi_free(name);
5661 msi_free(args);
5662 msi_free(vector);
5663 msi_free(display_name);
5664 return r;
5667 static UINT ACTION_StartServices( MSIPACKAGE *package )
5669 UINT rc;
5670 MSIQUERY *view;
5672 static const WCHAR query[] = {
5673 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5674 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5676 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5677 if (rc != ERROR_SUCCESS)
5678 return ERROR_SUCCESS;
5680 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5681 msiobj_release(&view->hdr);
5683 return rc;
5686 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5688 DWORD i, needed, count;
5689 ENUM_SERVICE_STATUSW *dependencies;
5690 SERVICE_STATUS ss;
5691 SC_HANDLE depserv;
5693 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5694 0, &needed, &count))
5695 return TRUE;
5697 if (GetLastError() != ERROR_MORE_DATA)
5698 return FALSE;
5700 dependencies = msi_alloc(needed);
5701 if (!dependencies)
5702 return FALSE;
5704 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5705 needed, &needed, &count))
5706 goto error;
5708 for (i = 0; i < count; i++)
5710 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5711 SERVICE_STOP | SERVICE_QUERY_STATUS);
5712 if (!depserv)
5713 goto error;
5715 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5716 goto error;
5719 return TRUE;
5721 error:
5722 msi_free(dependencies);
5723 return FALSE;
5726 static UINT stop_service( LPCWSTR name )
5728 SC_HANDLE scm = NULL, service = NULL;
5729 SERVICE_STATUS status;
5730 SERVICE_STATUS_PROCESS ssp;
5731 DWORD needed;
5733 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5734 if (!scm)
5736 WARN("Failed to open the SCM: %d\n", GetLastError());
5737 goto done;
5740 service = OpenServiceW(scm, name,
5741 SERVICE_STOP |
5742 SERVICE_QUERY_STATUS |
5743 SERVICE_ENUMERATE_DEPENDENTS);
5744 if (!service)
5746 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5747 goto done;
5750 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5751 sizeof(SERVICE_STATUS_PROCESS), &needed))
5753 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5754 goto done;
5757 if (ssp.dwCurrentState == SERVICE_STOPPED)
5758 goto done;
5760 stop_service_dependents(scm, service);
5762 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5763 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5765 done:
5766 CloseServiceHandle(service);
5767 CloseServiceHandle(scm);
5769 return ERROR_SUCCESS;
5772 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5774 MSIPACKAGE *package = param;
5775 MSICOMPONENT *comp;
5776 MSIRECORD *uirow;
5777 LPCWSTR component;
5778 LPWSTR name = NULL, display_name = NULL;
5779 DWORD event, len;
5780 SC_HANDLE scm;
5782 event = MSI_RecordGetInteger( rec, 3 );
5783 if (!(event & msidbServiceControlEventStop))
5784 return ERROR_SUCCESS;
5786 component = MSI_RecordGetString( rec, 6 );
5787 comp = get_loaded_component( package, component );
5788 if (!comp)
5789 return ERROR_SUCCESS;
5791 if (!comp->Enabled)
5793 TRACE("component is disabled\n");
5794 return ERROR_SUCCESS;
5797 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5799 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5800 comp->Action = comp->Installed;
5801 return ERROR_SUCCESS;
5803 comp->Action = INSTALLSTATE_ABSENT;
5805 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5806 if (!scm)
5808 ERR("Failed to open the service control manager\n");
5809 goto done;
5812 len = 0;
5813 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5814 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5816 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5817 GetServiceDisplayNameW( scm, name, display_name, &len );
5819 CloseServiceHandle( scm );
5821 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5822 stop_service( name );
5824 done:
5825 uirow = MSI_CreateRecord( 2 );
5826 MSI_RecordSetStringW( uirow, 1, display_name );
5827 MSI_RecordSetStringW( uirow, 2, name );
5828 ui_actiondata( package, szStopServices, uirow );
5829 msiobj_release( &uirow->hdr );
5831 msi_free( name );
5832 msi_free( display_name );
5833 return ERROR_SUCCESS;
5836 static UINT ACTION_StopServices( MSIPACKAGE *package )
5838 UINT rc;
5839 MSIQUERY *view;
5841 static const WCHAR query[] = {
5842 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5843 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5845 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5846 if (rc != ERROR_SUCCESS)
5847 return ERROR_SUCCESS;
5849 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5850 msiobj_release(&view->hdr);
5852 return rc;
5855 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5857 MSIPACKAGE *package = param;
5858 MSICOMPONENT *comp;
5859 MSIRECORD *uirow;
5860 LPCWSTR component;
5861 LPWSTR name = NULL, display_name = NULL;
5862 DWORD event, len;
5863 SC_HANDLE scm = NULL, service = NULL;
5865 event = MSI_RecordGetInteger( rec, 3 );
5866 if (!(event & msidbServiceControlEventDelete))
5867 return ERROR_SUCCESS;
5869 component = MSI_RecordGetString(rec, 6);
5870 comp = get_loaded_component(package, component);
5871 if (!comp)
5872 return ERROR_SUCCESS;
5874 if (!comp->Enabled)
5876 TRACE("component is disabled\n");
5877 return ERROR_SUCCESS;
5880 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
5882 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
5883 comp->Action = comp->Installed;
5884 return ERROR_SUCCESS;
5886 comp->Action = INSTALLSTATE_ABSENT;
5888 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5889 stop_service( name );
5891 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5892 if (!scm)
5894 WARN("Failed to open the SCM: %d\n", GetLastError());
5895 goto done;
5898 len = 0;
5899 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5900 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5902 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5903 GetServiceDisplayNameW( scm, name, display_name, &len );
5906 service = OpenServiceW( scm, name, DELETE );
5907 if (!service)
5909 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5910 goto done;
5913 if (!DeleteService( service ))
5914 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5916 done:
5917 uirow = MSI_CreateRecord( 2 );
5918 MSI_RecordSetStringW( uirow, 1, display_name );
5919 MSI_RecordSetStringW( uirow, 2, name );
5920 ui_actiondata( package, szDeleteServices, uirow );
5921 msiobj_release( &uirow->hdr );
5923 CloseServiceHandle( service );
5924 CloseServiceHandle( scm );
5925 msi_free( name );
5926 msi_free( display_name );
5928 return ERROR_SUCCESS;
5931 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5933 UINT rc;
5934 MSIQUERY *view;
5936 static const WCHAR query[] = {
5937 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5938 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5940 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5941 if (rc != ERROR_SUCCESS)
5942 return ERROR_SUCCESS;
5944 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5945 msiobj_release( &view->hdr );
5947 return rc;
5950 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5952 MSIPACKAGE *package = param;
5953 LPWSTR driver, driver_path, ptr;
5954 WCHAR outpath[MAX_PATH];
5955 MSIFILE *driver_file = NULL, *setup_file = NULL;
5956 MSICOMPONENT *comp;
5957 MSIRECORD *uirow;
5958 LPCWSTR desc, file_key, component;
5959 DWORD len, usage;
5960 UINT r = ERROR_SUCCESS;
5962 static const WCHAR driver_fmt[] = {
5963 'D','r','i','v','e','r','=','%','s',0};
5964 static const WCHAR setup_fmt[] = {
5965 'S','e','t','u','p','=','%','s',0};
5966 static const WCHAR usage_fmt[] = {
5967 'F','i','l','e','U','s','a','g','e','=','1',0};
5969 component = MSI_RecordGetString( rec, 2 );
5970 comp = get_loaded_component( package, component );
5971 if (!comp)
5972 return ERROR_SUCCESS;
5974 if (!comp->Enabled)
5976 TRACE("component is disabled\n");
5977 return ERROR_SUCCESS;
5980 desc = MSI_RecordGetString(rec, 3);
5982 file_key = MSI_RecordGetString( rec, 4 );
5983 if (file_key) driver_file = get_loaded_file( package, file_key );
5985 file_key = MSI_RecordGetString( rec, 5 );
5986 if (file_key) setup_file = get_loaded_file( package, file_key );
5988 if (!driver_file)
5990 ERR("ODBC Driver entry not found!\n");
5991 return ERROR_FUNCTION_FAILED;
5994 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5995 if (setup_file)
5996 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5997 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5999 driver = msi_alloc(len * sizeof(WCHAR));
6000 if (!driver)
6001 return ERROR_OUTOFMEMORY;
6003 ptr = driver;
6004 lstrcpyW(ptr, desc);
6005 ptr += lstrlenW(ptr) + 1;
6007 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6008 ptr += len + 1;
6010 if (setup_file)
6012 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6013 ptr += len + 1;
6016 lstrcpyW(ptr, usage_fmt);
6017 ptr += lstrlenW(ptr) + 1;
6018 *ptr = '\0';
6020 driver_path = strdupW(driver_file->TargetPath);
6021 ptr = strrchrW(driver_path, '\\');
6022 if (ptr) *ptr = '\0';
6024 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6025 NULL, ODBC_INSTALL_COMPLETE, &usage))
6027 ERR("Failed to install SQL driver!\n");
6028 r = ERROR_FUNCTION_FAILED;
6031 uirow = MSI_CreateRecord( 5 );
6032 MSI_RecordSetStringW( uirow, 1, desc );
6033 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6034 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6035 ui_actiondata( package, szInstallODBC, uirow );
6036 msiobj_release( &uirow->hdr );
6038 msi_free(driver);
6039 msi_free(driver_path);
6041 return r;
6044 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6046 MSIPACKAGE *package = param;
6047 LPWSTR translator, translator_path, ptr;
6048 WCHAR outpath[MAX_PATH];
6049 MSIFILE *translator_file = NULL, *setup_file = NULL;
6050 MSICOMPONENT *comp;
6051 MSIRECORD *uirow;
6052 LPCWSTR desc, file_key, component;
6053 DWORD len, usage;
6054 UINT r = ERROR_SUCCESS;
6056 static const WCHAR translator_fmt[] = {
6057 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6058 static const WCHAR setup_fmt[] = {
6059 'S','e','t','u','p','=','%','s',0};
6061 component = MSI_RecordGetString( rec, 2 );
6062 comp = get_loaded_component( package, component );
6063 if (!comp)
6064 return ERROR_SUCCESS;
6066 if (!comp->Enabled)
6068 TRACE("component is disabled\n");
6069 return ERROR_SUCCESS;
6072 desc = MSI_RecordGetString(rec, 3);
6074 file_key = MSI_RecordGetString( rec, 4 );
6075 if (file_key) translator_file = get_loaded_file( package, file_key );
6077 file_key = MSI_RecordGetString( rec, 5 );
6078 if (file_key) setup_file = get_loaded_file( package, file_key );
6080 if (!translator_file)
6082 ERR("ODBC Translator entry not found!\n");
6083 return ERROR_FUNCTION_FAILED;
6086 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6087 if (setup_file)
6088 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6090 translator = msi_alloc(len * sizeof(WCHAR));
6091 if (!translator)
6092 return ERROR_OUTOFMEMORY;
6094 ptr = translator;
6095 lstrcpyW(ptr, desc);
6096 ptr += lstrlenW(ptr) + 1;
6098 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6099 ptr += len + 1;
6101 if (setup_file)
6103 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6104 ptr += len + 1;
6106 *ptr = '\0';
6108 translator_path = strdupW(translator_file->TargetPath);
6109 ptr = strrchrW(translator_path, '\\');
6110 if (ptr) *ptr = '\0';
6112 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6113 NULL, ODBC_INSTALL_COMPLETE, &usage))
6115 ERR("Failed to install SQL translator!\n");
6116 r = ERROR_FUNCTION_FAILED;
6119 uirow = MSI_CreateRecord( 5 );
6120 MSI_RecordSetStringW( uirow, 1, desc );
6121 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6122 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6123 ui_actiondata( package, szInstallODBC, uirow );
6124 msiobj_release( &uirow->hdr );
6126 msi_free(translator);
6127 msi_free(translator_path);
6129 return r;
6132 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6134 MSIPACKAGE *package = param;
6135 MSICOMPONENT *comp;
6136 LPWSTR attrs;
6137 LPCWSTR desc, driver, component;
6138 WORD request = ODBC_ADD_SYS_DSN;
6139 INT registration;
6140 DWORD len;
6141 UINT r = ERROR_SUCCESS;
6142 MSIRECORD *uirow;
6144 static const WCHAR attrs_fmt[] = {
6145 'D','S','N','=','%','s',0 };
6147 component = MSI_RecordGetString( rec, 2 );
6148 comp = get_loaded_component( package, component );
6149 if (!comp)
6150 return ERROR_SUCCESS;
6152 if (!comp->Enabled)
6154 TRACE("component is disabled\n");
6155 return ERROR_SUCCESS;
6158 desc = MSI_RecordGetString(rec, 3);
6159 driver = MSI_RecordGetString(rec, 4);
6160 registration = MSI_RecordGetInteger(rec, 5);
6162 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6163 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6165 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6166 attrs = msi_alloc(len * sizeof(WCHAR));
6167 if (!attrs)
6168 return ERROR_OUTOFMEMORY;
6170 len = sprintfW(attrs, attrs_fmt, desc);
6171 attrs[len + 1] = 0;
6173 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6175 ERR("Failed to install SQL data source!\n");
6176 r = ERROR_FUNCTION_FAILED;
6179 uirow = MSI_CreateRecord( 5 );
6180 MSI_RecordSetStringW( uirow, 1, desc );
6181 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6182 MSI_RecordSetInteger( uirow, 3, request );
6183 ui_actiondata( package, szInstallODBC, uirow );
6184 msiobj_release( &uirow->hdr );
6186 msi_free(attrs);
6188 return r;
6191 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6193 UINT rc;
6194 MSIQUERY *view;
6196 static const WCHAR driver_query[] = {
6197 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6198 'O','D','B','C','D','r','i','v','e','r',0 };
6200 static const WCHAR translator_query[] = {
6201 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6202 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6204 static const WCHAR source_query[] = {
6205 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6206 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6208 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6209 if (rc != ERROR_SUCCESS)
6210 return ERROR_SUCCESS;
6212 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6213 msiobj_release(&view->hdr);
6215 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6216 if (rc != ERROR_SUCCESS)
6217 return ERROR_SUCCESS;
6219 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6220 msiobj_release(&view->hdr);
6222 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6223 if (rc != ERROR_SUCCESS)
6224 return ERROR_SUCCESS;
6226 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6227 msiobj_release(&view->hdr);
6229 return rc;
6232 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6234 MSIPACKAGE *package = param;
6235 MSICOMPONENT *comp;
6236 MSIRECORD *uirow;
6237 DWORD usage;
6238 LPCWSTR desc, component;
6240 component = MSI_RecordGetString( rec, 2 );
6241 comp = get_loaded_component( package, component );
6242 if (!comp)
6243 return ERROR_SUCCESS;
6245 if (!comp->Enabled)
6247 TRACE("component is disabled\n");
6248 return ERROR_SUCCESS;
6251 desc = MSI_RecordGetString( rec, 3 );
6252 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6254 WARN("Failed to remove ODBC driver\n");
6256 else if (!usage)
6258 FIXME("Usage count reached 0\n");
6261 uirow = MSI_CreateRecord( 2 );
6262 MSI_RecordSetStringW( uirow, 1, desc );
6263 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6264 ui_actiondata( package, szRemoveODBC, uirow );
6265 msiobj_release( &uirow->hdr );
6267 return ERROR_SUCCESS;
6270 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6272 MSIPACKAGE *package = param;
6273 MSICOMPONENT *comp;
6274 MSIRECORD *uirow;
6275 DWORD usage;
6276 LPCWSTR desc, component;
6278 component = MSI_RecordGetString( rec, 2 );
6279 comp = get_loaded_component( package, component );
6280 if (!comp)
6281 return ERROR_SUCCESS;
6283 if (!comp->Enabled)
6285 TRACE("component is disabled\n");
6286 return ERROR_SUCCESS;
6289 desc = MSI_RecordGetString( rec, 3 );
6290 if (!SQLRemoveTranslatorW( desc, &usage ))
6292 WARN("Failed to remove ODBC translator\n");
6294 else if (!usage)
6296 FIXME("Usage count reached 0\n");
6299 uirow = MSI_CreateRecord( 2 );
6300 MSI_RecordSetStringW( uirow, 1, desc );
6301 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6302 ui_actiondata( package, szRemoveODBC, uirow );
6303 msiobj_release( &uirow->hdr );
6305 return ERROR_SUCCESS;
6308 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6310 MSIPACKAGE *package = param;
6311 MSICOMPONENT *comp;
6312 MSIRECORD *uirow;
6313 LPWSTR attrs;
6314 LPCWSTR desc, driver, component;
6315 WORD request = ODBC_REMOVE_SYS_DSN;
6316 INT registration;
6317 DWORD len;
6319 static const WCHAR attrs_fmt[] = {
6320 'D','S','N','=','%','s',0 };
6322 component = MSI_RecordGetString( rec, 2 );
6323 comp = get_loaded_component( package, component );
6324 if (!comp)
6325 return ERROR_SUCCESS;
6327 if (!comp->Enabled)
6329 TRACE("component is disabled\n");
6330 return ERROR_SUCCESS;
6333 desc = MSI_RecordGetString( rec, 3 );
6334 driver = MSI_RecordGetString( rec, 4 );
6335 registration = MSI_RecordGetInteger( rec, 5 );
6337 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6338 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6340 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6341 attrs = msi_alloc( len * sizeof(WCHAR) );
6342 if (!attrs)
6343 return ERROR_OUTOFMEMORY;
6345 FIXME("Use ODBCSourceAttribute table\n");
6347 len = sprintfW( attrs, attrs_fmt, desc );
6348 attrs[len + 1] = 0;
6350 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6352 WARN("Failed to remove ODBC data source\n");
6354 msi_free( attrs );
6356 uirow = MSI_CreateRecord( 3 );
6357 MSI_RecordSetStringW( uirow, 1, desc );
6358 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6359 MSI_RecordSetInteger( uirow, 3, request );
6360 ui_actiondata( package, szRemoveODBC, uirow );
6361 msiobj_release( &uirow->hdr );
6363 return ERROR_SUCCESS;
6366 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6368 UINT rc;
6369 MSIQUERY *view;
6371 static const WCHAR driver_query[] = {
6372 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6373 'O','D','B','C','D','r','i','v','e','r',0 };
6375 static const WCHAR translator_query[] = {
6376 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6377 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6379 static const WCHAR source_query[] = {
6380 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6381 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
6383 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6384 if (rc != ERROR_SUCCESS)
6385 return ERROR_SUCCESS;
6387 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6388 msiobj_release( &view->hdr );
6390 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6391 if (rc != ERROR_SUCCESS)
6392 return ERROR_SUCCESS;
6394 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6395 msiobj_release( &view->hdr );
6397 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6398 if (rc != ERROR_SUCCESS)
6399 return ERROR_SUCCESS;
6401 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6402 msiobj_release( &view->hdr );
6404 return rc;
6407 #define ENV_ACT_SETALWAYS 0x1
6408 #define ENV_ACT_SETABSENT 0x2
6409 #define ENV_ACT_REMOVE 0x4
6410 #define ENV_ACT_REMOVEMATCH 0x8
6412 #define ENV_MOD_MACHINE 0x20000000
6413 #define ENV_MOD_APPEND 0x40000000
6414 #define ENV_MOD_PREFIX 0x80000000
6415 #define ENV_MOD_MASK 0xC0000000
6417 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6419 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6421 LPCWSTR cptr = *name;
6423 static const WCHAR prefix[] = {'[','~',']',0};
6424 static const int prefix_len = 3;
6426 *flags = 0;
6427 while (*cptr)
6429 if (*cptr == '=')
6430 *flags |= ENV_ACT_SETALWAYS;
6431 else if (*cptr == '+')
6432 *flags |= ENV_ACT_SETABSENT;
6433 else if (*cptr == '-')
6434 *flags |= ENV_ACT_REMOVE;
6435 else if (*cptr == '!')
6436 *flags |= ENV_ACT_REMOVEMATCH;
6437 else if (*cptr == '*')
6438 *flags |= ENV_MOD_MACHINE;
6439 else
6440 break;
6442 cptr++;
6443 (*name)++;
6446 if (!*cptr)
6448 ERR("Missing environment variable\n");
6449 return ERROR_FUNCTION_FAILED;
6452 if (*value)
6454 LPCWSTR ptr = *value;
6455 if (!strncmpW(ptr, prefix, prefix_len))
6457 if (ptr[prefix_len] == szSemiColon[0])
6459 *flags |= ENV_MOD_APPEND;
6460 *value += lstrlenW(prefix);
6462 else
6464 *value = NULL;
6467 else if (lstrlenW(*value) >= prefix_len)
6469 ptr += lstrlenW(ptr) - prefix_len;
6470 if (!strcmpW( ptr, prefix ))
6472 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6474 *flags |= ENV_MOD_PREFIX;
6475 /* the "[~]" will be removed by deformat_string */;
6477 else
6479 *value = NULL;
6485 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6486 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6487 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6488 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6490 ERR("Invalid flags: %08x\n", *flags);
6491 return ERROR_FUNCTION_FAILED;
6494 if (!*flags)
6495 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6497 return ERROR_SUCCESS;
6500 static UINT open_env_key( DWORD flags, HKEY *key )
6502 static const WCHAR user_env[] =
6503 {'E','n','v','i','r','o','n','m','e','n','t',0};
6504 static const WCHAR machine_env[] =
6505 {'S','y','s','t','e','m','\\',
6506 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6507 'C','o','n','t','r','o','l','\\',
6508 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6509 'E','n','v','i','r','o','n','m','e','n','t',0};
6510 const WCHAR *env;
6511 HKEY root;
6512 LONG res;
6514 if (flags & ENV_MOD_MACHINE)
6516 env = machine_env;
6517 root = HKEY_LOCAL_MACHINE;
6519 else
6521 env = user_env;
6522 root = HKEY_CURRENT_USER;
6525 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6526 if (res != ERROR_SUCCESS)
6528 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6529 return ERROR_FUNCTION_FAILED;
6532 return ERROR_SUCCESS;
6535 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6537 MSIPACKAGE *package = param;
6538 LPCWSTR name, value, component;
6539 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6540 DWORD flags, type, size;
6541 UINT res;
6542 HKEY env = NULL;
6543 MSICOMPONENT *comp;
6544 MSIRECORD *uirow;
6545 int action = 0;
6547 component = MSI_RecordGetString(rec, 4);
6548 comp = get_loaded_component(package, component);
6549 if (!comp)
6550 return ERROR_SUCCESS;
6552 if (!comp->Enabled)
6554 TRACE("component is disabled\n");
6555 return ERROR_SUCCESS;
6558 if (comp->ActionRequest != INSTALLSTATE_LOCAL)
6560 TRACE("Component not scheduled for installation: %s\n", debugstr_w(component));
6561 comp->Action = comp->Installed;
6562 return ERROR_SUCCESS;
6564 comp->Action = INSTALLSTATE_LOCAL;
6566 name = MSI_RecordGetString(rec, 2);
6567 value = MSI_RecordGetString(rec, 3);
6569 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6571 res = env_parse_flags(&name, &value, &flags);
6572 if (res != ERROR_SUCCESS || !value)
6573 goto done;
6575 if (value && !deformat_string(package, value, &deformatted))
6577 res = ERROR_OUTOFMEMORY;
6578 goto done;
6581 value = deformatted;
6583 res = open_env_key( flags, &env );
6584 if (res != ERROR_SUCCESS)
6585 goto done;
6587 if (flags & ENV_MOD_MACHINE)
6588 action |= 0x20000000;
6590 size = 0;
6591 type = REG_SZ;
6592 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6593 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6594 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6595 goto done;
6597 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6599 action = 0x2;
6601 /* Nothing to do. */
6602 if (!value)
6604 res = ERROR_SUCCESS;
6605 goto done;
6608 /* If we are appending but the string was empty, strip ; */
6609 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6611 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6612 newval = strdupW(value);
6613 if (!newval)
6615 res = ERROR_OUTOFMEMORY;
6616 goto done;
6619 else
6621 action = 0x1;
6623 /* Contrary to MSDN, +-variable to [~];path works */
6624 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6626 res = ERROR_SUCCESS;
6627 goto done;
6630 data = msi_alloc(size);
6631 if (!data)
6633 RegCloseKey(env);
6634 return ERROR_OUTOFMEMORY;
6637 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6638 if (res != ERROR_SUCCESS)
6639 goto done;
6641 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6643 action = 0x4;
6644 res = RegDeleteValueW(env, name);
6645 if (res != ERROR_SUCCESS)
6646 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6647 goto done;
6650 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6651 if (flags & ENV_MOD_MASK)
6653 DWORD mod_size;
6654 int multiplier = 0;
6655 if (flags & ENV_MOD_APPEND) multiplier++;
6656 if (flags & ENV_MOD_PREFIX) multiplier++;
6657 mod_size = lstrlenW(value) * multiplier;
6658 size += mod_size * sizeof(WCHAR);
6661 newval = msi_alloc(size);
6662 ptr = newval;
6663 if (!newval)
6665 res = ERROR_OUTOFMEMORY;
6666 goto done;
6669 if (flags & ENV_MOD_PREFIX)
6671 lstrcpyW(newval, value);
6672 ptr = newval + lstrlenW(value);
6673 action |= 0x80000000;
6676 lstrcpyW(ptr, data);
6678 if (flags & ENV_MOD_APPEND)
6680 lstrcatW(newval, value);
6681 action |= 0x40000000;
6684 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6685 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6686 if (res)
6688 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6691 done:
6692 uirow = MSI_CreateRecord( 3 );
6693 MSI_RecordSetStringW( uirow, 1, name );
6694 MSI_RecordSetStringW( uirow, 2, newval );
6695 MSI_RecordSetInteger( uirow, 3, action );
6696 ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6697 msiobj_release( &uirow->hdr );
6699 if (env) RegCloseKey(env);
6700 msi_free(deformatted);
6701 msi_free(data);
6702 msi_free(newval);
6703 return res;
6706 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6708 UINT rc;
6709 MSIQUERY * view;
6710 static const WCHAR ExecSeqQuery[] =
6711 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6712 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6713 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
6714 if (rc != ERROR_SUCCESS)
6715 return ERROR_SUCCESS;
6717 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6718 msiobj_release(&view->hdr);
6720 return rc;
6723 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6725 MSIPACKAGE *package = param;
6726 LPCWSTR name, value, component;
6727 LPWSTR deformatted = NULL;
6728 DWORD flags;
6729 HKEY env;
6730 MSICOMPONENT *comp;
6731 MSIRECORD *uirow;
6732 int action = 0;
6733 LONG res;
6734 UINT r;
6736 component = MSI_RecordGetString( rec, 4 );
6737 comp = get_loaded_component( package, component );
6738 if (!comp)
6739 return ERROR_SUCCESS;
6741 if (!comp->Enabled)
6743 TRACE("component is disabled\n");
6744 return ERROR_SUCCESS;
6747 if (comp->ActionRequest != INSTALLSTATE_ABSENT)
6749 TRACE("Component not scheduled for removal: %s\n", debugstr_w(component));
6750 comp->Action = comp->Installed;
6751 return ERROR_SUCCESS;
6753 comp->Action = INSTALLSTATE_ABSENT;
6755 name = MSI_RecordGetString( rec, 2 );
6756 value = MSI_RecordGetString( rec, 3 );
6758 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6760 r = env_parse_flags( &name, &value, &flags );
6761 if (r != ERROR_SUCCESS)
6762 return r;
6764 if (!(flags & ENV_ACT_REMOVE))
6766 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6767 return ERROR_SUCCESS;
6770 if (value && !deformat_string( package, value, &deformatted ))
6771 return ERROR_OUTOFMEMORY;
6773 value = deformatted;
6775 r = open_env_key( flags, &env );
6776 if (r != ERROR_SUCCESS)
6778 r = ERROR_SUCCESS;
6779 goto done;
6782 if (flags & ENV_MOD_MACHINE)
6783 action |= 0x20000000;
6785 TRACE("Removing %s\n", debugstr_w(name));
6787 res = RegDeleteValueW( env, name );
6788 if (res != ERROR_SUCCESS)
6790 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6791 r = ERROR_SUCCESS;
6794 done:
6795 uirow = MSI_CreateRecord( 3 );
6796 MSI_RecordSetStringW( uirow, 1, name );
6797 MSI_RecordSetStringW( uirow, 2, value );
6798 MSI_RecordSetInteger( uirow, 3, action );
6799 ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6800 msiobj_release( &uirow->hdr );
6802 if (env) RegCloseKey( env );
6803 msi_free( deformatted );
6804 return r;
6807 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6809 UINT rc;
6810 MSIQUERY *view;
6811 static const WCHAR query[] =
6812 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6813 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6815 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6816 if (rc != ERROR_SUCCESS)
6817 return ERROR_SUCCESS;
6819 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6820 msiobj_release( &view->hdr );
6822 return rc;
6825 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6827 LPWSTR key, template, id;
6828 UINT r = ERROR_SUCCESS;
6830 id = msi_dup_property( package->db, szProductID );
6831 if (id)
6833 msi_free( id );
6834 return ERROR_SUCCESS;
6836 template = msi_dup_property( package->db, szPIDTemplate );
6837 key = msi_dup_property( package->db, szPIDKEY );
6839 if (key && template)
6841 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6842 r = msi_set_property( package->db, szProductID, key );
6844 msi_free( template );
6845 msi_free( key );
6846 return r;
6849 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6851 TRACE("\n");
6852 package->need_reboot = 1;
6853 return ERROR_SUCCESS;
6856 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6858 static const WCHAR szAvailableFreeReg[] =
6859 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6860 MSIRECORD *uirow;
6861 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6863 TRACE("%p %d kilobytes\n", package, space);
6865 uirow = MSI_CreateRecord( 1 );
6866 MSI_RecordSetInteger( uirow, 1, space );
6867 ui_actiondata( package, szAllocateRegistrySpace, uirow );
6868 msiobj_release( &uirow->hdr );
6870 return ERROR_SUCCESS;
6873 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6875 FIXME("%p\n", package);
6876 return ERROR_SUCCESS;
6879 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6881 FIXME("%p\n", package);
6882 return ERROR_SUCCESS;
6885 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6887 UINT r, count;
6888 MSIQUERY *view;
6890 static const WCHAR driver_query[] = {
6891 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6892 'O','D','B','C','D','r','i','v','e','r',0 };
6894 static const WCHAR translator_query[] = {
6895 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6896 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
6898 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6899 if (r == ERROR_SUCCESS)
6901 count = 0;
6902 r = MSI_IterateRecords( view, &count, NULL, package );
6903 msiobj_release( &view->hdr );
6904 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6907 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6908 if (r == ERROR_SUCCESS)
6910 count = 0;
6911 r = MSI_IterateRecords( view, &count, NULL, package );
6912 msiobj_release( &view->hdr );
6913 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6916 return ERROR_SUCCESS;
6919 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6921 MSIPACKAGE *package = param;
6922 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6923 WCHAR *value;
6925 if ((value = msi_dup_property( package->db, property )))
6927 FIXME("remove %s\n", debugstr_w(value));
6928 msi_free( value );
6930 return ERROR_SUCCESS;
6933 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6935 UINT r;
6936 MSIQUERY *view;
6938 static const WCHAR query[] =
6939 {'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',
6940 ' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
6942 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6943 if (r == ERROR_SUCCESS)
6945 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6946 msiobj_release( &view->hdr );
6948 return ERROR_SUCCESS;
6951 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6953 MSIPACKAGE *package = param;
6954 int attributes = MSI_RecordGetInteger( rec, 5 );
6956 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6958 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6959 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6960 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6961 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6962 HKEY hkey;
6963 UINT r;
6965 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6967 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6968 if (r != ERROR_SUCCESS)
6969 return ERROR_SUCCESS;
6971 else
6973 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6974 if (r != ERROR_SUCCESS)
6975 return ERROR_SUCCESS;
6977 RegCloseKey( hkey );
6979 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
6980 debugstr_w(upgrade_code), debugstr_w(version_min),
6981 debugstr_w(version_max), debugstr_w(language));
6983 return ERROR_SUCCESS;
6986 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6988 UINT r;
6989 MSIQUERY *view;
6991 static const WCHAR query[] =
6992 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
6994 if (msi_get_property_int( package->db, szInstalled, 0 ))
6996 TRACE("product is installed, skipping action\n");
6997 return ERROR_SUCCESS;
6999 if (msi_get_property_int( package->db, szPreselected, 0 ))
7001 TRACE("Preselected property is set, not migrating feature states\n");
7002 return ERROR_SUCCESS;
7005 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7006 if (r == ERROR_SUCCESS)
7008 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7009 msiobj_release( &view->hdr );
7011 return ERROR_SUCCESS;
7014 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
7015 LPCSTR action, LPCWSTR table )
7017 static const WCHAR query[] = {
7018 'S','E','L','E','C','T',' ','*',' ',
7019 'F','R','O','M',' ','`','%','s','`',0 };
7020 MSIQUERY *view = NULL;
7021 DWORD count = 0;
7022 UINT r;
7024 r = MSI_OpenQuery( package->db, &view, query, table );
7025 if (r == ERROR_SUCCESS)
7027 r = MSI_IterateRecords(view, &count, NULL, package);
7028 msiobj_release(&view->hdr);
7031 if (count)
7032 FIXME("%s -> %u ignored %s table values\n",
7033 action, count, debugstr_w(table));
7035 return ERROR_SUCCESS;
7038 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
7040 static const WCHAR table[] = { 'P','a','t','c','h',0 };
7041 return msi_unimplemented_action_stub( package, "PatchFiles", table );
7044 static UINT ACTION_BindImage( MSIPACKAGE *package )
7046 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
7047 return msi_unimplemented_action_stub( package, "BindImage", table );
7050 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7052 static const WCHAR table[] = {
7053 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7054 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7057 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
7059 static const WCHAR table[] = {
7060 'M','s','i','A','s','s','e','m','b','l','y',0 };
7061 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
7064 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7066 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7067 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7070 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7072 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7073 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7076 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7078 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7079 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7082 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7084 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7085 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7088 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
7090 static const struct
7092 const WCHAR *action;
7093 UINT (*handler)(MSIPACKAGE *);
7095 StandardActions[] =
7097 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
7098 { szAppSearch, ACTION_AppSearch },
7099 { szBindImage, ACTION_BindImage },
7100 { szCCPSearch, ACTION_CCPSearch },
7101 { szCostFinalize, ACTION_CostFinalize },
7102 { szCostInitialize, ACTION_CostInitialize },
7103 { szCreateFolders, ACTION_CreateFolders },
7104 { szCreateShortcuts, ACTION_CreateShortcuts },
7105 { szDeleteServices, ACTION_DeleteServices },
7106 { szDisableRollback, ACTION_DisableRollback },
7107 { szDuplicateFiles, ACTION_DuplicateFiles },
7108 { szExecuteAction, ACTION_ExecuteAction },
7109 { szFileCost, ACTION_FileCost },
7110 { szFindRelatedProducts, ACTION_FindRelatedProducts },
7111 { szForceReboot, ACTION_ForceReboot },
7112 { szInstallAdminPackage, ACTION_InstallAdminPackage },
7113 { szInstallExecute, ACTION_InstallExecute },
7114 { szInstallExecuteAgain, ACTION_InstallExecute },
7115 { szInstallFiles, ACTION_InstallFiles},
7116 { szInstallFinalize, ACTION_InstallFinalize },
7117 { szInstallInitialize, ACTION_InstallInitialize },
7118 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
7119 { szInstallValidate, ACTION_InstallValidate },
7120 { szIsolateComponents, ACTION_IsolateComponents },
7121 { szLaunchConditions, ACTION_LaunchConditions },
7122 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
7123 { szMoveFiles, ACTION_MoveFiles },
7124 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
7125 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
7126 { szInstallODBC, ACTION_InstallODBC },
7127 { szInstallServices, ACTION_InstallServices },
7128 { szPatchFiles, ACTION_PatchFiles },
7129 { szProcessComponents, ACTION_ProcessComponents },
7130 { szPublishComponents, ACTION_PublishComponents },
7131 { szPublishFeatures, ACTION_PublishFeatures },
7132 { szPublishProduct, ACTION_PublishProduct },
7133 { szRegisterClassInfo, ACTION_RegisterClassInfo },
7134 { szRegisterComPlus, ACTION_RegisterComPlus},
7135 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
7136 { szRegisterFonts, ACTION_RegisterFonts },
7137 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
7138 { szRegisterProduct, ACTION_RegisterProduct },
7139 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
7140 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
7141 { szRegisterUser, ACTION_RegisterUser },
7142 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
7143 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
7144 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
7145 { szRemoveFiles, ACTION_RemoveFiles },
7146 { szRemoveFolders, ACTION_RemoveFolders },
7147 { szRemoveIniValues, ACTION_RemoveIniValues },
7148 { szRemoveODBC, ACTION_RemoveODBC },
7149 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
7150 { szRemoveShortcuts, ACTION_RemoveShortcuts },
7151 { szResolveSource, ACTION_ResolveSource },
7152 { szRMCCPSearch, ACTION_RMCCPSearch },
7153 { szScheduleReboot, ACTION_ScheduleReboot },
7154 { szSelfRegModules, ACTION_SelfRegModules },
7155 { szSelfUnregModules, ACTION_SelfUnregModules },
7156 { szSetODBCFolders, ACTION_SetODBCFolders },
7157 { szStartServices, ACTION_StartServices },
7158 { szStopServices, ACTION_StopServices },
7159 { szUnpublishComponents, ACTION_UnpublishComponents },
7160 { szUnpublishFeatures, ACTION_UnpublishFeatures },
7161 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
7162 { szUnregisterComPlus, ACTION_UnregisterComPlus },
7163 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
7164 { szUnregisterFonts, ACTION_UnregisterFonts },
7165 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
7166 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
7167 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
7168 { szValidateProductID, ACTION_ValidateProductID },
7169 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
7170 { szWriteIniValues, ACTION_WriteIniValues },
7171 { szWriteRegistryValues, ACTION_WriteRegistryValues },
7172 { NULL, NULL },
7175 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7177 BOOL ret = FALSE;
7178 UINT i;
7180 i = 0;
7181 while (StandardActions[i].action != NULL)
7183 if (!strcmpW( StandardActions[i].action, action ))
7185 ui_actionstart( package, action );
7186 if (StandardActions[i].handler)
7188 ui_actioninfo( package, action, TRUE, 0 );
7189 *rc = StandardActions[i].handler( package );
7190 ui_actioninfo( package, action, FALSE, *rc );
7192 else
7194 FIXME("unhandled standard action %s\n", debugstr_w(action));
7195 *rc = ERROR_SUCCESS;
7197 ret = TRUE;
7198 break;
7200 i++;
7202 return ret;
7205 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7207 UINT rc = ERROR_SUCCESS;
7208 BOOL handled;
7210 TRACE("Performing action (%s)\n", debugstr_w(action));
7212 handled = ACTION_HandleStandardAction(package, action, &rc);
7214 if (!handled)
7215 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7217 if (!handled)
7219 WARN("unhandled msi action %s\n", debugstr_w(action));
7220 rc = ERROR_FUNCTION_NOT_CALLED;
7223 return rc;
7226 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7228 UINT rc = ERROR_SUCCESS;
7229 BOOL handled = FALSE;
7231 TRACE("Performing action (%s)\n", debugstr_w(action));
7233 handled = ACTION_HandleStandardAction(package, action, &rc);
7235 if (!handled)
7236 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7238 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7239 handled = TRUE;
7241 if (!handled)
7243 WARN("unhandled msi action %s\n", debugstr_w(action));
7244 rc = ERROR_FUNCTION_NOT_CALLED;
7247 return rc;
7250 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7252 UINT rc = ERROR_SUCCESS;
7253 MSIRECORD *row;
7255 static const WCHAR ExecSeqQuery[] =
7256 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7257 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7258 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7259 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7260 static const WCHAR UISeqQuery[] =
7261 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7262 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7263 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7264 ' ', '=',' ','%','i',0};
7266 if (needs_ui_sequence(package))
7267 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
7268 else
7269 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
7271 if (row)
7273 LPCWSTR action, cond;
7275 TRACE("Running the actions\n");
7277 /* check conditions */
7278 cond = MSI_RecordGetString(row, 2);
7280 /* this is a hack to skip errors in the condition code */
7281 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7283 msiobj_release(&row->hdr);
7284 return ERROR_SUCCESS;
7287 action = MSI_RecordGetString(row, 1);
7288 if (!action)
7290 ERR("failed to fetch action\n");
7291 msiobj_release(&row->hdr);
7292 return ERROR_FUNCTION_FAILED;
7295 if (needs_ui_sequence(package))
7296 rc = ACTION_PerformUIAction(package, action, -1);
7297 else
7298 rc = ACTION_PerformAction(package, action, -1);
7300 msiobj_release(&row->hdr);
7303 return rc;
7306 /****************************************************
7307 * TOP level entry points
7308 *****************************************************/
7310 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7311 LPCWSTR szCommandLine )
7313 UINT rc;
7314 BOOL ui_exists;
7316 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7317 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7319 msi_set_property( package->db, szAction, szInstall );
7321 package->script->InWhatSequence = SEQUENCE_INSTALL;
7323 if (szPackagePath)
7325 LPWSTR p, dir;
7326 LPCWSTR file;
7328 dir = strdupW(szPackagePath);
7329 p = strrchrW(dir, '\\');
7330 if (p)
7332 *(++p) = 0;
7333 file = szPackagePath + (p - dir);
7335 else
7337 msi_free(dir);
7338 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7339 GetCurrentDirectoryW(MAX_PATH, dir);
7340 lstrcatW(dir, szBackSlash);
7341 file = szPackagePath;
7344 msi_free( package->PackagePath );
7345 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7346 if (!package->PackagePath)
7348 msi_free(dir);
7349 return ERROR_OUTOFMEMORY;
7352 lstrcpyW(package->PackagePath, dir);
7353 lstrcatW(package->PackagePath, file);
7354 msi_free(dir);
7356 msi_set_sourcedir_props(package, FALSE);
7359 msi_parse_command_line( package, szCommandLine, FALSE );
7361 msi_apply_transforms( package );
7362 msi_apply_patches( package );
7364 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7366 TRACE("setting reinstall property\n");
7367 msi_set_property( package->db, szReinstall, szAll );
7370 /* properties may have been added by a transform */
7371 msi_clone_properties( package );
7373 msi_parse_command_line( package, szCommandLine, FALSE );
7374 msi_adjust_privilege_properties( package );
7375 msi_set_context( package );
7377 if (needs_ui_sequence( package))
7379 package->script->InWhatSequence |= SEQUENCE_UI;
7380 rc = ACTION_ProcessUISequence(package);
7381 ui_exists = ui_sequence_exists(package);
7382 if (rc == ERROR_SUCCESS || !ui_exists)
7384 package->script->InWhatSequence |= SEQUENCE_EXEC;
7385 rc = ACTION_ProcessExecSequence(package, ui_exists);
7388 else
7389 rc = ACTION_ProcessExecSequence(package, FALSE);
7391 package->script->CurrentlyScripting = FALSE;
7393 /* process the ending type action */
7394 if (rc == ERROR_SUCCESS)
7395 ACTION_PerformActionSequence(package, -1);
7396 else if (rc == ERROR_INSTALL_USEREXIT)
7397 ACTION_PerformActionSequence(package, -2);
7398 else if (rc == ERROR_INSTALL_SUSPEND)
7399 ACTION_PerformActionSequence(package, -4);
7400 else /* failed */
7401 ACTION_PerformActionSequence(package, -3);
7403 /* finish up running custom actions */
7404 ACTION_FinishCustomActions(package);
7406 if (rc == ERROR_SUCCESS && package->need_reboot)
7407 return ERROR_SUCCESS_REBOOT_REQUIRED;
7409 return rc;