/* * Implementation of the Microsoft Installer (msi.dll) * * Copyright 2004,2005 Aric Stewart for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winreg.h" #include "winsvc.h" #include "odbcinst.h" #include "wine/debug.h" #include "msidefs.h" #include "msipriv.h" #include "winuser.h" #include "shlobj.h" #include "objbase.h" #include "mscoree.h" #include "shlwapi.h" #include "imagehlp.h" #include "wine/unicode.h" #include "winver.h" #define REG_PROGRESS_VALUE 13200 #define COMPONENT_PROGRESS_VALUE 24000 WINE_DEFAULT_DEBUG_CHANNEL(msi); static const WCHAR szCreateFolders[] = {'C','r','e','a','t','e','F','o','l','d','e','r','s',0}; static const WCHAR szCostFinalize[] = {'C','o','s','t','F','i','n','a','l','i','z','e',0}; static const WCHAR szWriteRegistryValues[] = {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0}; static const WCHAR szFileCost[] = {'F','i','l','e','C','o','s','t',0}; static const WCHAR szInstallInitialize[] = {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0}; static const WCHAR szInstallValidate[] = {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0}; static const WCHAR szLaunchConditions[] = {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0}; static const WCHAR szProcessComponents[] = {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0}; static const WCHAR szRegisterTypeLibraries[] = {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0}; static const WCHAR szCreateShortcuts[] = {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0}; static const WCHAR szPublishProduct[] = {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0}; static const WCHAR szWriteIniValues[] = {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0}; static const WCHAR szSelfRegModules[] = {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0}; static const WCHAR szPublishFeatures[] = {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; static const WCHAR szRegisterProduct[] = {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0}; static const WCHAR szInstallExecute[] = {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0}; static const WCHAR szInstallExecuteAgain[] = {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0}; static const WCHAR szInstallFinalize[] = {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0}; static const WCHAR szForceReboot[] = {'F','o','r','c','e','R','e','b','o','o','t',0}; static const WCHAR szResolveSource[] = {'R','e','s','o','l','v','e','S','o','u','r','c','e',0}; static const WCHAR szAllocateRegistrySpace[] = {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0}; static const WCHAR szBindImage[] = {'B','i','n','d','I','m','a','g','e',0}; static const WCHAR szDeleteServices[] = {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0}; static const WCHAR szDisableRollback[] = {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0}; static const WCHAR szExecuteAction[] = {'E','x','e','c','u','t','e','A','c','t','i','o','n',0}; static const WCHAR szInstallAdminPackage[] = {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0}; static const WCHAR szInstallSFPCatalogFile[] = {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0}; static const WCHAR szIsolateComponents[] = {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0}; static const WCHAR szMigrateFeatureStates[] = {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0}; static const WCHAR szMsiUnpublishAssemblies[] = {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; static const WCHAR szInstallODBC[] = {'I','n','s','t','a','l','l','O','D','B','C',0}; static const WCHAR szInstallServices[] = {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0}; static const WCHAR szPublishComponents[] = {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0}; static const WCHAR szRegisterComPlus[] = {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; static const WCHAR szRegisterUser[] = {'R','e','g','i','s','t','e','r','U','s','e','r',0}; static const WCHAR szRemoveEnvironmentStrings[] = {'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}; static const WCHAR szRemoveExistingProducts[] = {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0}; static const WCHAR szRemoveFolders[] = {'R','e','m','o','v','e','F','o','l','d','e','r','s',0}; static const WCHAR szRemoveIniValues[] = {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0}; static const WCHAR szRemoveODBC[] = {'R','e','m','o','v','e','O','D','B','C',0}; static const WCHAR szRemoveRegistryValues[] = {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0}; static const WCHAR szRemoveShortcuts[] = {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0}; static const WCHAR szRMCCPSearch[] = {'R','M','C','C','P','S','e','a','r','c','h',0}; static const WCHAR szScheduleReboot[] = {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0}; static const WCHAR szSelfUnregModules[] = {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0}; static const WCHAR szSetODBCFolders[] = {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0}; static const WCHAR szStartServices[] = {'S','t','a','r','t','S','e','r','v','i','c','e','s',0}; static const WCHAR szStopServices[] = {'S','t','o','p','S','e','r','v','i','c','e','s',0}; static const WCHAR szUnpublishComponents[] = {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0}; static const WCHAR szUnpublishFeatures[] = {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; static const WCHAR szUnregisterComPlus[] = {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; static const WCHAR szUnregisterTypeLibraries[] = {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0}; static const WCHAR szValidateProductID[] = {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0}; static const WCHAR szWriteEnvironmentStrings[] = {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0}; static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action) { static const WCHAR Query_t[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', ' ','\'','%','s','\'',0}; MSIRECORD * row; row = MSI_QueryGetRecord( package->db, Query_t, action ); if (!row) return; MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row); msiobj_release(&row->hdr); } static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, UINT rc) { MSIRECORD * row; static const WCHAR template_s[]= {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ', '%','s', '.',0}; static const WCHAR template_e[]= {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ', '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ', '%','i','.',0}; static const WCHAR format[] = {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0}; WCHAR message[1024]; WCHAR timet[0x100]; GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100); if (start) sprintfW(message,template_s,timet,action); else sprintfW(message,template_e,timet,action,rc); row = MSI_CreateRecord(1); MSI_RecordSetStringW(row,1,message); MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row); msiobj_release(&row->hdr); } enum parse_state { state_whitespace, state_token, state_quote }; static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes ) { enum parse_state state = state_quote; const WCHAR *p; WCHAR *out = value; int ignore, in_quotes = 0, count = 0, len = 0; for (p = str; *p; p++) { ignore = 0; switch (state) { case state_whitespace: switch (*p) { case ' ': in_quotes = 1; ignore = 1; len++; break; case '"': state = state_quote; if (in_quotes && p[1] != '\"') count--; else count++; break; default: state = state_token; in_quotes = 1; len++; break; } break; case state_token: switch (*p) { case '"': state = state_quote; if (in_quotes) count--; else count++; break; case ' ': state = state_whitespace; if (!count) goto done; in_quotes = 1; len++; break; default: if (!count) in_quotes = 0; else in_quotes = 1; len++; break; } break; case state_quote: switch (*p) { case '"': if (in_quotes && p[1] != '\"') count--; else count++; break; case ' ': state = state_whitespace; if (!count || (count > 1 && !len)) goto done; in_quotes = 1; len++; break; default: state = state_token; if (!count) in_quotes = 0; else in_quotes = 1; len++; break; } break; default: break; } if (!ignore) *out++ = *p; } done: if (!len) *value = 0; else *out = 0; *quotes = count; return p - str; } static void remove_quotes( WCHAR *str ) { WCHAR *p = str; int len = strlenW( str ); while ((p = strchrW( p, '"' ))) { memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) ); p++; } } UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine, BOOL preserve_case ) { LPCWSTR ptr, ptr2; int num_quotes; DWORD len; WCHAR *prop, *val; UINT r; if (!szCommandLine) return ERROR_SUCCESS; ptr = szCommandLine; while (*ptr) { while (*ptr == ' ') ptr++; if (!*ptr) break; ptr2 = strchrW( ptr, '=' ); if (!ptr2) return ERROR_INVALID_COMMAND_LINE; len = ptr2 - ptr; if (!len) return ERROR_INVALID_COMMAND_LINE; prop = msi_alloc( (len + 1) * sizeof(WCHAR) ); memcpy( prop, ptr, len * sizeof(WCHAR) ); prop[len] = 0; if (!preserve_case) struprW( prop ); ptr2++; while (*ptr2 == ' ') ptr2++; num_quotes = 0; val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) ); len = parse_prop( ptr2, val, &num_quotes ); if (num_quotes % 2) { WARN("unbalanced quotes\n"); msi_free( val ); msi_free( prop ); return ERROR_INVALID_COMMAND_LINE; } remove_quotes( val ); TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val)); r = msi_set_property( package->db, prop, val, -1 ); if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir )) msi_reset_folders( package, TRUE ); msi_free( val ); msi_free( prop ); ptr = ptr2 + len; } return ERROR_SUCCESS; } WCHAR **msi_split_string( const WCHAR *str, WCHAR sep ) { LPCWSTR pc; LPWSTR p, *ret = NULL; UINT count = 0; if (!str) return ret; /* count the number of substrings */ for ( pc = str, count = 0; pc; count++ ) { pc = strchrW( pc, sep ); if (pc) pc++; } /* allocate space for an array of substring pointers and the substrings */ ret = msi_alloc( (count+1) * sizeof (LPWSTR) + (lstrlenW(str)+1) * sizeof(WCHAR) ); if (!ret) return ret; /* copy the string and set the pointers */ p = (LPWSTR) &ret[count+1]; lstrcpyW( p, str ); for( count = 0; (ret[count] = p); count++ ) { p = strchrW( p, sep ); if (p) *p++ = 0; } return ret; } static BOOL ui_sequence_exists( MSIPACKAGE *package ) { static const WCHAR query [] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ', 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc == ERROR_SUCCESS) { msiobj_release(&view->hdr); return TRUE; } return FALSE; } UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace) { LPWSTR source, check; if (msi_get_property_int( package->db, szInstalled, 0 )) { HKEY hkey; MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ); source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW ); RegCloseKey( hkey ); } else { LPWSTR p, db; DWORD len; db = msi_dup_property( package->db, szOriginalDatabase ); if (!db) return ERROR_OUTOFMEMORY; p = strrchrW( db, '\\' ); if (!p) { p = strrchrW( db, '/' ); if (!p) { msi_free(db); return ERROR_SUCCESS; } } len = p - db + 2; source = msi_alloc( len * sizeof(WCHAR) ); lstrcpynW( source, db, len ); msi_free( db ); } check = msi_dup_property( package->db, szSourceDir ); if (!check || replace) { UINT r = msi_set_property( package->db, szSourceDir, source, -1 ); if (r == ERROR_SUCCESS) msi_reset_folders( package, TRUE ); } msi_free( check ); check = msi_dup_property( package->db, szSOURCEDIR ); if (!check || replace) msi_set_property( package->db, szSOURCEDIR, source, -1 ); msi_free( check ); msi_free( source ); return ERROR_SUCCESS; } static BOOL needs_ui_sequence(MSIPACKAGE *package) { return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED; } UINT msi_set_context(MSIPACKAGE *package) { UINT r = msi_locate_product( package->ProductCode, &package->Context ); if (r != ERROR_SUCCESS) { int num = msi_get_property_int( package->db, szAllUsers, 0 ); if (num == 1 || num == 2) package->Context = MSIINSTALLCONTEXT_MACHINE; else package->Context = MSIINSTALLCONTEXT_USERUNMANAGED; } return ERROR_SUCCESS; } static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param) { UINT rc; LPCWSTR cond, action; MSIPACKAGE *package = param; action = MSI_RecordGetString(row,1); if (!action) { ERR("Error is retrieving action name\n"); return ERROR_FUNCTION_FAILED; } /* check conditions */ cond = MSI_RecordGetString(row,2); /* this is a hack to skip errors in the condition code */ if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE) { TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action)); return ERROR_SUCCESS; } if (needs_ui_sequence(package)) rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE); else rc = ACTION_PerformAction(package, action, SCRIPT_NONE); msi_dialog_check_messages( NULL ); if (package->CurrentInstallState != ERROR_SUCCESS) rc = package->CurrentInstallState; if (rc == ERROR_FUNCTION_NOT_CALLED) rc = ERROR_SUCCESS; if (rc != ERROR_SUCCESS) ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc); if (package->need_reboot_now) { TRACE("action %s asked for immediate reboot, suspending installation\n", debugstr_w(action)); rc = ACTION_ForceReboot( package ); } return rc; } UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`', ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ', '`','S','e','q','u','e','n','c','e','`',0}; MSIQUERY *view; UINT r; TRACE("%p %s\n", package, debugstr_w(table)); r = MSI_OpenQuery( package->db, &view, query, table ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package ); msiobj_release(&view->hdr); } return r; } static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ', 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; static const WCHAR query_validate[] = { 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`', ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l', 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=', ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0}; MSIQUERY *view; INT seq = 0; UINT rc; if (package->script->ExecuteSequenceRun) { TRACE("Execute Sequence already Run\n"); return ERROR_SUCCESS; } package->script->ExecuteSequenceRun = TRUE; /* get the sequence number */ if (UIran) { MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate); if (!row) return ERROR_FUNCTION_FAILED; seq = MSI_RecordGetInteger(row,1); msiobj_release(&row->hdr); } rc = MSI_OpenQuery(package->db, &view, query, seq); if (rc == ERROR_SUCCESS) { TRACE("Running the actions\n"); msi_set_property( package->db, szSourceDir, NULL, -1 ); rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package); msiobj_release(&view->hdr); } return rc; } static UINT ACTION_ProcessUISequence(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ', 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc == ERROR_SUCCESS) { TRACE("Running the actions\n"); rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package); msiobj_release(&view->hdr); } return rc; } /******************************************************** * ACTION helper functions and functions that perform the actions *******************************************************/ static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action, UINT* rc, UINT script, BOOL force ) { BOOL ret=FALSE; UINT arc; arc = ACTION_CustomAction(package, action, script, force); if (arc != ERROR_CALL_NOT_IMPLEMENTED) { *rc = arc; ret = TRUE; } return ret; } MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component ) { MSICOMPONENT *comp; LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) { if (!strcmpW( Component, comp->Component )) return comp; } return NULL; } MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature ) { MSIFEATURE *feature; LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if (!strcmpW( Feature, feature->Feature )) return feature; } return NULL; } MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key ) { MSIFILE *file; LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) { if (!strcmpW( key, file->File )) return file; } return NULL; } MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key ) { MSIFILEPATCH *patch; /* FIXME: There might be more than one patch */ LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) { if (!strcmpW( key, patch->File->File )) return patch; } return NULL; } MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir ) { MSIFOLDER *folder; LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry ) { if (!strcmpW( dir, folder->Directory )) return folder; } return NULL; } /* * Recursively create all directories in the path. * shamelessly stolen from setupapi/queue.c */ BOOL msi_create_full_path( const WCHAR *path ) { BOOL ret = TRUE; WCHAR *new_path; int len; new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) ); strcpyW( new_path, path ); while ((len = strlenW( new_path )) && new_path[len - 1] == '\\') new_path[len - 1] = 0; while (!CreateDirectoryW( new_path, NULL )) { WCHAR *slash; DWORD last_error = GetLastError(); if (last_error == ERROR_ALREADY_EXISTS) break; if (last_error != ERROR_PATH_NOT_FOUND) { ret = FALSE; break; } if (!(slash = strrchrW( new_path, '\\' ))) { ret = FALSE; break; } len = slash - new_path; new_path[len] = 0; if (!msi_create_full_path( new_path )) { ret = FALSE; break; } new_path[len] = '\\'; } msi_free( new_path ); return ret; } void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d ) { MSIRECORD *row; row = MSI_CreateRecord( 4 ); MSI_RecordSetInteger( row, 1, a ); MSI_RecordSetInteger( row, 2, b ); MSI_RecordSetInteger( row, 3, c ); MSI_RecordSetInteger( row, 4, d ); MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row ); msiobj_release( &row->hdr ); msi_dialog_check_messages( NULL ); } void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record ) { static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0}; WCHAR message[1024]; MSIRECORD *row = 0; DWORD size; if (!package->LastAction || strcmpW( package->LastAction, action )) { if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return; if (MSI_RecordIsNull( row, 3 )) { msiobj_release( &row->hdr ); return; } /* update the cached action format */ msi_free( package->ActionFormat ); package->ActionFormat = msi_dup_record_field( row, 3 ); msi_free( package->LastAction ); package->LastAction = strdupW( action ); msiobj_release( &row->hdr ); } size = 1024; MSI_RecordSetStringW( record, 0, package->ActionFormat ); MSI_FormatRecordW( package, record, message, &size ); row = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( row, 1, message ); MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row ); msiobj_release( &row->hdr ); } INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp ) { if (!comp->Enabled) { TRACE("component is disabled: %s\n", debugstr_w(comp->Component)); return INSTALLSTATE_UNKNOWN; } if (package->need_rollback) return comp->Installed; if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT) { TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients); return INSTALLSTATE_UNKNOWN; } return comp->ActionRequest; } INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature ) { if (package->need_rollback) return feature->Installed; return feature->ActionRequest; } static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPCWSTR dir, component, full_path; MSIRECORD *uirow; MSIFOLDER *folder; MSICOMPONENT *comp; component = MSI_RecordGetString(row, 2); if (!component) return ERROR_SUCCESS; comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation: %s\n", debugstr_w(component)); return ERROR_SUCCESS; } dir = MSI_RecordGetString(row,1); if (!dir) { ERR("Unable to get folder id\n"); return ERROR_SUCCESS; } uirow = MSI_CreateRecord(1); MSI_RecordSetStringW(uirow, 1, dir); msi_ui_actiondata(package, szCreateFolders, uirow); msiobj_release(&uirow->hdr); full_path = msi_get_target_folder( package, dir ); if (!full_path) { ERR("Unable to retrieve folder %s\n", debugstr_w(dir)); return ERROR_SUCCESS; } TRACE("folder is %s\n", debugstr_w(full_path)); folder = msi_get_loaded_folder( package, dir ); if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path ); folder->State = FOLDER_STATE_CREATED; return ERROR_SUCCESS; } static UINT ACTION_CreateFolders(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package); msiobj_release(&view->hdr); return rc; } static void remove_persistent_folder( MSIFOLDER *folder ) { FolderList *fl; LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) { remove_persistent_folder( fl->folder ); } if (folder->persistent && folder->State != FOLDER_STATE_REMOVED) { if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED; } } static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR dir, component, full_path; MSIRECORD *uirow; MSIFOLDER *folder; MSICOMPONENT *comp; component = MSI_RecordGetString(row, 2); if (!component) return ERROR_SUCCESS; comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } dir = MSI_RecordGetString( row, 1 ); if (!dir) { ERR("Unable to get folder id\n"); return ERROR_SUCCESS; } full_path = msi_get_target_folder( package, dir ); if (!full_path) { ERR("Unable to resolve folder %s\n", debugstr_w(dir)); return ERROR_SUCCESS; } TRACE("folder is %s\n", debugstr_w(full_path)); uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, dir ); msi_ui_actiondata( package, szRemoveFolders, uirow ); msiobj_release( &uirow->hdr ); folder = msi_get_loaded_folder( package, dir ); remove_persistent_folder( folder ); return ERROR_SUCCESS; } static UINT ACTION_RemoveFolders( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package ); msiobj_release( &view->hdr ); return rc; } static UINT load_component( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; comp = msi_alloc_zero( sizeof(MSICOMPONENT) ); if (!comp) return ERROR_FUNCTION_FAILED; list_add_tail( &package->components, &comp->entry ); /* fill in the data */ comp->Component = msi_dup_record_field( row, 1 ); TRACE("Loading Component %s\n", debugstr_w(comp->Component)); comp->ComponentId = msi_dup_record_field( row, 2 ); comp->Directory = msi_dup_record_field( row, 3 ); comp->Attributes = MSI_RecordGetInteger(row,4); comp->Condition = msi_dup_record_field( row, 5 ); comp->KeyPath = msi_dup_record_field( row, 6 ); comp->Installed = INSTALLSTATE_UNKNOWN; comp->Action = INSTALLSTATE_UNKNOWN; comp->ActionRequest = INSTALLSTATE_UNKNOWN; comp->assembly = msi_load_assembly( package, comp ); return ERROR_SUCCESS; } UINT msi_load_all_components( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','o','m','p','o','n','e','n','t','`',0}; MSIQUERY *view; UINT r; if (!list_empty(&package->components)) return ERROR_SUCCESS; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r != ERROR_SUCCESS) return r; if (!msi_init_assembly_caches( package )) { ERR("can't initialize assembly caches\n"); msiobj_release( &view->hdr ); return ERROR_FUNCTION_FAILED; } r = MSI_IterateRecords(view, NULL, load_component, package); msiobj_release(&view->hdr); return r; } typedef struct { MSIPACKAGE *package; MSIFEATURE *feature; } _ilfs; static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp ) { ComponentList *cl; cl = msi_alloc( sizeof (*cl) ); if ( !cl ) return ERROR_NOT_ENOUGH_MEMORY; cl->component = comp; list_add_tail( &feature->Components, &cl->entry ); return ERROR_SUCCESS; } static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child ) { FeatureList *fl; fl = msi_alloc( sizeof(*fl) ); if ( !fl ) return ERROR_NOT_ENOUGH_MEMORY; fl->feature = child; list_add_tail( &parent->Children, &fl->entry ); return ERROR_SUCCESS; } static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param) { _ilfs* ilfs = param; LPCWSTR component; MSICOMPONENT *comp; component = MSI_RecordGetString(row,1); /* check to see if the component is already loaded */ comp = msi_get_loaded_component( ilfs->package, component ); if (!comp) { WARN("ignoring unknown component %s\n", debugstr_w(component)); return ERROR_SUCCESS; } add_feature_component( ilfs->feature, comp ); comp->Enabled = TRUE; return ERROR_SUCCESS; } static UINT load_feature(MSIRECORD * row, LPVOID param) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`', ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e', 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ', '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0}; MSIPACKAGE *package = param; MSIFEATURE *feature; MSIQUERY *view; _ilfs ilfs; UINT rc; /* fill in the data */ feature = msi_alloc_zero( sizeof (MSIFEATURE) ); if (!feature) return ERROR_NOT_ENOUGH_MEMORY; list_init( &feature->Children ); list_init( &feature->Components ); feature->Feature = msi_dup_record_field( row, 1 ); TRACE("Loading feature %s\n",debugstr_w(feature->Feature)); feature->Feature_Parent = msi_dup_record_field( row, 2 ); feature->Title = msi_dup_record_field( row, 3 ); feature->Description = msi_dup_record_field( row, 4 ); if (!MSI_RecordIsNull(row,5)) feature->Display = MSI_RecordGetInteger(row,5); feature->Level= MSI_RecordGetInteger(row,6); feature->Directory = msi_dup_record_field( row, 7 ); feature->Attributes = MSI_RecordGetInteger(row,8); feature->Installed = INSTALLSTATE_UNKNOWN; feature->Action = INSTALLSTATE_UNKNOWN; feature->ActionRequest = INSTALLSTATE_UNKNOWN; list_add_tail( &package->features, &feature->entry ); /* load feature components */ rc = MSI_OpenQuery( package->db, &view, query, feature->Feature ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; ilfs.package = package; ilfs.feature = feature; rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs); msiobj_release(&view->hdr); return rc; } static UINT find_feature_children(MSIRECORD * row, LPVOID param) { MSIPACKAGE *package = param; MSIFEATURE *parent, *child; child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) ); if (!child) return ERROR_FUNCTION_FAILED; if (!child->Feature_Parent) return ERROR_SUCCESS; parent = msi_get_loaded_feature( package, child->Feature_Parent ); if (!parent) return ERROR_FUNCTION_FAILED; add_feature_child( parent, child ); return ERROR_SUCCESS; } UINT msi_load_all_features( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ', '`','D','i','s','p','l','a','y','`',0}; MSIQUERY *view; UINT r; if (!list_empty(&package->features)) return ERROR_SUCCESS; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r != ERROR_SUCCESS) return r; r = MSI_IterateRecords( view, NULL, load_feature, package ); if (r != ERROR_SUCCESS) { msiobj_release( &view->hdr ); return r; } r = MSI_IterateRecords( view, NULL, find_feature_children, package ); msiobj_release( &view->hdr ); return r; } static LPWSTR folder_split_path(LPWSTR p, WCHAR ch) { if (!p) return p; p = strchrW(p, ch); if (!p) return p; *p = 0; return p+1; } static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','M','s','i','F','i','l','e','H','a','s','h','`',' ', 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0}; MSIQUERY *view = NULL; MSIRECORD *row = NULL; UINT r; TRACE("%s\n", debugstr_w(file->File)); r = MSI_OpenQuery(package->db, &view, query, file->File); if (r != ERROR_SUCCESS) goto done; r = MSI_ViewExecute(view, NULL); if (r != ERROR_SUCCESS) goto done; r = MSI_ViewFetch(view, &row); if (r != ERROR_SUCCESS) goto done; file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); file->hash.dwData[0] = MSI_RecordGetInteger(row, 3); file->hash.dwData[1] = MSI_RecordGetInteger(row, 4); file->hash.dwData[2] = MSI_RecordGetInteger(row, 5); file->hash.dwData[3] = MSI_RecordGetInteger(row, 6); done: if (view) msiobj_release(&view->hdr); if (row) msiobj_release(&row->hdr); return r; } static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file ) { MSIRECORD *row; static const WCHAR query[] = { 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ', '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0}; row = MSI_QueryGetRecord( package->db, query, file->Sequence ); if (!row) { WARN("query failed\n"); return ERROR_FUNCTION_FAILED; } file->disk_id = MSI_RecordGetInteger( row, 1 ); msiobj_release( &row->hdr ); return ERROR_SUCCESS; } static UINT load_file(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = param; LPCWSTR component; MSIFILE *file; /* fill in the data */ file = msi_alloc_zero( sizeof (MSIFILE) ); if (!file) return ERROR_NOT_ENOUGH_MEMORY; file->File = msi_dup_record_field( row, 1 ); component = MSI_RecordGetString( row, 2 ); file->Component = msi_get_loaded_component( package, component ); if (!file->Component) { WARN("Component not found: %s\n", debugstr_w(component)); msi_free(file->File); msi_free(file); return ERROR_SUCCESS; } file->FileName = msi_dup_record_field( row, 3 ); msi_reduce_to_long_filename( file->FileName ); file->ShortName = msi_dup_record_field( row, 3 ); file->LongName = strdupW( folder_split_path(file->ShortName, '|')); file->FileSize = MSI_RecordGetInteger( row, 4 ); file->Version = msi_dup_record_field( row, 5 ); file->Language = msi_dup_record_field( row, 6 ); file->Attributes = MSI_RecordGetInteger( row, 7 ); file->Sequence = MSI_RecordGetInteger( row, 8 ); file->state = msifs_invalid; /* if the compressed bits are not set in the file attributes, * then read the information from the package word count property */ if (package->WordCount & msidbSumInfoSourceTypeAdminImage) { file->IsCompressed = FALSE; } else if (file->Attributes & (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded)) { file->IsCompressed = TRUE; } else if (file->Attributes & msidbFileAttributesNoncompressed) { file->IsCompressed = FALSE; } else { file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed; } load_file_hash(package, file); load_file_disk_id(package, file); TRACE("File Loaded (%s)\n",debugstr_w(file->File)); list_add_tail( &package->files, &file->entry ); return ERROR_SUCCESS; } static UINT load_all_files(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ', '`','S','e','q','u','e','n','c','e','`', 0}; MSIQUERY *view; UINT rc; if (!list_empty(&package->files)) return ERROR_SUCCESS; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, load_file, package); msiobj_release(&view->hdr); return rc; } static UINT load_media( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; UINT disk_id = MSI_RecordGetInteger( row, 1 ); const WCHAR *cabinet = MSI_RecordGetString( row, 4 ); /* FIXME: load external cabinets and directory sources too */ if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS; msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet ); return ERROR_SUCCESS; } static UINT load_all_media( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`', 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ', '`','D','i','s','k','I','d','`',0}; MSIQUERY *view; UINT r; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r != ERROR_SUCCESS) return ERROR_SUCCESS; r = MSI_IterateRecords( view, NULL, load_media, package ); msiobj_release( &view->hdr ); return r; } static UINT load_patch(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; MSIFILEPATCH *patch; LPWSTR file_key; patch = msi_alloc_zero( sizeof (MSIFILEPATCH) ); if (!patch) return ERROR_NOT_ENOUGH_MEMORY; file_key = msi_dup_record_field( row, 1 ); patch->File = msi_get_loaded_file( package, file_key ); msi_free(file_key); if( !patch->File ) { ERR("Failed to find target for patch in File table\n"); msi_free(patch); return ERROR_FUNCTION_FAILED; } patch->Sequence = MSI_RecordGetInteger( row, 2 ); /* FIXME: The database should be properly transformed */ patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; patch->PatchSize = MSI_RecordGetInteger( row, 3 ); patch->Attributes = MSI_RecordGetInteger( row, 4 ); patch->IsApplied = FALSE; /* FIXME: * Header field - for patch validation. * _StreamRef - External key into MsiPatchHeaders (instead of the header field) */ TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File)); list_add_tail( &package->filepatches, &patch->entry ); return ERROR_SUCCESS; } static UINT load_all_patches(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ', '`','S','e','q','u','e','n','c','e','`',0}; MSIQUERY *view; UINT rc; if (!list_empty(&package->filepatches)) return ERROR_SUCCESS; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, load_patch, package); msiobj_release(&view->hdr); return rc; } static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ', '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0}; MSIQUERY *view; folder->persistent = FALSE; if (!MSI_OpenQuery( package->db, &view, query, folder->Directory )) { if (!MSI_ViewExecute( view, NULL )) { MSIRECORD *rec; if (!MSI_ViewFetch( view, &rec )) { TRACE("directory %s is persistent\n", debugstr_w(folder->Directory)); folder->persistent = TRUE; msiobj_release( &rec->hdr ); } } msiobj_release( &view->hdr ); } return ERROR_SUCCESS; } static UINT load_folder( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; static WCHAR szEmpty[] = { 0 }; LPWSTR p, tgt_short, tgt_long, src_short, src_long; MSIFOLDER *folder; if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY; list_init( &folder->children ); folder->Directory = msi_dup_record_field( row, 1 ); folder->Parent = msi_dup_record_field( row, 2 ); p = msi_dup_record_field(row, 3); TRACE("%s\n", debugstr_w(folder->Directory)); /* split src and target dir */ tgt_short = p; src_short = folder_split_path( p, ':' ); /* split the long and short paths */ tgt_long = folder_split_path( tgt_short, '|' ); src_long = folder_split_path( src_short, '|' ); /* check for no-op dirs */ if (tgt_short && !strcmpW( szDot, tgt_short )) tgt_short = szEmpty; if (src_short && !strcmpW( szDot, src_short )) src_short = szEmpty; if (!tgt_long) tgt_long = tgt_short; if (!src_short) { src_short = tgt_short; src_long = tgt_long; } if (!src_long) src_long = src_short; /* FIXME: use the target short path too */ folder->TargetDefault = strdupW(tgt_long); folder->SourceShortPath = strdupW(src_short); folder->SourceLongPath = strdupW(src_long); msi_free(p); TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault )); TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath )); TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath )); load_folder_persistence( package, folder ); list_add_tail( &package->folders, &folder->entry ); return ERROR_SUCCESS; } static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child ) { FolderList *fl; if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY; fl->folder = child; list_add_tail( &parent->children, &fl->entry ); return ERROR_SUCCESS; } static UINT find_folder_children( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; MSIFOLDER *parent, *child; if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) ))) return ERROR_FUNCTION_FAILED; if (!child->Parent) return ERROR_SUCCESS; if (!(parent = msi_get_loaded_folder( package, child->Parent ))) return ERROR_FUNCTION_FAILED; return add_folder_child( parent, child ); } static UINT load_all_folders( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','D','i','r','e','c','t','o','r','y','`',0}; MSIQUERY *view; UINT r; if (!list_empty(&package->folders)) return ERROR_SUCCESS; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r != ERROR_SUCCESS) return r; r = MSI_IterateRecords( view, NULL, load_folder, package ); if (r != ERROR_SUCCESS) { msiobj_release( &view->hdr ); return r; } r = MSI_IterateRecords( view, NULL, find_folder_children, package ); msiobj_release( &view->hdr ); return r; } static UINT ACTION_CostInitialize(MSIPACKAGE *package) { msi_set_property( package->db, szCostingComplete, szZero, -1 ); msi_set_property( package->db, szRootDrive, szCRoot, -1 ); load_all_folders( package ); msi_load_all_components( package ); msi_load_all_features( package ); load_all_files( package ); load_all_patches( package ); load_all_media( package ); return ERROR_SUCCESS; } static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index ) { const WCHAR *action = package->script->Actions[script][index]; ui_actionstart( package, action ); TRACE("executing %s\n", debugstr_w(action)); return ACTION_PerformAction( package, action, script ); } static UINT execute_script( MSIPACKAGE *package, UINT script ) { UINT i, rc = ERROR_SUCCESS; TRACE("executing script %u\n", script); if (!package->script) { ERR("no script!\n"); return ERROR_FUNCTION_FAILED; } if (script == SCRIPT_ROLLBACK) { for (i = package->script->ActionCount[script]; i > 0; i--) { rc = execute_script_action( package, script, i - 1 ); if (rc != ERROR_SUCCESS) break; } } else { for (i = 0; i < package->script->ActionCount[script]; i++) { rc = execute_script_action( package, script, i ); if (rc != ERROR_SUCCESS) break; } } msi_free_action_script(package, script); return rc; } static UINT ACTION_FileCost(MSIPACKAGE *package) { return ERROR_SUCCESS; } static void get_client_counts( MSIPACKAGE *package ) { MSICOMPONENT *comp; HKEY hkey; LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) { if (!comp->ComponentId) continue; if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) && MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE )) { comp->num_clients = 0; continue; } RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients, NULL, NULL, NULL, NULL ); RegCloseKey( hkey ); } } static void ACTION_GetComponentInstallStates(MSIPACKAGE *package) { MSICOMPONENT *comp; UINT r; LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) { if (!comp->ComponentId) continue; r = MsiQueryComponentStateW( package->ProductCode, NULL, MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId, &comp->Installed ); if (r == ERROR_SUCCESS) continue; r = MsiQueryComponentStateW( package->ProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId, &comp->Installed ); if (r == ERROR_SUCCESS) continue; r = MsiQueryComponentStateW( package->ProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, comp->ComponentId, &comp->Installed ); if (r == ERROR_SUCCESS) continue; comp->Installed = INSTALLSTATE_ABSENT; } } static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package) { MSIFEATURE *feature; LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature ); if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG) feature->Installed = INSTALLSTATE_ABSENT; else feature->Installed = state; } } static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level ) { return (feature->Level > 0 && feature->Level <= level); } static BOOL process_state_property(MSIPACKAGE* package, int level, LPCWSTR property, INSTALLSTATE state) { LPWSTR override; MSIFEATURE *feature; override = msi_dup_property( package->db, property ); if (!override) return FALSE; LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level )) continue; if (!strcmpW(property, szReinstall)) state = feature->Installed; if (!strcmpiW( override, szAll )) { if (feature->Installed != state) { feature->Action = state; feature->ActionRequest = state; } } else { LPWSTR ptr = override; LPWSTR ptr2 = strchrW(override,','); while (ptr) { int len = ptr2 - ptr; if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len)) || (!ptr2 && !strcmpW(ptr, feature->Feature))) { if (feature->Installed != state) { feature->Action = state; feature->ActionRequest = state; } break; } if (ptr2) { ptr=ptr2+1; ptr2 = strchrW(ptr,','); } else break; } } } msi_free(override); return TRUE; } static BOOL process_overrides( MSIPACKAGE *package, int level ) { static const WCHAR szAddLocal[] = {'A','D','D','L','O','C','A','L',0}; static const WCHAR szAddSource[] = {'A','D','D','S','O','U','R','C','E',0}; static const WCHAR szAdvertise[] = {'A','D','V','E','R','T','I','S','E',0}; BOOL ret = FALSE; /* all these activation/deactivation things happen in order and things * later on the list override things earlier on the list. * * 0 INSTALLLEVEL processing * 1 ADDLOCAL * 2 REMOVE * 3 ADDSOURCE * 4 ADDDEFAULT * 5 REINSTALL * 6 ADVERTISE * 7 COMPADDLOCAL * 8 COMPADDSOURCE * 9 FILEADDLOCAL * 10 FILEADDSOURCE * 11 FILEADDDEFAULT */ ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL ); ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT ); ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE ); ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN ); ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED ); if (ret) msi_set_property( package->db, szPreselected, szOne, -1 ); return ret; } UINT MSI_SetFeatureStates(MSIPACKAGE *package) { int level; MSICOMPONENT* component; MSIFEATURE *feature; TRACE("Checking Install Level\n"); level = msi_get_property_int(package->db, szInstallLevel, 1); if (!msi_get_property_int( package->db, szPreselected, 0 )) { LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if (!is_feature_selected( feature, level )) continue; if (feature->ActionRequest == INSTALLSTATE_UNKNOWN) { if (feature->Attributes & msidbFeatureAttributesFavorSource) { feature->Action = INSTALLSTATE_SOURCE; feature->ActionRequest = INSTALLSTATE_SOURCE; } else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) { feature->Action = INSTALLSTATE_ADVERTISED; feature->ActionRequest = INSTALLSTATE_ADVERTISED; } else { feature->Action = INSTALLSTATE_LOCAL; feature->ActionRequest = INSTALLSTATE_LOCAL; } } } /* disable child features of unselected parent or follow parent */ LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { FeatureList *fl; LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) { if (!is_feature_selected( feature, level )) { fl->feature->Action = INSTALLSTATE_UNKNOWN; fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN; } else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent) { TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n", debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest, debugstr_w(feature->Feature), feature->Level, feature->ActionRequest); fl->feature->Action = feature->Action; fl->feature->ActionRequest = feature->ActionRequest; } } } } else /* preselected */ { LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if (!is_feature_selected( feature, level )) continue; if (feature->ActionRequest == INSTALLSTATE_UNKNOWN) { if (feature->Installed == INSTALLSTATE_ABSENT) { feature->Action = INSTALLSTATE_UNKNOWN; feature->ActionRequest = INSTALLSTATE_UNKNOWN; } else { feature->Action = feature->Installed; feature->ActionRequest = feature->Installed; } } } LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { FeatureList *fl; LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) { if (fl->feature->Attributes & msidbFeatureAttributesFollowParent && (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise))) { TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n", debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest, debugstr_w(feature->Feature), feature->Level, feature->ActionRequest); fl->feature->Action = feature->Action; fl->feature->ActionRequest = feature->ActionRequest; } } } } /* now we want to set component state based based on feature state */ LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { ComponentList *cl; TRACE("examining feature %s (level %d installed %d request %d action %d)\n", debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->ActionRequest, feature->Action); if (!is_feature_selected( feature, level )) continue; /* features with components that have compressed files are made local */ LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { if (cl->component->ForceLocalState && feature->ActionRequest == INSTALLSTATE_SOURCE) { feature->Action = INSTALLSTATE_LOCAL; feature->ActionRequest = INSTALLSTATE_LOCAL; break; } } LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { component = cl->component; switch (feature->ActionRequest) { case INSTALLSTATE_ABSENT: component->anyAbsent = 1; break; case INSTALLSTATE_ADVERTISED: component->hasAdvertiseFeature = 1; break; case INSTALLSTATE_SOURCE: component->hasSourceFeature = 1; break; case INSTALLSTATE_LOCAL: component->hasLocalFeature = 1; break; case INSTALLSTATE_DEFAULT: if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) component->hasAdvertiseFeature = 1; else if (feature->Attributes & msidbFeatureAttributesFavorSource) component->hasSourceFeature = 1; else component->hasLocalFeature = 1; break; default: break; } } } LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry ) { /* check if it's local or source */ if (!(component->Attributes & msidbComponentAttributesOptional) && (component->hasLocalFeature || component->hasSourceFeature)) { if ((component->Attributes & msidbComponentAttributesSourceOnly) && !component->ForceLocalState) { component->Action = INSTALLSTATE_SOURCE; component->ActionRequest = INSTALLSTATE_SOURCE; } else { component->Action = INSTALLSTATE_LOCAL; component->ActionRequest = INSTALLSTATE_LOCAL; } continue; } /* if any feature is local, the component must be local too */ if (component->hasLocalFeature) { component->Action = INSTALLSTATE_LOCAL; component->ActionRequest = INSTALLSTATE_LOCAL; continue; } if (component->hasSourceFeature) { component->Action = INSTALLSTATE_SOURCE; component->ActionRequest = INSTALLSTATE_SOURCE; continue; } if (component->hasAdvertiseFeature) { component->Action = INSTALLSTATE_ADVERTISED; component->ActionRequest = INSTALLSTATE_ADVERTISED; continue; } TRACE("nobody wants component %s\n", debugstr_w(component->Component)); if (component->anyAbsent && component->ComponentId) { component->Action = INSTALLSTATE_ABSENT; component->ActionRequest = INSTALLSTATE_ABSENT; } } LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry ) { if (component->ActionRequest == INSTALLSTATE_DEFAULT) { TRACE("%s was default, setting to local\n", debugstr_w(component->Component)); component->Action = INSTALLSTATE_LOCAL; component->ActionRequest = INSTALLSTATE_LOCAL; } if (component->ActionRequest == INSTALLSTATE_SOURCE && component->Installed == INSTALLSTATE_SOURCE && component->hasSourceFeature) { component->Action = INSTALLSTATE_UNKNOWN; component->ActionRequest = INSTALLSTATE_UNKNOWN; } TRACE("component %s (installed %d request %d action %d)\n", debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action); if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE) component->num_clients++; else if (component->Action == INSTALLSTATE_ABSENT) component->num_clients--; } return ERROR_SUCCESS; } static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPCWSTR name; MSIFEATURE *feature; name = MSI_RecordGetString( row, 1 ); feature = msi_get_loaded_feature( package, name ); if (!feature) ERR("FAILED to find loaded feature %s\n",debugstr_w(name)); else { LPCWSTR Condition; Condition = MSI_RecordGetString(row,3); if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE) { int level = MSI_RecordGetInteger(row,2); TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level); feature->Level = level; } } return ERROR_SUCCESS; } VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename ) { static const WCHAR name[] = {'\\',0}; VS_FIXEDFILEINFO *ptr, *ret; LPVOID version; DWORD versize, handle; UINT sz; versize = GetFileVersionInfoSizeW( filename, &handle ); if (!versize) return NULL; version = msi_alloc( versize ); if (!version) return NULL; GetFileVersionInfoW( filename, 0, versize, version ); if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz )) { msi_free( version ); return NULL; } ret = msi_alloc( sz ); memcpy( ret, ptr, sz ); msi_free( version ); return ret; } int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version ) { DWORD ms, ls; msi_parse_version_string( version, &ms, &ls ); if (fi->dwFileVersionMS > ms) return 1; else if (fi->dwFileVersionMS < ms) return -1; else if (fi->dwFileVersionLS > ls) return 1; else if (fi->dwFileVersionLS < ls) return -1; return 0; } int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 ) { DWORD ms1, ms2; msi_parse_version_string( ver1, &ms1, NULL ); msi_parse_version_string( ver2, &ms2, NULL ); if (ms1 > ms2) return 1; else if (ms1 < ms2) return -1; return 0; } DWORD msi_get_disk_file_size( LPCWSTR filename ) { HANDLE file; DWORD size; file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (file == INVALID_HANDLE_VALUE) return INVALID_FILE_SIZE; size = GetFileSize( file, NULL ); TRACE("size is %u\n", size); CloseHandle( file ); return size; } BOOL msi_file_hash_matches( MSIFILE *file ) { UINT r; MSIFILEHASHINFO hash; hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); r = MsiGetFileHashW( file->TargetPath, 0, &hash ); if (r != ERROR_SUCCESS) return FALSE; return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) ); } static WCHAR *get_temp_dir( void ) { static UINT id; WCHAR tmp[MAX_PATH], dir[MAX_PATH]; GetTempPathW( MAX_PATH, tmp ); for (;;) { if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL; if (CreateDirectoryW( dir, NULL )) break; } return strdupW( dir ); } /* * msi_build_directory_name() * * This function is to save messing round with directory names * It handles adding backslashes between path segments, * and can add \ at the end of the directory name if told to. * * It takes a variable number of arguments. * It always allocates a new string for the result, so make sure * to free the return value when finished with it. * * The first arg is the number of path segments that follow. * The arguments following count are a list of path segments. * A path segment may be NULL. * * Path segments will be added with a \ separating them. * A \ will not be added after the last segment, however if the * last segment is NULL, then the last character will be a \ */ WCHAR *msi_build_directory_name( DWORD count, ... ) { DWORD sz = 1, i; WCHAR *dir; va_list va; va_start( va, count ); for (i = 0; i < count; i++) { const WCHAR *str = va_arg( va, const WCHAR * ); if (str) sz += strlenW( str ) + 1; } va_end( va ); dir = msi_alloc( sz * sizeof(WCHAR) ); dir[0] = 0; va_start( va, count ); for (i = 0; i < count; i++) { const WCHAR *str = va_arg( va, const WCHAR * ); if (!str) continue; strcatW( dir, str ); if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash ); } va_end( va ); return dir; } static void set_target_path( MSIPACKAGE *package, MSIFILE *file ) { MSIASSEMBLY *assembly = file->Component->assembly; TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName)); msi_free( file->TargetPath ); if (assembly && !assembly->application) { if (!assembly->tempdir) assembly->tempdir = get_temp_dir(); file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName ); msi_track_tempfile( package, file->TargetPath ); } else { const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory ); file->TargetPath = msi_build_directory_name( 2, dir, file->FileName ); } TRACE("resolves to %s\n", debugstr_w(file->TargetPath)); } static UINT calculate_file_cost( MSIPACKAGE *package ) { VS_FIXEDFILEINFO *file_version; WCHAR *font_version; MSIFILE *file; LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) { MSICOMPONENT *comp = file->Component; DWORD file_size; if (!comp->Enabled) continue; if (file->IsCompressed) comp->ForceLocalState = TRUE; set_target_path( package, file ); if ((comp->assembly && !comp->assembly->installed) || GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES) { comp->Cost += file->FileSize; continue; } file_size = msi_get_disk_file_size( file->TargetPath ); if (file->Version) { if ((file_version = msi_get_disk_file_version( file->TargetPath ))) { if (msi_compare_file_versions( file_version, file->Version ) < 0) { comp->Cost += file->FileSize - file_size; } msi_free( file_version ); continue; } else if ((font_version = msi_font_version_from_file( file->TargetPath ))) { if (msi_compare_font_versions( font_version, file->Version ) < 0) { comp->Cost += file->FileSize - file_size; } msi_free( font_version ); continue; } } if (file_size != file->FileSize) { comp->Cost += file->FileSize - file_size; } } return ERROR_SUCCESS; } WCHAR *msi_normalize_path( const WCHAR *in ) { const WCHAR *p = in; WCHAR *q, *ret; int n, len = strlenW( in ) + 2; if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL; len = 0; while (1) { /* copy until the end of the string or a space */ while (*p != ' ' && (*q = *p)) { p++, len++; /* reduce many backslashes to one */ if (*p != '\\' || *q != '\\') q++; } /* quit at the end of the string */ if (!*p) break; /* count the number of spaces */ n = 0; while (p[n] == ' ') n++; /* if it's leading or trailing space, skip it */ if ( len == 0 || p[-1] == '\\' || p[n] == '\\' ) p += n; else /* copy n spaces */ while (n && (*q++ = *p++)) n--; } while (q - ret > 0 && q[-1] == ' ') q--; if (q - ret > 0 && q[-1] != '\\') { q[0] = '\\'; q[1] = 0; } return ret; } static WCHAR *get_install_location( MSIPACKAGE *package ) { HKEY hkey; WCHAR *path; if (!package->ProductCode) return NULL; if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL; path = msi_reg_get_val_str( hkey, szInstallLocation ); RegCloseKey( hkey ); return path; } void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop ) { FolderList *fl; MSIFOLDER *folder, *parent, *child; WCHAR *path, *normalized_path; TRACE("resolving %s\n", debugstr_w(name)); if (!(folder = msi_get_loaded_folder( package, name ))) return; if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */ { if (!(path = get_install_location( package )) && (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))) { path = msi_dup_property( package->db, szRootDrive ); } } else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory ))) { if (folder->Parent && strcmpW( folder->Directory, folder->Parent )) { parent = msi_get_loaded_folder( package, folder->Parent ); path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL ); } else path = msi_build_directory_name( 2, folder->TargetDefault, NULL ); } normalized_path = msi_normalize_path( path ); msi_free( path ); if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget )) { TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget)); msi_free( normalized_path ); return; } msi_set_property( package->db, folder->Directory, normalized_path, -1 ); msi_free( folder->ResolvedTarget ); folder->ResolvedTarget = normalized_path; LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) { child = fl->folder; msi_resolve_target_folder( package, child->Directory, load_prop ); } TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget)); } static UINT ACTION_CostFinalize(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','o','n','d','i','t','i','o','n','`',0}; static const WCHAR szOutOfDiskSpace[] = { 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0}; MSICOMPONENT *comp; MSIQUERY *view; LPWSTR level; UINT rc; TRACE("Building directory properties\n"); msi_resolve_target_folder( package, szTargetDir, TRUE ); TRACE("Evaluating component conditions\n"); LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) { if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE) { TRACE("Disabling component %s\n", debugstr_w(comp->Component)); comp->Enabled = FALSE; } else comp->Enabled = TRUE; } get_client_counts( package ); /* read components states from the registry */ ACTION_GetComponentInstallStates(package); ACTION_GetFeatureInstallStates(package); if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) )) { TRACE("Evaluating feature conditions\n"); rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } } TRACE("Calculating file cost\n"); calculate_file_cost( package ); msi_set_property( package->db, szCostingComplete, szOne, -1 ); /* set default run level if not set */ level = msi_dup_property( package->db, szInstallLevel ); if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 ); msi_free(level); /* FIXME: check volume disk space */ msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 ); return MSI_SetFeatureStates(package); } static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size ) { BYTE *data = NULL; if (!value) { *size = sizeof(WCHAR); *type = REG_SZ; if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0; return data; } if (value[0]=='#' && value[1]!='#' && value[1]!='%') { if (value[1]=='x') { LPWSTR ptr; CHAR byte[5]; LPWSTR deformated = NULL; int count; deformat_string(package, &value[2], &deformated); /* binary value type */ ptr = deformated; *type = REG_BINARY; if (strlenW(ptr)%2) *size = (strlenW(ptr)/2)+1; else *size = strlenW(ptr)/2; data = msi_alloc(*size); byte[0] = '0'; byte[1] = 'x'; byte[4] = 0; count = 0; /* if uneven pad with a zero in front */ if (strlenW(ptr)%2) { byte[2]= '0'; byte[3]= *ptr; ptr++; data[count] = (BYTE)strtol(byte,NULL,0); count ++; TRACE("Uneven byte count\n"); } while (*ptr) { byte[2]= *ptr; ptr++; byte[3]= *ptr; ptr++; data[count] = (BYTE)strtol(byte,NULL,0); count ++; } msi_free(deformated); TRACE("Data %i bytes(%i)\n",*size,count); } else { LPWSTR deformated; LPWSTR p; DWORD d = 0; deformat_string(package, &value[1], &deformated); *type=REG_DWORD; *size = sizeof(DWORD); data = msi_alloc(*size); p = deformated; if (*p == '-') p++; while (*p) { if ( (*p < '0') || (*p > '9') ) break; d *= 10; d += (*p - '0'); p++; } if (deformated[0] == '-') d = -d; *(LPDWORD)data = d; TRACE("DWORD %i\n",*(LPDWORD)data); msi_free(deformated); } } else { const WCHAR *ptr = value; DWORD len; *type = REG_SZ; if (value[0] == '#') { ptr++; if (value[1] == '%') { ptr++; *type = REG_EXPAND_SZ; } } len = deformat_string( package, ptr, (WCHAR **)&data ); if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ; *size = (len + 1) * sizeof(WCHAR); } return data; } static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key ) { const WCHAR *ret; switch (root) { case -1: if (msi_get_property_int( package->db, szAllUsers, 0 )) { *root_key = HKEY_LOCAL_MACHINE; ret = szHLM; } else { *root_key = HKEY_CURRENT_USER; ret = szHCU; } break; case 0: *root_key = HKEY_CLASSES_ROOT; ret = szHCR; break; case 1: *root_key = HKEY_CURRENT_USER; ret = szHCU; break; case 2: *root_key = HKEY_LOCAL_MACHINE; ret = szHLM; break; case 3: *root_key = HKEY_USERS; ret = szHU; break; default: ERR("Unknown root %i\n", root); return NULL; } return ret; } static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path ) { static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'}; static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]); if ((is_64bit || is_wow64) && !(comp->Attributes & msidbComponentAttributes64bit) && root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len )) { UINT size; WCHAR *path_32node; size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR); if (!(path_32node = msi_alloc( size ))) return NULL; memcpy( path_32node, path, len * sizeof(WCHAR) ); strcpyW( path_32node + len, szWow6432Node ); strcatW( path_32node, szBackSlash ); strcatW( path_32node, path + len ); return path_32node; } return strdupW( path ); } static HKEY open_key( HKEY root, const WCHAR *path, BOOL create ) { REGSAM access = KEY_ALL_ACCESS; WCHAR *subkey, *p, *q; HKEY hkey, ret = NULL; LONG res; if (is_wow64) access |= KEY_WOW64_64KEY; if (!(subkey = strdupW( path ))) return NULL; p = subkey; if ((q = strchrW( p, '\\' ))) *q = 0; if (create) res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL ); else res = RegOpenKeyExW( root, subkey, 0, access, &hkey ); if (res) { TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res); msi_free( subkey ); return NULL; } if (q && q[1]) { ret = open_key( hkey, q + 1, create ); RegCloseKey( hkey ); } else ret = hkey; msi_free( subkey ); return ret; } static BOOL is_special_entry( const WCHAR *name ) { return (name && (name[0] == '*' || name[0] == '+') && !name[1]); } static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count ) { const WCHAR *p = str; WCHAR **ret; int i = 0; *count = 0; if (!str) return NULL; while ((p - str) < len) { p += strlenW( p ) + 1; (*count)++; } if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL; p = str; while ((p - str) < len) { if (!(ret[i] = strdupW( p ))) { for (; i >= 0; i--) msi_free( ret[i] ); msi_free( ret ); return NULL; } p += strlenW( p ) + 1; i++; } return ret; } static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count, WCHAR **right, DWORD right_count, DWORD *size ) { WCHAR *ret, *p; unsigned int i; *size = sizeof(WCHAR); for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR); for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR); if (!(ret = p = msi_alloc( *size ))) return NULL; for (i = 0; i < left_count; i++) { strcpyW( p, left[i] ); p += strlenW( p ) + 1; } for (i = 0; i < right_count; i++) { strcpyW( p, right[i] ); p += strlenW( p ) + 1; } *p = 0; return ret; } static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count, WCHAR **new, DWORD new_count ) { DWORD ret = old_count; unsigned int i, j, k; for (i = 0; i < new_count; i++) { for (j = 0; j < old_count; j++) { if (old[j] && !strcmpW( new[i], old[j] )) { msi_free( old[j] ); for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; } old[k] = NULL; ret--; } } } return ret; } enum join_op { JOIN_OP_APPEND, JOIN_OP_PREPEND, JOIN_OP_REPLACE }; static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count, WCHAR **new, DWORD new_count, DWORD *size ) { switch (op) { case JOIN_OP_APPEND: old_count = remove_duplicate_values( old, old_count, new, new_count ); return flatten_multi_string_values( old, old_count, new, new_count, size ); case JOIN_OP_PREPEND: old_count = remove_duplicate_values( old, old_count, new, new_count ); return flatten_multi_string_values( new, new_count, old, old_count, size ); case JOIN_OP_REPLACE: return flatten_multi_string_values( new, new_count, NULL, 0, size ); default: ERR("unhandled join op %u\n", op); return NULL; } } static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size, BYTE *new_value, DWORD new_size, DWORD *size ) { DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0; const WCHAR *new_ptr = NULL, *old_ptr = NULL; enum join_op op = JOIN_OP_REPLACE; WCHAR **old = NULL, **new = NULL; BYTE *ret; if (new_size / sizeof(WCHAR) - 1 > 1) { new_ptr = (const WCHAR *)new_value; new_len = new_size / sizeof(WCHAR) - 1; if (!new_ptr[0] && new_ptr[new_len - 1]) { op = JOIN_OP_APPEND; new_len--; new_ptr++; } else if (new_ptr[0] && !new_ptr[new_len - 1]) { op = JOIN_OP_PREPEND; new_len--; } else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1]) { op = JOIN_OP_REPLACE; new_len -= 2; new_ptr++; } new = split_multi_string_values( new_ptr, new_len, &new_count ); } if (old_size / sizeof(WCHAR) - 1 > 1) { old_ptr = (const WCHAR *)old_value; old_len = old_size / sizeof(WCHAR) - 1; old = split_multi_string_values( old_ptr, old_len, &old_count ); } ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size ); for (i = 0; i < old_count; i++) msi_free( old[i] ); for (i = 0; i < new_count; i++) msi_free( new[i] ); msi_free( old ); msi_free( new ); return ret; } static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size ) { BYTE *ret; if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL; if (!(ret = msi_alloc( *size ))) return NULL; RegQueryValueExW( hkey, name, NULL, type, ret, size ); return ret; } static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; BYTE *new_value, *old_value = NULL; HKEY root_key, hkey; DWORD type, old_type, new_size, old_size = 0; LPWSTR deformated, uikey, keypath; const WCHAR *szRoot, *component, *name, *key, *str; MSICOMPONENT *comp; MSIRECORD * uirow; INT root; BOOL check_first = FALSE; int len; msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 ); component = MSI_RecordGetString(row, 6); comp = msi_get_loaded_component(package,component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } name = MSI_RecordGetString(row, 4); if( MSI_RecordIsNull(row,5) && name ) { /* null values can have special meanings */ if (name[0]=='-' && name[1] == 0) return ERROR_SUCCESS; if ((name[0] == '+' || name[0] == '*') && !name[1]) check_first = TRUE; } root = MSI_RecordGetInteger(row,2); key = MSI_RecordGetString(row, 3); szRoot = get_root_key( package, root, &root_key ); if (!szRoot) return ERROR_SUCCESS; deformat_string(package, key , &deformated); uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) ); strcpyW(uikey,szRoot); strcatW(uikey,deformated); keypath = get_keypath( comp, root_key, deformated ); msi_free( deformated ); if (!(hkey = open_key( root_key, keypath, TRUE ))) { ERR("Could not create key %s\n", debugstr_w(keypath)); msi_free(uikey); msi_free(keypath); return ERROR_FUNCTION_FAILED; } str = msi_record_get_string( row, 5, &len ); if (str && len > strlenW( str )) { type = REG_MULTI_SZ; new_size = (len + 1) * sizeof(WCHAR); new_value = (BYTE *)msi_strdupW( str, len ); } else new_value = parse_value( package, str, &type, &new_size ); deformat_string(package, name, &deformated); if (!is_special_entry( name )) { old_value = reg_get_value( hkey, deformated, &old_type, &old_size ); if (type == REG_MULTI_SZ) { BYTE *new; if (old_value && old_type != REG_MULTI_SZ) { msi_free( old_value ); old_value = NULL; old_size = 0; } new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size ); msi_free( new_value ); new_value = new; } if (!check_first) { TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type); RegSetValueExW( hkey, deformated, 0, type, new_value, new_size ); } else if (!old_value) { if (deformated || new_size) { TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type); RegSetValueExW( hkey, deformated, 0, type, new_value, new_size ); } } else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey)); } RegCloseKey(hkey); uirow = MSI_CreateRecord(3); MSI_RecordSetStringW(uirow,2,deformated); MSI_RecordSetStringW(uirow,1,uikey); if (type == REG_SZ || type == REG_EXPAND_SZ) MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value); msi_ui_actiondata( package, szWriteRegistryValues, uirow ); msiobj_release( &uirow->hdr ); msi_free(new_value); msi_free(old_value); msi_free(deformated); msi_free(uikey); msi_free(keypath); return ERROR_SUCCESS; } static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','R','e','g','i','s','t','r','y','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package); msiobj_release(&view->hdr); return rc; } static void delete_key( HKEY root, const WCHAR *path ) { REGSAM access = 0; WCHAR *subkey, *p; HKEY hkey; LONG res; if (is_wow64) access |= KEY_WOW64_64KEY; if (!(subkey = strdupW( path ))) return; for (;;) { if ((p = strrchrW( subkey, '\\' ))) *p = 0; hkey = open_key( root, subkey, FALSE ); if (!hkey) break; if (p && p[1]) res = RegDeleteKeyExW( hkey, p + 1, access, 0 ); else res = RegDeleteKeyExW( root, subkey, access, 0 ); if (res) { TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res); break; } if (p && p[1]) RegCloseKey( hkey ); else break; } msi_free( subkey ); } static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value ) { LONG res; HKEY hkey; DWORD num_subkeys, num_values; if ((hkey = open_key( root, path, FALSE ))) { if ((res = RegDeleteValueW( hkey, value ))) TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res); res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values, NULL, NULL, NULL, NULL ); RegCloseKey( hkey ); if (!res && !num_subkeys && !num_values) { TRACE("removing empty key %s\n", debugstr_w(path)); delete_key( root, path ); } } } static void delete_tree( HKEY root, const WCHAR *path ) { LONG res; HKEY hkey; if (!(hkey = open_key( root, path, FALSE ))) return; res = RegDeleteTreeW( hkey, NULL ); if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res); delete_key( root, path ); RegCloseKey( hkey ); } static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR component, name, key_str, root_key_str; LPWSTR deformated_key, deformated_name, ui_key_str, keypath; MSICOMPONENT *comp; MSIRECORD *uirow; BOOL delete_key = FALSE; HKEY hkey_root; UINT size; INT root; msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 ); component = MSI_RecordGetString( row, 6 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } name = MSI_RecordGetString( row, 4 ); if (MSI_RecordIsNull( row, 5 ) && name ) { if (name[0] == '+' && !name[1]) return ERROR_SUCCESS; if ((name[0] == '-' || name[0] == '*') && !name[1]) { delete_key = TRUE; name = NULL; } } root = MSI_RecordGetInteger( row, 2 ); key_str = MSI_RecordGetString( row, 3 ); root_key_str = get_root_key( package, root, &hkey_root ); if (!root_key_str) return ERROR_SUCCESS; deformat_string( package, key_str, &deformated_key ); size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1; ui_key_str = msi_alloc( size * sizeof(WCHAR) ); strcpyW( ui_key_str, root_key_str ); strcatW( ui_key_str, deformated_key ); deformat_string( package, name, &deformated_name ); keypath = get_keypath( comp, hkey_root, deformated_key ); msi_free( deformated_key ); if (delete_key) delete_tree( hkey_root, keypath ); else delete_value( hkey_root, keypath, deformated_name ); msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, ui_key_str ); MSI_RecordSetStringW( uirow, 2, deformated_name ); msi_ui_actiondata( package, szRemoveRegistryValues, uirow ); msiobj_release( &uirow->hdr ); msi_free( ui_key_str ); msi_free( deformated_name ); return ERROR_SUCCESS; } static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR component, name, key_str, root_key_str; LPWSTR deformated_key, deformated_name, ui_key_str, keypath; MSICOMPONENT *comp; MSIRECORD *uirow; BOOL delete_key = FALSE; HKEY hkey_root; UINT size; INT root; component = MSI_RecordGetString( row, 5 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } if ((name = MSI_RecordGetString( row, 4 ))) { if (name[0] == '-' && !name[1]) { delete_key = TRUE; name = NULL; } } root = MSI_RecordGetInteger( row, 2 ); key_str = MSI_RecordGetString( row, 3 ); root_key_str = get_root_key( package, root, &hkey_root ); if (!root_key_str) return ERROR_SUCCESS; deformat_string( package, key_str, &deformated_key ); size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1; ui_key_str = msi_alloc( size * sizeof(WCHAR) ); strcpyW( ui_key_str, root_key_str ); strcatW( ui_key_str, deformated_key ); deformat_string( package, name, &deformated_name ); keypath = get_keypath( comp, hkey_root, deformated_key ); msi_free( deformated_key ); if (delete_key) delete_tree( hkey_root, keypath ); else delete_value( hkey_root, keypath, deformated_name ); msi_free( keypath ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, ui_key_str ); MSI_RecordSetStringW( uirow, 2, deformated_name ); msi_ui_actiondata( package, szRemoveRegistryValues, uirow ); msiobj_release( &uirow->hdr ); msi_free( ui_key_str ); msi_free( deformated_name ); return ERROR_SUCCESS; } static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package ) { static const WCHAR registry_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','R','e','g','i','s','t','r','y','`',0}; static const WCHAR remove_registry_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } return ERROR_SUCCESS; } static UINT ACTION_InstallInitialize(MSIPACKAGE *package) { package->script->CurrentlyScripting = TRUE; return ERROR_SUCCESS; } static UINT ACTION_InstallValidate(MSIPACKAGE *package) { static const WCHAR query[]= { 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','R','e','g','i','s','t','r','y','`',0}; MSICOMPONENT *comp; DWORD total = 0, count = 0; MSIQUERY *view; MSIFEATURE *feature; MSIFILE *file; UINT rc; TRACE("InstallValidate\n"); rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, &count, NULL, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; total += count * REG_PROGRESS_VALUE; } LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) total += COMPONENT_PROGRESS_VALUE; LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) total += file->FileSize; msi_ui_progress( package, 0, total, 0, 0 ); LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { TRACE("Feature: %s Installed %d Request %d Action %d\n", debugstr_w(feature->Feature), feature->Installed, feature->ActionRequest, feature->Action); } return ERROR_SUCCESS; } static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = param; LPCWSTR cond = NULL; LPCWSTR message = NULL; UINT r; static const WCHAR title[]= {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0}; cond = MSI_RecordGetString(row,1); r = MSI_EvaluateConditionW(package,cond); if (r == MSICONDITION_FALSE) { if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) { LPWSTR deformated; message = MSI_RecordGetString(row,2); deformat_string(package,message,&deformated); MessageBoxW(NULL,deformated,title,MB_OK); msi_free(deformated); } return ERROR_INSTALL_FAILURE; } return ERROR_SUCCESS; } static UINT ACTION_LaunchConditions(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0}; MSIQUERY *view; UINT rc; TRACE("Checking launch conditions\n"); rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package); msiobj_release(&view->hdr); return rc; } static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp ) { if (!cmp->KeyPath) return strdupW( msi_get_target_folder( package, cmp->Directory ) ); if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0}; static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0}; static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0}; MSIRECORD *row; UINT root, len; LPWSTR deformated, buffer, deformated_name; LPCWSTR key, name; row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath); if (!row) return NULL; root = MSI_RecordGetInteger(row,2); key = MSI_RecordGetString(row, 3); name = MSI_RecordGetString(row, 4); deformat_string(package, key , &deformated); deformat_string(package, name, &deformated_name); len = strlenW(deformated) + 6; if (deformated_name) len+=strlenW(deformated_name); buffer = msi_alloc( len *sizeof(WCHAR)); if (deformated_name) sprintfW(buffer,fmt2,root,deformated,deformated_name); else sprintfW(buffer,fmt,root,deformated); msi_free(deformated); msi_free(deformated_name); msiobj_release(&row->hdr); return buffer; } else if (cmp->Attributes & msidbComponentAttributesODBCDataSource) { FIXME("UNIMPLEMENTED keypath as ODBC Source\n"); return NULL; } else { MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath ); if (file) return strdupW( file->TargetPath ); } return NULL; } static HKEY openSharedDLLsKey(void) { HKEY hkey=0; static const WCHAR path[] = {'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\', 'W','i','n','d','o','w','s','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'S','h','a','r','e','d','D','L','L','s',0}; RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey); return hkey; } static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll) { HKEY hkey; DWORD count=0; DWORD type; DWORD sz = sizeof(count); DWORD rc; hkey = openSharedDLLsKey(); rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz); if (rc != ERROR_SUCCESS) count = 0; RegCloseKey(hkey); return count; } static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count) { HKEY hkey; hkey = openSharedDLLsKey(); if (count > 0) msi_reg_set_val_dword( hkey, path, count ); else RegDeleteValueW(hkey,path); RegCloseKey(hkey); return count; } static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp ) { MSIFEATURE *feature; INT count = 0; BOOL write = FALSE; /* only refcount DLLs */ if (comp->KeyPath == NULL || comp->assembly || comp->Attributes & msidbComponentAttributesRegistryKeyPath || comp->Attributes & msidbComponentAttributesODBCDataSource) write = FALSE; else { count = ACTION_GetSharedDLLsCount( comp->FullKeypath); write = (count > 0); if (comp->Attributes & msidbComponentAttributesSharedDllRefCount) write = TRUE; } /* increment counts */ LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { ComponentList *cl; if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL) continue; LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { if ( cl->component == comp ) count++; } } /* decrement counts */ LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { ComponentList *cl; if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT) continue; LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { if ( cl->component == comp ) count--; } } /* ref count all the files in the component */ if (write) { MSIFILE *file; LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) { if (file->Component == comp) ACTION_WriteSharedDLLsCount( file->TargetPath, count ); } } /* add a count for permanent */ if (comp->Attributes & msidbComponentAttributesPermanent) count ++; comp->RefCount = count; if (write) ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount ); } static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp ) { if (comp->assembly) { const WCHAR prefixW[] = {'<','\\',0}; DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name ); WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) ); if (keypath) { strcpyW( keypath, prefixW ); strcatW( keypath, comp->assembly->display_name ); } return keypath; } return resolve_keypath( package, comp ); } static UINT ACTION_ProcessComponents(MSIPACKAGE *package) { WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE]; UINT rc; MSICOMPONENT *comp; HKEY hkey; TRACE("\n"); squash_guid(package->ProductCode,squished_pc); msi_set_sourcedir_props(package, FALSE); LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) { MSIRECORD *uirow; INSTALLSTATE action; msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 ); if (!comp->ComponentId) continue; squash_guid( comp->ComponentId, squished_cc ); msi_free( comp->FullKeypath ); comp->FullKeypath = build_full_keypath( package, comp ); ACTION_RefCountComponent( package, comp ); if (package->need_rollback) action = comp->Installed; else action = comp->ActionRequest; TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n", debugstr_w(comp->Component), debugstr_w(squished_cc), debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action); if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE) { if (package->Context == MSIINSTALLCONTEXT_MACHINE) rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE); else rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE); if (rc != ERROR_SUCCESS) continue; if (comp->Attributes & msidbComponentAttributesPermanent) { static const WCHAR szPermKey[] = { '0','0','0','0','0','0','0','0','0','0','0','0', '0','0','0','0','0','0','0','0','0','0','0','0', '0','0','0','0','0','0','0','0',0 }; msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath); } if (action == INSTALLSTATE_LOCAL) msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath); else { MSIFILE *file; MSIRECORD *row; LPWSTR ptr, ptr2; WCHAR source[MAX_PATH]; WCHAR base[MAX_PATH]; LPWSTR sourcepath; static const WCHAR fmt[] = {'%','0','2','d','\\',0}; static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ', '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ', '`','D','i','s','k','I','d','`',0}; if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath))) continue; row = MSI_QueryGetRecord(package->db, query, file->Sequence); sprintfW(source, fmt, MSI_RecordGetInteger(row, 1)); ptr2 = strrchrW(source, '\\') + 1; msiobj_release(&row->hdr); lstrcpyW(base, package->PackagePath); ptr = strrchrW(base, '\\'); *(ptr + 1) = '\0'; sourcepath = msi_resolve_file_source(package, file); ptr = sourcepath + lstrlenW(base); lstrcpyW(ptr2, ptr); msi_free(sourcepath); msi_reg_set_val_str(hkey, squished_pc, source); } RegCloseKey(hkey); } else if (action == INSTALLSTATE_ABSENT) { if (comp->num_clients <= 0) { if (package->Context == MSIINSTALLCONTEXT_MACHINE) MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid ); else MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL ); } } /* UI stuff */ uirow = MSI_CreateRecord(3); MSI_RecordSetStringW(uirow,1,package->ProductCode); MSI_RecordSetStringW(uirow,2,comp->ComponentId); MSI_RecordSetStringW(uirow,3,comp->FullKeypath); msi_ui_actiondata( package, szProcessComponents, uirow ); msiobj_release( &uirow->hdr ); } return ERROR_SUCCESS; } typedef struct { CLSID clsid; LPWSTR source; LPWSTR path; ITypeLib *ptLib; } typelib_struct; static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam) { TLIBATTR *attr; typelib_struct *tl_struct = (typelib_struct*) lParam; static const WCHAR fmt[] = {'%','s','\\','%','i',0}; int sz; HRESULT res; if (!IS_INTRESOURCE(lpszName)) { ERR("Not Int Resource Name %s\n",debugstr_w(lpszName)); return TRUE; } sz = strlenW(tl_struct->source)+4; sz *= sizeof(WCHAR); if ((INT_PTR)lpszName == 1) tl_struct->path = strdupW(tl_struct->source); else { tl_struct->path = msi_alloc(sz); sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName); } TRACE("trying %s\n", debugstr_w(tl_struct->path)); res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib); if (FAILED(res)) { msi_free(tl_struct->path); tl_struct->path = NULL; return TRUE; } ITypeLib_GetLibAttr(tl_struct->ptLib, &attr); if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid))) { ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); return FALSE; } msi_free(tl_struct->path); tl_struct->path = NULL; ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); ITypeLib_Release(tl_struct->ptLib); return TRUE; } static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = param; LPCWSTR component; MSICOMPONENT *comp; MSIFILE *file; typelib_struct tl_struct; ITypeLib *tlib; HMODULE module; HRESULT hr; component = MSI_RecordGetString(row,3); comp = msi_get_loaded_component(package,component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath ))) { TRACE("component has no key path\n"); return ERROR_SUCCESS; } msi_ui_actiondata( package, szRegisterTypeLibraries, row ); module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE ); if (module) { LPCWSTR guid; guid = MSI_RecordGetString(row,1); CLSIDFromString( guid, &tl_struct.clsid); tl_struct.source = strdupW( file->TargetPath ); tl_struct.path = NULL; EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc, (LONG_PTR)&tl_struct); if (tl_struct.path) { LPCWSTR helpid, help_path = NULL; HRESULT res; helpid = MSI_RecordGetString(row,6); if (helpid) help_path = msi_get_target_folder( package, helpid ); res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path ); if (FAILED(res)) ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path)); else TRACE("Registered %s\n", debugstr_w(tl_struct.path)); ITypeLib_Release(tl_struct.ptLib); msi_free(tl_struct.path); } else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source)); FreeLibrary(module); msi_free(tl_struct.source); } else { hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib); if (FAILED(hr)) { ERR("Failed to load type library: %08x\n", hr); return ERROR_INSTALL_FAILURE; } ITypeLib_Release(tlib); } return ERROR_SUCCESS; } static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','T','y','p','e','L','i','b','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR component, guid; MSICOMPONENT *comp; GUID libid; UINT version; LCID language; SYSKIND syskind; HRESULT hr; component = MSI_RecordGetString( row, 3 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } msi_ui_actiondata( package, szUnregisterTypeLibraries, row ); guid = MSI_RecordGetString( row, 1 ); CLSIDFromString( guid, &libid ); version = MSI_RecordGetInteger( row, 4 ); language = MSI_RecordGetInteger( row, 2 ); #ifdef _WIN64 syskind = SYS_WIN64; #else syskind = SYS_WIN32; #endif hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind ); if (FAILED(hr)) { WARN("Failed to unregister typelib: %08x\n", hr); } return ERROR_SUCCESS; } static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','T','y','p','e','L','i','b','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package ); msiobj_release( &view->hdr ); return rc; } static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row ) { static const WCHAR szlnk[] = {'.','l','n','k',0}; LPCWSTR directory, extension, link_folder; LPWSTR link_file, filename; directory = MSI_RecordGetString( row, 2 ); link_folder = msi_get_target_folder( package, directory ); if (!link_folder) { ERR("unable to resolve folder %s\n", debugstr_w(directory)); return NULL; } /* may be needed because of a bug somewhere else */ msi_create_full_path( link_folder ); filename = msi_dup_record_field( row, 3 ); msi_reduce_to_long_filename( filename ); extension = strchrW( filename, '.' ); if (!extension || strcmpiW( extension, szlnk )) { int len = strlenW( filename ); filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) ); memcpy( filename + len, szlnk, sizeof(szlnk) ); } link_file = msi_build_directory_name( 2, link_folder, filename ); msi_free( filename ); return link_file; } WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name ) { static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0}; static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0}; WCHAR *folder, *dest, *path; if (package->Context == MSIINSTALLCONTEXT_MACHINE) folder = msi_dup_property( package->db, szWindowsFolder ); else { WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder ); folder = msi_build_directory_name( 2, appdata, szMicrosoft ); msi_free( appdata ); } dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode ); msi_create_full_path( dest ); path = msi_build_directory_name( 2, dest, icon_name ); msi_free( folder ); msi_free( dest ); return path; } static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPWSTR link_file, deformated, path; LPCWSTR component, target; MSICOMPONENT *comp; IShellLinkW *sl = NULL; IPersistFile *pf = NULL; HRESULT res; component = MSI_RecordGetString(row, 4); comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } msi_ui_actiondata( package, szCreateShortcuts, row ); res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID *) &sl ); if (FAILED( res )) { ERR("CLSID_ShellLink not available\n"); goto err; } res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf ); if (FAILED( res )) { ERR("QueryInterface(IID_IPersistFile) failed\n"); goto err; } target = MSI_RecordGetString(row, 5); if (strchrW(target, '[')) { deformat_string( package, target, &path ); TRACE("target path is %s\n", debugstr_w(path)); IShellLinkW_SetPath( sl, path ); msi_free( path ); } else { FIXME("poorly handled shortcut format, advertised shortcut\n"); IShellLinkW_SetPath(sl,comp->FullKeypath); } if (!MSI_RecordIsNull(row,6)) { LPCWSTR arguments = MSI_RecordGetString(row, 6); deformat_string(package, arguments, &deformated); IShellLinkW_SetArguments(sl,deformated); msi_free(deformated); } if (!MSI_RecordIsNull(row,7)) { LPCWSTR description = MSI_RecordGetString(row, 7); IShellLinkW_SetDescription(sl, description); } if (!MSI_RecordIsNull(row,8)) IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8)); if (!MSI_RecordIsNull(row,9)) { INT index; LPCWSTR icon = MSI_RecordGetString(row, 9); path = msi_build_icon_path(package, icon); index = MSI_RecordGetInteger(row,10); /* no value means 0 */ if (index == MSI_NULL_INTEGER) index = 0; IShellLinkW_SetIconLocation(sl, path, index); msi_free(path); } if (!MSI_RecordIsNull(row,11)) IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11)); if (!MSI_RecordIsNull(row,12)) { LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 ); full_path = msi_get_target_folder( package, wkdir ); if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path ); } link_file = get_link_file(package, row); TRACE("Writing shortcut to %s\n", debugstr_w(link_file)); IPersistFile_Save(pf, link_file, FALSE); msi_free(link_file); err: if (pf) IPersistFile_Release( pf ); if (sl) IShellLinkW_Release( sl ); return ERROR_SUCCESS; } static UINT ACTION_CreateShortcuts(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','S','h','o','r','t','c','u','t','`',0}; MSIQUERY *view; HRESULT res; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; res = CoInitialize( NULL ); rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package); msiobj_release(&view->hdr); if (SUCCEEDED(res)) CoUninitialize(); return rc; } static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPWSTR link_file; LPCWSTR component; MSICOMPONENT *comp; component = MSI_RecordGetString( row, 4 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } msi_ui_actiondata( package, szRemoveShortcuts, row ); link_file = get_link_file( package, row ); TRACE("Removing shortcut file %s\n", debugstr_w( link_file )); if (!DeleteFileW( link_file )) { WARN("Failed to remove shortcut file %u\n", GetLastError()); } msi_free( link_file ); return ERROR_SUCCESS; } static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','S','h','o','r','t','c','u','t','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package ); msiobj_release( &view->hdr ); return rc; } static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = param; HANDLE the_file; LPWSTR FilePath; LPCWSTR FileName; CHAR buffer[1024]; DWORD sz; UINT rc; FileName = MSI_RecordGetString(row,1); if (!FileName) { ERR("Unable to get FileName\n"); return ERROR_SUCCESS; } FilePath = msi_build_icon_path(package, FileName); TRACE("Creating icon file at %s\n",debugstr_w(FilePath)); the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (the_file == INVALID_HANDLE_VALUE) { ERR("Unable to create file %s\n",debugstr_w(FilePath)); msi_free(FilePath); return ERROR_SUCCESS; } do { DWORD write; sz = 1024; rc = MSI_RecordReadStream(row,2,buffer,&sz); if (rc != ERROR_SUCCESS) { ERR("Failed to get stream\n"); CloseHandle(the_file); DeleteFileW(FilePath); break; } WriteFile(the_file,buffer,sz,&write,NULL); } while (sz == 1024); msi_free(FilePath); CloseHandle(the_file); return ERROR_SUCCESS; } static UINT msi_publish_icons(MSIPACKAGE *package) { static const WCHAR query[]= { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','c','o','n','`',0}; MSIQUERY *view; UINT r; r = MSI_DatabaseOpenViewW(package->db, query, &view); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package); msiobj_release(&view->hdr); if (r != ERROR_SUCCESS) return r; } return ERROR_SUCCESS; } static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey) { UINT r; HKEY source; LPWSTR buffer; MSIMEDIADISK *disk; MSISOURCELISTINFO *info; r = RegCreateKeyW(hkey, szSourceList, &source); if (r != ERROR_SUCCESS) return r; RegCloseKey(source); buffer = strrchrW(package->PackagePath, '\\') + 1; r = MsiSourceListSetInfoW(package->ProductCode, NULL, package->Context, MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, buffer); if (r != ERROR_SUCCESS) return r; r = MsiSourceListSetInfoW(package->ProductCode, NULL, package->Context, MSICODE_PRODUCT, INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty); if (r != ERROR_SUCCESS) return r; r = MsiSourceListSetInfoW(package->ProductCode, NULL, package->Context, MSICODE_PRODUCT, INSTALLPROPERTY_DISKPROMPTW, szEmpty); if (r != ERROR_SUCCESS) return r; LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry) { if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW )) msi_set_last_used_source(package->ProductCode, NULL, info->context, info->options, info->value); else MsiSourceListSetInfoW(package->ProductCode, NULL, info->context, info->options, info->property, info->value); } LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry) { MsiSourceListAddMediaDiskW(package->ProductCode, NULL, disk->context, disk->options, disk->disk_id, disk->volume_label, disk->disk_prompt); } return ERROR_SUCCESS; } static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey) { MSIHANDLE hdb, suminfo; WCHAR guids[MAX_PATH]; WCHAR packcode[SQUISH_GUID_SIZE]; LPWSTR buffer; LPWSTR ptr; DWORD langid; DWORD size; UINT r; static const WCHAR szARPProductIcon[] = {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0}; static const WCHAR szAssignment[] = {'A','s','s','i','g','n','m','e','n','t',0}; static const WCHAR szAdvertiseFlags[] = {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0}; static const WCHAR szClients[] = {'C','l','i','e','n','t','s',0}; static const WCHAR szColon[] = {':',0}; buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW); msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer); msi_free(buffer); langid = msi_get_property_int(package->db, szProductLanguage, 0); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid); /* FIXME */ msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0); buffer = msi_dup_property(package->db, szARPProductIcon); if (buffer) { LPWSTR path = msi_build_icon_path(package, buffer); msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path); msi_free(path); msi_free(buffer); } buffer = msi_dup_property(package->db, szProductVersion); if (buffer) { DWORD verdword = msi_version_str_to_dword(buffer); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword); msi_free(buffer); } msi_reg_set_val_dword(hkey, szAssignment, 0); msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0); msi_reg_set_val_str(hkey, szClients, szColon); hdb = alloc_msihandle(&package->db->hdr); if (!hdb) return ERROR_NOT_ENOUGH_MEMORY; r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo); MsiCloseHandle(hdb); if (r != ERROR_SUCCESS) goto done; size = MAX_PATH; r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL, NULL, guids, &size); if (r != ERROR_SUCCESS) goto done; ptr = strchrW(guids, ';'); if (ptr) *ptr = 0; squash_guid(guids, packcode); msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode); done: MsiCloseHandle(suminfo); return ERROR_SUCCESS; } static UINT msi_publish_upgrade_code(MSIPACKAGE *package) { UINT r; HKEY hkey; LPWSTR upgrade; WCHAR squashed_pc[SQUISH_GUID_SIZE]; upgrade = msi_dup_property(package->db, szUpgradeCode); if (!upgrade) return ERROR_SUCCESS; if (package->Context == MSIINSTALLCONTEXT_MACHINE) r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE); else r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE); if (r != ERROR_SUCCESS) { WARN("failed to open upgrade code key\n"); msi_free(upgrade); return ERROR_SUCCESS; } squash_guid(package->ProductCode, squashed_pc); msi_reg_set_val_str(hkey, squashed_pc, NULL); RegCloseKey(hkey); msi_free(upgrade); return ERROR_SUCCESS; } static BOOL msi_check_publish(MSIPACKAGE *package) { MSIFEATURE *feature; LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) { feature->Action = msi_get_feature_action( package, feature ); if (feature->Action == INSTALLSTATE_LOCAL) return TRUE; } return FALSE; } static BOOL msi_check_unpublish(MSIPACKAGE *package) { MSIFEATURE *feature; LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) { feature->Action = msi_get_feature_action( package, feature ); if (feature->Action != INSTALLSTATE_ABSENT) return FALSE; } return TRUE; } static UINT msi_publish_patches( MSIPACKAGE *package ) { static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0}; WCHAR patch_squashed[GUID_SIZE]; HKEY patches_key = NULL, product_patches_key = NULL, product_key; LONG res; MSIPATCHINFO *patch; UINT r; WCHAR *p, *all_patches = NULL; DWORD len = 0; r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE ); if (r != ERROR_SUCCESS) return ERROR_FUNCTION_FAILED; res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL ); if (res != ERROR_SUCCESS) { r = ERROR_FUNCTION_FAILED; goto done; } r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE ); if (r != ERROR_SUCCESS) goto done; LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry ) { squash_guid( patch->patchcode, patch_squashed ); len += strlenW( patch_squashed ) + 1; } p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) ); if (!all_patches) goto done; LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry ) { HKEY patch_key; squash_guid( patch->patchcode, p ); p += strlenW( p ) + 1; res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ, (const BYTE *)patch->transforms, (strlenW(patch->transforms) + 1) * sizeof(WCHAR) ); if (res != ERROR_SUCCESS) goto done; r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE ); if (r != ERROR_SUCCESS) goto done; res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile, (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) ); RegCloseKey( patch_key ); if (res != ERROR_SUCCESS) goto done; if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE )) { res = GetLastError(); ERR("Unable to copy patch package %d\n", res); goto done; } res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL ); if (res != ERROR_SUCCESS) goto done; res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) ); RegCloseKey( patch_key ); if (res != ERROR_SUCCESS) goto done; } all_patches[len] = 0; res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ, (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) ); if (res != ERROR_SUCCESS) goto done; res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ, (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) ); if (res != ERROR_SUCCESS) r = ERROR_FUNCTION_FAILED; done: RegCloseKey( product_patches_key ); RegCloseKey( patches_key ); RegCloseKey( product_key ); msi_free( all_patches ); return r; } static UINT ACTION_PublishProduct(MSIPACKAGE *package) { UINT rc; HKEY hukey = NULL, hudkey = NULL; MSIRECORD *uirow; if (!list_empty(&package->patches)) { rc = msi_publish_patches(package); if (rc != ERROR_SUCCESS) goto end; } /* FIXME: also need to publish if the product is in advertise mode */ if (!msi_check_publish(package)) return ERROR_SUCCESS; rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context, &hukey, TRUE); if (rc != ERROR_SUCCESS) goto end; rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context, NULL, &hudkey, TRUE); if (rc != ERROR_SUCCESS) goto end; rc = msi_publish_upgrade_code(package); if (rc != ERROR_SUCCESS) goto end; rc = msi_publish_product_properties(package, hukey); if (rc != ERROR_SUCCESS) goto end; rc = msi_publish_sourcelist(package, hukey); if (rc != ERROR_SUCCESS) goto end; rc = msi_publish_icons(package); end: uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, package->ProductCode ); msi_ui_actiondata( package, szPublishProduct, uirow ); msiobj_release( &uirow->hdr ); RegCloseKey(hukey); RegCloseKey(hudkey); return rc; } static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row ) { WCHAR *filename, *ptr, *folder, *ret; const WCHAR *dirprop; filename = msi_dup_record_field( row, 2 ); if (filename && (ptr = strchrW( filename, '|' ))) ptr++; else ptr = filename; dirprop = MSI_RecordGetString( row, 3 ); if (dirprop) { folder = strdupW( msi_get_target_folder( package, dirprop ) ); if (!folder) folder = msi_dup_property( package->db, dirprop ); } else folder = msi_dup_property( package->db, szWindowsFolder ); if (!folder) { ERR("Unable to resolve folder %s\n", debugstr_w(dirprop)); msi_free( filename ); return NULL; } ret = msi_build_directory_name( 2, folder, ptr ); msi_free( filename ); msi_free( folder ); return ret; } static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPCWSTR component, section, key, value, identifier; LPWSTR deformated_section, deformated_key, deformated_value, fullname; MSIRECORD * uirow; INT action; MSICOMPONENT *comp; component = MSI_RecordGetString(row, 8); comp = msi_get_loaded_component(package,component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } identifier = MSI_RecordGetString(row,1); section = MSI_RecordGetString(row,4); key = MSI_RecordGetString(row,5); value = MSI_RecordGetString(row,6); action = MSI_RecordGetInteger(row,7); deformat_string(package,section,&deformated_section); deformat_string(package,key,&deformated_key); deformat_string(package,value,&deformated_value); fullname = get_ini_file_name(package, row); if (action == 0) { TRACE("Adding value %s to section %s in %s\n", debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(fullname)); WritePrivateProfileStringW(deformated_section, deformated_key, deformated_value, fullname); } else if (action == 1) { WCHAR returned[10]; GetPrivateProfileStringW(deformated_section, deformated_key, NULL, returned, 10, fullname); if (returned[0] == 0) { TRACE("Adding value %s to section %s in %s\n", debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(fullname)); WritePrivateProfileStringW(deformated_section, deformated_key, deformated_value, fullname); } } else if (action == 3) FIXME("Append to existing section not yet implemented\n"); uirow = MSI_CreateRecord(4); MSI_RecordSetStringW(uirow,1,identifier); MSI_RecordSetStringW(uirow,2,deformated_section); MSI_RecordSetStringW(uirow,3,deformated_key); MSI_RecordSetStringW(uirow,4,deformated_value); msi_ui_actiondata( package, szWriteIniValues, uirow ); msiobj_release( &uirow->hdr ); msi_free(fullname); msi_free(deformated_key); msi_free(deformated_value); msi_free(deformated_section); return ERROR_SUCCESS; } static UINT ACTION_WriteIniValues(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','i','F','i','l','e','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR component, section, key, value, identifier; LPWSTR deformated_section, deformated_key, deformated_value, filename; MSICOMPONENT *comp; MSIRECORD *uirow; INT action; component = MSI_RecordGetString( row, 8 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } identifier = MSI_RecordGetString( row, 1 ); section = MSI_RecordGetString( row, 4 ); key = MSI_RecordGetString( row, 5 ); value = MSI_RecordGetString( row, 6 ); action = MSI_RecordGetInteger( row, 7 ); deformat_string( package, section, &deformated_section ); deformat_string( package, key, &deformated_key ); deformat_string( package, value, &deformated_value ); if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine) { filename = get_ini_file_name( package, row ); TRACE("Removing key %s from section %s in %s\n", debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename)); if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename )) { WARN("Unable to remove key %u\n", GetLastError()); } msi_free( filename ); } else FIXME("Unsupported action %d\n", action); uirow = MSI_CreateRecord( 4 ); MSI_RecordSetStringW( uirow, 1, identifier ); MSI_RecordSetStringW( uirow, 2, deformated_section ); MSI_RecordSetStringW( uirow, 3, deformated_key ); MSI_RecordSetStringW( uirow, 4, deformated_value ); msi_ui_actiondata( package, szRemoveIniValues, uirow ); msiobj_release( &uirow->hdr ); msi_free( deformated_key ); msi_free( deformated_value ); msi_free( deformated_section ); return ERROR_SUCCESS; } static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR component, section, key, value, identifier; LPWSTR deformated_section, deformated_key, deformated_value, filename; MSICOMPONENT *comp; MSIRECORD *uirow; INT action; component = MSI_RecordGetString( row, 8 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } identifier = MSI_RecordGetString( row, 1 ); section = MSI_RecordGetString( row, 4 ); key = MSI_RecordGetString( row, 5 ); value = MSI_RecordGetString( row, 6 ); action = MSI_RecordGetInteger( row, 7 ); deformat_string( package, section, &deformated_section ); deformat_string( package, key, &deformated_key ); deformat_string( package, value, &deformated_value ); if (action == msidbIniFileActionRemoveLine) { filename = get_ini_file_name( package, row ); TRACE("Removing key %s from section %s in %s\n", debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename)); if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename )) { WARN("Unable to remove key %u\n", GetLastError()); } msi_free( filename ); } else FIXME("Unsupported action %d\n", action); uirow = MSI_CreateRecord( 4 ); MSI_RecordSetStringW( uirow, 1, identifier ); MSI_RecordSetStringW( uirow, 2, deformated_section ); MSI_RecordSetStringW( uirow, 3, deformated_key ); MSI_RecordSetStringW( uirow, 4, deformated_value ); msi_ui_actiondata( package, szRemoveIniValues, uirow ); msiobj_release( &uirow->hdr ); msi_free( deformated_key ); msi_free( deformated_value ); msi_free( deformated_section ); return ERROR_SUCCESS; } static UINT ACTION_RemoveIniValues( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','i','F','i','l','e','`',0}; static const WCHAR remove_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } return ERROR_SUCCESS; } static void register_dll( const WCHAR *dll, BOOL unregister ) { static const WCHAR regW[] = {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0}; static const WCHAR unregW[] = {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0}; PROCESS_INFORMATION pi; STARTUPINFOW si; WCHAR *cmd; if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return; if (unregister) sprintfW( cmd, unregW, dll ); else sprintfW( cmd, regW, dll ); memset( &si, 0, sizeof(STARTUPINFOW) ); if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi )) { CloseHandle( pi.hThread ); msi_dialog_check_messages( pi.hProcess ); CloseHandle( pi.hProcess ); } msi_free( cmd ); } static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; LPCWSTR filename; MSIFILE *file; MSIRECORD *uirow; filename = MSI_RecordGetString( row, 1 ); file = msi_get_loaded_file( package, filename ); if (!file) { WARN("unable to find file %s\n", debugstr_w(filename)); return ERROR_SUCCESS; } file->Component->Action = msi_get_component_action( package, file->Component ); if (file->Component->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component)); return ERROR_SUCCESS; } TRACE("Registering %s\n", debugstr_w( file->TargetPath )); register_dll( file->TargetPath, FALSE ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, file->File ); MSI_RecordSetStringW( uirow, 2, file->Component->Directory ); msi_ui_actiondata( package, szSelfRegModules, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_SelfRegModules(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','S','e','l','f','R','e','g','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR filename; MSIFILE *file; MSIRECORD *uirow; filename = MSI_RecordGetString( row, 1 ); file = msi_get_loaded_file( package, filename ); if (!file) { WARN("unable to find file %s\n", debugstr_w(filename)); return ERROR_SUCCESS; } file->Component->Action = msi_get_component_action( package, file->Component ); if (file->Component->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component)); return ERROR_SUCCESS; } TRACE("Unregistering %s\n", debugstr_w( file->TargetPath )); register_dll( file->TargetPath, TRUE ); uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, file->File ); MSI_RecordSetStringW( uirow, 2, file->Component->Directory ); msi_ui_actiondata( package, szSelfUnregModules, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_SelfUnregModules( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','S','e','l','f','R','e','g','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package ); msiobj_release( &view->hdr ); return rc; } static UINT ACTION_PublishFeatures(MSIPACKAGE *package) { MSIFEATURE *feature; UINT rc; HKEY hkey = NULL, userdata = NULL; if (!msi_check_publish(package)) return ERROR_SUCCESS; rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context, &hkey, TRUE); if (rc != ERROR_SUCCESS) goto end; rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context, &userdata, TRUE); if (rc != ERROR_SUCCESS) goto end; /* here the guids are base 85 encoded */ LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { ComponentList *cl; LPWSTR data = NULL; GUID clsid; INT size; BOOL absent = FALSE; MSIRECORD *uirow; if (feature->Action != INSTALLSTATE_LOCAL && feature->Action != INSTALLSTATE_SOURCE && feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE; size = 1; LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { size += 21; } if (feature->Feature_Parent) size += strlenW( feature->Feature_Parent )+2; data = msi_alloc(size * sizeof(WCHAR)); data[0] = 0; LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) { MSICOMPONENT* component = cl->component; WCHAR buf[21]; buf[0] = 0; if (component->ComponentId) { TRACE("From %s\n",debugstr_w(component->ComponentId)); CLSIDFromString(component->ComponentId, &clsid); encode_base85_guid(&clsid,buf); TRACE("to %s\n",debugstr_w(buf)); strcatW(data,buf); } } if (feature->Feature_Parent) { static const WCHAR sep[] = {'\2',0}; strcatW(data,sep); strcatW(data,feature->Feature_Parent); } msi_reg_set_val_str( userdata, feature->Feature, data ); msi_free(data); size = 0; if (feature->Feature_Parent) size = strlenW(feature->Feature_Parent)*sizeof(WCHAR); if (!absent) { size += sizeof(WCHAR); RegSetValueExW(hkey,feature->Feature,0,REG_SZ, (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size); } else { size += 2*sizeof(WCHAR); data = msi_alloc(size); data[0] = 0x6; data[1] = 0; if (feature->Feature_Parent) strcpyW( &data[1], feature->Feature_Parent ); RegSetValueExW(hkey,feature->Feature,0,REG_SZ, (LPBYTE)data,size); msi_free(data); } /* the UI chunk */ uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, feature->Feature ); msi_ui_actiondata( package, szPublishFeatures, uirow ); msiobj_release( &uirow->hdr ); /* FIXME: call msi_ui_progress? */ } end: RegCloseKey(hkey); RegCloseKey(userdata); return rc; } static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature) { UINT r; HKEY hkey; MSIRECORD *uirow; TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature)); r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context, &hkey, FALSE); if (r == ERROR_SUCCESS) { RegDeleteValueW(hkey, feature->Feature); RegCloseKey(hkey); } r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context, &hkey, FALSE); if (r == ERROR_SUCCESS) { RegDeleteValueW(hkey, feature->Feature); RegCloseKey(hkey); } uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, feature->Feature ); msi_ui_actiondata( package, szUnpublishFeatures, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package) { MSIFEATURE *feature; if (!msi_check_unpublish(package)) return ERROR_SUCCESS; LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) { msi_unpublish_feature(package, feature); } return ERROR_SUCCESS; } static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey) { SYSTEMTIME systime; DWORD size, langid; WCHAR date[9], *val, *buffer; const WCHAR *prop, *key; static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0}; static const WCHAR modpath_fmt[] = {'M','s','i','E','x','e','c','.','e','x','e',' ', '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0}; static const WCHAR szModifyPath[] = {'M','o','d','i','f','y','P','a','t','h',0}; static const WCHAR szUninstallString[] = {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0}; static const WCHAR szEstimatedSize[] = {'E','s','t','i','m','a','t','e','d','S','i','z','e',0}; static const WCHAR szDisplayVersion[] = {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; static const WCHAR szInstallSource[] = {'I','n','s','t','a','l','l','S','o','u','r','c','e',0}; static const WCHAR szARPAUTHORIZEDCDFPREFIX[] = {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0}; static const WCHAR szAuthorizedCDFPrefix[] = {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0}; static const WCHAR szARPCONTACT[] = {'A','R','P','C','O','N','T','A','C','T',0}; static const WCHAR szContact[] = {'C','o','n','t','a','c','t',0}; static const WCHAR szARPCOMMENTS[] = {'A','R','P','C','O','M','M','E','N','T','S',0}; static const WCHAR szComments[] = {'C','o','m','m','e','n','t','s',0}; static const WCHAR szProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0}; static const WCHAR szDisplayName[] = {'D','i','s','p','l','a','y','N','a','m','e',0}; static const WCHAR szARPHELPLINK[] = {'A','R','P','H','E','L','P','L','I','N','K',0}; static const WCHAR szHelpLink[] = {'H','e','l','p','L','i','n','k',0}; static const WCHAR szARPHELPTELEPHONE[] = {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0}; static const WCHAR szHelpTelephone[] = {'H','e','l','p','T','e','l','e','p','h','o','n','e',0}; static const WCHAR szARPINSTALLLOCATION[] = {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0}; static const WCHAR szManufacturer[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0}; static const WCHAR szPublisher[] = {'P','u','b','l','i','s','h','e','r',0}; static const WCHAR szARPREADME[] = {'A','R','P','R','E','A','D','M','E',0}; static const WCHAR szReadme[] = {'R','e','a','d','M','e',0}; static const WCHAR szARPSIZE[] = {'A','R','P','S','I','Z','E',0}; static const WCHAR szSize[] = {'S','i','z','e',0}; static const WCHAR szARPURLINFOABOUT[] = {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0}; static const WCHAR szURLInfoAbout[] = {'U','R','L','I','n','f','o','A','b','o','u','t',0}; static const WCHAR szARPURLUPDATEINFO[] = {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0}; static const WCHAR szURLUpdateInfo[] = {'U','R','L','U','p','d','a','t','e','I','n','f','o',0}; static const WCHAR szARPSYSTEMCOMPONENT[] = {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0}; static const WCHAR szSystemComponent[] = {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0}; static const WCHAR *propval[] = { szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix, szARPCONTACT, szContact, szARPCOMMENTS, szComments, szProductName, szDisplayName, szARPHELPLINK, szHelpLink, szARPHELPTELEPHONE, szHelpTelephone, szARPINSTALLLOCATION, szInstallLocation, szSourceDir, szInstallSource, szManufacturer, szPublisher, szARPREADME, szReadme, szARPSIZE, szSize, szARPURLINFOABOUT, szURLInfoAbout, szARPURLUPDATEINFO, szURLUpdateInfo, NULL }; const WCHAR **p = propval; while (*p) { prop = *p++; key = *p++; val = msi_dup_property(package->db, prop); msi_reg_set_val_str(hkey, key, val); msi_free(val); } msi_reg_set_val_dword(hkey, szWindowsInstaller, 1); if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 )) { msi_reg_set_val_dword( hkey, szSystemComponent, 1 ); } size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR); RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size); RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size); msi_free(buffer); /* FIXME: Write real Estimated Size when we have it */ msi_reg_set_val_dword(hkey, szEstimatedSize, 0); GetLocalTime(&systime); sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay); msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date); langid = msi_get_property_int(package->db, szProductLanguage, 0); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid); buffer = msi_dup_property(package->db, szProductVersion); msi_reg_set_val_str(hkey, szDisplayVersion, buffer); if (buffer) { DWORD verdword = msi_version_str_to_dword(buffer); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24); msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF); msi_free(buffer); } return ERROR_SUCCESS; } static UINT ACTION_RegisterProduct(MSIPACKAGE *package) { WCHAR squashed_pc[SQUISH_GUID_SIZE]; MSIRECORD *uirow; LPWSTR upgrade_code; HKEY hkey, props, upgrade_key; UINT rc; /* FIXME: also need to publish if the product is in advertise mode */ if (!msi_check_publish(package)) return ERROR_SUCCESS; rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE); if (rc != ERROR_SUCCESS) return rc; rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE); if (rc != ERROR_SUCCESS) goto done; rc = msi_publish_install_properties(package, hkey); if (rc != ERROR_SUCCESS) goto done; rc = msi_publish_install_properties(package, props); if (rc != ERROR_SUCCESS) goto done; upgrade_code = msi_dup_property(package->db, szUpgradeCode); if (upgrade_code) { rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE ); if (rc == ERROR_SUCCESS) { squash_guid( package->ProductCode, squashed_pc ); msi_reg_set_val_str( upgrade_key, squashed_pc, NULL ); RegCloseKey( upgrade_key ); } msi_free( upgrade_code ); } msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile ); package->delete_on_close = FALSE; done: uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, package->ProductCode ); msi_ui_actiondata( package, szRegisterProduct, uirow ); msiobj_release( &uirow->hdr ); RegCloseKey(hkey); return ERROR_SUCCESS; } static UINT ACTION_InstallExecute(MSIPACKAGE *package) { return execute_script(package, SCRIPT_INSTALL); } static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param ) { MSIPACKAGE *package = param; const WCHAR *icon = MSI_RecordGetString( row, 1 ); WCHAR *p, *icon_path; if (!icon) return ERROR_SUCCESS; if ((icon_path = msi_build_icon_path( package, icon ))) { TRACE("removing icon file %s\n", debugstr_w(icon_path)); DeleteFileW( icon_path ); if ((p = strrchrW( icon_path, '\\' ))) { *p = 0; RemoveDirectoryW( icon_path ); } msi_free( icon_path ); } return ERROR_SUCCESS; } static UINT msi_unpublish_icons( MSIPACKAGE *package ) { static const WCHAR query[]= { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0}; MSIQUERY *view; UINT r; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; } return ERROR_SUCCESS; } static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove ) { static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0}; WCHAR *upgrade, **features; BOOL full_uninstall = TRUE; MSIFEATURE *feature; MSIPATCHINFO *patch; UINT i; LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) { if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE; } features = msi_split_string( remove, ',' ); for (i = 0; features && features[i]; i++) { if (!strcmpW( features[i], szAll )) full_uninstall = TRUE; } msi_free(features); if (!full_uninstall) return ERROR_SUCCESS; MSIREG_DeleteProductKey(package->ProductCode); MSIREG_DeleteUserDataProductKey(package->ProductCode); MSIREG_DeleteUninstallKey(package->ProductCode, package->platform); MSIREG_DeleteLocalClassesProductKey(package->ProductCode); MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode); MSIREG_DeleteUserProductKey(package->ProductCode); MSIREG_DeleteUserFeaturesKey(package->ProductCode); upgrade = msi_dup_property(package->db, szUpgradeCode); if (upgrade) { MSIREG_DeleteUserUpgradeCodesKey(upgrade); MSIREG_DeleteClassesUpgradeCodesKey(upgrade); msi_free(upgrade); } LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry) { MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context); if (!strcmpW( package->ProductCode, patch->products )) { TRACE("removing local patch package %s\n", debugstr_w(patch->localfile)); patch->delete_on_close = TRUE; } /* FIXME: remove local patch package if this is the last product */ } TRACE("removing local package %s\n", debugstr_w(package->localfile)); package->delete_on_close = TRUE; msi_unpublish_icons( package ); return ERROR_SUCCESS; } static UINT ACTION_InstallFinalize(MSIPACKAGE *package) { UINT rc; WCHAR *remove; /* turn off scheduling */ package->script->CurrentlyScripting= FALSE; /* first do the same as an InstallExecute */ rc = ACTION_InstallExecute(package); if (rc != ERROR_SUCCESS) return rc; /* then handle commit actions */ rc = execute_script(package, SCRIPT_COMMIT); if (rc != ERROR_SUCCESS) return rc; remove = msi_dup_property(package->db, szRemove); rc = msi_unpublish_product(package, remove); msi_free(remove); return rc; } UINT ACTION_ForceReboot(MSIPACKAGE *package) { static const WCHAR RunOnce[] = { 'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\', 'W','i','n','d','o','w','s','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'R','u','n','O','n','c','e',0}; static const WCHAR InstallRunOnce[] = { 'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\', 'W','i','n','d','o','w','s','\\', 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'I','n','s','t','a','l','l','e','r','\\', 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0}; static const WCHAR msiexec_fmt[] = { '%','s', '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ', '\"','%','s','\"',0}; static const WCHAR install_fmt[] = { '/','I',' ','\"','%','s','\"',' ', 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ', 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0}; WCHAR buffer[256], sysdir[MAX_PATH]; HKEY hkey; WCHAR squished_pc[100]; squash_guid(package->ProductCode,squished_pc); GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0])); RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey); snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir, squished_pc); msi_reg_set_val_str( hkey, squished_pc, buffer ); RegCloseKey(hkey); TRACE("Reboot command %s\n",debugstr_w(buffer)); RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey); sprintfW(buffer,install_fmt,package->ProductCode,squished_pc); msi_reg_set_val_str( hkey, squished_pc, buffer ); RegCloseKey(hkey); return ERROR_INSTALL_SUSPEND; } WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... ) { static const WCHAR query[] = {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ', 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ', '`','E','r','r','o','r','`',' ','=',' ','%','i',0}; MSIRECORD *rec, *row; DWORD i, size = 0; va_list va; const WCHAR *str; WCHAR *data; if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0; rec = MSI_CreateRecord( count + 2 ); str = MSI_RecordGetString( row, 1 ); MSI_RecordSetStringW( rec, 0, str ); msiobj_release( &row->hdr ); MSI_RecordSetInteger( rec, 1, error ); va_start( va, count ); for (i = 0; i < count; i++) { str = va_arg( va, const WCHAR *); MSI_RecordSetStringW( rec, i + 2, str ); } va_end( va ); MSI_FormatRecordW( package, rec, NULL, &size ); size++; data = msi_alloc( size * sizeof(WCHAR) ); if (size > 1) MSI_FormatRecordW( package, rec, data, &size ); else data[0] = 0; msiobj_release( &rec->hdr ); return data; } static UINT ACTION_ResolveSource(MSIPACKAGE* package) { DWORD attrib; UINT rc; /* * We are currently doing what should be done here in the top level Install * however for Administrative and uninstalls this step will be needed */ if (!package->PackagePath) return ERROR_SUCCESS; msi_set_sourcedir_props(package, TRUE); attrib = GetFileAttributesW(package->db->path); if (attrib == INVALID_FILE_ATTRIBUTES) { LPWSTR prompt, msg; DWORD size = 0; rc = MsiSourceListGetInfoW(package->ProductCode, NULL, package->Context, MSICODE_PRODUCT, INSTALLPROPERTY_DISKPROMPTW,NULL,&size); if (rc == ERROR_MORE_DATA) { prompt = msi_alloc(size * sizeof(WCHAR)); MsiSourceListGetInfoW(package->ProductCode, NULL, package->Context, MSICODE_PRODUCT, INSTALLPROPERTY_DISKPROMPTW,prompt,&size); } else prompt = strdupW(package->db->path); msg = msi_build_error_string(package, 1302, 1, prompt); msi_free(prompt); while(attrib == INVALID_FILE_ATTRIBUTES) { rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL); if (rc == IDCANCEL) { msi_free(msg); return ERROR_INSTALL_USEREXIT; } attrib = GetFileAttributesW(package->db->path); } msi_free(msg); rc = ERROR_SUCCESS; } else return ERROR_SUCCESS; return rc; } static UINT ACTION_RegisterUser(MSIPACKAGE *package) { HKEY hkey = 0; LPWSTR buffer, productid = NULL; UINT i, rc = ERROR_SUCCESS; MSIRECORD *uirow; static const WCHAR szPropKeys[][80] = { {'P','r','o','d','u','c','t','I','D',0}, {'U','S','E','R','N','A','M','E',0}, {'C','O','M','P','A','N','Y','N','A','M','E',0}, {0}, }; static const WCHAR szRegKeys[][80] = { {'P','r','o','d','u','c','t','I','D',0}, {'R','e','g','O','w','n','e','r',0}, {'R','e','g','C','o','m','p','a','n','y',0}, {0}, }; if (msi_check_unpublish(package)) { MSIREG_DeleteUserDataProductKey(package->ProductCode); goto end; } productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW ); if (!productid) goto end; rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &hkey, TRUE); if (rc != ERROR_SUCCESS) goto end; for( i = 0; szPropKeys[i][0]; i++ ) { buffer = msi_dup_property( package->db, szPropKeys[i] ); msi_reg_set_val_str( hkey, szRegKeys[i], buffer ); msi_free( buffer ); } end: uirow = MSI_CreateRecord( 1 ); MSI_RecordSetStringW( uirow, 1, productid ); msi_ui_actiondata( package, szRegisterUser, uirow ); msiobj_release( &uirow->hdr ); msi_free(productid); RegCloseKey(hkey); return rc; } static UINT ACTION_ExecuteAction(MSIPACKAGE *package) { UINT rc; package->script->InWhatSequence |= SEQUENCE_EXEC; rc = ACTION_ProcessExecSequence(package,FALSE); return rc; } WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature ) { static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0}; WCHAR productid_85[21], component_85[21], *ret; GUID clsid; DWORD sz; /* > is used if there is a component GUID and < if not. */ productid_85[0] = 0; component_85[0] = 0; CLSIDFromString( package->ProductCode, &clsid ); encode_base85_guid( &clsid, productid_85 ); if (component) { CLSIDFromString( component->ComponentId, &clsid ); encode_base85_guid( &clsid, component_85 ); } TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature), debugstr_w(component_85)); sz = 20 + strlenW( feature ) + 20 + 3; ret = msi_alloc_zero( sz * sizeof(WCHAR) ); if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 ); return ret; } static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param) { MSIPACKAGE *package = param; LPCWSTR compgroupid, component, feature, qualifier, text; LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q; HKEY hkey = NULL; UINT rc; MSICOMPONENT *comp; MSIFEATURE *feat; DWORD sz; MSIRECORD *uirow; int len; feature = MSI_RecordGetString(rec, 5); feat = msi_get_loaded_feature(package, feature); if (!feat) return ERROR_SUCCESS; feat->Action = msi_get_feature_action( package, feat ); if (feat->Action != INSTALLSTATE_LOCAL && feat->Action != INSTALLSTATE_SOURCE && feat->Action != INSTALLSTATE_ADVERTISED) { TRACE("feature not scheduled for installation %s\n", debugstr_w(feature)); return ERROR_SUCCESS; } component = MSI_RecordGetString(rec, 3); comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; compgroupid = MSI_RecordGetString(rec,1); qualifier = MSI_RecordGetString(rec,2); rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE); if (rc != ERROR_SUCCESS) goto end; advertise = msi_create_component_advertise_string( package, comp, feature ); text = MSI_RecordGetString( rec, 4 ); if (text) { p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) ); strcpyW( p, advertise ); strcatW( p, text ); msi_free( advertise ); advertise = p; } existing = msi_reg_get_val_str( hkey, qualifier ); sz = strlenW( advertise ) + 1; if (existing) { for (p = existing; *p; p += len) { len = strlenW( p ) + 1; if (strcmpW( advertise, p )) sz += len; } } if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) ))) { rc = ERROR_OUTOFMEMORY; goto end; } q = output; if (existing) { for (p = existing; *p; p += len) { len = strlenW( p ) + 1; if (strcmpW( advertise, p )) { memcpy( q, p, len * sizeof(WCHAR) ); q += len; } } } strcpyW( q, advertise ); q[strlenW( q ) + 1] = 0; msi_reg_set_val_multi_str( hkey, qualifier, output ); end: RegCloseKey(hkey); msi_free( output ); msi_free( advertise ); msi_free( existing ); /* the UI chunk */ uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, compgroupid ); MSI_RecordSetStringW( uirow, 2, qualifier); msi_ui_actiondata( package, szPublishComponents, uirow ); msiobj_release( &uirow->hdr ); /* FIXME: call ui_progress? */ return rc; } /* * At present I am ignorning the advertised components part of this and only * focusing on the qualified component sets */ static UINT ACTION_PublishComponents(MSIPACKAGE *package) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param ) { static const WCHAR szInstallerComponents[] = { 'S','o','f','t','w','a','r','e','\\', 'M','i','c','r','o','s','o','f','t','\\', 'I','n','s','t','a','l','l','e','r','\\', 'C','o','m','p','o','n','e','n','t','s','\\',0}; MSIPACKAGE *package = param; LPCWSTR compgroupid, component, feature, qualifier; MSICOMPONENT *comp; MSIFEATURE *feat; MSIRECORD *uirow; WCHAR squashed[GUID_SIZE], keypath[MAX_PATH]; LONG res; feature = MSI_RecordGetString( rec, 5 ); feat = msi_get_loaded_feature( package, feature ); if (!feat) return ERROR_SUCCESS; feat->Action = msi_get_feature_action( package, feat ); if (feat->Action != INSTALLSTATE_ABSENT) { TRACE("feature not scheduled for removal %s\n", debugstr_w(feature)); return ERROR_SUCCESS; } component = MSI_RecordGetString( rec, 3 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; compgroupid = MSI_RecordGetString( rec, 1 ); qualifier = MSI_RecordGetString( rec, 2 ); squash_guid( compgroupid, squashed ); strcpyW( keypath, szInstallerComponents ); strcatW( keypath, squashed ); res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath ); if (res != ERROR_SUCCESS) { WARN("Unable to delete component key %d\n", res); } uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, compgroupid ); MSI_RecordSetStringW( uirow, 2, qualifier ); msi_ui_actiondata( package, szUnpublishComponents, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_UnpublishComponents( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package ); msiobj_release( &view->hdr ); return rc; } static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param) { static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ', '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0}; MSIPACKAGE *package = param; MSICOMPONENT *component; MSIRECORD *row; MSIFILE *file; SC_HANDLE hscm = NULL, service = NULL; LPCWSTR comp, key; LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL; LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL; DWORD serv_type, start_type, err_control; SERVICE_DESCRIPTIONW sd = {NULL}; UINT ret = ERROR_SUCCESS; comp = MSI_RecordGetString( rec, 12 ); component = msi_get_loaded_component( package, comp ); if (!component) { WARN("service component not found\n"); goto done; } component->Action = msi_get_component_action( package, component ); if (component->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(comp)); goto done; } hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE); if (!hscm) { ERR("Failed to open the SC Manager!\n"); goto done; } start_type = MSI_RecordGetInteger(rec, 5); if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START) goto done; deformat_string(package, MSI_RecordGetString(rec, 2), &name); deformat_string(package, MSI_RecordGetString(rec, 3), &disp); serv_type = MSI_RecordGetInteger(rec, 4); err_control = MSI_RecordGetInteger(rec, 6); deformat_string(package, MSI_RecordGetString(rec, 7), &load_order); deformat_string(package, MSI_RecordGetString(rec, 8), &depends); deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name); deformat_string(package, MSI_RecordGetString(rec, 10), &pass); deformat_string(package, MSI_RecordGetString(rec, 11), &args); deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription); /* fetch the service path */ row = MSI_QueryGetRecord(package->db, query, comp); if (!row) { ERR("Query failed\n"); goto done; } key = MSI_RecordGetString(row, 6); file = msi_get_loaded_file(package, key); msiobj_release(&row->hdr); if (!file) { ERR("Failed to load the service file\n"); goto done; } if (!args || !args[0]) image_path = file->TargetPath; else { int len = strlenW(file->TargetPath) + strlenW(args) + 2; if (!(image_path = msi_alloc(len * sizeof(WCHAR)))) { ret = ERROR_OUTOFMEMORY; goto done; } strcpyW(image_path, file->TargetPath); strcatW(image_path, szSpace); strcatW(image_path, args); } service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type, start_type, err_control, image_path, load_order, NULL, depends, serv_name, pass); if (!service) { if (GetLastError() != ERROR_SERVICE_EXISTS) ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError()); } else if (sd.lpDescription) { if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd)) WARN("failed to set service description %u\n", GetLastError()); } if (image_path != file->TargetPath) msi_free(image_path); done: CloseServiceHandle(service); CloseServiceHandle(hscm); msi_free(name); msi_free(disp); msi_free(sd.lpDescription); msi_free(load_order); msi_free(serv_name); msi_free(pass); msi_free(depends); msi_free(args); return ret; } static UINT ACTION_InstallServices( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package); msiobj_release(&view->hdr); return rc; } /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */ static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs) { LPCWSTR *vector, *temp_vector; LPWSTR p, q; DWORD sep_len; static const WCHAR separator[] = {'[','~',']',0}; *numargs = 0; sep_len = sizeof(separator) / sizeof(WCHAR) - 1; if (!args) return NULL; vector = msi_alloc(sizeof(LPWSTR)); if (!vector) return NULL; p = args; do { (*numargs)++; vector[*numargs - 1] = p; if ((q = strstrW(p, separator))) { *q = '\0'; temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR)); if (!temp_vector) { msi_free(vector); return NULL; } vector = temp_vector; p = q + sep_len; } } while (q); return vector; } static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; SC_HANDLE scm = NULL, service = NULL; LPCWSTR component, *vector = NULL; LPWSTR name, args, display_name = NULL; DWORD event, numargs, len, wait, dummy; UINT r = ERROR_FUNCTION_FAILED; SERVICE_STATUS_PROCESS status; ULONGLONG start_time; component = MSI_RecordGetString(rec, 6); comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } deformat_string(package, MSI_RecordGetString(rec, 2), &name); deformat_string(package, MSI_RecordGetString(rec, 4), &args); event = MSI_RecordGetInteger(rec, 3); wait = MSI_RecordGetInteger(rec, 5); if (!(event & msidbServiceControlEventStart)) { r = ERROR_SUCCESS; goto done; } scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (!scm) { ERR("Failed to open the service control manager\n"); goto done; } len = 0; if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) GetServiceDisplayNameW( scm, name, display_name, &len ); } service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS); if (!service) { ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError()); goto done; } vector = msi_service_args_to_vector(args, &numargs); if (!StartServiceW(service, numargs, vector) && GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) { ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError()); goto done; } r = ERROR_SUCCESS; if (wait) { /* wait for at most 30 seconds for the service to be up and running */ if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy)) { TRACE("failed to query service status (%u)\n", GetLastError()); goto done; } start_time = GetTickCount64(); while (status.dwCurrentState == SERVICE_START_PENDING) { if (GetTickCount64() - start_time > 30000) break; Sleep(1000); if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy)) { TRACE("failed to query service status (%u)\n", GetLastError()); goto done; } } if (status.dwCurrentState != SERVICE_RUNNING) { WARN("service failed to start %u\n", status.dwCurrentState); r = ERROR_FUNCTION_FAILED; } } done: uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, display_name ); MSI_RecordSetStringW( uirow, 2, name ); msi_ui_actiondata( package, szStartServices, uirow ); msiobj_release( &uirow->hdr ); CloseServiceHandle(service); CloseServiceHandle(scm); msi_free(name); msi_free(args); msi_free(vector); msi_free(display_name); return r; } static UINT ACTION_StartServices( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package); msiobj_release(&view->hdr); return rc; } static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service) { DWORD i, needed, count; ENUM_SERVICE_STATUSW *dependencies; SERVICE_STATUS ss; SC_HANDLE depserv; BOOL stopped, ret = FALSE; if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL, 0, &needed, &count)) return TRUE; if (GetLastError() != ERROR_MORE_DATA) return FALSE; dependencies = msi_alloc(needed); if (!dependencies) return FALSE; if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies, needed, &needed, &count)) goto done; for (i = 0; i < count; i++) { depserv = OpenServiceW(scm, dependencies[i].lpServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS); if (!depserv) goto done; stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss); CloseServiceHandle(depserv); if (!stopped) goto done; } ret = TRUE; done: msi_free(dependencies); return ret; } static UINT stop_service( LPCWSTR name ) { SC_HANDLE scm = NULL, service = NULL; SERVICE_STATUS status; SERVICE_STATUS_PROCESS ssp; DWORD needed; scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!scm) { WARN("Failed to open the SCM: %d\n", GetLastError()); goto done; } service = OpenServiceW(scm, name, SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS); if (!service) { WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError()); goto done; } if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed)) { WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError()); goto done; } if (ssp.dwCurrentState == SERVICE_STOPPED) goto done; stop_service_dependents(scm, service); if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError()); done: CloseServiceHandle(service); CloseServiceHandle(scm); return ERROR_SUCCESS; } static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; LPCWSTR component; LPWSTR name = NULL, display_name = NULL; DWORD event, len; SC_HANDLE scm; event = MSI_RecordGetInteger( rec, 3 ); if (!(event & msidbServiceControlEventStop)) return ERROR_SUCCESS; component = MSI_RecordGetString( rec, 6 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ); if (!scm) { ERR("Failed to open the service control manager\n"); goto done; } len = 0; if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) GetServiceDisplayNameW( scm, name, display_name, &len ); } CloseServiceHandle( scm ); deformat_string( package, MSI_RecordGetString( rec, 2 ), &name ); stop_service( name ); done: uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, display_name ); MSI_RecordSetStringW( uirow, 2, name ); msi_ui_actiondata( package, szStopServices, uirow ); msiobj_release( &uirow->hdr ); msi_free( name ); msi_free( display_name ); return ERROR_SUCCESS; } static UINT ACTION_StopServices( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; LPWSTR name = NULL, display_name = NULL; DWORD event, len; SC_HANDLE scm = NULL, service = NULL; comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) ); if (!comp) return ERROR_SUCCESS; event = MSI_RecordGetInteger( rec, 3 ); deformat_string( package, MSI_RecordGetString(rec, 2), &name ); comp->Action = msi_get_component_action( package, comp ); if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) && !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete))) { TRACE("service %s not scheduled for removal\n", debugstr_w(name)); msi_free( name ); return ERROR_SUCCESS; } stop_service( name ); scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if (!scm) { WARN("Failed to open the SCM: %d\n", GetLastError()); goto done; } len = 0; if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) GetServiceDisplayNameW( scm, name, display_name, &len ); } service = OpenServiceW( scm, name, DELETE ); if (!service) { WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError()); goto done; } if (!DeleteService( service )) WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError()); done: uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, display_name ); MSI_RecordSetStringW( uirow, 2, name ); msi_ui_actiondata( package, szDeleteServices, uirow ); msiobj_release( &uirow->hdr ); CloseServiceHandle( service ); CloseServiceHandle( scm ); msi_free( name ); msi_free( display_name ); return ERROR_SUCCESS; } static UINT ACTION_DeleteServices( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package ); msiobj_release( &view->hdr ); return rc; } static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; LPWSTR driver, driver_path, ptr; WCHAR outpath[MAX_PATH]; MSIFILE *driver_file = NULL, *setup_file = NULL; MSICOMPONENT *comp; MSIRECORD *uirow; LPCWSTR desc, file_key, component; DWORD len, usage; UINT r = ERROR_SUCCESS; static const WCHAR driver_fmt[] = { 'D','r','i','v','e','r','=','%','s',0}; static const WCHAR setup_fmt[] = { 'S','e','t','u','p','=','%','s',0}; static const WCHAR usage_fmt[] = { 'F','i','l','e','U','s','a','g','e','=','1',0}; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString(rec, 3); file_key = MSI_RecordGetString( rec, 4 ); if (file_key) driver_file = msi_get_loaded_file( package, file_key ); file_key = MSI_RecordGetString( rec, 5 ); if (file_key) setup_file = msi_get_loaded_file( package, file_key ); if (!driver_file) { ERR("ODBC Driver entry not found!\n"); return ERROR_FUNCTION_FAILED; } len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName); if (setup_file) len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName); len += lstrlenW(usage_fmt) + 2; /* \0\0 */ driver = msi_alloc(len * sizeof(WCHAR)); if (!driver) return ERROR_OUTOFMEMORY; ptr = driver; lstrcpyW(ptr, desc); ptr += lstrlenW(ptr) + 1; len = sprintfW(ptr, driver_fmt, driver_file->FileName); ptr += len + 1; if (setup_file) { len = sprintfW(ptr, setup_fmt, setup_file->FileName); ptr += len + 1; } lstrcpyW(ptr, usage_fmt); ptr += lstrlenW(ptr) + 1; *ptr = '\0'; if (!driver_file->TargetPath) { const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory ); driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName ); } driver_path = strdupW(driver_file->TargetPath); ptr = strrchrW(driver_path, '\\'); if (ptr) *ptr = '\0'; if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH, NULL, ODBC_INSTALL_COMPLETE, &usage)) { ERR("Failed to install SQL driver!\n"); r = ERROR_FUNCTION_FAILED; } uirow = MSI_CreateRecord( 5 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory ); msi_ui_actiondata( package, szInstallODBC, uirow ); msiobj_release( &uirow->hdr ); msi_free(driver); msi_free(driver_path); return r; } static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; LPWSTR translator, translator_path, ptr; WCHAR outpath[MAX_PATH]; MSIFILE *translator_file = NULL, *setup_file = NULL; MSICOMPONENT *comp; MSIRECORD *uirow; LPCWSTR desc, file_key, component; DWORD len, usage; UINT r = ERROR_SUCCESS; static const WCHAR translator_fmt[] = { 'T','r','a','n','s','l','a','t','o','r','=','%','s',0}; static const WCHAR setup_fmt[] = { 'S','e','t','u','p','=','%','s',0}; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString(rec, 3); file_key = MSI_RecordGetString( rec, 4 ); if (file_key) translator_file = msi_get_loaded_file( package, file_key ); file_key = MSI_RecordGetString( rec, 5 ); if (file_key) setup_file = msi_get_loaded_file( package, file_key ); if (!translator_file) { ERR("ODBC Translator entry not found!\n"); return ERROR_FUNCTION_FAILED; } len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */ if (setup_file) len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName); translator = msi_alloc(len * sizeof(WCHAR)); if (!translator) return ERROR_OUTOFMEMORY; ptr = translator; lstrcpyW(ptr, desc); ptr += lstrlenW(ptr) + 1; len = sprintfW(ptr, translator_fmt, translator_file->FileName); ptr += len + 1; if (setup_file) { len = sprintfW(ptr, setup_fmt, setup_file->FileName); ptr += len + 1; } *ptr = '\0'; translator_path = strdupW(translator_file->TargetPath); ptr = strrchrW(translator_path, '\\'); if (ptr) *ptr = '\0'; if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH, NULL, ODBC_INSTALL_COMPLETE, &usage)) { ERR("Failed to install SQL translator!\n"); r = ERROR_FUNCTION_FAILED; } uirow = MSI_CreateRecord( 5 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory ); msi_ui_actiondata( package, szInstallODBC, uirow ); msiobj_release( &uirow->hdr ); msi_free(translator); msi_free(translator_path); return r; } static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; LPWSTR attrs; LPCWSTR desc, driver, component; WORD request = ODBC_ADD_SYS_DSN; INT registration; DWORD len; UINT r = ERROR_SUCCESS; MSIRECORD *uirow; static const WCHAR attrs_fmt[] = { 'D','S','N','=','%','s',0 }; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString(rec, 3); driver = MSI_RecordGetString(rec, 4); registration = MSI_RecordGetInteger(rec, 5); if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN; else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN; len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */ attrs = msi_alloc(len * sizeof(WCHAR)); if (!attrs) return ERROR_OUTOFMEMORY; len = sprintfW(attrs, attrs_fmt, desc); attrs[len + 1] = 0; if (!SQLConfigDataSourceW(NULL, request, driver, attrs)) { ERR("Failed to install SQL data source!\n"); r = ERROR_FUNCTION_FAILED; } uirow = MSI_CreateRecord( 5 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); MSI_RecordSetInteger( uirow, 3, request ); msi_ui_actiondata( package, szInstallODBC, uirow ); msiobj_release( &uirow->hdr ); msi_free(attrs); return r; } static UINT ACTION_InstallODBC( MSIPACKAGE *package ) { static const WCHAR driver_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','D','r','i','v','e','r',0}; static const WCHAR translator_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; static const WCHAR source_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package); msiobj_release(&view->hdr); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package); msiobj_release(&view->hdr); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW(package->db, source_query, &view); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package); msiobj_release(&view->hdr); if (rc != ERROR_SUCCESS) return rc; } return ERROR_SUCCESS; } static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; DWORD usage; LPCWSTR desc, component; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString( rec, 3 ); if (!SQLRemoveDriverW( desc, FALSE, &usage )) { WARN("Failed to remove ODBC driver\n"); } else if (!usage) { FIXME("Usage count reached 0\n"); } uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); msi_ui_actiondata( package, szRemoveODBC, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; DWORD usage; LPCWSTR desc, component; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString( rec, 3 ); if (!SQLRemoveTranslatorW( desc, &usage )) { WARN("Failed to remove ODBC translator\n"); } else if (!usage) { FIXME("Usage count reached 0\n"); } uirow = MSI_CreateRecord( 2 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); msi_ui_actiondata( package, szRemoveODBC, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; MSICOMPONENT *comp; MSIRECORD *uirow; LPWSTR attrs; LPCWSTR desc, driver, component; WORD request = ODBC_REMOVE_SYS_DSN; INT registration; DWORD len; static const WCHAR attrs_fmt[] = { 'D','S','N','=','%','s',0 }; component = MSI_RecordGetString( rec, 2 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } desc = MSI_RecordGetString( rec, 3 ); driver = MSI_RecordGetString( rec, 4 ); registration = MSI_RecordGetInteger( rec, 5 ); if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN; else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN; len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */ attrs = msi_alloc( len * sizeof(WCHAR) ); if (!attrs) return ERROR_OUTOFMEMORY; FIXME("Use ODBCSourceAttribute table\n"); len = sprintfW( attrs, attrs_fmt, desc ); attrs[len + 1] = 0; if (!SQLConfigDataSourceW( NULL, request, driver, attrs )) { WARN("Failed to remove ODBC data source\n"); } msi_free( attrs ); uirow = MSI_CreateRecord( 3 ); MSI_RecordSetStringW( uirow, 1, desc ); MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); MSI_RecordSetInteger( uirow, 3, request ); msi_ui_actiondata( package, szRemoveODBC, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_RemoveODBC( MSIPACKAGE *package ) { static const WCHAR driver_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','D','r','i','v','e','r',0}; static const WCHAR translator_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; static const WCHAR source_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } rc = MSI_DatabaseOpenViewW( package->db, source_query, &view ); if (rc == ERROR_SUCCESS) { rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package ); msiobj_release( &view->hdr ); if (rc != ERROR_SUCCESS) return rc; } return ERROR_SUCCESS; } #define ENV_ACT_SETALWAYS 0x1 #define ENV_ACT_SETABSENT 0x2 #define ENV_ACT_REMOVE 0x4 #define ENV_ACT_REMOVEMATCH 0x8 #define ENV_MOD_MACHINE 0x20000000 #define ENV_MOD_APPEND 0x40000000 #define ENV_MOD_PREFIX 0x80000000 #define ENV_MOD_MASK 0xC0000000 #define check_flag_combo(x, y) ((x) & ~(y)) == (y) static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags ) { LPCWSTR cptr = *name; static const WCHAR prefix[] = {'[','~',']',0}; static const int prefix_len = 3; *flags = 0; while (*cptr) { if (*cptr == '=') *flags |= ENV_ACT_SETALWAYS; else if (*cptr == '+') *flags |= ENV_ACT_SETABSENT; else if (*cptr == '-') *flags |= ENV_ACT_REMOVE; else if (*cptr == '!') *flags |= ENV_ACT_REMOVEMATCH; else if (*cptr == '*') *flags |= ENV_MOD_MACHINE; else break; cptr++; (*name)++; } if (!*cptr) { ERR("Missing environment variable\n"); return ERROR_FUNCTION_FAILED; } if (*value) { LPCWSTR ptr = *value; if (!strncmpW(ptr, prefix, prefix_len)) { if (ptr[prefix_len] == szSemiColon[0]) { *flags |= ENV_MOD_APPEND; *value += lstrlenW(prefix); } else { *value = NULL; } } else if (lstrlenW(*value) >= prefix_len) { ptr += lstrlenW(ptr) - prefix_len; if (!strcmpW( ptr, prefix )) { if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0]) { *flags |= ENV_MOD_PREFIX; /* the "[~]" will be removed by deformat_string */; } else { *value = NULL; } } } } if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) || check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) || check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) || check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK)) { ERR("Invalid flags: %08x\n", *flags); return ERROR_FUNCTION_FAILED; } if (!*flags) *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE; return ERROR_SUCCESS; } static UINT open_env_key( DWORD flags, HKEY *key ) { static const WCHAR user_env[] = {'E','n','v','i','r','o','n','m','e','n','t',0}; static const WCHAR machine_env[] = {'S','y','s','t','e','m','\\', 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', 'C','o','n','t','r','o','l','\\', 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\', 'E','n','v','i','r','o','n','m','e','n','t',0}; const WCHAR *env; HKEY root; LONG res; if (flags & ENV_MOD_MACHINE) { env = machine_env; root = HKEY_LOCAL_MACHINE; } else { env = user_env; root = HKEY_CURRENT_USER; } res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key ); if (res != ERROR_SUCCESS) { WARN("Failed to open key %s (%d)\n", debugstr_w(env), res); return ERROR_FUNCTION_FAILED; } return ERROR_SUCCESS; } static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR name, value, component; LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr; DWORD flags, type, size; UINT res; HKEY env = NULL; MSICOMPONENT *comp; MSIRECORD *uirow; int action = 0; component = MSI_RecordGetString(rec, 4); comp = msi_get_loaded_component(package, component); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_LOCAL) { TRACE("component not scheduled for installation %s\n", debugstr_w(component)); return ERROR_SUCCESS; } name = MSI_RecordGetString(rec, 2); value = MSI_RecordGetString(rec, 3); TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value)); res = env_parse_flags(&name, &value, &flags); if (res != ERROR_SUCCESS || !value) goto done; if (value && !deformat_string(package, value, &deformatted)) { res = ERROR_OUTOFMEMORY; goto done; } value = deformatted; res = open_env_key( flags, &env ); if (res != ERROR_SUCCESS) goto done; if (flags & ENV_MOD_MACHINE) action |= 0x20000000; size = 0; type = REG_SZ; res = RegQueryValueExW(env, name, NULL, &type, NULL, &size); if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) || (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ)) goto done; if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK))) { action = 0x2; /* Nothing to do. */ if (!value) { res = ERROR_SUCCESS; goto done; } /* If we are appending but the string was empty, strip ; */ if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++; size = (lstrlenW(value) + 1) * sizeof(WCHAR); newval = strdupW(value); if (!newval) { res = ERROR_OUTOFMEMORY; goto done; } } else { action = 0x1; /* Contrary to MSDN, +-variable to [~];path works */ if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK)) { res = ERROR_SUCCESS; goto done; } data = msi_alloc(size); if (!data) { RegCloseKey(env); return ERROR_OUTOFMEMORY; } res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size); if (res != ERROR_SUCCESS) goto done; if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value ))) { action = 0x4; res = RegDeleteValueW(env, name); if (res != ERROR_SUCCESS) WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res); goto done; } size = (lstrlenW(data) + 1) * sizeof(WCHAR); if (flags & ENV_MOD_MASK) { DWORD mod_size; int multiplier = 0; if (flags & ENV_MOD_APPEND) multiplier++; if (flags & ENV_MOD_PREFIX) multiplier++; mod_size = lstrlenW(value) * multiplier; size += mod_size * sizeof(WCHAR); } newval = msi_alloc(size); ptr = newval; if (!newval) { res = ERROR_OUTOFMEMORY; goto done; } if (flags & ENV_MOD_PREFIX) { lstrcpyW(newval, value); ptr = newval + lstrlenW(value); action |= 0x80000000; } lstrcpyW(ptr, data); if (flags & ENV_MOD_APPEND) { lstrcatW(newval, value); action |= 0x40000000; } } TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval)); res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size); if (res) { WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res); } done: uirow = MSI_CreateRecord( 3 ); MSI_RecordSetStringW( uirow, 1, name ); MSI_RecordSetStringW( uirow, 2, newval ); MSI_RecordSetInteger( uirow, 3, action ); msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow ); msiobj_release( &uirow->hdr ); if (env) RegCloseKey(env); msi_free(deformatted); msi_free(data); msi_free(newval); return res; } static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','E','n','v','i','r','o','n','m','e','n','t','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW(package->db, query, &view); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package); msiobj_release(&view->hdr); return rc; } static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; LPCWSTR name, value, component; LPWSTR deformatted = NULL; DWORD flags; HKEY env; MSICOMPONENT *comp; MSIRECORD *uirow; int action = 0; LONG res; UINT r; component = MSI_RecordGetString( rec, 4 ); comp = msi_get_loaded_component( package, component ); if (!comp) return ERROR_SUCCESS; comp->Action = msi_get_component_action( package, comp ); if (comp->Action != INSTALLSTATE_ABSENT) { TRACE("component not scheduled for removal %s\n", debugstr_w(component)); return ERROR_SUCCESS; } name = MSI_RecordGetString( rec, 2 ); value = MSI_RecordGetString( rec, 3 ); TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value)); r = env_parse_flags( &name, &value, &flags ); if (r != ERROR_SUCCESS) return r; if (!(flags & ENV_ACT_REMOVE)) { TRACE("Environment variable %s not marked for removal\n", debugstr_w(name)); return ERROR_SUCCESS; } if (value && !deformat_string( package, value, &deformatted )) return ERROR_OUTOFMEMORY; value = deformatted; r = open_env_key( flags, &env ); if (r != ERROR_SUCCESS) { r = ERROR_SUCCESS; goto done; } if (flags & ENV_MOD_MACHINE) action |= 0x20000000; TRACE("Removing %s\n", debugstr_w(name)); res = RegDeleteValueW( env, name ); if (res != ERROR_SUCCESS) { WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res); r = ERROR_SUCCESS; } done: uirow = MSI_CreateRecord( 3 ); MSI_RecordSetStringW( uirow, 1, name ); MSI_RecordSetStringW( uirow, 2, value ); MSI_RecordSetInteger( uirow, 3, action ); msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow ); msiobj_release( &uirow->hdr ); if (env) RegCloseKey( env ); msi_free( deformatted ); return r; } static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','E','n','v','i','r','o','n','m','e','n','t','`',0}; MSIQUERY *view; UINT rc; rc = MSI_DatabaseOpenViewW( package->db, query, &view ); if (rc != ERROR_SUCCESS) return ERROR_SUCCESS; rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package ); msiobj_release( &view->hdr ); return rc; } UINT msi_validate_product_id( MSIPACKAGE *package ) { LPWSTR key, template, id; UINT r = ERROR_SUCCESS; id = msi_dup_property( package->db, szProductID ); if (id) { msi_free( id ); return ERROR_SUCCESS; } template = msi_dup_property( package->db, szPIDTemplate ); key = msi_dup_property( package->db, szPIDKEY ); if (key && template) { FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) ); r = msi_set_property( package->db, szProductID, key, -1 ); } msi_free( template ); msi_free( key ); return r; } static UINT ACTION_ValidateProductID( MSIPACKAGE *package ) { return msi_validate_product_id( package ); } static UINT ACTION_ScheduleReboot( MSIPACKAGE *package ) { TRACE("\n"); package->need_reboot_at_end = 1; return ERROR_SUCCESS; } static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package ) { static const WCHAR szAvailableFreeReg[] = {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0}; MSIRECORD *uirow; int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 ); TRACE("%p %d kilobytes\n", package, space); uirow = MSI_CreateRecord( 1 ); MSI_RecordSetInteger( uirow, 1, space ); msi_ui_actiondata( package, szAllocateRegistrySpace, uirow ); msiobj_release( &uirow->hdr ); return ERROR_SUCCESS; } static UINT ACTION_DisableRollback( MSIPACKAGE *package ) { TRACE("%p\n", package); msi_set_property( package->db, szRollbackDisabled, szOne, -1 ); return ERROR_SUCCESS; } static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package ) { FIXME("%p\n", package); return ERROR_SUCCESS; } static UINT ACTION_SetODBCFolders( MSIPACKAGE *package ) { static const WCHAR driver_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','D','r','i','v','e','r',0}; static const WCHAR translator_query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; MSIQUERY *view; UINT r, count; r = MSI_DatabaseOpenViewW( package->db, driver_query, &view ); if (r == ERROR_SUCCESS) { count = 0; r = MSI_IterateRecords( view, &count, NULL, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; if (count) FIXME("ignored %u rows in ODBCDriver table\n", count); } r = MSI_DatabaseOpenViewW( package->db, translator_query, &view ); if (r == ERROR_SUCCESS) { count = 0; r = MSI_IterateRecords( view, &count, NULL, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count); } return ERROR_SUCCESS; } static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param ) { static const WCHAR fmtW[] = {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0}; MSIPACKAGE *package = param; const WCHAR *property = MSI_RecordGetString( rec, 7 ); UINT len = sizeof(fmtW)/sizeof(fmtW[0]); WCHAR *product, *features, *cmd; STARTUPINFOW si; PROCESS_INFORMATION info; BOOL ret; if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS; deformat_string( package, MSI_RecordGetString( rec, 6 ), &features ); len += strlenW( product ); if (features) len += strlenW( features ); else len += sizeof(szAll) / sizeof(szAll[0]); if (!(cmd = msi_alloc( len * sizeof(WCHAR) ))) { msi_free( product ); msi_free( features ); return ERROR_OUTOFMEMORY; } sprintfW( cmd, fmtW, product, features ? features : szAll ); msi_free( product ); msi_free( features ); memset( &si, 0, sizeof(STARTUPINFOW) ); ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info ); msi_free( cmd ); if (!ret) return GetLastError(); CloseHandle( info.hThread ); WaitForSingleObject( info.hProcess, INFINITE ); CloseHandle( info.hProcess ); return ERROR_SUCCESS; } static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0}; MSIQUERY *view; UINT r; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; } return ERROR_SUCCESS; } static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param ) { MSIPACKAGE *package = param; int attributes = MSI_RecordGetInteger( rec, 5 ); if (attributes & msidbUpgradeAttributesMigrateFeatures) { const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 ); const WCHAR *version_min = MSI_RecordGetString( rec, 2 ); const WCHAR *version_max = MSI_RecordGetString( rec, 3 ); const WCHAR *language = MSI_RecordGetString( rec, 4 ); HKEY hkey; UINT r; if (package->Context == MSIINSTALLCONTEXT_MACHINE) { r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE ); if (r != ERROR_SUCCESS) return ERROR_SUCCESS; } else { r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE ); if (r != ERROR_SUCCESS) return ERROR_SUCCESS; } RegCloseKey( hkey ); FIXME("migrate feature states from %s version min %s version max %s language %s\n", debugstr_w(upgrade_code), debugstr_w(version_min), debugstr_w(version_max), debugstr_w(language)); } return ERROR_SUCCESS; } static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'U','p','g','r','a','d','e',0}; MSIQUERY *view; UINT r; if (msi_get_property_int( package->db, szInstalled, 0 )) { TRACE("product is installed, skipping action\n"); return ERROR_SUCCESS; } if (msi_get_property_int( package->db, szPreselected, 0 )) { TRACE("Preselected property is set, not migrating feature states\n"); return ERROR_SUCCESS; } r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; } return ERROR_SUCCESS; } static void bind_image( const char *filename, const char *path ) { if (!BindImageEx( 0, filename, path, NULL, NULL )) { WARN("failed to bind image %u\n", GetLastError()); } } static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param ) { UINT i; MSIFILE *file; MSIPACKAGE *package = param; const WCHAR *key = MSI_RecordGetString( rec, 1 ); const WCHAR *paths = MSI_RecordGetString( rec, 2 ); char *filenameA, *pathA; WCHAR *pathW, **path_list; if (!(file = msi_get_loaded_file( package, key ))) { WARN("file %s not found\n", debugstr_w(key)); return ERROR_SUCCESS; } if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS; path_list = msi_split_string( paths, ';' ); if (!path_list) bind_image( filenameA, NULL ); else { for (i = 0; path_list[i] && path_list[i][0]; i++) { deformat_string( package, path_list[i], &pathW ); if ((pathA = strdupWtoA( pathW ))) { bind_image( filenameA, pathA ); msi_free( pathA ); } msi_free( pathW ); } } msi_free( path_list ); msi_free( filenameA ); return ERROR_SUCCESS; } static UINT ACTION_BindImage( MSIPACKAGE *package ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 'B','i','n','d','I','m','a','g','e',0}; MSIQUERY *view; UINT r; r = MSI_DatabaseOpenViewW( package->db, query, &view ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package ); msiobj_release( &view->hdr ); if (r != ERROR_SUCCESS) return r; } return ERROR_SUCCESS; } static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0}; MSIQUERY *view; DWORD count = 0; UINT r; r = MSI_OpenQuery( package->db, &view, query, table ); if (r == ERROR_SUCCESS) { r = MSI_IterateRecords(view, &count, NULL, package); msiobj_release(&view->hdr); if (r != ERROR_SUCCESS) return r; } if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table)); return ERROR_SUCCESS; } static UINT ACTION_IsolateComponents( MSIPACKAGE *package ) { static const WCHAR table[] = { 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 }; return msi_unimplemented_action_stub( package, "IsolateComponents", table ); } static UINT ACTION_RMCCPSearch( MSIPACKAGE *package ) { static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 }; return msi_unimplemented_action_stub( package, "RMCCPSearch", table ); } static UINT ACTION_RegisterComPlus( MSIPACKAGE *package ) { static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 }; return msi_unimplemented_action_stub( package, "RegisterComPlus", table ); } static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package ) { static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 }; return msi_unimplemented_action_stub( package, "UnregisterComPlus", table ); } static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package ) { static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 }; return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table ); } static const struct { const WCHAR *action; UINT (*handler)(MSIPACKAGE *); const WCHAR *action_rollback; } StandardActions[] = { { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL }, { szAppSearch, ACTION_AppSearch, NULL }, { szBindImage, ACTION_BindImage, NULL }, { szCCPSearch, ACTION_CCPSearch, NULL }, { szCostFinalize, ACTION_CostFinalize, NULL }, { szCostInitialize, ACTION_CostInitialize, NULL }, { szCreateFolders, ACTION_CreateFolders, szRemoveFolders }, { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts }, { szDeleteServices, ACTION_DeleteServices, szInstallServices }, { szDisableRollback, ACTION_DisableRollback, NULL }, { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles }, { szExecuteAction, ACTION_ExecuteAction, NULL }, { szFileCost, ACTION_FileCost, NULL }, { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL }, { szForceReboot, ACTION_ForceReboot, NULL }, { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL }, { szInstallExecute, ACTION_InstallExecute, NULL }, { szInstallExecuteAgain, ACTION_InstallExecute, NULL }, { szInstallFiles, ACTION_InstallFiles, szRemoveFiles }, { szInstallFinalize, ACTION_InstallFinalize, NULL }, { szInstallInitialize, ACTION_InstallInitialize, NULL }, { szInstallODBC, ACTION_InstallODBC, szRemoveODBC }, { szInstallServices, ACTION_InstallServices, szDeleteServices }, { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL }, { szInstallValidate, ACTION_InstallValidate, NULL }, { szIsolateComponents, ACTION_IsolateComponents, NULL }, { szLaunchConditions, ACTION_LaunchConditions, NULL }, { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL }, { szMoveFiles, ACTION_MoveFiles, NULL }, { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies }, { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies }, { szPatchFiles, ACTION_PatchFiles, NULL }, { szProcessComponents, ACTION_ProcessComponents, szProcessComponents }, { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents }, { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures }, { szPublishProduct, ACTION_PublishProduct, NULL }, { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo }, { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus }, { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo }, { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts }, { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo }, { szRegisterProduct, ACTION_RegisterProduct, NULL }, { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo }, { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries }, { szRegisterUser, ACTION_RegisterUser, NULL }, { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles }, { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings }, { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL }, { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles }, { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders }, { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues }, { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC }, { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues }, { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts }, { szResolveSource, ACTION_ResolveSource, NULL }, { szRMCCPSearch, ACTION_RMCCPSearch, NULL }, { szScheduleReboot, ACTION_ScheduleReboot, NULL }, { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules }, { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules }, { szSetODBCFolders, ACTION_SetODBCFolders, NULL }, { szStartServices, ACTION_StartServices, szStopServices }, { szStopServices, ACTION_StopServices, szStartServices }, { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents }, { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures }, { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo }, { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus }, { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo }, { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts }, { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo }, { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo }, { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries }, { szValidateProductID, ACTION_ValidateProductID, NULL }, { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings }, { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues }, { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues }, { NULL, NULL, NULL } }; static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc ) { BOOL ret = FALSE; UINT i; i = 0; while (StandardActions[i].action != NULL) { if (!strcmpW( StandardActions[i].action, action )) { ui_actionstart( package, action ); if (StandardActions[i].handler) { ui_actioninfo( package, action, TRUE, 0 ); *rc = StandardActions[i].handler( package ); ui_actioninfo( package, action, FALSE, *rc ); if (StandardActions[i].action_rollback && !package->need_rollback) { TRACE("scheduling rollback action\n"); msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback ); } } else { FIXME("unhandled standard action %s\n", debugstr_w(action)); *rc = ERROR_SUCCESS; } ret = TRUE; break; } i++; } return ret; } UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script) { UINT rc = ERROR_SUCCESS; BOOL handled; TRACE("Performing action (%s)\n", debugstr_w(action)); handled = ACTION_HandleStandardAction(package, action, &rc); if (!handled) handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE); if (!handled) { WARN("unhandled msi action %s\n", debugstr_w(action)); rc = ERROR_FUNCTION_NOT_CALLED; } return rc; } UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script) { UINT rc = ERROR_SUCCESS; BOOL handled = FALSE; TRACE("Performing action (%s)\n", debugstr_w(action)); package->action_progress_increment = 0; handled = ACTION_HandleStandardAction(package, action, &rc); if (!handled) handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE); if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS ) handled = TRUE; if (!handled) { WARN("unhandled msi action %s\n", debugstr_w(action)); rc = ERROR_FUNCTION_NOT_CALLED; } return rc; } static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq) { UINT rc = ERROR_SUCCESS; MSIRECORD *row; static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0}; static const WCHAR ui_query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e', '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`', ' ', '=',' ','%','i',0}; if (needs_ui_sequence(package)) row = MSI_QueryGetRecord(package->db, ui_query, seq); else row = MSI_QueryGetRecord(package->db, query, seq); if (row) { LPCWSTR action, cond; TRACE("Running the actions\n"); /* check conditions */ cond = MSI_RecordGetString(row, 2); /* this is a hack to skip errors in the condition code */ if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE) { msiobj_release(&row->hdr); return ERROR_SUCCESS; } action = MSI_RecordGetString(row, 1); if (!action) { ERR("failed to fetch action\n"); msiobj_release(&row->hdr); return ERROR_FUNCTION_FAILED; } if (needs_ui_sequence(package)) rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE); else rc = ACTION_PerformAction(package, action, SCRIPT_NONE); msiobj_release(&row->hdr); } return rc; } /**************************************************** * TOP level entry points *****************************************************/ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath, LPCWSTR szCommandLine ) { static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0}; static const WCHAR szAction[] = {'A','C','T','I','O','N',0}; static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0}; WCHAR *reinstall, *remove; BOOL ui_exists; UINT rc; msi_set_property( package->db, szAction, szInstall, -1 ); package->script->InWhatSequence = SEQUENCE_INSTALL; if (szPackagePath) { LPWSTR p, dir; LPCWSTR file; dir = strdupW(szPackagePath); p = strrchrW(dir, '\\'); if (p) { *(++p) = 0; file = szPackagePath + (p - dir); } else { msi_free(dir); dir = msi_alloc(MAX_PATH * sizeof(WCHAR)); GetCurrentDirectoryW(MAX_PATH, dir); lstrcatW(dir, szBackSlash); file = szPackagePath; } msi_free( package->PackagePath ); package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR)); if (!package->PackagePath) { msi_free(dir); return ERROR_OUTOFMEMORY; } lstrcpyW(package->PackagePath, dir); lstrcatW(package->PackagePath, file); msi_free(dir); msi_set_sourcedir_props(package, FALSE); } rc = msi_parse_command_line( package, szCommandLine, FALSE ); if (rc != ERROR_SUCCESS) return rc; msi_apply_transforms( package ); msi_apply_patches( package ); remove = msi_dup_property( package->db, szRemove ); reinstall = msi_dup_property( package->db, szReinstall ); if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall) { TRACE("setting REINSTALL property to ALL\n"); msi_set_property( package->db, szReinstall, szAll, -1 ); } /* properties may have been added by a transform */ msi_clone_properties( package ); msi_parse_command_line( package, szCommandLine, FALSE ); msi_adjust_privilege_properties( package ); msi_set_context( package ); if (msi_get_property_int( package->db, szDisableRollback, 0 )) { TRACE("disabling rollback\n"); msi_set_property( package->db, szRollbackDisabled, szOne, -1 ); } if (needs_ui_sequence( package)) { package->script->InWhatSequence |= SEQUENCE_UI; rc = ACTION_ProcessUISequence(package); ui_exists = ui_sequence_exists(package); if (rc == ERROR_SUCCESS || !ui_exists) { package->script->InWhatSequence |= SEQUENCE_EXEC; rc = ACTION_ProcessExecSequence(package, ui_exists); } } else rc = ACTION_ProcessExecSequence(package, FALSE); package->script->CurrentlyScripting = FALSE; /* process the ending type action */ if (rc == ERROR_SUCCESS) ACTION_PerformActionSequence(package, -1); else if (rc == ERROR_INSTALL_USEREXIT) ACTION_PerformActionSequence(package, -2); else if (rc == ERROR_INSTALL_SUSPEND) ACTION_PerformActionSequence(package, -4); else /* failed */ { ACTION_PerformActionSequence(package, -3); if (!msi_get_property_int( package->db, szRollbackDisabled, 0 )) { package->need_rollback = TRUE; } } /* finish up running custom actions */ ACTION_FinishCustomActions(package); if (package->need_rollback && !reinstall) { WARN("installation failed, running rollback script\n"); execute_script( package, SCRIPT_ROLLBACK ); } msi_free( reinstall ); msi_free( remove ); if (rc == ERROR_SUCCESS && package->need_reboot_at_end) return ERROR_SUCCESS_REBOOT_REQUIRED; return rc; }