| /* |
| * 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 <stdarg.h> |
| |
| #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 "winuser.h" |
| #include "shlobj.h" |
| #include "objbase.h" |
| #include "mscoree.h" |
| #include "shlwapi.h" |
| #include "imagehlp.h" |
| #include "wine/unicode.h" |
| #include "winver.h" |
| |
| #include "msipriv.h" |
| #include "resource.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 szUnpublishProduct[] = |
| {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',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 const WCHAR szINSTALL[] = |
| {'I','N','S','T','A','L','L',0}; |
| |
| static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template) |
| { |
| 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}; |
| MSIRECORD *row, *textrow; |
| INT rc; |
| |
| textrow = MSI_QueryGetRecord(package->db, query, action); |
| if (textrow) |
| { |
| description = MSI_RecordGetString(textrow, 2); |
| template = MSI_RecordGetString(textrow, 3); |
| } |
| |
| row = MSI_CreateRecord(3); |
| if (!row) return -1; |
| MSI_RecordSetStringW(row, 1, action); |
| MSI_RecordSetStringW(row, 2, description); |
| MSI_RecordSetStringW(row, 3, template); |
| rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row); |
| if (textrow) msiobj_release(&textrow->hdr); |
| msiobj_release(&row->hdr); |
| return rc; |
| } |
| |
| static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, |
| INT rc) |
| { |
| MSIRECORD *row; |
| WCHAR *template; |
| |
| template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED); |
| |
| row = MSI_CreateRecord(2); |
| if (!row) return; |
| MSI_RecordSetStringW(row, 0, template); |
| MSI_RecordSetStringW(row, 1, action); |
| MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row); |
| msiobj_release(&row->hdr); |
| msi_free(template); |
| if (!start) package->LastActionResult = rc; |
| } |
| |
| 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; |
| BOOL ignore, in_quotes = FALSE; |
| int count = 0, len = 0; |
| |
| for (p = str; *p; p++) |
| { |
| ignore = FALSE; |
| switch (state) |
| { |
| case state_whitespace: |
| switch (*p) |
| { |
| case ' ': |
| in_quotes = TRUE; |
| ignore = TRUE; |
| len++; |
| break; |
| case '"': |
| state = state_quote; |
| if (in_quotes && p[1] != '\"') count--; |
| else count++; |
| break; |
| default: |
| state = state_token; |
| in_quotes = TRUE; |
| 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 = TRUE; |
| len++; |
| break; |
| default: |
| if (count) in_quotes = TRUE; |
| 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 = TRUE; |
| len++; |
| break; |
| default: |
| state = state_token; |
| if (count) in_quotes = TRUE; |
| len++; |
| break; |
| } |
| break; |
| |
| default: break; |
| } |
| if (!ignore) *out++ = *p; |
| if (!count) in_quotes = FALSE; |
| } |
| |
| 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; |
| |
| while (ptr[len - 1] == ' ') len--; |
| |
| 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',0}; |
| MSIQUERY *view; |
| DWORD count = 0; |
| |
| if (!(MSI_DatabaseOpenViewW( package->db, query, &view ))) |
| { |
| MSI_IterateRecords( view, &count, NULL, package ); |
| msiobj_release( &view->hdr ); |
| } |
| return count != 0; |
| } |
| |
| UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace) |
| { |
| WCHAR *source, *check, *p, *db; |
| DWORD len; |
| |
| if (!(db = msi_dup_property( package->db, szOriginalDatabase ))) |
| return ERROR_OUTOFMEMORY; |
| |
| if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' ))) |
| { |
| 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; |
| } |
| |
| rc = ACTION_PerformAction(package, action); |
| |
| msi_dialog_check_messages( NULL ); |
| |
| 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) |
| { |
| 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','`',' ', '>',' ','0',' ', |
| 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; |
| MSIQUERY *view; |
| UINT rc; |
| |
| if (package->ExecuteSequenceRun) |
| { |
| TRACE("Execute Sequence already Run\n"); |
| return ERROR_SUCCESS; |
| } |
| |
| package->ExecuteSequenceRun = TRUE; |
| |
| rc = MSI_OpenQuery(package->db, &view, query); |
| 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 UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action) |
| { |
| UINT arc; |
| INT uirc; |
| |
| uirc = ui_actionstart(package, action, NULL, NULL); |
| if (uirc == IDCANCEL) |
| return ERROR_INSTALL_USEREXIT; |
| ui_actioninfo(package, action, TRUE, 0); |
| arc = ACTION_CustomAction(package, action); |
| uirc = !arc; |
| |
| if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package)) |
| { |
| uirc = ACTION_ShowDialog(package, action); |
| switch (uirc) |
| { |
| case -1: |
| return ERROR_SUCCESS; /* stop immediately */ |
| case 0: arc = ERROR_FUNCTION_NOT_CALLED; break; |
| case 1: arc = ERROR_SUCCESS; break; |
| case 2: arc = ERROR_INSTALL_USEREXIT; break; |
| case 3: arc = ERROR_INSTALL_FAILURE; break; |
| case 4: arc = ERROR_INSTALL_SUSPEND; break; |
| case 5: arc = ERROR_MORE_DATA; break; |
| case 6: arc = ERROR_INVALID_HANDLE_STATE; break; |
| case 7: arc = ERROR_INVALID_DATA; break; |
| case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break; |
| case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break; |
| default: arc = ERROR_FUNCTION_FAILED; break; |
| } |
| } |
| |
| ui_actioninfo(package, action, FALSE, uirc); |
| |
| return arc; |
| } |
| |
| 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; |
| } |
| |
| 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 ); |
| } |
| |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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 (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence); |
| |
| 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] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID) |
| return ERROR_SUCCESS; |
| |
| return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet ); |
| } |
| |
| 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_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch ) |
| { |
| 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','`',' ','>','=',' ','%','u',0}; |
| MSIRECORD *rec; |
| |
| if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence ))) |
| { |
| WARN("query failed\n"); |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| patch->disk_id = MSI_RecordGetInteger( rec, 1 ); |
| msiobj_release( &rec->hdr ); |
| return ERROR_SUCCESS; |
| } |
| |
| static UINT load_patch(MSIRECORD *row, LPVOID param) |
| { |
| MSIPACKAGE *package = param; |
| MSIFILEPATCH *patch; |
| const WCHAR *file_key; |
| |
| patch = msi_alloc_zero( sizeof (MSIFILEPATCH) ); |
| if (!patch) |
| return ERROR_NOT_ENOUGH_MEMORY; |
| |
| file_key = MSI_RecordGetString( row, 1 ); |
| patch->File = msi_get_loaded_file( package, 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 ); |
| patch->PatchSize = MSI_RecordGetInteger( row, 3 ); |
| patch->Attributes = MSI_RecordGetInteger( row, 4 ); |
| |
| /* FIXME: |
| * Header field - for patch validation. |
| * _StreamRef - External key into MsiPatchHeaders (instead of the header field) |
| */ |
| |
| load_patch_disk_id( package, patch ); |
| |
| TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence); |
| |
| 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( MSIPACKAGE *package, UINT script ) |
| { |
| UINT i, rc = ERROR_SUCCESS; |
| |
| TRACE("executing script %u\n", script); |
| |
| package->script = script; |
| |
| if (script == SCRIPT_ROLLBACK) |
| { |
| for (i = package->script_actions_count[script]; i > 0; i--) |
| { |
| rc = ACTION_PerformAction(package, package->script_actions[script][i-1]); |
| if (rc != ERROR_SUCCESS) |
| { |
| ERR("Execution of script %i halted; action %s returned %u\n", |
| script, debugstr_w(package->script_actions[script][i-1]), rc); |
| break; |
| } |
| } |
| } |
| else |
| { |
| for (i = 0; i < package->script_actions_count[script]; i++) |
| { |
| rc = ACTION_PerformAction(package, package->script_actions[script][i]); |
| if (rc != ERROR_SUCCESS) |
| { |
| ERR("Execution of script %i halted; action %s returned %u\n", |
| script, debugstr_w(package->script_actions[script][i]), rc); |
| break; |
| } |
| } |
| } |
| |
| package->script = SCRIPT_NONE; |
| |
| 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; |
| BOOL remove = !strcmpW(property, szRemove); |
| BOOL reinstall = !strcmpW(property, szReinstall); |
| |
| override = msi_dup_property( package->db, property ); |
| if (!override) |
| return FALSE; |
| |
| LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) |
| { |
| if (feature->Level <= 0) |
| continue; |
| |
| if (reinstall) |
| state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed); |
| else if (remove) |
| state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT); |
| |
| if (!strcmpiW( override, szAll )) |
| { |
| 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))) |
| { |
| 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 && !package->full_reinstall) |
| msi_set_property( package->db, szPreselected, szOne, -1 ); |
| |
| return ret; |
| } |
| |
| static void disable_children( MSIFEATURE *feature, int level ) |
| { |
| FeatureList *fl; |
| |
| LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) |
| { |
| if (!is_feature_selected( feature, level )) |
| { |
| TRACE("child %s (level %d request %d) follows disabled 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->Level = feature->Level; |
| fl->feature->Action = INSTALLSTATE_UNKNOWN; |
| fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN; |
| } |
| disable_children( fl->feature, level ); |
| } |
| } |
| |
| static void follow_parent( MSIFEATURE *feature ) |
| { |
| FeatureList *fl; |
| |
| LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) |
| { |
| if (fl->feature->Attributes & msidbFeatureAttributesFollowParent) |
| { |
| TRACE("child %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; |
| } |
| follow_parent( fl->feature ); |
| } |
| } |
| |
| 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 ) |
| { |
| if (feature->Feature_Parent) continue; |
| disable_children( feature, level ); |
| follow_parent( feature ); |
| } |
| } |
| 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 ) |
| { |
| if (feature->Feature_Parent) continue; |
| disable_children( feature, level ); |
| follow_parent( feature ); |
| } |
| } |
| |
| /* 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); |
| |
| /* 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->hasAdvertisedFeature = 1; |
| break; |
| case INSTALLSTATE_SOURCE: |
| component->hasSourceFeature = 1; |
| break; |
| case INSTALLSTATE_LOCAL: |
| component->hasLocalFeature = 1; |
| break; |
| case INSTALLSTATE_DEFAULT: |
| if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) |
| component->hasAdvertisedFeature = 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->hasAdvertisedFeature) |
| { |
| 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 ); |
| CloseHandle( file ); |
| return size; |
| } |
| |
| BOOL msi_file_hash_matches( MSIFILE *file ) |
| { |
| UINT r; |
| MSIFILEHASHINFO hash; |
| |
| hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); |
| r = msi_get_filehash( file->TargetPath, &hash ); |
| if (r != ERROR_SUCCESS) |
| return FALSE; |
| |
| return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) ); |
| } |
| |
| static WCHAR *create_temp_dir( MSIDATABASE *db ) |
| { |
| static UINT id; |
| WCHAR *ret; |
| |
| if (!db->tempfolder) |
| { |
| WCHAR tmp[MAX_PATH]; |
| UINT len = sizeof(tmp)/sizeof(tmp[0]); |
| |
| if (msi_get_property( db, szTempFolder, tmp, &len ) || |
| GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY) |
| { |
| GetTempPathW( MAX_PATH, tmp ); |
| } |
| if (!(db->tempfolder = strdupW( tmp ))) return NULL; |
| } |
| |
| if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) ))) |
| { |
| for (;;) |
| { |
| if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret )) |
| { |
| msi_free( ret ); |
| return NULL; |
| } |
| if (CreateDirectoryW( ret, NULL )) break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * 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; |
| } |
| |
| BOOL msi_is_global_assembly( MSICOMPONENT *comp ) |
| { |
| return comp->assembly && !comp->assembly->application; |
| } |
| |
| static void set_target_path( MSIPACKAGE *package, MSIFILE *file ) |
| { |
| msi_free( file->TargetPath ); |
| if (msi_is_global_assembly( file->Component )) |
| { |
| MSIASSEMBLY *assembly = file->Component->assembly; |
| |
| if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db ); |
| file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName ); |
| } |
| else |
| { |
| const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory ); |
| file->TargetPath = msi_build_directory_name( 2, dir, file->FileName ); |
| } |
| |
| TRACE("file %s resolves to %s\n", debugstr_w(file->File), 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 ); |
| TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size); |
| |
| 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; |
| if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0]) |
| { |
| msi_free( path ); |
| path = NULL; |
| } |
| 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 ULONGLONG get_volume_space_required( MSIPACKAGE *package ) |
| { |
| MSICOMPONENT *comp; |
| ULONGLONG ret = 0; |
| |
| LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) |
| { |
| if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost; |
| } |
| return ret; |
| } |
| |
| 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}; |
| static const WCHAR szPrimaryFolder[] = |
| {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0}; |
| static const WCHAR szPrimaryVolumePath[] = |
| {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0}; |
| static const WCHAR szPrimaryVolumeSpaceAvailable[] = |
| {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e', |
| 'A','v','a','i','l','a','b','l','e',0}; |
| static const WCHAR szPrimaryVolumeSpaceRequired[] = |
| {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e', |
| 'R','e','q','u','i','r','e','d',0}; |
| static const WCHAR szPrimaryVolumeSpaceRemaining[] = |
| {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e', |
| 'R','e','m','a','i','n','i','n','g',0}; |
| static const WCHAR szOutOfNoRbDiskSpace[] = |
| {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0}; |
| MSICOMPONENT *comp; |
| MSIQUERY *view; |
| WCHAR *level, *primary_key, *primary_folder; |
| 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); |
| |
| if ((rc = MSI_SetFeatureStates( package ))) return rc; |
| |
| if ((primary_key = msi_dup_property( package->db, szPrimaryFolder ))) |
| { |
| if ((primary_folder = msi_dup_property( package->db, primary_key ))) |
| { |
| if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') || |
| (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':') |
| { |
| static const WCHAR fmtW[] = {'%','l','u',0}; |
| ULARGE_INTEGER free; |
| ULONGLONG required; |
| WCHAR buf[21]; |
| |
| primary_folder[2] = 0; |
| if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL )) |
| { |
| sprintfW( buf, fmtW, free.QuadPart / 512 ); |
| msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 ); |
| } |
| required = get_volume_space_required( package ); |
| sprintfW( buf, fmtW, required / 512 ); |
| msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 ); |
| |
| sprintfW( buf, fmtW, (free.QuadPart - required) / 512 ); |
| msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 ); |
| msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 ); |
| } |
| msi_free( primary_folder ); |
| } |
| msi_free( primary_key ); |
| } |
| |
| /* FIXME: check volume disk space */ |
| msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 ); |
| msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 ); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size ) |
| { |
| BYTE *data; |
| |
| 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; |
| |
| *type = REG_SZ; |
| if (value[0] == '#') |
| { |
| ptr++; len--; |
| if (value[1] == '%') |
| { |
| ptr++; len--; |
| *type = REG_EXPAND_SZ; |
| } |
| } |
| data = (BYTE *)msi_strdupW( ptr, len ); |
| 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 inline REGSAM get_registry_view( const MSICOMPONENT *comp ) |
| { |
| REGSAM view = 0; |
| if (is_wow64 || is_64bit) |
| view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY; |
| return view; |
| } |
| |
| static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access ) |
| { |
| WCHAR *subkey, *p, *q; |
| HKEY hkey, ret = NULL; |
| LONG res; |
| |
| access |= get_registry_view( comp ); |
| |
| 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( comp, hkey, q + 1, create, access ); |
| 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; |
| 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); |
| |
| if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE ))) |
| { |
| ERR("Could not create key %s\n", debugstr_w(deformated)); |
| msi_free(uikey); |
| msi_free(deformated); |
| return ERROR_FUNCTION_FAILED; |
| } |
| msi_free( deformated ); |
| str = msi_record_get_string( row, 5, NULL ); |
| len = deformat_string( package, str, &deformated ); |
| new_value = parse_value( package, deformated, len, &type, &new_size ); |
| |
| msi_free( deformated ); |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); |
| msiobj_release( &uirow->hdr ); |
| |
| msi_free(new_value); |
| msi_free(old_value); |
| msi_free(deformated); |
| msi_free(uikey); |
| |
| 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( const MSICOMPONENT *comp, HKEY root, const WCHAR *path ) |
| { |
| REGSAM access = 0; |
| WCHAR *subkey, *p; |
| HKEY hkey; |
| LONG res; |
| |
| access |= get_registry_view( comp ); |
| |
| if (!(subkey = strdupW( path ))) return; |
| do |
| { |
| if ((p = strrchrW( subkey, '\\' ))) |
| { |
| *p = 0; |
| if (!p[1]) continue; /* trailing backslash */ |
| hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL ); |
| if (!hkey) break; |
| res = RegDeleteKeyExW( hkey, p + 1, access, 0 ); |
| RegCloseKey( hkey ); |
| } |
| else |
| res = RegDeleteKeyExW( root, subkey, access, 0 ); |
| if (res) |
| { |
| TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res); |
| break; |
| } |
| } while (p); |
| msi_free( subkey ); |
| } |
| |
| static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value ) |
| { |
| LONG res; |
| HKEY hkey; |
| DWORD num_subkeys, num_values; |
| |
| if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE ))) |
| { |
| 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( comp, root, path ); |
| } |
| } |
| } |
| |
| static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path ) |
| { |
| LONG res; |
| HKEY hkey; |
| |
| if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return; |
| res = RegDeleteTreeW( hkey, NULL ); |
| if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res); |
| delete_key( comp, 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; |
| 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 ); |
| |
| if (delete_key) delete_tree( comp, hkey_root, deformated_key ); |
| else delete_value( comp, hkey_root, deformated_key, deformated_name ); |
| msi_free( deformated_key ); |
| |
| uirow = MSI_CreateRecord( 2 ); |
| MSI_RecordSetStringW( uirow, 1, ui_key_str ); |
| MSI_RecordSetStringW( uirow, 2, deformated_name ); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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; |
| 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 ); |
| |
| if (delete_key) delete_tree( comp, hkey_root, deformated_key ); |
| else delete_value( comp, hkey_root, deformated_key, deformated_name ); |
| msi_free( deformated_key ); |
| |
| uirow = MSI_CreateRecord( 2 ); |
| MSI_RecordSetStringW( uirow, 1, ui_key_str ); |
| MSI_RecordSetStringW( uirow, 2, deformated_name ); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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) |
| { |
| 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 squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE]; |
| UINT rc; |
| MSICOMPONENT *comp; |
| HKEY hkey; |
| |
| TRACE("\n"); |
| |
| squash_guid( package->ProductCode, squashed_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, squashed_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(squashed_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, squashed_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; |
| |
| if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence))) |
| return ERROR_FUNCTION_FAILED; |
| |
| 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, squashed_pc, source ); |
| } |
| RegCloseKey(hkey); |
| } |
| else if (action == INSTALLSTATE_ABSENT) |
| { |
| if (comp->num_clients <= 0) |
| { |
| if (package->Context == MSIINSTALLCONTEXT_MACHINE) |
| rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid ); |
| else |
| rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL ); |
| |
| if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc ); |
| } |
| else |
| { |
| LONG res; |
| |
| if (package->Context == MSIINSTALLCONTEXT_MACHINE) |
| rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ); |
| else |
| rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ); |
| |
| if (rc != ERROR_SUCCESS) |
| { |
| WARN( "failed to open component key %u\n", rc ); |
| continue; |
| } |
| res = RegDeleteValueW( hkey, squashed_pc ); |
| RegCloseKey(hkey); |
| if (res) WARN( "failed to delete component value %d\n", res ); |
| } |
| } |
| |
| /* 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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 = strrchrW( 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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"); |
| path = resolve_keypath( package, comp ); |
| IShellLinkW_SetPath( sl, path ); |
| msi_free( path ); |
| } |
| |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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"); |
| 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) |
| { |
| 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}; |
| MSIHANDLE hdb, suminfo; |
| WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE]; |
| DWORD langid, size; |
| UINT r; |
| |
| 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; |
| WCHAR *upgrade, squashed_pc[SQUASHED_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) ); |
| if (res != ERROR_SUCCESS) |
| { |
| RegCloseKey( patch_key ); |
| goto done; |
| } |
| |
| res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable, |
| sizeof(patch->uninstallable) ); |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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, NULL, package->Context, |
| &hkey, TRUE); |
| if (rc != ERROR_SUCCESS) |
| goto end; |
| |
| rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, 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->Level <= 0) continue; |
| |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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, NULL, package->Context, |
| &hkey, FALSE); |
| if (r == ERROR_SUCCESS) |
| { |
| RegDeleteValueW(hkey, feature->Feature); |
| RegCloseKey(hkey); |
| } |
| |
| r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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 *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE]; |
| MSIRECORD *uirow; |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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 void remove_product_upgrade_code( MSIPACKAGE *package ) |
| { |
| WCHAR *code, product[SQUASHED_GUID_SIZE]; |
| HKEY hkey; |
| LONG res; |
| DWORD count; |
| |
| squash_guid( package->ProductCode, product ); |
| if (!(code = msi_dup_property( package->db, szUpgradeCode ))) |
| { |
| WARN( "upgrade code not found\n" ); |
| return; |
| } |
| if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE )) |
| { |
| RegDeleteValueW( hkey, product ); |
| res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL ); |
| RegCloseKey( hkey ); |
| if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code ); |
| } |
| if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE )) |
| { |
| RegDeleteValueW( hkey, product ); |
| res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL ); |
| RegCloseKey( hkey ); |
| if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code ); |
| } |
| if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE )) |
| { |
| RegDeleteValueW( hkey, product ); |
| res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL ); |
| RegCloseKey( hkey ); |
| if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code ); |
| } |
| |
| msi_free( code ); |
| } |
| |
| static UINT ACTION_UnpublishProduct(MSIPACKAGE *package) |
| { |
| MSIPATCHINFO *patch; |
| |
| MSIREG_DeleteProductKey(package->ProductCode); |
| MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context); |
| MSIREG_DeleteUninstallKey(package->ProductCode, package->platform); |
| |
| MSIREG_DeleteLocalClassesProductKey(package->ProductCode); |
| MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode); |
| MSIREG_DeleteUserProductKey(package->ProductCode); |
| MSIREG_DeleteUserFeaturesKey(package->ProductCode); |
| |
| remove_product_upgrade_code( package ); |
| |
| 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 BOOL is_full_uninstall( MSIPACKAGE *package ) |
| { |
| WCHAR **features, *remove = msi_dup_property( package->db, szRemove ); |
| MSIFEATURE *feature; |
| BOOL ret = TRUE; |
| UINT i; |
| |
| LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) |
| { |
| if (feature->Action == INSTALLSTATE_LOCAL) ret = FALSE; |
| } |
| |
| features = msi_split_string( remove, ',' ); |
| for (i = 0; features && features[i]; i++) |
| { |
| if (!strcmpW( features[i], szAll )) ret = TRUE; |
| } |
| |
| msi_free(features); |
| msi_free(remove); |
| return ret; |
| } |
| |
| static UINT ACTION_InstallFinalize(MSIPACKAGE *package) |
| { |
| UINT rc; |
| |
| /* first do the same as an InstallExecute */ |
| rc = execute_script(package, SCRIPT_INSTALL); |
| if (rc != ERROR_SUCCESS) |
| return rc; |
| |
| /* then handle commit actions */ |
| rc = execute_script(package, SCRIPT_COMMIT); |
| if (rc != ERROR_SUCCESS) |
| return rc; |
| |
| if (is_full_uninstall(package)) |
| rc = ACTION_UnpublishProduct(package); |
| |
| 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], squashed_pc[SQUASHED_GUID_SIZE]; |
| HKEY hkey; |
| |
| squash_guid( package->ProductCode, squashed_pc ); |
| |
| GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0])); |
| RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey); |
| snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc ); |
| |
| msi_reg_set_val_str( hkey, squashed_pc, buffer ); |
| RegCloseKey(hkey); |
| |
| TRACE("Reboot command %s\n",debugstr_w(buffer)); |
| |
| RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey); |
| sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc ); |
| |
| msi_reg_set_val_str( hkey, squashed_pc, buffer ); |
| RegCloseKey(hkey); |
| |
| return ERROR_INSTALL_SUSPEND; |
| } |
| |
| 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) |
| { |
| MSIRECORD *record; |
| LPWSTR prompt; |
| 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); |
| |
| record = MSI_CreateRecord(2); |
| MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK); |
| MSI_RecordSetStringW(record, 2, prompt); |
| msi_free(prompt); |
| while(attrib == INVALID_FILE_ATTRIBUTES) |
| { |
| MSI_RecordSetStringW(record, 0, NULL); |
| rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record); |
| if (rc == IDCANCEL) |
| return ERROR_INSTALL_USEREXIT; |
| attrib = GetFileAttributesW(package->db->path); |
| } |
| 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, package->Context); |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); |
| msiobj_release( &uirow->hdr ); |
| |
| msi_free(productid); |
| RegCloseKey(hkey); |
| return rc; |
| } |
| |
| static UINT iterate_properties(MSIRECORD *record, void *param) |
| { |
| static const WCHAR prop_template[] = |
| {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0}; |
| MSIRECORD *uirow; |
| |
| uirow = MSI_CloneRecord(record); |
| if (!uirow) return ERROR_OUTOFMEMORY; |
| MSI_RecordSetStringW(uirow, 0, prop_template); |
| MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow); |
| msiobj_release(&uirow->hdr); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| |
| static UINT ACTION_ExecuteAction(MSIPACKAGE *package) |
| { |
| static const WCHAR prop_query[] = |
| {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0}; |
| WCHAR *productname; |
| WCHAR *action; |
| WCHAR *info_template; |
| MSIQUERY *view; |
| MSIRECORD *uirow, *uirow_info; |
| UINT rc; |
| |
| /* Send COMMONDATA and INFO messages. */ |
| /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */ |
| uirow = MSI_CreateRecord(3); |
| if (!uirow) return ERROR_OUTOFMEMORY; |
| MSI_RecordSetStringW(uirow, 0, NULL); |
| MSI_RecordSetInteger(uirow, 1, 0); |
| MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0); |
| MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings)); |
| MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| /* FIXME: send INSTALLMESSAGE_PROGRESS */ |
| MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| |
| if (!(needs_ui_sequence(package) && ui_sequence_exists(package))) |
| { |
| uirow_info = MSI_CreateRecord(0); |
| if (!uirow_info) |
| { |
| msiobj_release(&uirow->hdr); |
| return ERROR_OUTOFMEMORY; |
| } |
| info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART); |
| MSI_RecordSetStringW(uirow_info, 0, info_template); |
| msi_free(info_template); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info); |
| msiobj_release(&uirow_info->hdr); |
| } |
| |
| MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| |
| productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW); |
| MSI_RecordSetInteger(uirow, 1, 1); |
| MSI_RecordSetStringW(uirow, 2, productname); |
| MSI_RecordSetStringW(uirow, 3, NULL); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| msiobj_release(&uirow->hdr); |
| |
| package->LastActionResult = MSI_NULL_INTEGER; |
| |
| action = msi_dup_property(package->db, szEXECUTEACTION); |
| if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL)); |
| |
| /* Perform the action. Top-level actions trigger a sequence. */ |
| if (!strcmpW(action, szINSTALL)) |
| { |
| /* Send ACTIONSTART/INFO and INSTALLSTART. */ |
| ui_actionstart(package, szINSTALL, NULL, NULL); |
| ui_actioninfo(package, szINSTALL, TRUE, 0); |
| uirow = MSI_CreateRecord(2); |
| if (!uirow) |
| { |
| rc = ERROR_OUTOFMEMORY; |
| goto end; |
| } |
| MSI_RecordSetStringW(uirow, 0, NULL); |
| MSI_RecordSetStringW(uirow, 1, productname); |
| MSI_RecordSetStringW(uirow, 2, package->ProductCode); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow); |
| msiobj_release(&uirow->hdr); |
| |
| /* Perform the installation. Always use the ExecuteSequence. */ |
| package->InWhatSequence |= SEQUENCE_EXEC; |
| rc = ACTION_ProcessExecSequence(package); |
| |
| /* Send return value and INSTALLEND. */ |
| ui_actioninfo(package, szINSTALL, FALSE, !rc); |
| uirow = MSI_CreateRecord(3); |
| if (!uirow) |
| { |
| rc = ERROR_OUTOFMEMORY; |
| goto end; |
| } |
| MSI_RecordSetStringW(uirow, 0, NULL); |
| MSI_RecordSetStringW(uirow, 1, productname); |
| MSI_RecordSetStringW(uirow, 2, package->ProductCode); |
| MSI_RecordSetInteger(uirow, 3, !rc); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow); |
| msiobj_release(&uirow->hdr); |
| } |
| else |
| rc = ACTION_PerformAction(package, action); |
| |
| /* Send all set properties. */ |
| if (!MSI_OpenQuery(package->db, &view, prop_query)) |
| { |
| MSI_IterateRecords(view, NULL, iterate_properties, package); |
| msiobj_release(&view->hdr); |
| } |
| |
| /* And finally, toggle the cancel off and on. */ |
| uirow = MSI_CreateRecord(2); |
| if (!uirow) |
| { |
| rc = ERROR_OUTOFMEMORY; |
| goto end; |
| } |
| MSI_RecordSetStringW(uirow, 0, NULL); |
| MSI_RecordSetInteger(uirow, 1, 2); |
| MSI_RecordSetInteger(uirow, 2, 0); |
| MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| MSI_RecordSetInteger(uirow, 2, 1); |
| MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow); |
| msiobj_release(&uirow->hdr); |
| |
| end: |
| msi_free(productname); |
| msi_free(action); |
| return rc; |
| } |
| |
| static UINT ACTION_INSTALL(MSIPACKAGE *package) |
| { |
| msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1); |
| if (needs_ui_sequence(package) && ui_sequence_exists(package)) |
| { |
| package->InWhatSequence |= SEQUENCE_UI; |
| return ACTION_ProcessUISequence(package); |
| } |
| else |
| return ACTION_ExecuteAction(package); |
| } |
| |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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; |
| BOOL is_vital; |
| 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); |
| |
| /* Should the complete install fail if CreateService fails? */ |
| is_vital = (err_control & msidbServiceInstallErrorControlVital); |
| |
| /* Remove the msidbServiceInstallErrorControlVital-flag from err_control. |
| CreateService (under Windows) would fail if not. */ |
| err_control &= ~msidbServiceInstallErrorControlVital; |
| |
| /* fetch the service path */ |
| row = MSI_QueryGetRecord(package->db, query, comp); |
| if (!row) |
| { |
| ERR("Query failed\n"); |
| goto done; |
| } |
| if (!(key = MSI_RecordGetString(row, 6))) |
| { |
| msiobj_release(&row->hdr); |
| goto done; |
| } |
| 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) |
| { |
| WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError()); |
| if (is_vital) |
| ret = ERROR_INSTALL_FAILURE; |
| |
| } |
| } |
| 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: |
| if (service) CloseServiceHandle(service); |
| if (hscm) 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; |
| |
| 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 & msidbServiceControlEventStart)) && |
| !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart))) |
| { |
| TRACE("not starting %s\n", debugstr_w(name)); |
| msi_free( name ); |
| return ERROR_SUCCESS; |
| } |
| |
| deformat_string(package, MSI_RecordGetString(rec, 4), &args); |
| wait = MSI_RecordGetInteger(rec, 5); |
| |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); |
| msiobj_release( &uirow->hdr ); |
| |
| if (service) CloseServiceHandle(service); |
| if (scm) 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: |
| if (service) CloseServiceHandle(service); |
| if (scm) CloseServiceHandle(scm); |
| |
| return ERROR_SUCCESS; |
| } |
| |
| static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param ) |
| { |
| MSIPACKAGE *package = param; |
| MSICOMPONENT *comp; |
| MSIRECORD *uirow; |
| LPCWSTR component; |
| WCHAR *name, *display_name = NULL; |
| DWORD event, len; |
| SC_HANDLE scm; |
| |
| component = MSI_RecordGetString( rec, 6 ); |
| comp = msi_get_loaded_component( package, component ); |
| 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 & msidbServiceControlEventStop)) && |
| !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop))) |
| { |
| TRACE("not stopping %s\n", debugstr_w(name)); |
| msi_free( name ); |
| 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 ); |
| |
| stop_service( name ); |
| |
| done: |
| uirow = MSI_CreateRecord( 2 ); |
| MSI_RecordSetStringW( uirow, 1, display_name ); |
| MSI_RecordSetStringW( uirow, 2, name ); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); |
| msiobj_release( &uirow->hdr ); |
| |
| if (service) CloseServiceHandle( service ); |
| if (scm) 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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; |
| WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q; |
| DWORD flags, type, size, len, len_value = 0; |
| UINT res; |
| HKEY env = NULL; |
| MSICOMPONENT *comp; |
| MSIRECORD *uirow; |
| int action = 0, found = 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; |
| } |
| |
| if ((value = deformatted)) |
| { |
| if (flags & ENV_MOD_PREFIX) |
| { |
| p = strrchrW( value, ';' ); |
| len_value = p - value; |
| } |
| else if (flags & ENV_MOD_APPEND) |
| { |
| value = strchrW( value, ';' ) + 1; |
| len_value = strlenW( value ); |
| } |
| else len_value = strlenW( value ); |
| } |
| |
| 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; |
| } |
| 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; |
| } |
| |
| if (!(p = q = data = msi_alloc( size ))) |
| { |
| msi_free(deformatted); |
| RegCloseKey(env); |
| return ERROR_OUTOFMEMORY; |
| } |
| |
| res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)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; |
| } |
| |
| for (;;) |
| { |
| while (*q && *q != ';') q++; |
| len = q - p; |
| if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) && |
| (!p[len] || p[len] == ';')) |
| { |
| found = 1; |
| break; |
| } |
| if (!*q) break; |
| p = ++q; |
| } |
| |
| if (found) |
| { |
| TRACE("string already set\n"); |
| goto done; |
| } |
| |
| size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR); |
| if (!(p = newval = msi_alloc( size ))) |
| { |
| res = ERROR_OUTOFMEMORY; |
| goto done; |
| } |
| |
| if (flags & ENV_MOD_PREFIX) |
| { |
| memcpy( newval, value, len_value * sizeof(WCHAR) ); |
| newval[len_value] = ';'; |
| p = newval + len_value + 1; |
| action |= 0x80000000; |
| } |
| |
| strcpyW( p, data ); |
| |
| if (flags & ENV_MOD_APPEND) |
| { |
| p += strlenW( data ); |
| *p++ = ';'; |
| memcpy( p, value, (len_value + 1) * sizeof(WCHAR) ); |
| action |= 0x40000000; |
| } |
| } |
| TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval)); |
| res = RegSetValueExW( env, name, 0, type, (BYTE *)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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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; |
| WCHAR *p, *q, *deformatted = NULL, *new_value = NULL; |
| DWORD flags, type, size, len, len_value = 0, len_new_value; |
| 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; |
| |
| if ((value = deformatted)) |
| { |
| if (flags & ENV_MOD_PREFIX) |
| { |
| p = strchrW( value, ';' ); |
| len_value = p - value; |
| } |
| else if (flags & ENV_MOD_APPEND) |
| { |
| value = strchrW( value, ';' ) + 1; |
| len_value = strlenW( value ); |
| } |
| else len_value = strlenW( value ); |
| } |
| |
| r = open_env_key( flags, &env ); |
| if (r != ERROR_SUCCESS) |
| { |
| r = 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 || (type != REG_SZ && type != REG_EXPAND_SZ)) |
| goto done; |
| |
| if (!(new_value = msi_alloc( size ))) goto done; |
| |
| res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size ); |
| if (res != ERROR_SUCCESS) |
| goto done; |
| |
| len_new_value = size / sizeof(WCHAR) - 1; |
| p = q = new_value; |
| for (;;) |
| { |
| while (*q && *q != ';') q++; |
| len = q - p; |
| if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) )) |
| { |
| if (*q == ';') q++; |
| memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) ); |
| break; |
| } |
| if (!*q) break; |
| p = ++q; |
| } |
| |
| if (!new_value[0] || !value) |
| { |
| 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); |
| } |
| else |
| { |
| TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value)); |
| size = (strlenW( new_value ) + 1) * sizeof(WCHAR); |
| res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size ); |
| if (res != ERROR_SUCCESS) |
| WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res); |
| } |
| |
| done: |
| uirow = MSI_CreateRecord( 3 ); |
| MSI_RecordSetStringW( uirow, 1, name ); |
| MSI_RecordSetStringW( uirow, 2, value ); |
| MSI_RecordSetInteger( uirow, 3, action ); |
| MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); |
| msiobj_release( &uirow->hdr ); |
| |
| if (env) RegCloseKey( env ); |
| msi_free( deformatted ); |
| msi_free( new_value ); |
| 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_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, 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',' ','/','q','n',' ','/','i',' ','%','s',' ', |
| 'R','E','M','O','V','E','=','%','s',0}; |
| MSIPACKAGE *package = param; |
| const WCHAR *property = MSI_RecordGetString( rec, 7 ); |
| int attrs = MSI_RecordGetInteger( rec, 5 ); |
| UINT len = sizeof(fmtW)/sizeof(fmtW[0]); |
| WCHAR *product, *features, *cmd; |
| STARTUPINFOW si; |
| PROCESS_INFORMATION info; |
| BOOL ret; |
| |
| if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS; |
| 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; |
| const UINT description; |
| const UINT template; |
| UINT (*handler)(MSIPACKAGE *); |
| const WCHAR *action_rollback; |
| } |
| StandardActions[] = |
| { |
| { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL }, |
| { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL }, |
| { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL }, |
| { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL }, |
| { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL }, |
| { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL }, |
| { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders }, |
| { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts }, |
| { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices }, |
| { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL }, |
| { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles }, |
| { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL }, |
| { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL }, |
| { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL }, |
| { szForceReboot, 0, 0, ACTION_ForceReboot, NULL }, |
| { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL }, |
| { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL }, |
| { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL }, |
| { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles }, |
| { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL }, |
| { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL }, |
| { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC }, |
| { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices }, |
| { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL }, |
| { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL }, |
| { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL }, |
| { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL }, |
| { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL }, |
| { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL }, |
| { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies }, |
| { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies }, |
| { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL }, |
| { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents }, |
| { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents }, |
| { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures }, |
| { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct }, |
| { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo }, |
| { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus }, |
| { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo }, |
| { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts }, |
| { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo }, |
| { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL }, |
| { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo }, |
| { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries }, |
| { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL }, |
| { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles }, |
| { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings }, |
| { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL }, |
| { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles }, |
| { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders }, |
| { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues }, |
| { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC }, |
| { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues }, |
| { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts }, |
| { szResolveSource, 0, 0, ACTION_ResolveSource, NULL }, |
| { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL }, |
| { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL }, |
| { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules }, |
| { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules }, |
| { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL }, |
| { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices }, |
| { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices }, |
| { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents }, |
| { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures }, |
| { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */ |
| { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo }, |
| { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus }, |
| { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo }, |
| { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts }, |
| { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo }, |
| { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo }, |
| { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries }, |
| { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL }, |
| { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings }, |
| { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues }, |
| { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues }, |
| { szINSTALL, 0, 0, ACTION_INSTALL, NULL }, |
| { 0 } |
| }; |
| |
| static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action) |
| { |
| UINT rc = ERROR_FUNCTION_NOT_CALLED; |
| UINT i; |
| |
| i = 0; |
| while (StandardActions[i].action != NULL) |
| { |
| if (!strcmpW( StandardActions[i].action, action )) |
| { |
| WCHAR description[100] = {0}, template[100] = {0}; |
| |
| if (StandardActions[i].description != 0) |
| LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100); |
| if (StandardActions[i].template != 0) |
| LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100); |
| |
| ui_actionstart(package, action, description, template); |
| 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; |
| } |
| break; |
| } |
| i++; |
| } |
| return rc; |
| } |
| |
| UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action) |
| { |
| UINT rc; |
| |
| TRACE("Performing action (%s)\n", debugstr_w(action)); |
| |
| package->action_progress_increment = 0; |
| rc = ACTION_HandleStandardAction(package, action); |
| |
| if (rc == ERROR_FUNCTION_NOT_CALLED) |
| rc = ACTION_HandleCustomAction(package, action); |
| |
| if (rc == ERROR_FUNCTION_NOT_CALLED) |
| WARN("unhandled msi action %s\n", debugstr_w(action)); |
| |
| 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; |
| } |
| |
| rc = ACTION_PerformAction(package, action); |
| |
| 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}; |
| WCHAR *reinstall, *remove, *patch, *productcode, *action; |
| UINT rc; |
| DWORD len = 0; |
| |
| 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 ); |
| |
| patch = msi_dup_property( package->db, szPatch ); |
| 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 && !patch) |
| { |
| TRACE("setting REINSTALL property to ALL\n"); |
| msi_set_property( package->db, szReinstall, szAll, -1 ); |
| package->full_reinstall = 1; |
| } |
| if (msi_get_property( package->db, szAction, NULL, &len )) |
| msi_set_property( package->db, szAction, szINSTALL, -1 ); |
| action = msi_dup_property( package->db, szAction ); |
| CharUpperW(action); |
| |
| msi_set_original_database_property( package->db, szPackagePath ); |
| msi_parse_command_line( package, szCommandLine, FALSE ); |
| msi_adjust_privilege_properties( package ); |
| msi_set_context( package ); |
| |
| productcode = msi_dup_property( package->db, szProductCode ); |
| if (strcmpiW( productcode, package->ProductCode )) |
| { |
| TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) ); |
| msi_free( package->ProductCode ); |
| package->ProductCode = productcode; |
| } |
| else msi_free( productcode ); |
| |
| if (msi_get_property_int( package->db, szDisableRollback, 0 )) |
| { |
| TRACE("disabling rollback\n"); |
| msi_set_property( package->db, szRollbackDisabled, szOne, -1 ); |
| } |
| |
| rc = ACTION_PerformAction(package, action); |
| |
| /* 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 ); |
| msi_free( patch ); |
| msi_free( action ); |
| |
| if (rc == ERROR_SUCCESS && package->need_reboot_at_end) |
| return ERROR_SUCCESS_REBOOT_REQUIRED; |
| |
| return rc; |
| } |