diff options
64 files changed, 57916 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a61dc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/aclocal.m4 +/autom4te.cache +/config.h.in +/config.h.in~ +/configure +Makefile.in diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..137e739 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = libmsi diff --git a/build-aux/.gitignore b/build-aux/.gitignore new file mode 100644 index 0000000..2d4d6d2 --- /dev/null +++ b/build-aux/.gitignore @@ -0,0 +1,7 @@ +/config.guess +/config.sub +/depcomp +/install-sh +/ltmain.sh +/missing +/ylwrap diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f196c11 --- /dev/null +++ b/configure.ac @@ -0,0 +1,21 @@ +AC_INIT([msitools], [0.0]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SRCDIR([libmsi/sql.y]) +AM_INIT_AUTOMAKE([foreign]) + +AC_PROG_CC +AC_PROG_YACC + +AC_CHECK_TOOLS([WIDL], [widl], + [AC_MSG_ERROR([could not find widl])]) + +AC_ARG_VAR([RCFLAGS], [Flags for the Windows resource compiler]) +AC_ARG_VAR([IDLFLAGS], [Flags for the Windows IDL compiler]) + +LT_INIT([win32-dll disable-fast-install]) +LT_LANG([Windows Resource]) + +AC_CONFIG_FILES([Makefile libmsi/Makefile]) +AC_OUTPUT diff --git a/libmsi/Makefile.am b/libmsi/Makefile.am new file mode 100644 index 0000000..e752a77 --- /dev/null +++ b/libmsi/Makefile.am @@ -0,0 +1,69 @@ +lib_LTLIBRARIES = libmsi.la + +AM_CPPFLAGS = -I$(srcdir) -I. -D__WINESRC__ +AM_YFLAGS = -d +BUILT_SOURCES = msiserver.h msiserver_i.c sql.c sql.h cond.c cond.h + +noinst_HEADERS = \ + msi.h \ + msipriv.h \ + msiquery.h \ + msiserver_dispids.h \ + msiserver.h \ + query.h + +libmsi_la_SOURCES = action.c alter.c appsearch.c assembly.c \ + automation.c classes.c create.c custom.c database.c delete.c \ + dialog.c distinct.c drop.c events.c files.c font.c format.c \ + handle.c insert.c install.c media.c msi.c msi_main.c msiquery.c \ + package.c patch.c record.c registry.c script.c select.c source.c \ + storages.c streams.c string.c suminfo.c table.c tokenize.c update.c \ + upgrade.c where.c \ + cond.y sql.y \ + msiserver_i.c + +libmsi_la_DEPENDENCIES = \ + msiserver_r.res \ + msiserver_t.res \ + msi.res + +libmsi_la_LIBADD = \ + $(libmsi_la_DEPENDENCIES) \ + -luuid \ + -lurlmon \ + -lwininet \ + -lcomctl32 \ + -lshell32 \ + -lshlwapi \ + -lcabinet \ + -loleaut32 \ + -lole32 \ + -lversion \ + -luser32 \ + -lgdi32 \ + -ladvapi32 \ + -lodbccp32 \ + -lwintrust \ + -lcrypt32 \ + -limagehlp + +msi.res: msi.rc msiserver.rgs instabsent.bmp instadvert.bmp instlocal.bmp + +%.res: %.rc + $(RC) $(RCFLAGS) -o $@ $< +%.h: %.idl + $(WIDL) $(IDLFLAGS) -h -o $@ $< +%_c.c: %.idl + $(WIDL) $(IDLFLAGS) -c -o $@ $< +%_i.c: %.idl + $(WIDL) $(IDLFLAGS) -u -o $@ $< +%_p.c: %.idl + $(WIDL) $(IDLFLAGS) -p -o $@ $< +%_r.res: %.idl + $(WIDL) $(IDLFLAGS) -r -o $@ $< +%_s.c: %.idl + $(WIDL) $(IDLFLAGS) -s -o $@ $< +%.tlb: %.idl + $(WIDL) $(TARGETFLAGS) $(IDLFLAGS) -t -o $@ $< +%_t.res: %.idl + $(WIDL) $(TARGETFLAGS) $(IDLFLAGS) -t -o $@ $< diff --git a/libmsi/action.c b/libmsi/action.c new file mode 100644 index 0000000..f29be2b --- /dev/null +++ b/libmsi/action.c @@ -0,0 +1,7682 @@ +/* + * 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 "msipriv.h" +#include "winuser.h" +#include "shlobj.h" +#include "objbase.h" +#include "mscoree.h" +#include "shlwapi.h" +#include "imagehlp.h" +#include "wine/unicode.h" +#include "winver.h" + +#define REG_PROGRESS_VALUE 13200 +#define COMPONENT_PROGRESS_VALUE 24000 + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static const WCHAR szCreateFolders[] = + {'C','r','e','a','t','e','F','o','l','d','e','r','s',0}; +static const WCHAR szCostFinalize[] = + {'C','o','s','t','F','i','n','a','l','i','z','e',0}; +static const WCHAR szWriteRegistryValues[] = + {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0}; +static const WCHAR szFileCost[] = + {'F','i','l','e','C','o','s','t',0}; +static const WCHAR szInstallInitialize[] = + {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0}; +static const WCHAR szInstallValidate[] = + {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0}; +static const WCHAR szLaunchConditions[] = + {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0}; +static const WCHAR szProcessComponents[] = + {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szRegisterTypeLibraries[] = + {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0}; +static const WCHAR szCreateShortcuts[] = + {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0}; +static const WCHAR szPublishProduct[] = + {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0}; +static const WCHAR szWriteIniValues[] = + {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0}; +static const WCHAR szSelfRegModules[] = + {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0}; +static const WCHAR szPublishFeatures[] = + {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; +static const WCHAR szRegisterProduct[] = + {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0}; +static const WCHAR szInstallExecute[] = + {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0}; +static const WCHAR szInstallExecuteAgain[] = + {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0}; +static const WCHAR szInstallFinalize[] = + {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0}; +static const WCHAR szForceReboot[] = + {'F','o','r','c','e','R','e','b','o','o','t',0}; +static const WCHAR szResolveSource[] = + {'R','e','s','o','l','v','e','S','o','u','r','c','e',0}; +static const WCHAR szAllocateRegistrySpace[] = + {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0}; +static const WCHAR szBindImage[] = + {'B','i','n','d','I','m','a','g','e',0}; +static const WCHAR szDeleteServices[] = + {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0}; +static const WCHAR szDisableRollback[] = + {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0}; +static const WCHAR szExecuteAction[] = + {'E','x','e','c','u','t','e','A','c','t','i','o','n',0}; +static const WCHAR szInstallAdminPackage[] = + {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0}; +static const WCHAR szInstallSFPCatalogFile[] = + {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0}; +static const WCHAR szIsolateComponents[] = + {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szMigrateFeatureStates[] = + {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0}; +static const WCHAR szMsiUnpublishAssemblies[] = + {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; +static const WCHAR szInstallODBC[] = + {'I','n','s','t','a','l','l','O','D','B','C',0}; +static const WCHAR szInstallServices[] = + {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0}; +static const WCHAR szPublishComponents[] = + {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szRegisterComPlus[] = + {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; +static const WCHAR szRegisterUser[] = + {'R','e','g','i','s','t','e','r','U','s','e','r',0}; +static const WCHAR szRemoveEnvironmentStrings[] = + {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0}; +static const WCHAR szRemoveExistingProducts[] = + {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0}; +static const WCHAR szRemoveFolders[] = + {'R','e','m','o','v','e','F','o','l','d','e','r','s',0}; +static const WCHAR szRemoveIniValues[] = + {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0}; +static const WCHAR szRemoveODBC[] = + {'R','e','m','o','v','e','O','D','B','C',0}; +static const WCHAR szRemoveRegistryValues[] = + {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0}; +static const WCHAR szRemoveShortcuts[] = + {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0}; +static const WCHAR szRMCCPSearch[] = + {'R','M','C','C','P','S','e','a','r','c','h',0}; +static const WCHAR szScheduleReboot[] = + {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0}; +static const WCHAR szSelfUnregModules[] = + {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0}; +static const WCHAR szSetODBCFolders[] = + {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0}; +static const WCHAR szStartServices[] = + {'S','t','a','r','t','S','e','r','v','i','c','e','s',0}; +static const WCHAR szStopServices[] = + {'S','t','o','p','S','e','r','v','i','c','e','s',0}; +static const WCHAR szUnpublishComponents[] = + {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szUnpublishFeatures[] = + {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; +static const WCHAR szUnregisterComPlus[] = + {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; +static const WCHAR szUnregisterTypeLibraries[] = + {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0}; +static const WCHAR szValidateProductID[] = + {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0}; +static const WCHAR szWriteEnvironmentStrings[] = + {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0}; + +static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action) +{ + static const WCHAR Query_t[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', + 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', + ' ','\'','%','s','\'',0}; + MSIRECORD * row; + + row = MSI_QueryGetRecord( package->db, Query_t, action ); + if (!row) + return; + MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row); + msiobj_release(&row->hdr); +} + +static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, + UINT rc) +{ + MSIRECORD * row; + static const WCHAR template_s[]= + {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ', + '%','s', '.',0}; + static const WCHAR template_e[]= + {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ', + '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ', + '%','i','.',0}; + static const WCHAR format[] = + {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0}; + WCHAR message[1024]; + WCHAR timet[0x100]; + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100); + if (start) + sprintfW(message,template_s,timet,action); + else + sprintfW(message,template_e,timet,action,rc); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,message); + + MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row); + msiobj_release(&row->hdr); +} + +enum parse_state +{ + state_whitespace, + state_token, + state_quote +}; + +static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes ) +{ + enum parse_state state = state_quote; + const WCHAR *p; + WCHAR *out = value; + int ignore, in_quotes = 0, count = 0, len = 0; + + for (p = str; *p; p++) + { + ignore = 0; + switch (state) + { + case state_whitespace: + switch (*p) + { + case ' ': + in_quotes = 1; + ignore = 1; + len++; + break; + case '"': + state = state_quote; + if (in_quotes && p[1] != '\"') count--; + else count++; + break; + default: + state = state_token; + in_quotes = 1; + len++; + break; + } + break; + + case state_token: + switch (*p) + { + case '"': + state = state_quote; + if (in_quotes) count--; + else count++; + break; + case ' ': + state = state_whitespace; + if (!count) goto done; + in_quotes = 1; + len++; + break; + default: + if (!count) in_quotes = 0; + else in_quotes = 1; + len++; + break; + } + break; + + case state_quote: + switch (*p) + { + case '"': + if (in_quotes && p[1] != '\"') count--; + else count++; + break; + case ' ': + state = state_whitespace; + if (!count || (count > 1 && !len)) goto done; + in_quotes = 1; + len++; + break; + default: + state = state_token; + if (!count) in_quotes = 0; + else in_quotes = 1; + len++; + break; + } + break; + + default: break; + } + if (!ignore) *out++ = *p; + } + +done: + if (!len) *value = 0; + else *out = 0; + + *quotes = count; + return p - str; +} + +static void remove_quotes( WCHAR *str ) +{ + WCHAR *p = str; + int len = strlenW( str ); + + while ((p = strchrW( p, '"' ))) + { + memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) ); + p++; + } +} + +UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine, + BOOL preserve_case ) +{ + LPCWSTR ptr, ptr2; + int num_quotes; + DWORD len; + WCHAR *prop, *val; + UINT r; + + if (!szCommandLine) + return ERROR_SUCCESS; + + ptr = szCommandLine; + while (*ptr) + { + while (*ptr == ' ') ptr++; + if (!*ptr) break; + + ptr2 = strchrW( ptr, '=' ); + if (!ptr2) return ERROR_INVALID_COMMAND_LINE; + + len = ptr2 - ptr; + if (!len) return ERROR_INVALID_COMMAND_LINE; + + prop = msi_alloc( (len + 1) * sizeof(WCHAR) ); + memcpy( prop, ptr, len * sizeof(WCHAR) ); + prop[len] = 0; + if (!preserve_case) struprW( prop ); + + ptr2++; + while (*ptr2 == ' ') ptr2++; + + num_quotes = 0; + val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) ); + len = parse_prop( ptr2, val, &num_quotes ); + if (num_quotes % 2) + { + WARN("unbalanced quotes\n"); + msi_free( val ); + msi_free( prop ); + return ERROR_INVALID_COMMAND_LINE; + } + remove_quotes( val ); + TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val)); + + r = msi_set_property( package->db, prop, val ); + if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir )) + msi_reset_folders( package, TRUE ); + + msi_free( val ); + msi_free( prop ); + + ptr = ptr2 + len; + } + + return ERROR_SUCCESS; +} + +WCHAR **msi_split_string( const WCHAR *str, WCHAR sep ) +{ + LPCWSTR pc; + LPWSTR p, *ret = NULL; + UINT count = 0; + + if (!str) + return ret; + + /* count the number of substrings */ + for ( pc = str, count = 0; pc; count++ ) + { + pc = strchrW( pc, sep ); + if (pc) + pc++; + } + + /* allocate space for an array of substring pointers and the substrings */ + ret = msi_alloc( (count+1) * sizeof (LPWSTR) + + (lstrlenW(str)+1) * sizeof(WCHAR) ); + if (!ret) + return ret; + + /* copy the string and set the pointers */ + p = (LPWSTR) &ret[count+1]; + lstrcpyW( p, str ); + for( count = 0; (ret[count] = p); count++ ) + { + p = strchrW( p, sep ); + if (p) + *p++ = 0; + } + + return ret; +} + +static BOOL ui_sequence_exists( MSIPACKAGE *package ) +{ + static const WCHAR query [] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ', + 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc == ERROR_SUCCESS) + { + msiobj_release(&view->hdr); + return TRUE; + } + return FALSE; +} + +UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace) +{ + LPWSTR source, check; + + if (msi_get_property_int( package->db, szInstalled, 0 )) + { + HKEY hkey; + + MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ); + source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW ); + RegCloseKey( hkey ); + } + else + { + LPWSTR p, db; + DWORD len; + + db = msi_dup_property( package->db, szOriginalDatabase ); + if (!db) + return ERROR_OUTOFMEMORY; + + p = strrchrW( db, '\\' ); + if (!p) + { + p = strrchrW( db, '/' ); + if (!p) + { + msi_free(db); + return ERROR_SUCCESS; + } + } + + len = p - db + 2; + source = msi_alloc( len * sizeof(WCHAR) ); + lstrcpynW( source, db, len ); + msi_free( db ); + } + + check = msi_dup_property( package->db, szSourceDir ); + if (!check || replace) + { + UINT r = msi_set_property( package->db, szSourceDir, source ); + 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 ); + + msi_free( check ); + msi_free( source ); + + return ERROR_SUCCESS; +} + +static BOOL needs_ui_sequence(MSIPACKAGE *package) +{ + return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED; +} + +UINT msi_set_context(MSIPACKAGE *package) +{ + UINT r = msi_locate_product( package->ProductCode, &package->Context ); + if (r != ERROR_SUCCESS) + { + int num = msi_get_property_int( package->db, szAllUsers, 0 ); + if (num == 1 || num == 2) + package->Context = MSIINSTALLCONTEXT_MACHINE; + else + package->Context = MSIINSTALLCONTEXT_USERUNMANAGED; + } + return ERROR_SUCCESS; +} + +static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param) +{ + UINT rc; + LPCWSTR cond, action; + MSIPACKAGE *package = param; + + action = MSI_RecordGetString(row,1); + if (!action) + { + ERR("Error is retrieving action name\n"); + return ERROR_FUNCTION_FAILED; + } + + /* check conditions */ + cond = MSI_RecordGetString(row,2); + + /* this is a hack to skip errors in the condition code */ + if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE) + { + TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action)); + return ERROR_SUCCESS; + } + + if (needs_ui_sequence(package)) + rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE); + else + rc = ACTION_PerformAction(package, action, SCRIPT_NONE); + + msi_dialog_check_messages( NULL ); + + if (package->CurrentInstallState != ERROR_SUCCESS) + rc = package->CurrentInstallState; + + if (rc == ERROR_FUNCTION_NOT_CALLED) + rc = ERROR_SUCCESS; + + if (rc != ERROR_SUCCESS) + ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc); + + if (package->need_reboot_now) + { + TRACE("action %s asked for immediate reboot, suspending installation\n", + debugstr_w(action)); + rc = ACTION_ForceReboot( package ); + } + return rc; +} + +UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`', + ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ', + '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`',0}; + MSIQUERY *view; + UINT r; + + TRACE("%p %s\n", package, debugstr_w(table)); + + r = MSI_OpenQuery( package->db, &view, query, table ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package ); + msiobj_release(&view->hdr); + } + return r; +} + +static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', + 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ', + 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; + static const WCHAR query_validate[] = { + 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`', + ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l', + 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ', + 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=', + ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0}; + MSIQUERY *view; + INT seq = 0; + UINT rc; + + if (package->script->ExecuteSequenceRun) + { + TRACE("Execute Sequence already Run\n"); + return ERROR_SUCCESS; + } + + package->script->ExecuteSequenceRun = TRUE; + + /* get the sequence number */ + if (UIran) + { + MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate); + if (!row) return ERROR_FUNCTION_FAILED; + seq = MSI_RecordGetInteger(row,1); + msiobj_release(&row->hdr); + } + rc = MSI_OpenQuery(package->db, &view, query, seq); + if (rc == ERROR_SUCCESS) + { + TRACE("Running the actions\n"); + + msi_set_property(package->db, szSourceDir, NULL); + rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package); + msiobj_release(&view->hdr); + } + return rc; +} + +static UINT ACTION_ProcessUISequence(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ', + 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc == ERROR_SUCCESS) + { + TRACE("Running the actions\n"); + rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package); + msiobj_release(&view->hdr); + } + return rc; +} + +/******************************************************** + * ACTION helper functions and functions that perform the actions + *******************************************************/ +static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action, + UINT* rc, UINT script, BOOL force ) +{ + BOOL ret=FALSE; + UINT arc; + + arc = ACTION_CustomAction(package, action, script, force); + + if (arc != ERROR_CALL_NOT_IMPLEMENTED) + { + *rc = arc; + ret = TRUE; + } + return ret; +} + +MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component ) +{ + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (!strcmpW( Component, comp->Component )) return comp; + } + return NULL; +} + +MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!strcmpW( Feature, feature->Feature )) return feature; + } + return NULL; +} + +MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key ) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (!strcmpW( key, file->File )) return file; + } + return NULL; +} + +MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key ) +{ + MSIFILEPATCH *patch; + + /* FIXME: There might be more than one patch */ + LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) + { + if (!strcmpW( key, patch->File->File )) return patch; + } + return NULL; +} + +MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir ) +{ + MSIFOLDER *folder; + + LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry ) + { + if (!strcmpW( dir, folder->Directory )) return folder; + } + return NULL; +} + +/* + * Recursively create all directories in the path. + * shamelessly stolen from setupapi/queue.c + */ +BOOL msi_create_full_path( const WCHAR *path ) +{ + BOOL ret = TRUE; + WCHAR *new_path; + int len; + + new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) ); + strcpyW( new_path, path ); + + while ((len = strlenW( new_path )) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + + while (!CreateDirectoryW( new_path, NULL )) + { + WCHAR *slash; + DWORD last_error = GetLastError(); + if (last_error == ERROR_ALREADY_EXISTS) break; + if (last_error != ERROR_PATH_NOT_FOUND) + { + ret = FALSE; + break; + } + if (!(slash = strrchrW( new_path, '\\' ))) + { + ret = FALSE; + break; + } + len = slash - new_path; + new_path[len] = 0; + if (!msi_create_full_path( new_path )) + { + ret = FALSE; + break; + } + new_path[len] = '\\'; + } + msi_free( new_path ); + return ret; +} + +void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d ) +{ + MSIRECORD *row; + + row = MSI_CreateRecord( 4 ); + MSI_RecordSetInteger( row, 1, a ); + MSI_RecordSetInteger( row, 2, b ); + MSI_RecordSetInteger( row, 3, c ); + MSI_RecordSetInteger( row, 4, d ); + MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row ); + msiobj_release( &row->hdr ); + + msi_dialog_check_messages( NULL ); +} + +void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record ) +{ + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', + 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0}; + WCHAR message[1024]; + MSIRECORD *row = 0; + DWORD size; + + if (!package->LastAction || strcmpW( package->LastAction, action )) + { + if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return; + + if (MSI_RecordIsNull( row, 3 )) + { + msiobj_release( &row->hdr ); + return; + } + /* update the cached action format */ + msi_free( package->ActionFormat ); + package->ActionFormat = msi_dup_record_field( row, 3 ); + msi_free( package->LastAction ); + package->LastAction = strdupW( action ); + msiobj_release( &row->hdr ); + } + size = 1024; + MSI_RecordSetStringW( record, 0, package->ActionFormat ); + MSI_FormatRecordW( package, record, message, &size ); + row = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( row, 1, message ); + MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row ); + msiobj_release( &row->hdr ); +} + +INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + if (!comp->Enabled) + { + TRACE("component is disabled: %s\n", debugstr_w(comp->Component)); + return INSTALLSTATE_UNKNOWN; + } + if (package->need_rollback) return comp->Installed; + if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT) + { + TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients); + return INSTALLSTATE_UNKNOWN; + } + return comp->ActionRequest; +} + +INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature ) +{ + if (package->need_rollback) return feature->Installed; + return feature->ActionRequest; +} + +static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR dir, component, full_path; + MSIRECORD *uirow; + MSIFOLDER *folder; + MSICOMPONENT *comp; + + component = MSI_RecordGetString(row, 2); + if (!component) + return ERROR_SUCCESS; + + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation: %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + dir = MSI_RecordGetString(row,1); + if (!dir) + { + ERR("Unable to get folder id\n"); + return ERROR_SUCCESS; + } + + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW(uirow, 1, dir); + msi_ui_actiondata(package, szCreateFolders, uirow); + msiobj_release(&uirow->hdr); + + full_path = msi_get_target_folder( package, dir ); + if (!full_path) + { + ERR("Unable to retrieve folder %s\n", debugstr_w(dir)); + return ERROR_SUCCESS; + } + TRACE("folder is %s\n", debugstr_w(full_path)); + + folder = msi_get_loaded_folder( package, dir ); + if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path ); + folder->State = FOLDER_STATE_CREATED; + return ERROR_SUCCESS; +} + +static UINT ACTION_CreateFolders(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package); + msiobj_release(&view->hdr); + return rc; +} + +static void remove_persistent_folder( MSIFOLDER *folder ) +{ + FolderList *fl; + + LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) + { + remove_persistent_folder( fl->folder ); + } + if (folder->persistent && folder->State != FOLDER_STATE_REMOVED) + { + if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED; + } +} + +static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR dir, component, full_path; + MSIRECORD *uirow; + MSIFOLDER *folder; + MSICOMPONENT *comp; + + component = MSI_RecordGetString(row, 2); + if (!component) + return ERROR_SUCCESS; + + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + dir = MSI_RecordGetString( row, 1 ); + if (!dir) + { + ERR("Unable to get folder id\n"); + return ERROR_SUCCESS; + } + + full_path = msi_get_target_folder( package, dir ); + if (!full_path) + { + ERR("Unable to resolve folder %s\n", debugstr_w(dir)); + return ERROR_SUCCESS; + } + TRACE("folder is %s\n", debugstr_w(full_path)); + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, dir ); + msi_ui_actiondata( package, szRemoveFolders, uirow ); + msiobj_release( &uirow->hdr ); + + folder = msi_get_loaded_folder( package, dir ); + remove_persistent_folder( folder ); + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveFolders( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static UINT load_component( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + + comp = msi_alloc_zero( sizeof(MSICOMPONENT) ); + if (!comp) + return ERROR_FUNCTION_FAILED; + + list_add_tail( &package->components, &comp->entry ); + + /* fill in the data */ + comp->Component = msi_dup_record_field( row, 1 ); + + TRACE("Loading Component %s\n", debugstr_w(comp->Component)); + + comp->ComponentId = msi_dup_record_field( row, 2 ); + comp->Directory = msi_dup_record_field( row, 3 ); + comp->Attributes = MSI_RecordGetInteger(row,4); + comp->Condition = msi_dup_record_field( row, 5 ); + comp->KeyPath = msi_dup_record_field( row, 6 ); + + comp->Installed = INSTALLSTATE_UNKNOWN; + comp->Action = INSTALLSTATE_UNKNOWN; + comp->ActionRequest = INSTALLSTATE_UNKNOWN; + + comp->assembly = msi_load_assembly( package, comp ); + return ERROR_SUCCESS; +} + +UINT msi_load_all_components( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','o','m','p','o','n','e','n','t','`',0}; + MSIQUERY *view; + UINT r; + + if (!list_empty(&package->components)) + return ERROR_SUCCESS; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return r; + + if (!msi_init_assembly_caches( package )) + { + ERR("can't initialize assembly caches\n"); + msiobj_release( &view->hdr ); + return ERROR_FUNCTION_FAILED; + } + + r = MSI_IterateRecords(view, NULL, load_component, package); + msiobj_release(&view->hdr); + return r; +} + +typedef struct { + MSIPACKAGE *package; + MSIFEATURE *feature; +} _ilfs; + +static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp ) +{ + ComponentList *cl; + + cl = msi_alloc( sizeof (*cl) ); + if ( !cl ) + return ERROR_NOT_ENOUGH_MEMORY; + cl->component = comp; + list_add_tail( &feature->Components, &cl->entry ); + + return ERROR_SUCCESS; +} + +static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child ) +{ + FeatureList *fl; + + fl = msi_alloc( sizeof(*fl) ); + if ( !fl ) + return ERROR_NOT_ENOUGH_MEMORY; + fl->feature = child; + list_add_tail( &parent->Children, &fl->entry ); + + return ERROR_SUCCESS; +} + +static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param) +{ + _ilfs* ilfs = param; + LPCWSTR component; + MSICOMPONENT *comp; + + component = MSI_RecordGetString(row,1); + + /* check to see if the component is already loaded */ + comp = msi_get_loaded_component( ilfs->package, component ); + if (!comp) + { + WARN("ignoring unknown component %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + add_feature_component( ilfs->feature, comp ); + comp->Enabled = TRUE; + + return ERROR_SUCCESS; +} + +static UINT load_feature(MSIRECORD * row, LPVOID param) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`', + ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e', + 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ', + '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0}; + MSIPACKAGE *package = param; + MSIFEATURE *feature; + MSIQUERY *view; + _ilfs ilfs; + UINT rc; + + /* fill in the data */ + + feature = msi_alloc_zero( sizeof (MSIFEATURE) ); + if (!feature) + return ERROR_NOT_ENOUGH_MEMORY; + + list_init( &feature->Children ); + list_init( &feature->Components ); + + feature->Feature = msi_dup_record_field( row, 1 ); + + TRACE("Loading feature %s\n",debugstr_w(feature->Feature)); + + feature->Feature_Parent = msi_dup_record_field( row, 2 ); + feature->Title = msi_dup_record_field( row, 3 ); + feature->Description = msi_dup_record_field( row, 4 ); + + if (!MSI_RecordIsNull(row,5)) + feature->Display = MSI_RecordGetInteger(row,5); + + feature->Level= MSI_RecordGetInteger(row,6); + feature->Directory = msi_dup_record_field( row, 7 ); + feature->Attributes = MSI_RecordGetInteger(row,8); + + feature->Installed = INSTALLSTATE_UNKNOWN; + feature->Action = INSTALLSTATE_UNKNOWN; + feature->ActionRequest = INSTALLSTATE_UNKNOWN; + + list_add_tail( &package->features, &feature->entry ); + + /* load feature components */ + + rc = MSI_OpenQuery( package->db, &view, query, feature->Feature ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + ilfs.package = package; + ilfs.feature = feature; + + rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs); + msiobj_release(&view->hdr); + return rc; +} + +static UINT find_feature_children(MSIRECORD * row, LPVOID param) +{ + MSIPACKAGE *package = param; + MSIFEATURE *parent, *child; + + child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) ); + if (!child) + return ERROR_FUNCTION_FAILED; + + if (!child->Feature_Parent) + return ERROR_SUCCESS; + + parent = msi_get_loaded_feature( package, child->Feature_Parent ); + if (!parent) + return ERROR_FUNCTION_FAILED; + + add_feature_child( parent, child ); + return ERROR_SUCCESS; +} + +UINT msi_load_all_features( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ', + '`','D','i','s','p','l','a','y','`',0}; + MSIQUERY *view; + UINT r; + + if (!list_empty(&package->features)) + return ERROR_SUCCESS; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_IterateRecords( view, NULL, load_feature, package ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return r; + } + r = MSI_IterateRecords( view, NULL, find_feature_children, package ); + msiobj_release( &view->hdr ); + return r; +} + +static LPWSTR folder_split_path(LPWSTR p, WCHAR ch) +{ + if (!p) + return p; + p = strchrW(p, ch); + if (!p) + return p; + *p = 0; + return p+1; +} + +static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','s','i','F','i','l','e','H','a','s','h','`',' ', + 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0}; + MSIQUERY *view = NULL; + MSIRECORD *row = NULL; + UINT r; + + TRACE("%s\n", debugstr_w(file->File)); + + r = MSI_OpenQuery(package->db, &view, query, file->File); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewExecute(view, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(view, &row); + if (r != ERROR_SUCCESS) + goto done; + + file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + file->hash.dwData[0] = MSI_RecordGetInteger(row, 3); + file->hash.dwData[1] = MSI_RecordGetInteger(row, 4); + file->hash.dwData[2] = MSI_RecordGetInteger(row, 5); + file->hash.dwData[3] = MSI_RecordGetInteger(row, 6); + +done: + if (view) msiobj_release(&view->hdr); + if (row) msiobj_release(&row->hdr); + return r; +} + +static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file ) +{ + MSIRECORD *row; + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0}; + + row = MSI_QueryGetRecord( package->db, query, file->Sequence ); + if (!row) + { + WARN("query failed\n"); + return ERROR_FUNCTION_FAILED; + } + + file->disk_id = MSI_RecordGetInteger( row, 1 ); + msiobj_release( &row->hdr ); + return ERROR_SUCCESS; +} + +static UINT load_file(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = param; + LPCWSTR component; + MSIFILE *file; + + /* fill in the data */ + + file = msi_alloc_zero( sizeof (MSIFILE) ); + if (!file) + return ERROR_NOT_ENOUGH_MEMORY; + + file->File = msi_dup_record_field( row, 1 ); + + component = MSI_RecordGetString( row, 2 ); + file->Component = msi_get_loaded_component( package, component ); + + if (!file->Component) + { + WARN("Component not found: %s\n", debugstr_w(component)); + msi_free(file->File); + msi_free(file); + return ERROR_SUCCESS; + } + + file->FileName = msi_dup_record_field( row, 3 ); + msi_reduce_to_long_filename( file->FileName ); + + file->ShortName = msi_dup_record_field( row, 3 ); + file->LongName = strdupW( folder_split_path(file->ShortName, '|')); + + file->FileSize = MSI_RecordGetInteger( row, 4 ); + file->Version = msi_dup_record_field( row, 5 ); + file->Language = msi_dup_record_field( row, 6 ); + file->Attributes = MSI_RecordGetInteger( row, 7 ); + file->Sequence = MSI_RecordGetInteger( row, 8 ); + + file->state = msifs_invalid; + + /* if the compressed bits are not set in the file attributes, + * then read the information from the package word count property + */ + if (package->WordCount & msidbSumInfoSourceTypeAdminImage) + { + file->IsCompressed = FALSE; + } + else if (file->Attributes & + (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded)) + { + file->IsCompressed = TRUE; + } + else if (file->Attributes & msidbFileAttributesNoncompressed) + { + file->IsCompressed = FALSE; + } + else + { + file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed; + } + + load_file_hash(package, file); + load_file_disk_id(package, file); + + TRACE("File Loaded (%s)\n",debugstr_w(file->File)); + + list_add_tail( &package->files, &file->entry ); + + return ERROR_SUCCESS; +} + +static UINT load_all_files(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`', 0}; + MSIQUERY *view; + UINT rc; + + if (!list_empty(&package->files)) + return ERROR_SUCCESS; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, load_file, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT load_media( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + UINT disk_id = MSI_RecordGetInteger( row, 1 ); + const WCHAR *cabinet = MSI_RecordGetString( row, 4 ); + + /* FIXME: load external cabinets and directory sources too */ + if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS; + msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet ); + return ERROR_SUCCESS; +} + +static UINT load_all_media( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`', + 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ', + '`','D','i','s','k','I','d','`',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = MSI_IterateRecords( view, NULL, load_media, package ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT load_patch(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + MSIFILEPATCH *patch; + LPWSTR file_key; + + patch = msi_alloc_zero( sizeof (MSIFILEPATCH) ); + if (!patch) + return ERROR_NOT_ENOUGH_MEMORY; + + file_key = msi_dup_record_field( row, 1 ); + patch->File = msi_get_loaded_file( package, file_key ); + msi_free(file_key); + + if( !patch->File ) + { + ERR("Failed to find target for patch in File table\n"); + msi_free(patch); + return ERROR_FUNCTION_FAILED; + } + + patch->Sequence = MSI_RecordGetInteger( row, 2 ); + + /* FIXME: The database should be properly transformed */ + patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; + + patch->PatchSize = MSI_RecordGetInteger( row, 3 ); + patch->Attributes = MSI_RecordGetInteger( row, 4 ); + patch->IsApplied = FALSE; + + /* FIXME: + * Header field - for patch validation. + * _StreamRef - External key into MsiPatchHeaders (instead of the header field) + */ + + TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File)); + + list_add_tail( &package->filepatches, &patch->entry ); + + return ERROR_SUCCESS; +} + +static UINT load_all_patches(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`',0}; + MSIQUERY *view; + UINT rc; + + if (!list_empty(&package->filepatches)) + return ERROR_SUCCESS; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, load_patch, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ', + '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0}; + MSIQUERY *view; + + folder->persistent = FALSE; + if (!MSI_OpenQuery( package->db, &view, query, folder->Directory )) + { + if (!MSI_ViewExecute( view, NULL )) + { + MSIRECORD *rec; + if (!MSI_ViewFetch( view, &rec )) + { + TRACE("directory %s is persistent\n", debugstr_w(folder->Directory)); + folder->persistent = TRUE; + msiobj_release( &rec->hdr ); + } + } + msiobj_release( &view->hdr ); + } + return ERROR_SUCCESS; +} + +static UINT load_folder( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + static WCHAR szEmpty[] = { 0 }; + LPWSTR p, tgt_short, tgt_long, src_short, src_long; + MSIFOLDER *folder; + + if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY; + list_init( &folder->children ); + folder->Directory = msi_dup_record_field( row, 1 ); + folder->Parent = msi_dup_record_field( row, 2 ); + p = msi_dup_record_field(row, 3); + + TRACE("%s\n", debugstr_w(folder->Directory)); + + /* split src and target dir */ + tgt_short = p; + src_short = folder_split_path( p, ':' ); + + /* split the long and short paths */ + tgt_long = folder_split_path( tgt_short, '|' ); + src_long = folder_split_path( src_short, '|' ); + + /* check for no-op dirs */ + if (tgt_short && !strcmpW( szDot, tgt_short )) + tgt_short = szEmpty; + if (src_short && !strcmpW( szDot, src_short )) + src_short = szEmpty; + + if (!tgt_long) + tgt_long = tgt_short; + + if (!src_short) { + src_short = tgt_short; + src_long = tgt_long; + } + + if (!src_long) + src_long = src_short; + + /* FIXME: use the target short path too */ + folder->TargetDefault = strdupW(tgt_long); + folder->SourceShortPath = strdupW(src_short); + folder->SourceLongPath = strdupW(src_long); + msi_free(p); + + TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault )); + TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath )); + TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath )); + + load_folder_persistence( package, folder ); + + list_add_tail( &package->folders, &folder->entry ); + return ERROR_SUCCESS; +} + +static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child ) +{ + FolderList *fl; + + if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY; + fl->folder = child; + list_add_tail( &parent->children, &fl->entry ); + return ERROR_SUCCESS; +} + +static UINT find_folder_children( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSIFOLDER *parent, *child; + + if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) ))) + return ERROR_FUNCTION_FAILED; + + if (!child->Parent) return ERROR_SUCCESS; + + if (!(parent = msi_get_loaded_folder( package, child->Parent ))) + return ERROR_FUNCTION_FAILED; + + return add_folder_child( parent, child ); +} + +static UINT load_all_folders( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','i','r','e','c','t','o','r','y','`',0}; + MSIQUERY *view; + UINT r; + + if (!list_empty(&package->folders)) + return ERROR_SUCCESS; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_IterateRecords( view, NULL, load_folder, package ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return r; + } + r = MSI_IterateRecords( view, NULL, find_folder_children, package ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT ACTION_CostInitialize(MSIPACKAGE *package) +{ + msi_set_property( package->db, szCostingComplete, szZero ); + msi_set_property( package->db, szRootDrive, szCRoot ); + + load_all_folders( package ); + msi_load_all_components( package ); + msi_load_all_features( package ); + load_all_files( package ); + load_all_patches( package ); + load_all_media( package ); + + return ERROR_SUCCESS; +} + +static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index ) +{ + const WCHAR *action = package->script->Actions[script][index]; + ui_actionstart( package, action ); + TRACE("executing %s\n", debugstr_w(action)); + return ACTION_PerformAction( package, action, script ); +} + +static UINT execute_script( MSIPACKAGE *package, UINT script ) +{ + UINT i, rc = ERROR_SUCCESS; + + TRACE("executing script %u\n", script); + + if (!package->script) + { + ERR("no script!\n"); + return ERROR_FUNCTION_FAILED; + } + if (script == SCRIPT_ROLLBACK) + { + for (i = package->script->ActionCount[script]; i > 0; i--) + { + rc = execute_script_action( package, script, i - 1 ); + if (rc != ERROR_SUCCESS) break; + } + } + else + { + for (i = 0; i < package->script->ActionCount[script]; i++) + { + rc = execute_script_action( package, script, i ); + if (rc != ERROR_SUCCESS) break; + } + } + msi_free_action_script(package, script); + return rc; +} + +static UINT ACTION_FileCost(MSIPACKAGE *package) +{ + return ERROR_SUCCESS; +} + +static void get_client_counts( MSIPACKAGE *package ) +{ + MSICOMPONENT *comp; + HKEY hkey; + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (!comp->ComponentId) continue; + + if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) && + MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE )) + { + comp->num_clients = 0; + continue; + } + RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients, + NULL, NULL, NULL, NULL ); + RegCloseKey( hkey ); + } +} + +static void ACTION_GetComponentInstallStates(MSIPACKAGE *package) +{ + MSICOMPONENT *comp; + UINT r; + + LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) + { + if (!comp->ComponentId) continue; + + r = MsiQueryComponentStateW( package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId, + &comp->Installed ); + if (r == ERROR_SUCCESS) continue; + + r = MsiQueryComponentStateW( package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId, + &comp->Installed ); + if (r == ERROR_SUCCESS) continue; + + r = MsiQueryComponentStateW( package->ProductCode, NULL, + MSIINSTALLCONTEXT_MACHINE, comp->ComponentId, + &comp->Installed ); + if (r == ERROR_SUCCESS) continue; + + comp->Installed = INSTALLSTATE_ABSENT; + } +} + +static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature ); + + if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG) + feature->Installed = INSTALLSTATE_ABSENT; + else + feature->Installed = state; + } +} + +static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level ) +{ + return (feature->Level > 0 && feature->Level <= level); +} + +static BOOL process_state_property(MSIPACKAGE* package, int level, + LPCWSTR property, INSTALLSTATE state) +{ + LPWSTR override; + MSIFEATURE *feature; + + override = msi_dup_property( package->db, property ); + if (!override) + return FALSE; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level )) + continue; + + if (!strcmpW(property, szReinstall)) state = feature->Installed; + + if (!strcmpiW( override, szAll )) + { + if (feature->Installed != state) + { + feature->Action = state; + feature->ActionRequest = state; + } + } + else + { + LPWSTR ptr = override; + LPWSTR ptr2 = strchrW(override,','); + + while (ptr) + { + int len = ptr2 - ptr; + + if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len)) + || (!ptr2 && !strcmpW(ptr, feature->Feature))) + { + if (feature->Installed != state) + { + feature->Action = state; + feature->ActionRequest = state; + } + break; + } + if (ptr2) + { + ptr=ptr2+1; + ptr2 = strchrW(ptr,','); + } + else + break; + } + } + } + msi_free(override); + return TRUE; +} + +static BOOL process_overrides( MSIPACKAGE *package, int level ) +{ + static const WCHAR szAddLocal[] = + {'A','D','D','L','O','C','A','L',0}; + static const WCHAR szAddSource[] = + {'A','D','D','S','O','U','R','C','E',0}; + static const WCHAR szAdvertise[] = + {'A','D','V','E','R','T','I','S','E',0}; + BOOL ret = FALSE; + + /* all these activation/deactivation things happen in order and things + * later on the list override things earlier on the list. + * + * 0 INSTALLLEVEL processing + * 1 ADDLOCAL + * 2 REMOVE + * 3 ADDSOURCE + * 4 ADDDEFAULT + * 5 REINSTALL + * 6 ADVERTISE + * 7 COMPADDLOCAL + * 8 COMPADDSOURCE + * 9 FILEADDLOCAL + * 10 FILEADDSOURCE + * 11 FILEADDDEFAULT + */ + ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL ); + ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT ); + ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE ); + ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN ); + ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED ); + + if (ret) + msi_set_property( package->db, szPreselected, szOne ); + + return ret; +} + +UINT MSI_SetFeatureStates(MSIPACKAGE *package) +{ + int level; + MSICOMPONENT* component; + MSIFEATURE *feature; + + TRACE("Checking Install Level\n"); + + level = msi_get_property_int(package->db, szInstallLevel, 1); + + if (!msi_get_property_int( package->db, szPreselected, 0 )) + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!is_feature_selected( feature, level )) continue; + + if (feature->ActionRequest == INSTALLSTATE_UNKNOWN) + { + if (feature->Attributes & msidbFeatureAttributesFavorSource) + { + feature->Action = INSTALLSTATE_SOURCE; + feature->ActionRequest = INSTALLSTATE_SOURCE; + } + else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) + { + feature->Action = INSTALLSTATE_ADVERTISED; + feature->ActionRequest = INSTALLSTATE_ADVERTISED; + } + else + { + feature->Action = INSTALLSTATE_LOCAL; + feature->ActionRequest = INSTALLSTATE_LOCAL; + } + } + } + /* disable child features of unselected parent or follow parent */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + FeatureList *fl; + + LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) + { + if (!is_feature_selected( feature, level )) + { + fl->feature->Action = INSTALLSTATE_UNKNOWN; + fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN; + } + else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent) + { + TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n", + debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest, + debugstr_w(feature->Feature), feature->Level, feature->ActionRequest); + fl->feature->Action = feature->Action; + fl->feature->ActionRequest = feature->ActionRequest; + } + } + } + } + else /* preselected */ + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!is_feature_selected( feature, level )) continue; + + if (feature->ActionRequest == INSTALLSTATE_UNKNOWN) + { + if (feature->Installed == INSTALLSTATE_ABSENT) + { + feature->Action = INSTALLSTATE_UNKNOWN; + feature->ActionRequest = INSTALLSTATE_UNKNOWN; + } + else + { + feature->Action = feature->Installed; + feature->ActionRequest = feature->Installed; + } + } + } + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + FeatureList *fl; + + LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry ) + { + if (fl->feature->Attributes & msidbFeatureAttributesFollowParent && + (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise))) + { + TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n", + debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest, + debugstr_w(feature->Feature), feature->Level, feature->ActionRequest); + fl->feature->Action = feature->Action; + fl->feature->ActionRequest = feature->ActionRequest; + } + } + } + } + + /* now we want to set component state based based on feature state */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + TRACE("examining feature %s (level %d installed %d request %d action %d)\n", + debugstr_w(feature->Feature), feature->Level, feature->Installed, + feature->ActionRequest, feature->Action); + + if (!is_feature_selected( feature, level )) continue; + + /* features with components that have compressed files are made local */ + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + if (cl->component->ForceLocalState && + feature->ActionRequest == INSTALLSTATE_SOURCE) + { + feature->Action = INSTALLSTATE_LOCAL; + feature->ActionRequest = INSTALLSTATE_LOCAL; + break; + } + } + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + component = cl->component; + + switch (feature->ActionRequest) + { + case INSTALLSTATE_ABSENT: + component->anyAbsent = 1; + break; + case INSTALLSTATE_ADVERTISED: + component->hasAdvertiseFeature = 1; + break; + case INSTALLSTATE_SOURCE: + component->hasSourceFeature = 1; + break; + case INSTALLSTATE_LOCAL: + component->hasLocalFeature = 1; + break; + case INSTALLSTATE_DEFAULT: + if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) + component->hasAdvertiseFeature = 1; + else if (feature->Attributes & msidbFeatureAttributesFavorSource) + component->hasSourceFeature = 1; + else + component->hasLocalFeature = 1; + break; + default: + break; + } + } + } + + LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry ) + { + /* check if it's local or source */ + if (!(component->Attributes & msidbComponentAttributesOptional) && + (component->hasLocalFeature || component->hasSourceFeature)) + { + if ((component->Attributes & msidbComponentAttributesSourceOnly) && + !component->ForceLocalState) + { + component->Action = INSTALLSTATE_SOURCE; + component->ActionRequest = INSTALLSTATE_SOURCE; + } + else + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + continue; + } + + /* if any feature is local, the component must be local too */ + if (component->hasLocalFeature) + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + continue; + } + if (component->hasSourceFeature) + { + component->Action = INSTALLSTATE_SOURCE; + component->ActionRequest = INSTALLSTATE_SOURCE; + continue; + } + if (component->hasAdvertiseFeature) + { + component->Action = INSTALLSTATE_ADVERTISED; + component->ActionRequest = INSTALLSTATE_ADVERTISED; + continue; + } + TRACE("nobody wants component %s\n", debugstr_w(component->Component)); + if (component->anyAbsent && + (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE)) + { + component->Action = INSTALLSTATE_ABSENT; + component->ActionRequest = INSTALLSTATE_ABSENT; + } + } + + LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry ) + { + if (component->ActionRequest == INSTALLSTATE_DEFAULT) + { + TRACE("%s was default, setting to local\n", debugstr_w(component->Component)); + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + + if (component->ActionRequest == INSTALLSTATE_SOURCE && + component->Installed == INSTALLSTATE_SOURCE && + component->hasSourceFeature) + { + component->Action = INSTALLSTATE_UNKNOWN; + component->ActionRequest = INSTALLSTATE_UNKNOWN; + } + + TRACE("component %s (installed %d request %d action %d)\n", + debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action); + + if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE) + component->num_clients++; + else if (component->Action == INSTALLSTATE_ABSENT) + component->num_clients--; + } + + return ERROR_SUCCESS; +} + +static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR name; + MSIFEATURE *feature; + + name = MSI_RecordGetString( row, 1 ); + + feature = msi_get_loaded_feature( package, name ); + if (!feature) + ERR("FAILED to find loaded feature %s\n",debugstr_w(name)); + else + { + LPCWSTR Condition; + Condition = MSI_RecordGetString(row,3); + + if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE) + { + int level = MSI_RecordGetInteger(row,2); + TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level); + feature->Level = level; + } + } + return ERROR_SUCCESS; +} + +VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename ) +{ + static const WCHAR name[] = {'\\',0}; + VS_FIXEDFILEINFO *ptr, *ret; + LPVOID version; + DWORD versize, handle; + UINT sz; + + versize = GetFileVersionInfoSizeW( filename, &handle ); + if (!versize) + return NULL; + + version = msi_alloc( versize ); + if (!version) + return NULL; + + GetFileVersionInfoW( filename, 0, versize, version ); + + if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz )) + { + msi_free( version ); + return NULL; + } + + ret = msi_alloc( sz ); + memcpy( ret, ptr, sz ); + + msi_free( version ); + return ret; +} + +int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version ) +{ + DWORD ms, ls; + + msi_parse_version_string( version, &ms, &ls ); + + if (fi->dwFileVersionMS > ms) return 1; + else if (fi->dwFileVersionMS < ms) return -1; + else if (fi->dwFileVersionLS > ls) return 1; + else if (fi->dwFileVersionLS < ls) return -1; + return 0; +} + +int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 ) +{ + DWORD ms1, ms2; + + msi_parse_version_string( ver1, &ms1, NULL ); + msi_parse_version_string( ver2, &ms2, NULL ); + + if (ms1 > ms2) return 1; + else if (ms1 < ms2) return -1; + return 0; +} + +DWORD msi_get_disk_file_size( LPCWSTR filename ) +{ + HANDLE file; + DWORD size; + + file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + if (file == INVALID_HANDLE_VALUE) + return INVALID_FILE_SIZE; + + size = GetFileSize( file, NULL ); + TRACE("size is %u\n", size); + CloseHandle( file ); + return size; +} + +BOOL msi_file_hash_matches( MSIFILE *file ) +{ + UINT r; + MSIFILEHASHINFO hash; + + hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + r = MsiGetFileHashW( file->TargetPath, 0, &hash ); + if (r != ERROR_SUCCESS) + return FALSE; + + return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) ); +} + +static WCHAR *get_temp_dir( void ) +{ + static UINT id; + WCHAR tmp[MAX_PATH], dir[MAX_PATH]; + + GetTempPathW( MAX_PATH, tmp ); + for (;;) + { + if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL; + if (CreateDirectoryW( dir, NULL )) break; + } + return strdupW( dir ); +} + +/* + * msi_build_directory_name() + * + * This function is to save messing round with directory names + * It handles adding backslashes between path segments, + * and can add \ at the end of the directory name if told to. + * + * It takes a variable number of arguments. + * It always allocates a new string for the result, so make sure + * to free the return value when finished with it. + * + * The first arg is the number of path segments that follow. + * The arguments following count are a list of path segments. + * A path segment may be NULL. + * + * Path segments will be added with a \ separating them. + * A \ will not be added after the last segment, however if the + * last segment is NULL, then the last character will be a \ + */ +WCHAR *msi_build_directory_name( DWORD count, ... ) +{ + DWORD sz = 1, i; + WCHAR *dir; + va_list va; + + va_start( va, count ); + for (i = 0; i < count; i++) + { + const WCHAR *str = va_arg( va, const WCHAR * ); + if (str) sz += strlenW( str ) + 1; + } + va_end( va ); + + dir = msi_alloc( sz * sizeof(WCHAR) ); + dir[0] = 0; + + va_start( va, count ); + for (i = 0; i < count; i++) + { + const WCHAR *str = va_arg( va, const WCHAR * ); + if (!str) continue; + strcatW( dir, str ); + if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash ); + } + va_end( va ); + return dir; +} + +static void set_target_path( MSIPACKAGE *package, MSIFILE *file ) +{ + MSIASSEMBLY *assembly = file->Component->assembly; + + TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName)); + + msi_free( file->TargetPath ); + if (assembly && !assembly->application) + { + if (!assembly->tempdir) assembly->tempdir = get_temp_dir(); + file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName ); + msi_track_tempfile( package, file->TargetPath ); + } + else + { + const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory ); + file->TargetPath = msi_build_directory_name( 2, dir, file->FileName ); + } + + TRACE("resolves to %s\n", debugstr_w(file->TargetPath)); +} + +static UINT calculate_file_cost( MSIPACKAGE *package ) +{ + VS_FIXEDFILEINFO *file_version; + WCHAR *font_version; + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + MSICOMPONENT *comp = file->Component; + DWORD file_size; + + if (!comp->Enabled) continue; + + if (file->IsCompressed) + comp->ForceLocalState = TRUE; + + set_target_path( package, file ); + + if ((comp->assembly && !comp->assembly->installed) || + GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES) + { + comp->Cost += file->FileSize; + continue; + } + file_size = msi_get_disk_file_size( file->TargetPath ); + + if (file->Version) + { + if ((file_version = msi_get_disk_file_version( file->TargetPath ))) + { + if (msi_compare_file_versions( file_version, file->Version ) < 0) + { + comp->Cost += file->FileSize - file_size; + } + msi_free( file_version ); + continue; + } + else if ((font_version = msi_font_version_from_file( file->TargetPath ))) + { + if (msi_compare_font_versions( font_version, file->Version ) < 0) + { + comp->Cost += file->FileSize - file_size; + } + msi_free( font_version ); + continue; + } + } + if (file_size != file->FileSize) + { + comp->Cost += file->FileSize - file_size; + } + } + return ERROR_SUCCESS; +} + +WCHAR *msi_normalize_path( const WCHAR *in ) +{ + const WCHAR *p = in; + WCHAR *q, *ret; + int n, len = strlenW( in ) + 2; + + if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL; + + len = 0; + while (1) + { + /* copy until the end of the string or a space */ + while (*p != ' ' && (*q = *p)) + { + p++, len++; + /* reduce many backslashes to one */ + if (*p != '\\' || *q != '\\') + q++; + } + + /* quit at the end of the string */ + if (!*p) + break; + + /* count the number of spaces */ + n = 0; + while (p[n] == ' ') + n++; + + /* if it's leading or trailing space, skip it */ + if ( len == 0 || p[-1] == '\\' || p[n] == '\\' ) + p += n; + else /* copy n spaces */ + while (n && (*q++ = *p++)) n--; + } + while (q - ret > 0 && q[-1] == ' ') q--; + if (q - ret > 0 && q[-1] != '\\') + { + q[0] = '\\'; + q[1] = 0; + } + return ret; +} + +static WCHAR *get_install_location( MSIPACKAGE *package ) +{ + HKEY hkey; + WCHAR *path; + + if (!package->ProductCode) return NULL; + if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) + return NULL; + path = msi_reg_get_val_str( hkey, szInstallLocation ); + RegCloseKey( hkey ); + return path; +} + +void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop ) +{ + FolderList *fl; + MSIFOLDER *folder, *parent, *child; + WCHAR *path, *normalized_path; + + TRACE("resolving %s\n", debugstr_w(name)); + + if (!(folder = msi_get_loaded_folder( package, name ))) return; + + if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */ + { + if (!(path = get_install_location( package )) && + (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))) + { + path = msi_dup_property( package->db, szRootDrive ); + } + } + else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory ))) + { + if (folder->Parent && strcmpW( folder->Directory, folder->Parent )) + { + parent = msi_get_loaded_folder( package, folder->Parent ); + path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL ); + } + else + path = msi_build_directory_name( 2, folder->TargetDefault, NULL ); + } + normalized_path = msi_normalize_path( path ); + msi_free( path ); + if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget )) + { + TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget)); + msi_free( normalized_path ); + return; + } + msi_set_property( package->db, folder->Directory, normalized_path ); + msi_free( folder->ResolvedTarget ); + folder->ResolvedTarget = normalized_path; + + LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) + { + child = fl->folder; + msi_resolve_target_folder( package, child->Directory, load_prop ); + } + TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget)); +} + +static UINT ACTION_CostFinalize(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','o','n','d','i','t','i','o','n','`',0}; + static const WCHAR szOutOfDiskSpace[] = { + 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0}; + MSICOMPONENT *comp; + MSIQUERY *view; + LPWSTR level; + UINT rc; + + TRACE("Building directory properties\n"); + msi_resolve_target_folder( package, szTargetDir, TRUE ); + + TRACE("Evaluating component conditions\n"); + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE) + { + TRACE("Disabling component %s\n", debugstr_w(comp->Component)); + comp->Enabled = FALSE; + } + else + comp->Enabled = TRUE; + } + get_client_counts( package ); + + /* read components states from the registry */ + ACTION_GetComponentInstallStates(package); + ACTION_GetFeatureInstallStates(package); + + if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) )) + { + TRACE("Evaluating feature conditions\n"); + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + } + + TRACE("Calculating file cost\n"); + calculate_file_cost( package ); + + msi_set_property( package->db, szCostingComplete, szOne ); + /* set default run level if not set */ + level = msi_dup_property( package->db, szInstallLevel ); + if (!level) + msi_set_property( package->db, szInstallLevel, szOne ); + msi_free(level); + + /* FIXME: check volume disk space */ + msi_set_property( package->db, szOutOfDiskSpace, szZero ); + + return MSI_SetFeatureStates(package); +} + +static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size) +{ + LPSTR data = NULL; + + if (!value) + { + data = (LPSTR)strdupW(szEmpty); + *size = sizeof(szEmpty); + *type = REG_SZ; + 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 + { + static const WCHAR szMulti[] = {'[','~',']',0}; + LPCWSTR ptr; + *type=REG_SZ; + + if (value[0]=='#') + { + if (value[1]=='%') + { + ptr = &value[2]; + *type=REG_EXPAND_SZ; + } + else + ptr = &value[1]; + } + else + ptr=value; + + if (strstrW(value, szMulti)) + *type = REG_MULTI_SZ; + + /* remove initial delimiter */ + if (!strncmpW(value, szMulti, 3)) + ptr = value + 3; + + *size = deformat_string(package, ptr,(LPWSTR*)&data); + + /* add double NULL terminator */ + if (*type == REG_MULTI_SZ) + { + *size += 2 * sizeof(WCHAR); /* two NULL terminators */ + data = msi_realloc_zero(data, *size); + } + } + return data; +} + +static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key ) +{ + const WCHAR *ret; + + switch (root) + { + case -1: + if (msi_get_property_int( package->db, szAllUsers, 0 )) + { + *root_key = HKEY_LOCAL_MACHINE; + ret = szHLM; + } + else + { + *root_key = HKEY_CURRENT_USER; + ret = szHCU; + } + break; + case 0: + *root_key = HKEY_CLASSES_ROOT; + ret = szHCR; + break; + case 1: + *root_key = HKEY_CURRENT_USER; + ret = szHCU; + break; + case 2: + *root_key = HKEY_LOCAL_MACHINE; + ret = szHLM; + break; + case 3: + *root_key = HKEY_USERS; + ret = szHU; + break; + default: + ERR("Unknown root %i\n", root); + return NULL; + } + + return ret; +} + +static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path ) +{ + static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'}; + static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]); + + if ((is_64bit || is_wow64) && + !(comp->Attributes & msidbComponentAttributes64bit) && + root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len )) + { + UINT size; + WCHAR *path_32node; + + size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR); + if (!(path_32node = msi_alloc( size ))) return NULL; + + memcpy( path_32node, path, len * sizeof(WCHAR) ); + strcpyW( path_32node + len, szWow6432Node ); + strcatW( path_32node, szBackSlash ); + strcatW( path_32node, path + len ); + return path_32node; + } + return strdupW( path ); +} + +static HKEY open_key( HKEY root, const WCHAR *path, BOOL create ) +{ + REGSAM access = KEY_ALL_ACCESS; + WCHAR *subkey, *p, *q; + HKEY hkey, ret = NULL; + LONG res; + + if (is_wow64) access |= KEY_WOW64_64KEY; + + if (!(subkey = strdupW( path ))) return NULL; + p = subkey; + if ((q = strchrW( p, '\\' ))) *q = 0; + if (create) + res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL ); + else + res = RegOpenKeyExW( root, subkey, 0, access, &hkey ); + if (res) + { + TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res); + msi_free( subkey ); + return NULL; + } + if (q && q[1]) + { + ret = open_key( hkey, q + 1, create ); + RegCloseKey( hkey ); + } + else ret = hkey; + msi_free( subkey ); + return ret; +} + +static BOOL is_special_entry( const WCHAR *name ) +{ + return (name && (name[0] == '*' || name[0] == '+') && !name[1]); +} + +static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPSTR value; + HKEY root_key, hkey; + DWORD type,size; + LPWSTR deformated, uikey, keypath; + LPCWSTR szRoot, component, name, key; + MSICOMPONENT *comp; + MSIRECORD * uirow; + INT root; + BOOL check_first = FALSE; + UINT rc; + + 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); + size = strlenW(deformated) + strlenW(szRoot) + 1; + uikey = msi_alloc(size*sizeof(WCHAR)); + strcpyW(uikey,szRoot); + strcatW(uikey,deformated); + + keypath = get_keypath( comp, root_key, deformated ); + msi_free( deformated ); + if (!(hkey = open_key( root_key, keypath, TRUE ))) + { + ERR("Could not create key %s\n", debugstr_w(keypath)); + msi_free(uikey); + msi_free(keypath); + return ERROR_FUNCTION_FAILED; + } + value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size); + deformat_string(package, name, &deformated); + + if (!is_special_entry( name )) + { + if (!check_first) + { + TRACE("Setting value %s of %s\n", debugstr_w(deformated), + debugstr_w(uikey)); + RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size); + } + else + { + DWORD sz = 0; + rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz); + if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA) + { + TRACE("value %s of %s checked already exists\n", debugstr_w(deformated), + debugstr_w(uikey)); + } + else + { + TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated), + debugstr_w(uikey)); + if (deformated || size) + RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size); + } + } + } + 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)value); + msi_ui_actiondata( package, szWriteRegistryValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(value); + msi_free(deformated); + msi_free(uikey); + msi_free(keypath); + + return ERROR_SUCCESS; +} + +static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package); + msiobj_release(&view->hdr); + return rc; +} + +static void delete_key( HKEY root, const WCHAR *path ) +{ + REGSAM access = 0; + WCHAR *subkey, *p; + HKEY hkey; + LONG res; + + if (is_wow64) access |= KEY_WOW64_64KEY; + + if (!(subkey = strdupW( path ))) return; + for (;;) + { + if ((p = strrchrW( subkey, '\\' ))) *p = 0; + hkey = open_key( root, subkey, FALSE ); + if (!hkey) break; + if (p && p[1]) + res = RegDeleteKeyExW( hkey, p + 1, access, 0 ); + else + res = RegDeleteKeyExW( root, subkey, access, 0 ); + if (res) + { + TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res); + break; + } + if (p && p[1]) RegCloseKey( hkey ); + else break; + } + msi_free( subkey ); +} + +static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value ) +{ + LONG res; + HKEY hkey; + DWORD num_subkeys, num_values; + + if ((hkey = open_key( root, path, FALSE ))) + { + if ((res = RegDeleteValueW( hkey, value ))) + TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res); + + res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values, + NULL, NULL, NULL, NULL ); + RegCloseKey( hkey ); + if (!res && !num_subkeys && !num_values) + { + TRACE("removing empty key %s\n", debugstr_w(path)); + delete_key( root, path ); + } + } +} + +static void delete_tree( HKEY root, const WCHAR *path ) +{ + LONG res; + HKEY hkey; + + if (!(hkey = open_key( root, path, FALSE ))) return; + res = RegDeleteTreeW( hkey, NULL ); + if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res); + delete_key( root, path ); + RegCloseKey( hkey ); +} + +static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR component, name, key_str, root_key_str; + LPWSTR deformated_key, deformated_name, ui_key_str, keypath; + MSICOMPONENT *comp; + MSIRECORD *uirow; + BOOL delete_key = FALSE; + HKEY hkey_root; + UINT size; + INT root; + + msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 ); + + component = MSI_RecordGetString( row, 6 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + name = MSI_RecordGetString( row, 4 ); + if (MSI_RecordIsNull( row, 5 ) && name ) + { + if (name[0] == '+' && !name[1]) + return ERROR_SUCCESS; + if ((name[0] == '-' || name[0] == '*') && !name[1]) + { + delete_key = TRUE; + name = NULL; + } + } + + root = MSI_RecordGetInteger( row, 2 ); + key_str = MSI_RecordGetString( row, 3 ); + + root_key_str = get_root_key( package, root, &hkey_root ); + if (!root_key_str) + return ERROR_SUCCESS; + + deformat_string( package, key_str, &deformated_key ); + size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1; + ui_key_str = msi_alloc( size * sizeof(WCHAR) ); + strcpyW( ui_key_str, root_key_str ); + strcatW( ui_key_str, deformated_key ); + + deformat_string( package, name, &deformated_name ); + + keypath = get_keypath( comp, hkey_root, deformated_key ); + msi_free( deformated_key ); + if (delete_key) delete_tree( hkey_root, keypath ); + else delete_value( hkey_root, keypath, deformated_name ); + msi_free( keypath ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, ui_key_str ); + MSI_RecordSetStringW( uirow, 2, deformated_name ); + msi_ui_actiondata( package, szRemoveRegistryValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free( ui_key_str ); + msi_free( deformated_name ); + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR component, name, key_str, root_key_str; + LPWSTR deformated_key, deformated_name, ui_key_str, keypath; + MSICOMPONENT *comp; + MSIRECORD *uirow; + BOOL delete_key = FALSE; + HKEY hkey_root; + UINT size; + INT root; + + component = MSI_RecordGetString( row, 5 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + if ((name = MSI_RecordGetString( row, 4 ))) + { + if (name[0] == '-' && !name[1]) + { + delete_key = TRUE; + name = NULL; + } + } + + root = MSI_RecordGetInteger( row, 2 ); + key_str = MSI_RecordGetString( row, 3 ); + + root_key_str = get_root_key( package, root, &hkey_root ); + if (!root_key_str) + return ERROR_SUCCESS; + + deformat_string( package, key_str, &deformated_key ); + size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1; + ui_key_str = msi_alloc( size * sizeof(WCHAR) ); + strcpyW( ui_key_str, root_key_str ); + strcatW( ui_key_str, deformated_key ); + + deformat_string( package, name, &deformated_name ); + + keypath = get_keypath( comp, hkey_root, deformated_key ); + msi_free( deformated_key ); + if (delete_key) delete_tree( hkey_root, keypath ); + else delete_value( hkey_root, keypath, deformated_name ); + msi_free( keypath ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, ui_key_str ); + MSI_RecordSetStringW( uirow, 2, deformated_name ); + msi_ui_actiondata( package, szRemoveRegistryValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free( ui_key_str ); + msi_free( deformated_name ); + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package ) +{ + static const WCHAR registry_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',0}; + static const WCHAR remove_registry_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallInitialize(MSIPACKAGE *package) +{ + package->script->CurrentlyScripting = TRUE; + + return ERROR_SUCCESS; +} + + +static UINT ACTION_InstallValidate(MSIPACKAGE *package) +{ + static const WCHAR query[]= { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',0}; + MSICOMPONENT *comp; + DWORD total = 0, count = 0; + MSIQUERY *view; + MSIFEATURE *feature; + MSIFILE *file; + UINT rc; + + TRACE("InstallValidate\n"); + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, &count, NULL, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + total += count * REG_PROGRESS_VALUE; + } + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + total += COMPONENT_PROGRESS_VALUE; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + total += file->FileSize; + + msi_ui_progress( package, 0, total, 0, 0 ); + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + TRACE("Feature: %s Installed %d Request %d Action %d\n", + debugstr_w(feature->Feature), feature->Installed, + feature->ActionRequest, feature->Action); + } + return ERROR_SUCCESS; +} + +static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = param; + LPCWSTR cond = NULL; + LPCWSTR message = NULL; + UINT r; + + static const WCHAR title[]= + {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0}; + + cond = MSI_RecordGetString(row,1); + + r = MSI_EvaluateConditionW(package,cond); + if (r == MSICONDITION_FALSE) + { + if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) + { + LPWSTR deformated; + message = MSI_RecordGetString(row,2); + deformat_string(package,message,&deformated); + MessageBoxW(NULL,deformated,title,MB_OK); + msi_free(deformated); + } + + return ERROR_INSTALL_FAILURE; + } + + return ERROR_SUCCESS; +} + +static UINT ACTION_LaunchConditions(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0}; + MSIQUERY *view; + UINT rc; + + TRACE("Checking launch conditions\n"); + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package); + msiobj_release(&view->hdr); + return rc; +} + +static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp ) +{ + + if (!cmp->KeyPath) + return strdupW( msi_get_target_folder( package, cmp->Directory ) ); + + if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath) + { + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ', + '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0}; + static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0}; + static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0}; + MSIRECORD *row; + UINT root, len; + LPWSTR deformated, buffer, deformated_name; + LPCWSTR key, name; + + row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath); + if (!row) + return NULL; + + root = MSI_RecordGetInteger(row,2); + key = MSI_RecordGetString(row, 3); + name = MSI_RecordGetString(row, 4); + deformat_string(package, key , &deformated); + deformat_string(package, name, &deformated_name); + + len = strlenW(deformated) + 6; + if (deformated_name) + len+=strlenW(deformated_name); + + buffer = msi_alloc( len *sizeof(WCHAR)); + + if (deformated_name) + sprintfW(buffer,fmt2,root,deformated,deformated_name); + else + sprintfW(buffer,fmt,root,deformated); + + msi_free(deformated); + msi_free(deformated_name); + msiobj_release(&row->hdr); + + return buffer; + } + else if (cmp->Attributes & msidbComponentAttributesODBCDataSource) + { + FIXME("UNIMPLEMENTED keypath as ODBC Source\n"); + return NULL; + } + else + { + MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath ); + + if (file) + return strdupW( file->TargetPath ); + } + return NULL; +} + +static HKEY openSharedDLLsKey(void) +{ + HKEY hkey=0; + static const WCHAR path[] = + {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'S','h','a','r','e','d','D','L','L','s',0}; + + RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey); + return hkey; +} + +static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll) +{ + HKEY hkey; + DWORD count=0; + DWORD type; + DWORD sz = sizeof(count); + DWORD rc; + + hkey = openSharedDLLsKey(); + rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz); + if (rc != ERROR_SUCCESS) + count = 0; + RegCloseKey(hkey); + return count; +} + +static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count) +{ + HKEY hkey; + + hkey = openSharedDLLsKey(); + if (count > 0) + msi_reg_set_val_dword( hkey, path, count ); + else + RegDeleteValueW(hkey,path); + RegCloseKey(hkey); + return count; +} + +static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp ) +{ + MSIFEATURE *feature; + INT count = 0; + BOOL write = FALSE; + + /* only refcount DLLs */ + if (comp->KeyPath == NULL || + comp->assembly || + comp->Attributes & msidbComponentAttributesRegistryKeyPath || + comp->Attributes & msidbComponentAttributesODBCDataSource) + write = FALSE; + else + { + count = ACTION_GetSharedDLLsCount( comp->FullKeypath); + write = (count > 0); + + if (comp->Attributes & msidbComponentAttributesSharedDllRefCount) + write = TRUE; + } + + /* increment counts */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL) + continue; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + if ( cl->component == comp ) + count++; + } + } + + /* decrement counts */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT) + continue; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + if ( cl->component == comp ) + count--; + } + } + + /* ref count all the files in the component */ + if (write) + { + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (file->Component == comp) + ACTION_WriteSharedDLLsCount( file->TargetPath, count ); + } + } + + /* add a count for permanent */ + if (comp->Attributes & msidbComponentAttributesPermanent) + count ++; + + comp->RefCount = count; + + if (write) + ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount ); +} + +static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + if (comp->assembly) + { + const WCHAR prefixW[] = {'<','\\',0}; + DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name ); + WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) ); + + if (keypath) + { + strcpyW( keypath, prefixW ); + strcatW( keypath, comp->assembly->display_name ); + } + return keypath; + } + return resolve_keypath( package, comp ); +} + +static UINT ACTION_ProcessComponents(MSIPACKAGE *package) +{ + WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE]; + UINT rc; + MSICOMPONENT *comp; + HKEY hkey; + + TRACE("\n"); + + squash_guid(package->ProductCode,squished_pc); + msi_set_sourcedir_props(package, FALSE); + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + MSIRECORD *uirow; + INSTALLSTATE action; + + msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 ); + if (!comp->ComponentId) + continue; + + squash_guid( comp->ComponentId, squished_cc ); + msi_free( comp->FullKeypath ); + comp->FullKeypath = build_full_keypath( package, comp ); + + ACTION_RefCountComponent( package, comp ); + + if (package->need_rollback) action = comp->Installed; + else action = comp->ActionRequest; + + TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n", + debugstr_w(comp->Component), debugstr_w(squished_cc), + debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action); + + if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE) + { + if (package->Context == MSIINSTALLCONTEXT_MACHINE) + rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE); + else + rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE); + + if (rc != ERROR_SUCCESS) + continue; + + if (comp->Attributes & msidbComponentAttributesPermanent) + { + static const WCHAR szPermKey[] = + { '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0',0 }; + + msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath); + } + if (action == INSTALLSTATE_LOCAL) + msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath); + else + { + MSIFILE *file; + MSIRECORD *row; + LPWSTR ptr, ptr2; + WCHAR source[MAX_PATH]; + WCHAR base[MAX_PATH]; + LPWSTR sourcepath; + + static const WCHAR fmt[] = {'%','0','2','d','\\',0}; + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ', + '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ', + '`','D','i','s','k','I','d','`',0}; + + if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath))) + continue; + + row = MSI_QueryGetRecord(package->db, query, file->Sequence); + sprintfW(source, fmt, MSI_RecordGetInteger(row, 1)); + ptr2 = strrchrW(source, '\\') + 1; + msiobj_release(&row->hdr); + + lstrcpyW(base, package->PackagePath); + ptr = strrchrW(base, '\\'); + *(ptr + 1) = '\0'; + + sourcepath = msi_resolve_file_source(package, file); + ptr = sourcepath + lstrlenW(base); + lstrcpyW(ptr2, ptr); + msi_free(sourcepath); + + msi_reg_set_val_str(hkey, squished_pc, source); + } + RegCloseKey(hkey); + } + else if (action == INSTALLSTATE_ABSENT) + { + if (comp->num_clients <= 0) + { + if (package->Context == MSIINSTALLCONTEXT_MACHINE) + MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid ); + else + MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL ); + } + } + + /* UI stuff */ + uirow = MSI_CreateRecord(3); + MSI_RecordSetStringW(uirow,1,package->ProductCode); + MSI_RecordSetStringW(uirow,2,comp->ComponentId); + MSI_RecordSetStringW(uirow,3,comp->FullKeypath); + msi_ui_actiondata( package, szProcessComponents, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +typedef struct { + CLSID clsid; + LPWSTR source; + + LPWSTR path; + ITypeLib *ptLib; +} typelib_struct; + +static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, + LPWSTR lpszName, LONG_PTR lParam) +{ + TLIBATTR *attr; + typelib_struct *tl_struct = (typelib_struct*) lParam; + static const WCHAR fmt[] = {'%','s','\\','%','i',0}; + int sz; + HRESULT res; + + if (!IS_INTRESOURCE(lpszName)) + { + ERR("Not Int Resource Name %s\n",debugstr_w(lpszName)); + return TRUE; + } + + sz = strlenW(tl_struct->source)+4; + sz *= sizeof(WCHAR); + + if ((INT_PTR)lpszName == 1) + tl_struct->path = strdupW(tl_struct->source); + else + { + tl_struct->path = msi_alloc(sz); + sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName); + } + + TRACE("trying %s\n", debugstr_w(tl_struct->path)); + res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib); + if (FAILED(res)) + { + msi_free(tl_struct->path); + tl_struct->path = NULL; + + return TRUE; + } + + ITypeLib_GetLibAttr(tl_struct->ptLib, &attr); + if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid))) + { + ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); + return FALSE; + } + + msi_free(tl_struct->path); + tl_struct->path = NULL; + + ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); + ITypeLib_Release(tl_struct->ptLib); + + return TRUE; +} + +static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = param; + LPCWSTR component; + MSICOMPONENT *comp; + MSIFILE *file; + typelib_struct tl_struct; + ITypeLib *tlib; + HMODULE module; + HRESULT hr; + + component = MSI_RecordGetString(row,3); + comp = msi_get_loaded_component(package,component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath ))) + { + TRACE("component has no key path\n"); + return ERROR_SUCCESS; + } + msi_ui_actiondata( package, szRegisterTypeLibraries, row ); + + module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE ); + if (module) + { + LPCWSTR guid; + guid = MSI_RecordGetString(row,1); + CLSIDFromString( guid, &tl_struct.clsid); + tl_struct.source = strdupW( file->TargetPath ); + tl_struct.path = NULL; + + EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc, + (LONG_PTR)&tl_struct); + + if (tl_struct.path) + { + LPCWSTR helpid, help_path = NULL; + HRESULT res; + + helpid = MSI_RecordGetString(row,6); + + if (helpid) help_path = msi_get_target_folder( package, helpid ); + res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path ); + + if (FAILED(res)) + ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path)); + else + TRACE("Registered %s\n", debugstr_w(tl_struct.path)); + + ITypeLib_Release(tl_struct.ptLib); + msi_free(tl_struct.path); + } + else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source)); + + FreeLibrary(module); + msi_free(tl_struct.source); + } + else + { + hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib); + if (FAILED(hr)) + { + ERR("Failed to load type library: %08x\n", hr); + return ERROR_INSTALL_FAILURE; + } + + ITypeLib_Release(tlib); + } + + return ERROR_SUCCESS; +} + +static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','T','y','p','e','L','i','b','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR component, guid; + MSICOMPONENT *comp; + GUID libid; + UINT version; + LCID language; + SYSKIND syskind; + HRESULT hr; + + component = MSI_RecordGetString( row, 3 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + msi_ui_actiondata( package, szUnregisterTypeLibraries, row ); + + guid = MSI_RecordGetString( row, 1 ); + CLSIDFromString( guid, &libid ); + version = MSI_RecordGetInteger( row, 4 ); + language = MSI_RecordGetInteger( row, 2 ); + +#ifdef _WIN64 + syskind = SYS_WIN64; +#else + syskind = SYS_WIN32; +#endif + + hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind ); + if (FAILED(hr)) + { + WARN("Failed to unregister typelib: %08x\n", hr); + } + + return ERROR_SUCCESS; +} + +static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','T','y','p','e','L','i','b','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row ) +{ + static const WCHAR szlnk[] = {'.','l','n','k',0}; + LPCWSTR directory, extension, link_folder; + LPWSTR link_file, filename; + + directory = MSI_RecordGetString( row, 2 ); + link_folder = msi_get_target_folder( package, directory ); + if (!link_folder) + { + ERR("unable to resolve folder %s\n", debugstr_w(directory)); + return NULL; + } + /* may be needed because of a bug somewhere else */ + msi_create_full_path( link_folder ); + + filename = msi_dup_record_field( row, 3 ); + msi_reduce_to_long_filename( filename ); + + extension = strchrW( filename, '.' ); + if (!extension || strcmpiW( extension, szlnk )) + { + int len = strlenW( filename ); + filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) ); + memcpy( filename + len, szlnk, sizeof(szlnk) ); + } + link_file = msi_build_directory_name( 2, link_folder, filename ); + msi_free( filename ); + + return link_file; +} + +WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name ) +{ + static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0}; + static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0}; + WCHAR *folder, *dest, *path; + + if (package->Context == MSIINSTALLCONTEXT_MACHINE) + folder = msi_dup_property( package->db, szWindowsFolder ); + else + { + WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder ); + folder = msi_build_directory_name( 2, appdata, szMicrosoft ); + msi_free( appdata ); + } + dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode ); + msi_create_full_path( dest ); + path = msi_build_directory_name( 2, dest, icon_name ); + msi_free( folder ); + msi_free( dest ); + return path; +} + +static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPWSTR link_file, deformated, path; + LPCWSTR component, target; + MSICOMPONENT *comp; + IShellLinkW *sl = NULL; + IPersistFile *pf = NULL; + HRESULT res; + + component = MSI_RecordGetString(row, 4); + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + msi_ui_actiondata( package, szCreateShortcuts, row ); + + res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellLinkW, (LPVOID *) &sl ); + + if (FAILED( res )) + { + ERR("CLSID_ShellLink not available\n"); + goto err; + } + + res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf ); + if (FAILED( res )) + { + ERR("QueryInterface(IID_IPersistFile) failed\n"); + goto err; + } + + target = MSI_RecordGetString(row, 5); + if (strchrW(target, '[')) + { + deformat_string( package, target, &path ); + TRACE("target path is %s\n", debugstr_w(path)); + IShellLinkW_SetPath( sl, path ); + msi_free( path ); + } + else + { + FIXME("poorly handled shortcut format, advertised shortcut\n"); + IShellLinkW_SetPath(sl,comp->FullKeypath); + } + + if (!MSI_RecordIsNull(row,6)) + { + LPCWSTR arguments = MSI_RecordGetString(row, 6); + deformat_string(package, arguments, &deformated); + IShellLinkW_SetArguments(sl,deformated); + msi_free(deformated); + } + + if (!MSI_RecordIsNull(row,7)) + { + LPCWSTR description = MSI_RecordGetString(row, 7); + IShellLinkW_SetDescription(sl, description); + } + + if (!MSI_RecordIsNull(row,8)) + IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8)); + + if (!MSI_RecordIsNull(row,9)) + { + INT index; + LPCWSTR icon = MSI_RecordGetString(row, 9); + + path = msi_build_icon_path(package, icon); + index = MSI_RecordGetInteger(row,10); + + /* no value means 0 */ + if (index == MSI_NULL_INTEGER) + index = 0; + + IShellLinkW_SetIconLocation(sl, path, index); + msi_free(path); + } + + if (!MSI_RecordIsNull(row,11)) + IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11)); + + if (!MSI_RecordIsNull(row,12)) + { + LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 ); + full_path = msi_get_target_folder( package, wkdir ); + if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path ); + } + link_file = get_link_file(package, row); + + TRACE("Writing shortcut to %s\n", debugstr_w(link_file)); + IPersistFile_Save(pf, link_file, FALSE); + msi_free(link_file); + +err: + if (pf) + IPersistFile_Release( pf ); + if (sl) + IShellLinkW_Release( sl ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_CreateShortcuts(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','h','o','r','t','c','u','t','`',0}; + MSIQUERY *view; + HRESULT res; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + res = CoInitialize( NULL ); + + rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package); + msiobj_release(&view->hdr); + + if (SUCCEEDED(res)) CoUninitialize(); + return rc; +} + +static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPWSTR link_file; + LPCWSTR component; + MSICOMPONENT *comp; + + component = MSI_RecordGetString( row, 4 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + msi_ui_actiondata( package, szRemoveShortcuts, row ); + + link_file = get_link_file( package, row ); + + TRACE("Removing shortcut file %s\n", debugstr_w( link_file )); + if (!DeleteFileW( link_file )) + { + WARN("Failed to remove shortcut file %u\n", GetLastError()); + } + msi_free( link_file ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','h','o','r','t','c','u','t','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = param; + HANDLE the_file; + LPWSTR FilePath; + LPCWSTR FileName; + CHAR buffer[1024]; + DWORD sz; + UINT rc; + + FileName = MSI_RecordGetString(row,1); + if (!FileName) + { + ERR("Unable to get FileName\n"); + return ERROR_SUCCESS; + } + + FilePath = msi_build_icon_path(package, FileName); + + TRACE("Creating icon file at %s\n",debugstr_w(FilePath)); + + the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (the_file == INVALID_HANDLE_VALUE) + { + ERR("Unable to create file %s\n",debugstr_w(FilePath)); + msi_free(FilePath); + return ERROR_SUCCESS; + } + + do + { + DWORD write; + sz = 1024; + rc = MSI_RecordReadStream(row,2,buffer,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get stream\n"); + CloseHandle(the_file); + DeleteFileW(FilePath); + break; + } + WriteFile(the_file,buffer,sz,&write,NULL); + } while (sz == 1024); + + msi_free(FilePath); + CloseHandle(the_file); + + return ERROR_SUCCESS; +} + +static UINT msi_publish_icons(MSIPACKAGE *package) +{ + static const WCHAR query[]= { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','c','o','n','`',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW(package->db, query, &view); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package); + msiobj_release(&view->hdr); + if (r != ERROR_SUCCESS) + return r; + } + return ERROR_SUCCESS; +} + +static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey) +{ + UINT r; + HKEY source; + LPWSTR buffer; + MSIMEDIADISK *disk; + MSISOURCELISTINFO *info; + + r = RegCreateKeyW(hkey, szSourceList, &source); + if (r != ERROR_SUCCESS) + return r; + + RegCloseKey(source); + + buffer = strrchrW(package->PackagePath, '\\') + 1; + r = MsiSourceListSetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_PACKAGENAMEW, buffer); + if (r != ERROR_SUCCESS) + return r; + + r = MsiSourceListSetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty); + if (r != ERROR_SUCCESS) + return r; + + r = MsiSourceListSetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_DISKPROMPTW, szEmpty); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry) + { + if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW )) + msi_set_last_used_source(package->ProductCode, NULL, info->context, + info->options, info->value); + else + MsiSourceListSetInfoW(package->ProductCode, NULL, + info->context, info->options, + info->property, info->value); + } + + LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry) + { + MsiSourceListAddMediaDiskW(package->ProductCode, NULL, + disk->context, disk->options, + disk->disk_id, disk->volume_label, disk->disk_prompt); + } + + return ERROR_SUCCESS; +} + +static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey) +{ + MSIHANDLE hdb, suminfo; + WCHAR guids[MAX_PATH]; + WCHAR packcode[SQUISH_GUID_SIZE]; + LPWSTR buffer; + LPWSTR ptr; + DWORD langid; + DWORD size; + UINT r; + + static const WCHAR szARPProductIcon[] = + {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0}; + static const WCHAR szAssignment[] = + {'A','s','s','i','g','n','m','e','n','t',0}; + static const WCHAR szAdvertiseFlags[] = + {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0}; + static const WCHAR szClients[] = + {'C','l','i','e','n','t','s',0}; + static const WCHAR szColon[] = {':',0}; + + buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW); + msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer); + msi_free(buffer); + + langid = msi_get_property_int(package->db, szProductLanguage, 0); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid); + + /* FIXME */ + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0); + + buffer = msi_dup_property(package->db, szARPProductIcon); + if (buffer) + { + LPWSTR path = msi_build_icon_path(package, buffer); + msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path); + msi_free(path); + msi_free(buffer); + } + + buffer = msi_dup_property(package->db, szProductVersion); + if (buffer) + { + DWORD verdword = msi_version_str_to_dword(buffer); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword); + msi_free(buffer); + } + + msi_reg_set_val_dword(hkey, szAssignment, 0); + msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0); + msi_reg_set_val_str(hkey, szClients, szColon); + + hdb = alloc_msihandle(&package->db->hdr); + if (!hdb) + return ERROR_NOT_ENOUGH_MEMORY; + + r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo); + MsiCloseHandle(hdb); + if (r != ERROR_SUCCESS) + goto done; + + size = MAX_PATH; + r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL, + NULL, guids, &size); + if (r != ERROR_SUCCESS) + goto done; + + ptr = strchrW(guids, ';'); + if (ptr) *ptr = 0; + squash_guid(guids, packcode); + msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode); + +done: + MsiCloseHandle(suminfo); + return ERROR_SUCCESS; +} + +static UINT msi_publish_upgrade_code(MSIPACKAGE *package) +{ + UINT r; + HKEY hkey; + LPWSTR upgrade; + WCHAR squashed_pc[SQUISH_GUID_SIZE]; + + upgrade = msi_dup_property(package->db, szUpgradeCode); + if (!upgrade) + return ERROR_SUCCESS; + + if (package->Context == MSIINSTALLCONTEXT_MACHINE) + r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE); + else + r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE); + + if (r != ERROR_SUCCESS) + { + WARN("failed to open upgrade code key\n"); + msi_free(upgrade); + return ERROR_SUCCESS; + } + squash_guid(package->ProductCode, squashed_pc); + msi_reg_set_val_str(hkey, squashed_pc, NULL); + RegCloseKey(hkey); + msi_free(upgrade); + return ERROR_SUCCESS; +} + +static BOOL msi_check_publish(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) + { + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action == INSTALLSTATE_LOCAL) + return TRUE; + } + + return FALSE; +} + +static BOOL msi_check_unpublish(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) + { + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action != INSTALLSTATE_ABSENT) + return FALSE; + } + + return TRUE; +} + +static UINT msi_publish_patches( MSIPACKAGE *package ) +{ + static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0}; + WCHAR patch_squashed[GUID_SIZE]; + HKEY patches_key = NULL, product_patches_key = NULL, product_key; + LONG res; + MSIPATCHINFO *patch; + UINT r; + WCHAR *p, *all_patches = NULL; + DWORD len = 0; + + r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE ); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL ); + if (res != ERROR_SUCCESS) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE ); + if (r != ERROR_SUCCESS) + goto done; + + LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry ) + { + squash_guid( patch->patchcode, patch_squashed ); + len += strlenW( patch_squashed ) + 1; + } + + p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) ); + if (!all_patches) + goto done; + + LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry ) + { + HKEY patch_key; + + squash_guid( patch->patchcode, p ); + p += strlenW( p ) + 1; + + res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ, + (const BYTE *)patch->transforms, + (strlenW(patch->transforms) + 1) * sizeof(WCHAR) ); + if (res != ERROR_SUCCESS) + goto done; + + r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE ); + if (r != ERROR_SUCCESS) + goto done; + + res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile, + (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) ); + RegCloseKey( patch_key ); + if (res != ERROR_SUCCESS) + goto done; + + if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE )) + { + res = GetLastError(); + ERR("Unable to copy patch package %d\n", res); + goto done; + } + res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL ); + if (res != ERROR_SUCCESS) + goto done; + + res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) ); + RegCloseKey( patch_key ); + if (res != ERROR_SUCCESS) + goto done; + } + + all_patches[len] = 0; + res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ, + (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) ); + if (res != ERROR_SUCCESS) + goto done; + + res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ, + (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) ); + if (res != ERROR_SUCCESS) + r = ERROR_FUNCTION_FAILED; + +done: + RegCloseKey( product_patches_key ); + RegCloseKey( patches_key ); + RegCloseKey( product_key ); + msi_free( all_patches ); + return r; +} + +static UINT ACTION_PublishProduct(MSIPACKAGE *package) +{ + UINT rc; + HKEY hukey = NULL, hudkey = NULL; + MSIRECORD *uirow; + + if (!list_empty(&package->patches)) + { + rc = msi_publish_patches(package); + if (rc != ERROR_SUCCESS) + goto end; + } + + /* FIXME: also need to publish if the product is in advertise mode */ + if (!msi_check_publish(package)) + return ERROR_SUCCESS; + + rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context, + &hukey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context, + NULL, &hudkey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + rc = msi_publish_upgrade_code(package); + if (rc != ERROR_SUCCESS) + goto end; + + rc = msi_publish_product_properties(package, hukey); + if (rc != ERROR_SUCCESS) + goto end; + + rc = msi_publish_sourcelist(package, hukey); + if (rc != ERROR_SUCCESS) + goto end; + + rc = msi_publish_icons(package); + +end: + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, package->ProductCode ); + msi_ui_actiondata( package, szPublishProduct, uirow ); + msiobj_release( &uirow->hdr ); + + RegCloseKey(hukey); + RegCloseKey(hudkey); + return rc; +} + +static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row ) +{ + WCHAR *filename, *ptr, *folder, *ret; + const WCHAR *dirprop; + + filename = msi_dup_record_field( row, 2 ); + if (filename && (ptr = strchrW( filename, '|' ))) + ptr++; + else + ptr = filename; + + dirprop = MSI_RecordGetString( row, 3 ); + if (dirprop) + { + folder = strdupW( msi_get_target_folder( package, dirprop ) ); + if (!folder) folder = msi_dup_property( package->db, dirprop ); + } + else + folder = msi_dup_property( package->db, szWindowsFolder ); + + if (!folder) + { + ERR("Unable to resolve folder %s\n", debugstr_w(dirprop)); + msi_free( filename ); + return NULL; + } + + ret = msi_build_directory_name( 2, folder, ptr ); + + msi_free( filename ); + msi_free( folder ); + return ret; +} + +static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR component, section, key, value, identifier; + LPWSTR deformated_section, deformated_key, deformated_value, fullname; + MSIRECORD * uirow; + INT action; + MSICOMPONENT *comp; + + component = MSI_RecordGetString(row, 8); + comp = msi_get_loaded_component(package,component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + identifier = MSI_RecordGetString(row,1); + section = MSI_RecordGetString(row,4); + key = MSI_RecordGetString(row,5); + value = MSI_RecordGetString(row,6); + action = MSI_RecordGetInteger(row,7); + + deformat_string(package,section,&deformated_section); + deformat_string(package,key,&deformated_key); + deformat_string(package,value,&deformated_value); + + fullname = get_ini_file_name(package, row); + + if (action == 0) + { + TRACE("Adding value %s to section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), + debugstr_w(fullname)); + WritePrivateProfileStringW(deformated_section, deformated_key, + deformated_value, fullname); + } + else if (action == 1) + { + WCHAR returned[10]; + GetPrivateProfileStringW(deformated_section, deformated_key, NULL, + returned, 10, fullname); + if (returned[0] == 0) + { + TRACE("Adding value %s to section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), + debugstr_w(fullname)); + + WritePrivateProfileStringW(deformated_section, deformated_key, + deformated_value, fullname); + } + } + else if (action == 3) + FIXME("Append to existing section not yet implemented\n"); + + uirow = MSI_CreateRecord(4); + MSI_RecordSetStringW(uirow,1,identifier); + MSI_RecordSetStringW(uirow,2,deformated_section); + MSI_RecordSetStringW(uirow,3,deformated_key); + MSI_RecordSetStringW(uirow,4,deformated_value); + msi_ui_actiondata( package, szWriteIniValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(fullname); + msi_free(deformated_key); + msi_free(deformated_value); + msi_free(deformated_section); + return ERROR_SUCCESS; +} + +static UINT ACTION_WriteIniValues(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','i','F','i','l','e','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR component, section, key, value, identifier; + LPWSTR deformated_section, deformated_key, deformated_value, filename; + MSICOMPONENT *comp; + MSIRECORD *uirow; + INT action; + + component = MSI_RecordGetString( row, 8 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + identifier = MSI_RecordGetString( row, 1 ); + section = MSI_RecordGetString( row, 4 ); + key = MSI_RecordGetString( row, 5 ); + value = MSI_RecordGetString( row, 6 ); + action = MSI_RecordGetInteger( row, 7 ); + + deformat_string( package, section, &deformated_section ); + deformat_string( package, key, &deformated_key ); + deformat_string( package, value, &deformated_value ); + + if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine) + { + filename = get_ini_file_name( package, row ); + + TRACE("Removing key %s from section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename)); + + if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename )) + { + WARN("Unable to remove key %u\n", GetLastError()); + } + msi_free( filename ); + } + else + FIXME("Unsupported action %d\n", action); + + + uirow = MSI_CreateRecord( 4 ); + MSI_RecordSetStringW( uirow, 1, identifier ); + MSI_RecordSetStringW( uirow, 2, deformated_section ); + MSI_RecordSetStringW( uirow, 3, deformated_key ); + MSI_RecordSetStringW( uirow, 4, deformated_value ); + msi_ui_actiondata( package, szRemoveIniValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free( deformated_key ); + msi_free( deformated_value ); + msi_free( deformated_section ); + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR component, section, key, value, identifier; + LPWSTR deformated_section, deformated_key, deformated_value, filename; + MSICOMPONENT *comp; + MSIRECORD *uirow; + INT action; + + component = MSI_RecordGetString( row, 8 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + identifier = MSI_RecordGetString( row, 1 ); + section = MSI_RecordGetString( row, 4 ); + key = MSI_RecordGetString( row, 5 ); + value = MSI_RecordGetString( row, 6 ); + action = MSI_RecordGetInteger( row, 7 ); + + deformat_string( package, section, &deformated_section ); + deformat_string( package, key, &deformated_key ); + deformat_string( package, value, &deformated_value ); + + if (action == msidbIniFileActionRemoveLine) + { + filename = get_ini_file_name( package, row ); + + TRACE("Removing key %s from section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename)); + + if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename )) + { + WARN("Unable to remove key %u\n", GetLastError()); + } + msi_free( filename ); + } + else + FIXME("Unsupported action %d\n", action); + + uirow = MSI_CreateRecord( 4 ); + MSI_RecordSetStringW( uirow, 1, identifier ); + MSI_RecordSetStringW( uirow, 2, deformated_section ); + MSI_RecordSetStringW( uirow, 3, deformated_key ); + MSI_RecordSetStringW( uirow, 4, deformated_value ); + msi_ui_actiondata( package, szRemoveIniValues, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free( deformated_key ); + msi_free( deformated_value ); + msi_free( deformated_section ); + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveIniValues( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','i','F','i','l','e','`',0}; + static const WCHAR remove_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + return ERROR_SUCCESS; +} + +static void register_dll( const WCHAR *dll, BOOL unregister ) +{ + HMODULE hmod; + + hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH ); + if (hmod) + { + HRESULT (WINAPI *func_ptr)( void ); + const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer"; + + func_ptr = (void *)GetProcAddress( hmod, func ); + if (func_ptr) + { + HRESULT hr = func_ptr(); + if (FAILED( hr )) + WARN("failed to register dll 0x%08x\n", hr); + } + else + WARN("entry point %s not found\n", func); + FreeLibrary( hmod ); + return; + } + WARN("failed to load library %u\n", GetLastError()); +} + +static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR filename; + MSIFILE *file; + MSIRECORD *uirow; + + filename = MSI_RecordGetString( row, 1 ); + file = msi_get_loaded_file( package, filename ); + if (!file) + { + WARN("unable to find file %s\n", debugstr_w(filename)); + return ERROR_SUCCESS; + } + file->Component->Action = msi_get_component_action( package, file->Component ); + if (file->Component->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component)); + return ERROR_SUCCESS; + } + + TRACE("Registering %s\n", debugstr_w( file->TargetPath )); + register_dll( file->TargetPath, FALSE ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, file->File ); + MSI_RecordSetStringW( uirow, 2, file->Component->Directory ); + msi_ui_actiondata( package, szSelfRegModules, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_SelfRegModules(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','e','l','f','R','e','g','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR filename; + MSIFILE *file; + MSIRECORD *uirow; + + filename = MSI_RecordGetString( row, 1 ); + file = msi_get_loaded_file( package, filename ); + if (!file) + { + WARN("unable to find file %s\n", debugstr_w(filename)); + return ERROR_SUCCESS; + } + file->Component->Action = msi_get_component_action( package, file->Component ); + if (file->Component->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component)); + return ERROR_SUCCESS; + } + + TRACE("Unregistering %s\n", debugstr_w( file->TargetPath )); + register_dll( file->TargetPath, TRUE ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, file->File ); + MSI_RecordSetStringW( uirow, 2, file->Component->Directory ); + msi_ui_actiondata( package, szSelfUnregModules, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_SelfUnregModules( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','e','l','f','R','e','g','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static UINT ACTION_PublishFeatures(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + UINT rc; + HKEY hkey = NULL, userdata = NULL; + + if (!msi_check_publish(package)) + return ERROR_SUCCESS; + + rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context, + &hkey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context, + &userdata, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + /* here the guids are base 85 encoded */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + LPWSTR data = NULL; + GUID clsid; + INT size; + BOOL absent = FALSE; + MSIRECORD *uirow; + + if (feature->Action != INSTALLSTATE_LOCAL && + feature->Action != INSTALLSTATE_SOURCE && + feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE; + + size = 1; + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + size += 21; + } + if (feature->Feature_Parent) + size += strlenW( feature->Feature_Parent )+2; + + data = msi_alloc(size * sizeof(WCHAR)); + + data[0] = 0; + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + MSICOMPONENT* component = cl->component; + WCHAR buf[21]; + + buf[0] = 0; + if (component->ComponentId) + { + TRACE("From %s\n",debugstr_w(component->ComponentId)); + CLSIDFromString(component->ComponentId, &clsid); + encode_base85_guid(&clsid,buf); + TRACE("to %s\n",debugstr_w(buf)); + strcatW(data,buf); + } + } + + if (feature->Feature_Parent) + { + static const WCHAR sep[] = {'\2',0}; + strcatW(data,sep); + strcatW(data,feature->Feature_Parent); + } + + msi_reg_set_val_str( userdata, feature->Feature, data ); + msi_free(data); + + size = 0; + if (feature->Feature_Parent) + size = strlenW(feature->Feature_Parent)*sizeof(WCHAR); + if (!absent) + { + size += sizeof(WCHAR); + RegSetValueExW(hkey,feature->Feature,0,REG_SZ, + (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size); + } + else + { + size += 2*sizeof(WCHAR); + data = msi_alloc(size); + data[0] = 0x6; + data[1] = 0; + if (feature->Feature_Parent) + strcpyW( &data[1], feature->Feature_Parent ); + RegSetValueExW(hkey,feature->Feature,0,REG_SZ, + (LPBYTE)data,size); + msi_free(data); + } + + /* the UI chunk */ + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, feature->Feature ); + msi_ui_actiondata( package, szPublishFeatures, uirow ); + msiobj_release( &uirow->hdr ); + /* FIXME: call msi_ui_progress? */ + } + +end: + RegCloseKey(hkey); + RegCloseKey(userdata); + return rc; +} + +static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature) +{ + UINT r; + HKEY hkey; + MSIRECORD *uirow; + + TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature)); + + r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context, + &hkey, FALSE); + if (r == ERROR_SUCCESS) + { + RegDeleteValueW(hkey, feature->Feature); + RegCloseKey(hkey); + } + + r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context, + &hkey, FALSE); + if (r == ERROR_SUCCESS) + { + RegDeleteValueW(hkey, feature->Feature); + RegCloseKey(hkey); + } + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, feature->Feature ); + msi_ui_actiondata( package, szUnpublishFeatures, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + + if (!msi_check_unpublish(package)) + return ERROR_SUCCESS; + + LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry) + { + msi_unpublish_feature(package, feature); + } + + return ERROR_SUCCESS; +} + +static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey) +{ + SYSTEMTIME systime; + DWORD size, langid; + WCHAR date[9], *val, *buffer; + const WCHAR *prop, *key; + + static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0}; + static const WCHAR modpath_fmt[] = + {'M','s','i','E','x','e','c','.','e','x','e',' ', + '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0}; + static const WCHAR szModifyPath[] = + {'M','o','d','i','f','y','P','a','t','h',0}; + static const WCHAR szUninstallString[] = + {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0}; + static const WCHAR szEstimatedSize[] = + {'E','s','t','i','m','a','t','e','d','S','i','z','e',0}; + static const WCHAR szDisplayVersion[] = + {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; + static const WCHAR szInstallSource[] = + {'I','n','s','t','a','l','l','S','o','u','r','c','e',0}; + static const WCHAR szARPAUTHORIZEDCDFPREFIX[] = + {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0}; + static const WCHAR szAuthorizedCDFPrefix[] = + {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0}; + static const WCHAR szARPCONTACT[] = + {'A','R','P','C','O','N','T','A','C','T',0}; + static const WCHAR szContact[] = + {'C','o','n','t','a','c','t',0}; + static const WCHAR szARPCOMMENTS[] = + {'A','R','P','C','O','M','M','E','N','T','S',0}; + static const WCHAR szComments[] = + {'C','o','m','m','e','n','t','s',0}; + static const WCHAR szProductName[] = + {'P','r','o','d','u','c','t','N','a','m','e',0}; + static const WCHAR szDisplayName[] = + {'D','i','s','p','l','a','y','N','a','m','e',0}; + static const WCHAR szARPHELPLINK[] = + {'A','R','P','H','E','L','P','L','I','N','K',0}; + static const WCHAR szHelpLink[] = + {'H','e','l','p','L','i','n','k',0}; + static const WCHAR szARPHELPTELEPHONE[] = + {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0}; + static const WCHAR szHelpTelephone[] = + {'H','e','l','p','T','e','l','e','p','h','o','n','e',0}; + static const WCHAR szARPINSTALLLOCATION[] = + {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0}; + static const WCHAR szManufacturer[] = + {'M','a','n','u','f','a','c','t','u','r','e','r',0}; + static const WCHAR szPublisher[] = + {'P','u','b','l','i','s','h','e','r',0}; + static const WCHAR szARPREADME[] = + {'A','R','P','R','E','A','D','M','E',0}; + static const WCHAR szReadme[] = + {'R','e','a','d','M','e',0}; + static const WCHAR szARPSIZE[] = + {'A','R','P','S','I','Z','E',0}; + static const WCHAR szSize[] = + {'S','i','z','e',0}; + static const WCHAR szARPURLINFOABOUT[] = + {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0}; + static const WCHAR szURLInfoAbout[] = + {'U','R','L','I','n','f','o','A','b','o','u','t',0}; + static const WCHAR szARPURLUPDATEINFO[] = + {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0}; + static const WCHAR szURLUpdateInfo[] = + {'U','R','L','U','p','d','a','t','e','I','n','f','o',0}; + static const WCHAR szARPSYSTEMCOMPONENT[] = + {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0}; + static const WCHAR szSystemComponent[] = + {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0}; + + static const WCHAR *propval[] = { + szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix, + szARPCONTACT, szContact, + szARPCOMMENTS, szComments, + szProductName, szDisplayName, + szARPHELPLINK, szHelpLink, + szARPHELPTELEPHONE, szHelpTelephone, + szARPINSTALLLOCATION, szInstallLocation, + szSourceDir, szInstallSource, + szManufacturer, szPublisher, + szARPREADME, szReadme, + szARPSIZE, szSize, + szARPURLINFOABOUT, szURLInfoAbout, + szARPURLUPDATEINFO, szURLUpdateInfo, + NULL + }; + const WCHAR **p = propval; + + while (*p) + { + prop = *p++; + key = *p++; + val = msi_dup_property(package->db, prop); + msi_reg_set_val_str(hkey, key, val); + msi_free(val); + } + + msi_reg_set_val_dword(hkey, szWindowsInstaller, 1); + if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 )) + { + msi_reg_set_val_dword( hkey, szSystemComponent, 1 ); + } + size = deformat_string(package, modpath_fmt, &buffer); + RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size); + RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size); + msi_free(buffer); + + /* FIXME: Write real Estimated Size when we have it */ + msi_reg_set_val_dword(hkey, szEstimatedSize, 0); + + GetLocalTime(&systime); + sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay); + msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date); + + langid = msi_get_property_int(package->db, szProductLanguage, 0); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid); + + buffer = msi_dup_property(package->db, szProductVersion); + msi_reg_set_val_str(hkey, szDisplayVersion, buffer); + if (buffer) + { + DWORD verdword = msi_version_str_to_dword(buffer); + + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24); + msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF); + msi_free(buffer); + } + + return ERROR_SUCCESS; +} + +static UINT ACTION_RegisterProduct(MSIPACKAGE *package) +{ + WCHAR squashed_pc[SQUISH_GUID_SIZE]; + MSIRECORD *uirow; + LPWSTR upgrade_code; + HKEY hkey, props, upgrade_key; + UINT rc; + + /* FIXME: also need to publish if the product is in advertise mode */ + if (!msi_check_publish(package)) + return ERROR_SUCCESS; + + rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE); + if (rc != ERROR_SUCCESS) + return rc; + + rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE); + if (rc != ERROR_SUCCESS) + goto done; + + rc = msi_publish_install_properties(package, hkey); + if (rc != ERROR_SUCCESS) + goto done; + + rc = msi_publish_install_properties(package, props); + if (rc != ERROR_SUCCESS) + goto done; + + upgrade_code = msi_dup_property(package->db, szUpgradeCode); + if (upgrade_code) + { + rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE ); + if (rc == ERROR_SUCCESS) + { + squash_guid( package->ProductCode, squashed_pc ); + msi_reg_set_val_str( upgrade_key, squashed_pc, NULL ); + RegCloseKey( upgrade_key ); + } + msi_free( upgrade_code ); + } + msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile ); + package->delete_on_close = FALSE; + +done: + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, package->ProductCode ); + msi_ui_actiondata( package, szRegisterProduct, uirow ); + msiobj_release( &uirow->hdr ); + + RegCloseKey(hkey); + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallExecute(MSIPACKAGE *package) +{ + return execute_script(package, SCRIPT_INSTALL); +} + +static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + const WCHAR *icon = MSI_RecordGetString( row, 1 ); + WCHAR *p, *icon_path; + + if (!icon) return ERROR_SUCCESS; + if ((icon_path = msi_build_icon_path( package, icon ))) + { + TRACE("removing icon file %s\n", debugstr_w(icon_path)); + DeleteFileW( icon_path ); + if ((p = strrchrW( icon_path, '\\' ))) + { + *p = 0; + RemoveDirectoryW( icon_path ); + } + msi_free( icon_path ); + } + return ERROR_SUCCESS; +} + +static UINT msi_unpublish_icons( MSIPACKAGE *package ) +{ + static const WCHAR query[]= { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + } + return ERROR_SUCCESS; +} + +static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove ) +{ + static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0}; + WCHAR *upgrade, **features; + BOOL full_uninstall = TRUE; + MSIFEATURE *feature; + MSIPATCHINFO *patch; + UINT i; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE; + } + features = msi_split_string( remove, ',' ); + for (i = 0; features && features[i]; i++) + { + if (!strcmpW( features[i], szAll )) full_uninstall = TRUE; + } + msi_free(features); + + if (!full_uninstall) + return ERROR_SUCCESS; + + MSIREG_DeleteProductKey(package->ProductCode); + MSIREG_DeleteUserDataProductKey(package->ProductCode); + MSIREG_DeleteUninstallKey(package->ProductCode, package->platform); + + MSIREG_DeleteLocalClassesProductKey(package->ProductCode); + MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode); + MSIREG_DeleteUserProductKey(package->ProductCode); + MSIREG_DeleteUserFeaturesKey(package->ProductCode); + + upgrade = msi_dup_property(package->db, szUpgradeCode); + if (upgrade) + { + MSIREG_DeleteUserUpgradeCodesKey(upgrade); + MSIREG_DeleteClassesUpgradeCodesKey(upgrade); + msi_free(upgrade); + } + + LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry) + { + MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context); + if (!strcmpW( package->ProductCode, patch->products )) + { + TRACE("removing local patch package %s\n", debugstr_w(patch->localfile)); + patch->delete_on_close = TRUE; + } + /* FIXME: remove local patch package if this is the last product */ + } + TRACE("removing local package %s\n", debugstr_w(package->localfile)); + package->delete_on_close = TRUE; + + msi_unpublish_icons( package ); + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallFinalize(MSIPACKAGE *package) +{ + UINT rc; + WCHAR *remove; + + /* turn off scheduling */ + package->script->CurrentlyScripting= FALSE; + + /* first do the same as an InstallExecute */ + rc = ACTION_InstallExecute(package); + if (rc != ERROR_SUCCESS) + return rc; + + /* then handle commit actions */ + rc = execute_script(package, SCRIPT_COMMIT); + if (rc != ERROR_SUCCESS) + return rc; + + remove = msi_dup_property(package->db, szRemove); + rc = msi_unpublish_product(package, remove); + msi_free(remove); + return rc; +} + +UINT ACTION_ForceReboot(MSIPACKAGE *package) +{ + static const WCHAR RunOnce[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'R','u','n','O','n','c','e',0}; + static const WCHAR InstallRunOnce[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'I','n','s','t','a','l','l','e','r','\\', + 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0}; + + static const WCHAR msiexec_fmt[] = { + '%','s', + '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ', + '\"','%','s','\"',0}; + static const WCHAR install_fmt[] = { + '/','I',' ','\"','%','s','\"',' ', + 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ', + 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0}; + WCHAR buffer[256], sysdir[MAX_PATH]; + HKEY hkey; + WCHAR squished_pc[100]; + + squash_guid(package->ProductCode,squished_pc); + + GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0])); + RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey); + snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir, + squished_pc); + + msi_reg_set_val_str( hkey, squished_pc, buffer ); + RegCloseKey(hkey); + + TRACE("Reboot command %s\n",debugstr_w(buffer)); + + RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey); + sprintfW(buffer,install_fmt,package->ProductCode,squished_pc); + + msi_reg_set_val_str( hkey, squished_pc, buffer ); + RegCloseKey(hkey); + + return ERROR_INSTALL_SUSPEND; +} + +WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... ) +{ + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ', + 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ', + '`','E','r','r','o','r','`',' ','=',' ','%','i',0}; + MSIRECORD *rec, *row; + DWORD i, size = 0; + va_list va; + const WCHAR *str; + WCHAR *data; + + if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0; + + rec = MSI_CreateRecord( count + 2 ); + str = MSI_RecordGetString( row, 1 ); + MSI_RecordSetStringW( rec, 0, str ); + msiobj_release( &row->hdr ); + MSI_RecordSetInteger( rec, 1, error ); + + va_start( va, count ); + for (i = 0; i < count; i++) + { + str = va_arg( va, const WCHAR *); + MSI_RecordSetStringW( rec, i + 2, str ); + } + va_end( va ); + + MSI_FormatRecordW( package, rec, NULL, &size ); + size++; + data = msi_alloc( size * sizeof(WCHAR) ); + if (size > 1) MSI_FormatRecordW( package, rec, data, &size ); + else data[0] = 0; + msiobj_release( &rec->hdr ); + return data; +} + +static UINT ACTION_ResolveSource(MSIPACKAGE* package) +{ + DWORD attrib; + UINT rc; + + /* + * We are currently doing what should be done here in the top level Install + * however for Administrative and uninstalls this step will be needed + */ + if (!package->PackagePath) + return ERROR_SUCCESS; + + msi_set_sourcedir_props(package, TRUE); + + attrib = GetFileAttributesW(package->db->path); + if (attrib == INVALID_FILE_ATTRIBUTES) + { + LPWSTR prompt, msg; + DWORD size = 0; + + rc = MsiSourceListGetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_DISKPROMPTW,NULL,&size); + if (rc == ERROR_MORE_DATA) + { + prompt = msi_alloc(size * sizeof(WCHAR)); + MsiSourceListGetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_DISKPROMPTW,prompt,&size); + } + else + prompt = strdupW(package->db->path); + + msg = msi_build_error_string(package, 1302, 1, prompt); + msi_free(prompt); + while(attrib == INVALID_FILE_ATTRIBUTES) + { + rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL); + if (rc == IDCANCEL) + { + msi_free(msg); + return ERROR_INSTALL_USEREXIT; + } + attrib = GetFileAttributesW(package->db->path); + } + msi_free(msg); + rc = ERROR_SUCCESS; + } + else + return ERROR_SUCCESS; + + return rc; +} + +static UINT ACTION_RegisterUser(MSIPACKAGE *package) +{ + HKEY hkey = 0; + LPWSTR buffer, productid = NULL; + UINT i, rc = ERROR_SUCCESS; + MSIRECORD *uirow; + + static const WCHAR szPropKeys[][80] = + { + {'P','r','o','d','u','c','t','I','D',0}, + {'U','S','E','R','N','A','M','E',0}, + {'C','O','M','P','A','N','Y','N','A','M','E',0}, + {0}, + }; + + static const WCHAR szRegKeys[][80] = + { + {'P','r','o','d','u','c','t','I','D',0}, + {'R','e','g','O','w','n','e','r',0}, + {'R','e','g','C','o','m','p','a','n','y',0}, + {0}, + }; + + if (msi_check_unpublish(package)) + { + MSIREG_DeleteUserDataProductKey(package->ProductCode); + goto end; + } + + productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW ); + if (!productid) + goto end; + + rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, + NULL, &hkey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + for( i = 0; szPropKeys[i][0]; i++ ) + { + buffer = msi_dup_property( package->db, szPropKeys[i] ); + msi_reg_set_val_str( hkey, szRegKeys[i], buffer ); + msi_free( buffer ); + } + +end: + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, productid ); + msi_ui_actiondata( package, szRegisterUser, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(productid); + RegCloseKey(hkey); + return rc; +} + + +static UINT ACTION_ExecuteAction(MSIPACKAGE *package) +{ + UINT rc; + + package->script->InWhatSequence |= SEQUENCE_EXEC; + rc = ACTION_ProcessExecSequence(package,FALSE); + return rc; +} + +WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature ) +{ + static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0}; + WCHAR productid_85[21], component_85[21], *ret; + GUID clsid; + DWORD sz; + + /* > is used if there is a component GUID and < if not. */ + + productid_85[0] = 0; + component_85[0] = 0; + CLSIDFromString( package->ProductCode, &clsid ); + + encode_base85_guid( &clsid, productid_85 ); + if (component) + { + CLSIDFromString( component->ComponentId, &clsid ); + encode_base85_guid( &clsid, component_85 ); + } + + TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature), + debugstr_w(component_85)); + + sz = 20 + strlenW( feature ) + 20 + 3; + ret = msi_alloc_zero( sz * sizeof(WCHAR) ); + if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 ); + return ret; +} + +static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR compgroupid, component, feature, qualifier, text; + LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q; + HKEY hkey = NULL; + UINT rc; + MSICOMPONENT *comp; + MSIFEATURE *feat; + DWORD sz; + MSIRECORD *uirow; + int len; + + feature = MSI_RecordGetString(rec, 5); + feat = msi_get_loaded_feature(package, feature); + if (!feat) + return ERROR_SUCCESS; + + feat->Action = msi_get_feature_action( package, feat ); + if (feat->Action != INSTALLSTATE_LOCAL && + feat->Action != INSTALLSTATE_SOURCE && + feat->Action != INSTALLSTATE_ADVERTISED) + { + TRACE("feature not scheduled for installation %s\n", debugstr_w(feature)); + return ERROR_SUCCESS; + } + + component = MSI_RecordGetString(rec, 3); + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + compgroupid = MSI_RecordGetString(rec,1); + qualifier = MSI_RecordGetString(rec,2); + + rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + advertise = msi_create_component_advertise_string( package, comp, feature ); + text = MSI_RecordGetString( rec, 4 ); + if (text) + { + p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) ); + strcpyW( p, advertise ); + strcatW( p, text ); + msi_free( advertise ); + advertise = p; + } + existing = msi_reg_get_val_str( hkey, qualifier ); + + sz = strlenW( advertise ) + 1; + if (existing) + { + for (p = existing; *p; p += len) + { + len = strlenW( p ) + 1; + if (strcmpW( advertise, p )) sz += len; + } + } + if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) ))) + { + rc = ERROR_OUTOFMEMORY; + goto end; + } + q = output; + if (existing) + { + for (p = existing; *p; p += len) + { + len = strlenW( p ) + 1; + if (strcmpW( advertise, p )) + { + memcpy( q, p, len * sizeof(WCHAR) ); + q += len; + } + } + } + strcpyW( q, advertise ); + q[strlenW( q ) + 1] = 0; + + msi_reg_set_val_multi_str( hkey, qualifier, output ); + +end: + RegCloseKey(hkey); + msi_free( output ); + msi_free( advertise ); + msi_free( existing ); + + /* the UI chunk */ + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, compgroupid ); + MSI_RecordSetStringW( uirow, 2, qualifier); + msi_ui_actiondata( package, szPublishComponents, uirow ); + msiobj_release( &uirow->hdr ); + /* FIXME: call ui_progress? */ + + return rc; +} + +/* + * At present I am ignorning the advertised components part of this and only + * focusing on the qualified component sets + */ +static UINT ACTION_PublishComponents(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR szInstallerComponents[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\', + 'C','o','m','p','o','n','e','n','t','s','\\',0}; + + MSIPACKAGE *package = param; + LPCWSTR compgroupid, component, feature, qualifier; + MSICOMPONENT *comp; + MSIFEATURE *feat; + MSIRECORD *uirow; + WCHAR squashed[GUID_SIZE], keypath[MAX_PATH]; + LONG res; + + feature = MSI_RecordGetString( rec, 5 ); + feat = msi_get_loaded_feature( package, feature ); + if (!feat) + return ERROR_SUCCESS; + + feat->Action = msi_get_feature_action( package, feat ); + if (feat->Action != INSTALLSTATE_ABSENT) + { + TRACE("feature not scheduled for removal %s\n", debugstr_w(feature)); + return ERROR_SUCCESS; + } + + component = MSI_RecordGetString( rec, 3 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + compgroupid = MSI_RecordGetString( rec, 1 ); + qualifier = MSI_RecordGetString( rec, 2 ); + + squash_guid( compgroupid, squashed ); + strcpyW( keypath, szInstallerComponents ); + strcatW( keypath, squashed ); + + res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath ); + if (res != ERROR_SUCCESS) + { + WARN("Unable to delete component key %d\n", res); + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, compgroupid ); + MSI_RecordSetStringW( uirow, 2, qualifier ); + msi_ui_actiondata( package, szUnpublishComponents, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_UnpublishComponents( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param) +{ + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ', + '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0}; + MSIPACKAGE *package = param; + MSICOMPONENT *component; + MSIRECORD *row; + MSIFILE *file; + SC_HANDLE hscm = NULL, service = NULL; + LPCWSTR comp, key; + LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL; + LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL; + DWORD serv_type, start_type, err_control; + SERVICE_DESCRIPTIONW sd = {NULL}; + + comp = MSI_RecordGetString( rec, 12 ); + component = msi_get_loaded_component( package, comp ); + if (!component) + { + WARN("service component not found\n"); + goto done; + } + component->Action = msi_get_component_action( package, component ); + if (component->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(comp)); + goto done; + } + hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE); + if (!hscm) + { + ERR("Failed to open the SC Manager!\n"); + goto done; + } + + start_type = MSI_RecordGetInteger(rec, 5); + if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START) + goto done; + + deformat_string(package, MSI_RecordGetString(rec, 2), &name); + deformat_string(package, MSI_RecordGetString(rec, 3), &disp); + serv_type = MSI_RecordGetInteger(rec, 4); + err_control = MSI_RecordGetInteger(rec, 6); + deformat_string(package, MSI_RecordGetString(rec, 7), &load_order); + deformat_string(package, MSI_RecordGetString(rec, 8), &depends); + deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name); + deformat_string(package, MSI_RecordGetString(rec, 10), &pass); + deformat_string(package, MSI_RecordGetString(rec, 11), &args); + deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription); + + /* fetch the service path */ + row = MSI_QueryGetRecord(package->db, query, comp); + if (!row) + { + ERR("Query failed\n"); + goto done; + } + key = MSI_RecordGetString(row, 6); + file = msi_get_loaded_file(package, key); + msiobj_release(&row->hdr); + if (!file) + { + ERR("Failed to load the service file\n"); + goto done; + } + + if (!args || !args[0]) image_path = file->TargetPath; + else + { + int len = strlenW(file->TargetPath) + strlenW(args) + 2; + if (!(image_path = msi_alloc(len * sizeof(WCHAR)))) + return ERROR_OUTOFMEMORY; + + strcpyW(image_path, file->TargetPath); + strcatW(image_path, szSpace); + strcatW(image_path, args); + } + service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type, + start_type, err_control, image_path, load_order, + NULL, depends, serv_name, pass); + + if (!service) + { + if (GetLastError() != ERROR_SERVICE_EXISTS) + ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError()); + } + else if (sd.lpDescription) + { + if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd)) + WARN("failed to set service description %u\n", GetLastError()); + } + + if (image_path != file->TargetPath) msi_free(image_path); +done: + CloseServiceHandle(service); + CloseServiceHandle(hscm); + msi_free(name); + msi_free(disp); + msi_free(sd.lpDescription); + msi_free(load_order); + msi_free(serv_name); + msi_free(pass); + msi_free(depends); + msi_free(args); + + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallServices( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package); + msiobj_release(&view->hdr); + return rc; +} + +/* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */ +static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs) +{ + LPCWSTR *vector, *temp_vector; + LPWSTR p, q; + DWORD sep_len; + + static const WCHAR separator[] = {'[','~',']',0}; + + *numargs = 0; + sep_len = sizeof(separator) / sizeof(WCHAR) - 1; + + if (!args) + return NULL; + + vector = msi_alloc(sizeof(LPWSTR)); + if (!vector) + return NULL; + + p = args; + do + { + (*numargs)++; + vector[*numargs - 1] = p; + + if ((q = strstrW(p, separator))) + { + *q = '\0'; + + temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR)); + if (!temp_vector) + { + msi_free(vector); + return NULL; + } + vector = temp_vector; + + p = q + sep_len; + } + } while (q); + + return vector; +} + +static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + SC_HANDLE scm = NULL, service = NULL; + LPCWSTR component, *vector = NULL; + LPWSTR name, args, display_name = NULL; + DWORD event, numargs, len, wait, dummy; + UINT r = ERROR_FUNCTION_FAILED; + SERVICE_STATUS_PROCESS status; + ULONGLONG start_time; + + component = MSI_RecordGetString(rec, 6); + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + deformat_string(package, MSI_RecordGetString(rec, 2), &name); + deformat_string(package, MSI_RecordGetString(rec, 4), &args); + event = MSI_RecordGetInteger(rec, 3); + wait = MSI_RecordGetInteger(rec, 5); + + if (!(event & msidbServiceControlEventStart)) + { + r = ERROR_SUCCESS; + goto done; + } + + scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) + { + ERR("Failed to open the service control manager\n"); + goto done; + } + + len = 0; + if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) + GetServiceDisplayNameW( scm, name, display_name, &len ); + } + + service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS); + if (!service) + { + ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError()); + goto done; + } + + vector = msi_service_args_to_vector(args, &numargs); + + if (!StartServiceW(service, numargs, vector) && + GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) + { + ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError()); + goto done; + } + + r = ERROR_SUCCESS; + if (wait) + { + /* wait for at most 30 seconds for the service to be up and running */ + if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, + (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy)) + { + TRACE("failed to query service status (%u)\n", GetLastError()); + goto done; + } + start_time = GetTickCount64(); + while (status.dwCurrentState == SERVICE_START_PENDING) + { + if (GetTickCount64() - start_time > 30000) break; + Sleep(1000); + if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, + (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy)) + { + TRACE("failed to query service status (%u)\n", GetLastError()); + goto done; + } + } + if (status.dwCurrentState != SERVICE_RUNNING) + { + WARN("service failed to start %u\n", status.dwCurrentState); + r = ERROR_FUNCTION_FAILED; + } + } + +done: + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, display_name ); + MSI_RecordSetStringW( uirow, 2, name ); + msi_ui_actiondata( package, szStartServices, uirow ); + msiobj_release( &uirow->hdr ); + + CloseServiceHandle(service); + CloseServiceHandle(scm); + + msi_free(name); + msi_free(args); + msi_free(vector); + msi_free(display_name); + return r; +} + +static UINT ACTION_StartServices( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package); + msiobj_release(&view->hdr); + return rc; +} + +static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service) +{ + DWORD i, needed, count; + ENUM_SERVICE_STATUSW *dependencies; + SERVICE_STATUS ss; + SC_HANDLE depserv; + + 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 error; + + for (i = 0; i < count; i++) + { + depserv = OpenServiceW(scm, dependencies[i].lpServiceName, + SERVICE_STOP | SERVICE_QUERY_STATUS); + if (!depserv) + goto error; + + if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss)) + goto error; + } + + return TRUE; + +error: + msi_free(dependencies); + return FALSE; +} + +static UINT stop_service( LPCWSTR name ) +{ + SC_HANDLE scm = NULL, service = NULL; + SERVICE_STATUS status; + SERVICE_STATUS_PROCESS ssp; + DWORD needed; + + scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!scm) + { + WARN("Failed to open the SCM: %d\n", GetLastError()); + goto done; + } + + service = OpenServiceW(scm, name, + SERVICE_STOP | + SERVICE_QUERY_STATUS | + SERVICE_ENUMERATE_DEPENDENTS); + if (!service) + { + WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError()); + goto done; + } + + if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, + sizeof(SERVICE_STATUS_PROCESS), &needed)) + { + WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError()); + goto done; + } + + if (ssp.dwCurrentState == SERVICE_STOPPED) + goto done; + + stop_service_dependents(scm, service); + + if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) + WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError()); + +done: + CloseServiceHandle(service); + CloseServiceHandle(scm); + + return ERROR_SUCCESS; +} + +static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPCWSTR component; + LPWSTR name = NULL, display_name = NULL; + DWORD event, len; + SC_HANDLE scm; + + event = MSI_RecordGetInteger( rec, 3 ); + if (!(event & msidbServiceControlEventStop)) + return ERROR_SUCCESS; + + component = MSI_RecordGetString( rec, 6 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ); + if (!scm) + { + ERR("Failed to open the service control manager\n"); + goto done; + } + + len = 0; + if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) + GetServiceDisplayNameW( scm, name, display_name, &len ); + } + CloseServiceHandle( scm ); + + deformat_string( package, MSI_RecordGetString( rec, 2 ), &name ); + stop_service( name ); + +done: + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, display_name ); + MSI_RecordSetStringW( uirow, 2, name ); + msi_ui_actiondata( package, szStopServices, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free( name ); + msi_free( display_name ); + return ERROR_SUCCESS; +} + +static UINT ACTION_StopServices( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPWSTR name = NULL, display_name = NULL; + DWORD event, len; + SC_HANDLE scm = NULL, service = NULL; + + comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) ); + if (!comp) + return ERROR_SUCCESS; + + event = MSI_RecordGetInteger( rec, 3 ); + deformat_string( package, MSI_RecordGetString(rec, 2), &name ); + + comp->Action = msi_get_component_action( package, comp ); + if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) && + !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete))) + { + TRACE("service %s not scheduled for removal\n", debugstr_w(name)); + msi_free( name ); + return ERROR_SUCCESS; + } + stop_service( name ); + + scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + if (!scm) + { + WARN("Failed to open the SCM: %d\n", GetLastError()); + goto done; + } + + len = 0; + if (!GetServiceDisplayNameW( scm, name, NULL, &len ) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if ((display_name = msi_alloc( ++len * sizeof(WCHAR )))) + GetServiceDisplayNameW( scm, name, display_name, &len ); + } + + service = OpenServiceW( scm, name, DELETE ); + if (!service) + { + WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError()); + goto done; + } + + if (!DeleteService( service )) + WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError()); + +done: + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, display_name ); + MSI_RecordSetStringW( uirow, 2, name ); + msi_ui_actiondata( package, szDeleteServices, uirow ); + msiobj_release( &uirow->hdr ); + + CloseServiceHandle( service ); + CloseServiceHandle( scm ); + msi_free( name ); + msi_free( display_name ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_DeleteServices( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPWSTR driver, driver_path, ptr; + WCHAR outpath[MAX_PATH]; + MSIFILE *driver_file = NULL, *setup_file = NULL; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPCWSTR desc, file_key, component; + DWORD len, usage; + UINT r = ERROR_SUCCESS; + + static const WCHAR driver_fmt[] = { + 'D','r','i','v','e','r','=','%','s',0}; + static const WCHAR setup_fmt[] = { + 'S','e','t','u','p','=','%','s',0}; + static const WCHAR usage_fmt[] = { + 'F','i','l','e','U','s','a','g','e','=','1',0}; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + desc = MSI_RecordGetString(rec, 3); + + file_key = MSI_RecordGetString( rec, 4 ); + if (file_key) driver_file = msi_get_loaded_file( package, file_key ); + + file_key = MSI_RecordGetString( rec, 5 ); + if (file_key) setup_file = msi_get_loaded_file( package, file_key ); + + if (!driver_file) + { + ERR("ODBC Driver entry not found!\n"); + return ERROR_FUNCTION_FAILED; + } + + len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName); + if (setup_file) + len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName); + len += lstrlenW(usage_fmt) + 2; /* \0\0 */ + + driver = msi_alloc(len * sizeof(WCHAR)); + if (!driver) + return ERROR_OUTOFMEMORY; + + ptr = driver; + lstrcpyW(ptr, desc); + ptr += lstrlenW(ptr) + 1; + + len = sprintfW(ptr, driver_fmt, driver_file->FileName); + ptr += len + 1; + + if (setup_file) + { + len = sprintfW(ptr, setup_fmt, setup_file->FileName); + ptr += len + 1; + } + + lstrcpyW(ptr, usage_fmt); + ptr += lstrlenW(ptr) + 1; + *ptr = '\0'; + + if (!driver_file->TargetPath) + { + const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory ); + driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName ); + } + driver_path = strdupW(driver_file->TargetPath); + ptr = strrchrW(driver_path, '\\'); + if (ptr) *ptr = '\0'; + + if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH, + NULL, ODBC_INSTALL_COMPLETE, &usage)) + { + ERR("Failed to install SQL driver!\n"); + r = ERROR_FUNCTION_FAILED; + } + + uirow = MSI_CreateRecord( 5 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory ); + msi_ui_actiondata( package, szInstallODBC, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(driver); + msi_free(driver_path); + + return r; +} + +static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPWSTR translator, translator_path, ptr; + WCHAR outpath[MAX_PATH]; + MSIFILE *translator_file = NULL, *setup_file = NULL; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPCWSTR desc, file_key, component; + DWORD len, usage; + UINT r = ERROR_SUCCESS; + + static const WCHAR translator_fmt[] = { + 'T','r','a','n','s','l','a','t','o','r','=','%','s',0}; + static const WCHAR setup_fmt[] = { + 'S','e','t','u','p','=','%','s',0}; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + desc = MSI_RecordGetString(rec, 3); + + file_key = MSI_RecordGetString( rec, 4 ); + if (file_key) translator_file = msi_get_loaded_file( package, file_key ); + + file_key = MSI_RecordGetString( rec, 5 ); + if (file_key) setup_file = msi_get_loaded_file( package, file_key ); + + if (!translator_file) + { + ERR("ODBC Translator entry not found!\n"); + return ERROR_FUNCTION_FAILED; + } + + len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */ + if (setup_file) + len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName); + + translator = msi_alloc(len * sizeof(WCHAR)); + if (!translator) + return ERROR_OUTOFMEMORY; + + ptr = translator; + lstrcpyW(ptr, desc); + ptr += lstrlenW(ptr) + 1; + + len = sprintfW(ptr, translator_fmt, translator_file->FileName); + ptr += len + 1; + + if (setup_file) + { + len = sprintfW(ptr, setup_fmt, setup_file->FileName); + ptr += len + 1; + } + *ptr = '\0'; + + translator_path = strdupW(translator_file->TargetPath); + ptr = strrchrW(translator_path, '\\'); + if (ptr) *ptr = '\0'; + + if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH, + NULL, ODBC_INSTALL_COMPLETE, &usage)) + { + ERR("Failed to install SQL translator!\n"); + r = ERROR_FUNCTION_FAILED; + } + + uirow = MSI_CreateRecord( 5 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory ); + msi_ui_actiondata( package, szInstallODBC, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(translator); + msi_free(translator_path); + + return r; +} + +static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + LPWSTR attrs; + LPCWSTR desc, driver, component; + WORD request = ODBC_ADD_SYS_DSN; + INT registration; + DWORD len; + UINT r = ERROR_SUCCESS; + MSIRECORD *uirow; + + static const WCHAR attrs_fmt[] = { + 'D','S','N','=','%','s',0 }; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + desc = MSI_RecordGetString(rec, 3); + driver = MSI_RecordGetString(rec, 4); + registration = MSI_RecordGetInteger(rec, 5); + + if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN; + else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN; + + len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */ + attrs = msi_alloc(len * sizeof(WCHAR)); + if (!attrs) + return ERROR_OUTOFMEMORY; + + len = sprintfW(attrs, attrs_fmt, desc); + attrs[len + 1] = 0; + + if (!SQLConfigDataSourceW(NULL, request, driver, attrs)) + { + ERR("Failed to install SQL data source!\n"); + r = ERROR_FUNCTION_FAILED; + } + + uirow = MSI_CreateRecord( 5 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + MSI_RecordSetInteger( uirow, 3, request ); + msi_ui_actiondata( package, szInstallODBC, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(attrs); + + return r; +} + +static UINT ACTION_InstallODBC( MSIPACKAGE *package ) +{ + static const WCHAR driver_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','D','r','i','v','e','r',0}; + static const WCHAR translator_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; + static const WCHAR source_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package); + msiobj_release(&view->hdr); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package); + msiobj_release(&view->hdr); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW(package->db, source_query, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package); + msiobj_release(&view->hdr); + if (rc != ERROR_SUCCESS) + return rc; + } + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + DWORD usage; + LPCWSTR desc, component; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + desc = MSI_RecordGetString( rec, 3 ); + if (!SQLRemoveDriverW( desc, FALSE, &usage )) + { + WARN("Failed to remove ODBC driver\n"); + } + else if (!usage) + { + FIXME("Usage count reached 0\n"); + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + msi_ui_actiondata( package, szRemoveODBC, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + DWORD usage; + LPCWSTR desc, component; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + desc = MSI_RecordGetString( rec, 3 ); + if (!SQLRemoveTranslatorW( desc, &usage )) + { + WARN("Failed to remove ODBC translator\n"); + } + else if (!usage) + { + FIXME("Usage count reached 0\n"); + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + msi_ui_actiondata( package, szRemoveODBC, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPWSTR attrs; + LPCWSTR desc, driver, component; + WORD request = ODBC_REMOVE_SYS_DSN; + INT registration; + DWORD len; + + static const WCHAR attrs_fmt[] = { + 'D','S','N','=','%','s',0 }; + + component = MSI_RecordGetString( rec, 2 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + + desc = MSI_RecordGetString( rec, 3 ); + driver = MSI_RecordGetString( rec, 4 ); + registration = MSI_RecordGetInteger( rec, 5 ); + + if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN; + else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN; + + len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */ + attrs = msi_alloc( len * sizeof(WCHAR) ); + if (!attrs) + return ERROR_OUTOFMEMORY; + + FIXME("Use ODBCSourceAttribute table\n"); + + len = sprintfW( attrs, attrs_fmt, desc ); + attrs[len + 1] = 0; + + if (!SQLConfigDataSourceW( NULL, request, driver, attrs )) + { + WARN("Failed to remove ODBC data source\n"); + } + msi_free( attrs ); + + uirow = MSI_CreateRecord( 3 ); + MSI_RecordSetStringW( uirow, 1, desc ); + MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) ); + MSI_RecordSetInteger( uirow, 3, request ); + msi_ui_actiondata( package, szRemoveODBC, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveODBC( MSIPACKAGE *package ) +{ + static const WCHAR driver_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','D','r','i','v','e','r',0}; + static const WCHAR translator_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; + static const WCHAR source_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + rc = MSI_DatabaseOpenViewW( package->db, source_query, &view ); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package ); + msiobj_release( &view->hdr ); + if (rc != ERROR_SUCCESS) + return rc; + } + return ERROR_SUCCESS; +} + +#define ENV_ACT_SETALWAYS 0x1 +#define ENV_ACT_SETABSENT 0x2 +#define ENV_ACT_REMOVE 0x4 +#define ENV_ACT_REMOVEMATCH 0x8 + +#define ENV_MOD_MACHINE 0x20000000 +#define ENV_MOD_APPEND 0x40000000 +#define ENV_MOD_PREFIX 0x80000000 +#define ENV_MOD_MASK 0xC0000000 + +#define check_flag_combo(x, y) ((x) & ~(y)) == (y) + +static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags ) +{ + LPCWSTR cptr = *name; + + static const WCHAR prefix[] = {'[','~',']',0}; + static const int prefix_len = 3; + + *flags = 0; + while (*cptr) + { + if (*cptr == '=') + *flags |= ENV_ACT_SETALWAYS; + else if (*cptr == '+') + *flags |= ENV_ACT_SETABSENT; + else if (*cptr == '-') + *flags |= ENV_ACT_REMOVE; + else if (*cptr == '!') + *flags |= ENV_ACT_REMOVEMATCH; + else if (*cptr == '*') + *flags |= ENV_MOD_MACHINE; + else + break; + + cptr++; + (*name)++; + } + + if (!*cptr) + { + ERR("Missing environment variable\n"); + return ERROR_FUNCTION_FAILED; + } + + if (*value) + { + LPCWSTR ptr = *value; + if (!strncmpW(ptr, prefix, prefix_len)) + { + if (ptr[prefix_len] == szSemiColon[0]) + { + *flags |= ENV_MOD_APPEND; + *value += lstrlenW(prefix); + } + else + { + *value = NULL; + } + } + else if (lstrlenW(*value) >= prefix_len) + { + ptr += lstrlenW(ptr) - prefix_len; + if (!strcmpW( ptr, prefix )) + { + if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0]) + { + *flags |= ENV_MOD_PREFIX; + /* the "[~]" will be removed by deformat_string */; + } + else + { + *value = NULL; + } + } + } + } + + if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) || + check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) || + check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) || + check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK)) + { + ERR("Invalid flags: %08x\n", *flags); + return ERROR_FUNCTION_FAILED; + } + + if (!*flags) + *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE; + + return ERROR_SUCCESS; +} + +static UINT open_env_key( DWORD flags, HKEY *key ) +{ + static const WCHAR user_env[] = + {'E','n','v','i','r','o','n','m','e','n','t',0}; + static const WCHAR machine_env[] = + {'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\', + 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\', + 'E','n','v','i','r','o','n','m','e','n','t',0}; + const WCHAR *env; + HKEY root; + LONG res; + + if (flags & ENV_MOD_MACHINE) + { + env = machine_env; + root = HKEY_LOCAL_MACHINE; + } + else + { + env = user_env; + root = HKEY_CURRENT_USER; + } + + res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key ); + if (res != ERROR_SUCCESS) + { + WARN("Failed to open key %s (%d)\n", debugstr_w(env), res); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR name, value, component; + LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr; + DWORD flags, type, size; + UINT res; + HKEY env = NULL; + MSICOMPONENT *comp; + MSIRECORD *uirow; + int action = 0; + + component = MSI_RecordGetString(rec, 4); + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + name = MSI_RecordGetString(rec, 2); + value = MSI_RecordGetString(rec, 3); + + TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value)); + + res = env_parse_flags(&name, &value, &flags); + if (res != ERROR_SUCCESS || !value) + goto done; + + if (value && !deformat_string(package, value, &deformatted)) + { + res = ERROR_OUTOFMEMORY; + goto done; + } + + value = deformatted; + + res = open_env_key( flags, &env ); + if (res != ERROR_SUCCESS) + goto done; + + if (flags & ENV_MOD_MACHINE) + action |= 0x20000000; + + size = 0; + type = REG_SZ; + res = RegQueryValueExW(env, name, NULL, &type, NULL, &size); + if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) || + (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ)) + goto done; + + if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK))) + { + action = 0x2; + + /* Nothing to do. */ + if (!value) + { + res = ERROR_SUCCESS; + goto done; + } + + /* If we are appending but the string was empty, strip ; */ + if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++; + + size = (lstrlenW(value) + 1) * sizeof(WCHAR); + newval = strdupW(value); + if (!newval) + { + res = ERROR_OUTOFMEMORY; + goto done; + } + } + else + { + action = 0x1; + + /* Contrary to MSDN, +-variable to [~];path works */ + if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK)) + { + res = ERROR_SUCCESS; + goto done; + } + + data = msi_alloc(size); + if (!data) + { + RegCloseKey(env); + return ERROR_OUTOFMEMORY; + } + + res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size); + if (res != ERROR_SUCCESS) + goto done; + + if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value ))) + { + action = 0x4; + res = RegDeleteValueW(env, name); + if (res != ERROR_SUCCESS) + WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res); + goto done; + } + + size = (lstrlenW(data) + 1) * sizeof(WCHAR); + if (flags & ENV_MOD_MASK) + { + DWORD mod_size; + int multiplier = 0; + if (flags & ENV_MOD_APPEND) multiplier++; + if (flags & ENV_MOD_PREFIX) multiplier++; + mod_size = lstrlenW(value) * multiplier; + size += mod_size * sizeof(WCHAR); + } + + newval = msi_alloc(size); + ptr = newval; + if (!newval) + { + res = ERROR_OUTOFMEMORY; + goto done; + } + + if (flags & ENV_MOD_PREFIX) + { + lstrcpyW(newval, value); + ptr = newval + lstrlenW(value); + action |= 0x80000000; + } + + lstrcpyW(ptr, data); + + if (flags & ENV_MOD_APPEND) + { + lstrcatW(newval, value); + action |= 0x40000000; + } + } + TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval)); + res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size); + if (res) + { + WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res); + } + +done: + uirow = MSI_CreateRecord( 3 ); + MSI_RecordSetStringW( uirow, 1, name ); + MSI_RecordSetStringW( uirow, 2, newval ); + MSI_RecordSetInteger( uirow, 3, action ); + msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow ); + msiobj_release( &uirow->hdr ); + + if (env) RegCloseKey(env); + msi_free(deformatted); + msi_free(data); + msi_free(newval); + return res; +} + +static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','n','v','i','r','o','n','m','e','n','t','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPCWSTR name, value, component; + LPWSTR deformatted = NULL; + DWORD flags; + HKEY env; + MSICOMPONENT *comp; + MSIRECORD *uirow; + int action = 0; + LONG res; + UINT r; + + component = MSI_RecordGetString( rec, 4 ); + comp = msi_get_loaded_component( package, component ); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(component)); + return ERROR_SUCCESS; + } + name = MSI_RecordGetString( rec, 2 ); + value = MSI_RecordGetString( rec, 3 ); + + TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value)); + + r = env_parse_flags( &name, &value, &flags ); + if (r != ERROR_SUCCESS) + return r; + + if (!(flags & ENV_ACT_REMOVE)) + { + TRACE("Environment variable %s not marked for removal\n", debugstr_w(name)); + return ERROR_SUCCESS; + } + + if (value && !deformat_string( package, value, &deformatted )) + return ERROR_OUTOFMEMORY; + + value = deformatted; + + r = open_env_key( flags, &env ); + if (r != ERROR_SUCCESS) + { + r = ERROR_SUCCESS; + goto done; + } + + if (flags & ENV_MOD_MACHINE) + action |= 0x20000000; + + TRACE("Removing %s\n", debugstr_w(name)); + + res = RegDeleteValueW( env, name ); + if (res != ERROR_SUCCESS) + { + WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res); + r = ERROR_SUCCESS; + } + +done: + uirow = MSI_CreateRecord( 3 ); + MSI_RecordSetStringW( uirow, 1, name ); + MSI_RecordSetStringW( uirow, 2, value ); + MSI_RecordSetInteger( uirow, 3, action ); + msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow ); + msiobj_release( &uirow->hdr ); + + if (env) RegCloseKey( env ); + msi_free( deformatted ); + return r; +} + +static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','n','v','i','r','o','n','m','e','n','t','`',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package ); + msiobj_release( &view->hdr ); + return rc; +} + +UINT msi_validate_product_id( MSIPACKAGE *package ) +{ + LPWSTR key, template, id; + UINT r = ERROR_SUCCESS; + + id = msi_dup_property( package->db, szProductID ); + if (id) + { + msi_free( id ); + return ERROR_SUCCESS; + } + template = msi_dup_property( package->db, szPIDTemplate ); + key = msi_dup_property( package->db, szPIDKEY ); + if (key && template) + { + FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) ); + r = msi_set_property( package->db, szProductID, key ); + } + msi_free( template ); + msi_free( key ); + return r; +} + +static UINT ACTION_ValidateProductID( MSIPACKAGE *package ) +{ + return msi_validate_product_id( package ); +} + +static UINT ACTION_ScheduleReboot( MSIPACKAGE *package ) +{ + TRACE("\n"); + package->need_reboot_at_end = 1; + return ERROR_SUCCESS; +} + +static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package ) +{ + static const WCHAR szAvailableFreeReg[] = + {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0}; + MSIRECORD *uirow; + int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 ); + + TRACE("%p %d kilobytes\n", package, space); + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger( uirow, 1, space ); + msi_ui_actiondata( package, szAllocateRegistrySpace, uirow ); + msiobj_release( &uirow->hdr ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_DisableRollback( MSIPACKAGE *package ) +{ + TRACE("%p\n", package); + + msi_set_property( package->db, szRollbackDisabled, szOne ); + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package ) +{ + FIXME("%p\n", package); + return ERROR_SUCCESS; +} + +static UINT ACTION_SetODBCFolders( MSIPACKAGE *package ) +{ + static const WCHAR driver_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','D','r','i','v','e','r',0}; + static const WCHAR translator_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0}; + MSIQUERY *view; + UINT r, count; + + r = MSI_DatabaseOpenViewW( package->db, driver_query, &view ); + if (r == ERROR_SUCCESS) + { + count = 0; + r = MSI_IterateRecords( view, &count, NULL, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + if (count) FIXME("ignored %u rows in ODBCDriver table\n", count); + } + r = MSI_DatabaseOpenViewW( package->db, translator_query, &view ); + if (r == ERROR_SUCCESS) + { + count = 0; + r = MSI_IterateRecords( view, &count, NULL, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count); + } + return ERROR_SUCCESS; +} + +static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR fmtW[] = + {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0}; + MSIPACKAGE *package = param; + const WCHAR *property = MSI_RecordGetString( rec, 7 ); + UINT len = sizeof(fmtW)/sizeof(fmtW[0]); + WCHAR *product, *features, *cmd; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL ret; + + if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS; + + deformat_string( package, MSI_RecordGetString( rec, 6 ), &features ); + + len += strlenW( product ); + if (features) + len += strlenW( features ); + else + len += sizeof(szAll) / sizeof(szAll[0]); + + if (!(cmd = msi_alloc( len * sizeof(WCHAR) ))) + { + msi_free( product ); + msi_free( features ); + return ERROR_OUTOFMEMORY; + } + sprintfW( cmd, fmtW, product, features ? features : szAll ); + msi_free( product ); + msi_free( features ); + + memset( &si, 0, sizeof(STARTUPINFOW) ); + ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info ); + msi_free( cmd ); + if (!ret) return GetLastError(); + CloseHandle( info.hThread ); + + WaitForSingleObject( info.hProcess, INFINITE ); + CloseHandle( info.hProcess ); + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + } + return ERROR_SUCCESS; +} + +static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + int attributes = MSI_RecordGetInteger( rec, 5 ); + + if (attributes & msidbUpgradeAttributesMigrateFeatures) + { + const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 ); + const WCHAR *version_min = MSI_RecordGetString( rec, 2 ); + const WCHAR *version_max = MSI_RecordGetString( rec, 3 ); + const WCHAR *language = MSI_RecordGetString( rec, 4 ); + HKEY hkey; + UINT r; + + if (package->Context == MSIINSTALLCONTEXT_MACHINE) + { + r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + } + else + { + r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + } + RegCloseKey( hkey ); + + FIXME("migrate feature states from %s version min %s version max %s language %s\n", + debugstr_w(upgrade_code), debugstr_w(version_min), + debugstr_w(version_max), debugstr_w(language)); + } + return ERROR_SUCCESS; +} + +static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'U','p','g','r','a','d','e',0}; + MSIQUERY *view; + UINT r; + + if (msi_get_property_int( package->db, szInstalled, 0 )) + { + TRACE("product is installed, skipping action\n"); + return ERROR_SUCCESS; + } + if (msi_get_property_int( package->db, szPreselected, 0 )) + { + TRACE("Preselected property is set, not migrating feature states\n"); + return ERROR_SUCCESS; + } + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + } + return ERROR_SUCCESS; +} + +static void bind_image( const char *filename, const char *path ) +{ + if (!BindImageEx( 0, filename, path, NULL, NULL )) + { + WARN("failed to bind image %u\n", GetLastError()); + } +} + +static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param ) +{ + UINT i; + MSIFILE *file; + MSIPACKAGE *package = param; + const WCHAR *key = MSI_RecordGetString( rec, 1 ); + const WCHAR *paths = MSI_RecordGetString( rec, 2 ); + char *filenameA, *pathA; + WCHAR *pathW, **path_list; + + if (!(file = msi_get_loaded_file( package, key ))) + { + WARN("file %s not found\n", debugstr_w(key)); + return ERROR_SUCCESS; + } + if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS; + path_list = msi_split_string( paths, ';' ); + if (!path_list) bind_image( filenameA, NULL ); + else + { + for (i = 0; path_list[i] && path_list[i][0]; i++) + { + deformat_string( package, path_list[i], &pathW ); + if ((pathA = strdupWtoA( pathW ))) + { + bind_image( filenameA, pathA ); + msi_free( pathA ); + } + msi_free( pathW ); + } + } + msi_free( path_list ); + msi_free( filenameA ); + return ERROR_SUCCESS; +} + +static UINT ACTION_BindImage( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'B','i','n','d','I','m','a','g','e',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package ); + msiobj_release( &view->hdr ); + if (r != ERROR_SUCCESS) + return r; + } + return ERROR_SUCCESS; +} + +static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0}; + MSIQUERY *view; + DWORD count = 0; + UINT r; + + r = MSI_OpenQuery( package->db, &view, query, table ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, &count, NULL, package); + msiobj_release(&view->hdr); + if (r != ERROR_SUCCESS) + return r; + } + if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table)); + return ERROR_SUCCESS; +} + +static UINT ACTION_IsolateComponents( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 }; + return msi_unimplemented_action_stub( package, "IsolateComponents", table ); +} + +static UINT ACTION_RMCCPSearch( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 }; + return msi_unimplemented_action_stub( package, "RMCCPSearch", table ); +} + +static UINT ACTION_RegisterComPlus( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 }; + return msi_unimplemented_action_stub( package, "RegisterComPlus", table ); +} + +static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 }; + return msi_unimplemented_action_stub( package, "UnregisterComPlus", table ); +} + +static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 }; + return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table ); +} + +static const struct +{ + const WCHAR *action; + UINT (*handler)(MSIPACKAGE *); + const WCHAR *action_rollback; +} +StandardActions[] = +{ + { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL }, + { szAppSearch, ACTION_AppSearch, NULL }, + { szBindImage, ACTION_BindImage, NULL }, + { szCCPSearch, ACTION_CCPSearch, NULL }, + { szCostFinalize, ACTION_CostFinalize, NULL }, + { szCostInitialize, ACTION_CostInitialize, NULL }, + { szCreateFolders, ACTION_CreateFolders, szRemoveFolders }, + { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts }, + { szDeleteServices, ACTION_DeleteServices, szInstallServices }, + { szDisableRollback, ACTION_DisableRollback, NULL }, + { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles }, + { szExecuteAction, ACTION_ExecuteAction, NULL }, + { szFileCost, ACTION_FileCost, NULL }, + { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL }, + { szForceReboot, ACTION_ForceReboot, NULL }, + { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL }, + { szInstallExecute, ACTION_InstallExecute, NULL }, + { szInstallExecuteAgain, ACTION_InstallExecute, NULL }, + { szInstallFiles, ACTION_InstallFiles, szRemoveFiles }, + { szInstallFinalize, ACTION_InstallFinalize, NULL }, + { szInstallInitialize, ACTION_InstallInitialize, NULL }, + { szInstallODBC, ACTION_InstallODBC, szRemoveODBC }, + { szInstallServices, ACTION_InstallServices, szDeleteServices }, + { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL }, + { szInstallValidate, ACTION_InstallValidate, NULL }, + { szIsolateComponents, ACTION_IsolateComponents, NULL }, + { szLaunchConditions, ACTION_LaunchConditions, NULL }, + { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL }, + { szMoveFiles, ACTION_MoveFiles, NULL }, + { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies }, + { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies }, + { szPatchFiles, ACTION_PatchFiles, NULL }, + { szProcessComponents, ACTION_ProcessComponents, szProcessComponents }, + { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents }, + { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures }, + { szPublishProduct, ACTION_PublishProduct, NULL }, + { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo }, + { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus }, + { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo }, + { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts }, + { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo }, + { szRegisterProduct, ACTION_RegisterProduct, NULL }, + { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo }, + { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries }, + { szRegisterUser, ACTION_RegisterUser, NULL }, + { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles }, + { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings }, + { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL }, + { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles }, + { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders }, + { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues }, + { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC }, + { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues }, + { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts }, + { szResolveSource, ACTION_ResolveSource, NULL }, + { szRMCCPSearch, ACTION_RMCCPSearch, NULL }, + { szScheduleReboot, ACTION_ScheduleReboot, NULL }, + { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules }, + { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules }, + { szSetODBCFolders, ACTION_SetODBCFolders, NULL }, + { szStartServices, ACTION_StartServices, szStopServices }, + { szStopServices, ACTION_StopServices, szStartServices }, + { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents }, + { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures }, + { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo }, + { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus }, + { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo }, + { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts }, + { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo }, + { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo }, + { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries }, + { szValidateProductID, ACTION_ValidateProductID, NULL }, + { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings }, + { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues }, + { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues }, + { NULL, NULL, NULL } +}; + +static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc ) +{ + BOOL ret = FALSE; + UINT i; + + i = 0; + while (StandardActions[i].action != NULL) + { + if (!strcmpW( StandardActions[i].action, action )) + { + ui_actionstart( package, action ); + if (StandardActions[i].handler) + { + ui_actioninfo( package, action, TRUE, 0 ); + *rc = StandardActions[i].handler( package ); + ui_actioninfo( package, action, FALSE, *rc ); + + if (StandardActions[i].action_rollback && !package->need_rollback) + { + TRACE("scheduling rollback action\n"); + msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback ); + } + } + else + { + FIXME("unhandled standard action %s\n", debugstr_w(action)); + *rc = ERROR_SUCCESS; + } + ret = TRUE; + break; + } + i++; + } + return ret; +} + +UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script) +{ + UINT rc = ERROR_SUCCESS; + BOOL handled; + + TRACE("Performing action (%s)\n", debugstr_w(action)); + + handled = ACTION_HandleStandardAction(package, action, &rc); + + if (!handled) + handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE); + + if (!handled) + { + WARN("unhandled msi action %s\n", debugstr_w(action)); + rc = ERROR_FUNCTION_NOT_CALLED; + } + + return rc; +} + +UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script) +{ + UINT rc = ERROR_SUCCESS; + BOOL handled = FALSE; + + TRACE("Performing action (%s)\n", debugstr_w(action)); + + package->action_progress_increment = 0; + handled = ACTION_HandleStandardAction(package, action, &rc); + + if (!handled) + handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE); + + if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS ) + handled = TRUE; + + if (!handled) + { + WARN("unhandled msi action %s\n", debugstr_w(action)); + rc = ERROR_FUNCTION_NOT_CALLED; + } + + return rc; +} + +static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq) +{ + UINT rc = ERROR_SUCCESS; + MSIRECORD *row; + + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', + 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0}; + static const WCHAR ui_query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e', + '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`', + ' ', '=',' ','%','i',0}; + + if (needs_ui_sequence(package)) + row = MSI_QueryGetRecord(package->db, ui_query, seq); + else + row = MSI_QueryGetRecord(package->db, query, seq); + + if (row) + { + LPCWSTR action, cond; + + TRACE("Running the actions\n"); + + /* check conditions */ + cond = MSI_RecordGetString(row, 2); + + /* this is a hack to skip errors in the condition code */ + if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE) + { + msiobj_release(&row->hdr); + return ERROR_SUCCESS; + } + + action = MSI_RecordGetString(row, 1); + if (!action) + { + ERR("failed to fetch action\n"); + msiobj_release(&row->hdr); + return ERROR_FUNCTION_FAILED; + } + + if (needs_ui_sequence(package)) + rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE); + else + rc = ACTION_PerformAction(package, action, SCRIPT_NONE); + + msiobj_release(&row->hdr); + } + + return rc; +} + +/**************************************************** + * TOP level entry points + *****************************************************/ + +UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath, + LPCWSTR szCommandLine ) +{ + static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0}; + static const WCHAR szAction[] = {'A','C','T','I','O','N',0}; + static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0}; + WCHAR *reinstall = NULL; + BOOL ui_exists; + UINT rc; + + msi_set_property( package->db, szAction, szInstall ); + + package->script->InWhatSequence = SEQUENCE_INSTALL; + + if (szPackagePath) + { + LPWSTR p, dir; + LPCWSTR file; + + dir = strdupW(szPackagePath); + p = strrchrW(dir, '\\'); + if (p) + { + *(++p) = 0; + file = szPackagePath + (p - dir); + } + else + { + msi_free(dir); + dir = msi_alloc(MAX_PATH * sizeof(WCHAR)); + GetCurrentDirectoryW(MAX_PATH, dir); + lstrcatW(dir, szBackSlash); + file = szPackagePath; + } + + msi_free( package->PackagePath ); + package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR)); + if (!package->PackagePath) + { + msi_free(dir); + return ERROR_OUTOFMEMORY; + } + + lstrcpyW(package->PackagePath, dir); + lstrcatW(package->PackagePath, file); + msi_free(dir); + + msi_set_sourcedir_props(package, FALSE); + } + + rc = msi_parse_command_line( package, szCommandLine, FALSE ); + if (rc != ERROR_SUCCESS) + return rc; + + msi_apply_transforms( package ); + msi_apply_patches( package ); + + if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 )) + { + TRACE("setting reinstall property\n"); + msi_set_property( package->db, szReinstall, szAll ); + } + + /* properties may have been added by a transform */ + msi_clone_properties( package ); + + msi_parse_command_line( package, szCommandLine, FALSE ); + msi_adjust_privilege_properties( package ); + msi_set_context( package ); + + if (msi_get_property_int( package->db, szDisableRollback, 0 )) + { + TRACE("disabling rollback\n"); + msi_set_property( package->db, szRollbackDisabled, szOne ); + } + + if (needs_ui_sequence( package)) + { + package->script->InWhatSequence |= SEQUENCE_UI; + rc = ACTION_ProcessUISequence(package); + ui_exists = ui_sequence_exists(package); + if (rc == ERROR_SUCCESS || !ui_exists) + { + package->script->InWhatSequence |= SEQUENCE_EXEC; + rc = ACTION_ProcessExecSequence(package, ui_exists); + } + } + else + rc = ACTION_ProcessExecSequence(package, FALSE); + + package->script->CurrentlyScripting = FALSE; + + /* process the ending type action */ + if (rc == ERROR_SUCCESS) + ACTION_PerformActionSequence(package, -1); + else if (rc == ERROR_INSTALL_USEREXIT) + ACTION_PerformActionSequence(package, -2); + else if (rc == ERROR_INSTALL_SUSPEND) + ACTION_PerformActionSequence(package, -4); + else /* failed */ + { + ACTION_PerformActionSequence(package, -3); + if (!msi_get_property_int( package->db, szRollbackDisabled, 0 )) + { + package->need_rollback = TRUE; + } + } + + /* finish up running custom actions */ + ACTION_FinishCustomActions(package); + + if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall ))) + { + WARN("installation failed, running rollback script\n"); + execute_script( package, SCRIPT_ROLLBACK ); + } + msi_free( reinstall ); + + if (rc == ERROR_SUCCESS && package->need_reboot_at_end) + return ERROR_SUCCESS_REBOOT_REQUIRED; + + return rc; +} diff --git a/libmsi/alter.c b/libmsi/alter.c new file mode 100644 index 0000000..8b11351 --- /dev/null +++ b/libmsi/alter.c @@ -0,0 +1,285 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2006 Mike McCormack + * + * 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct tagMSIALTERVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + column_info *colinfo; + INT hold; +} MSIALTERVIEW; + +static UINT ALTER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %d %d %p\n", av, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT ALTER_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %d %d %p\n", av, row, col, stm ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT ALTER_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %d %p\n", av, row, rec ); + + return av->table->ops->get_row(av->table, row, rec); +} + +static UINT ITERATE_columns(MSIRECORD *row, LPVOID param) +{ + (*(UINT *)param)++; + return ERROR_SUCCESS; +} + +static BOOL check_column_exists(MSIDATABASE *db, LPCWSTR table, LPCWSTR column) +{ + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ', + '`','T','a','b','l','e','`','=','\'','%','s','\'',' ','A','N','D',' ', + '`','N','a','m','e','`','=','\'','%','s','\'',0 + }; + + r = MSI_OpenQuery(db, &view, query, table, column); + if (r != ERROR_SUCCESS) + return FALSE; + + r = MSI_ViewExecute(view, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(view, &rec); + if (r == ERROR_SUCCESS) + msiobj_release(&rec->hdr); + +done: + msiobj_release(&view->hdr); + return (r == ERROR_SUCCESS); +} + +static UINT alter_add_column(MSIALTERVIEW *av) +{ + UINT r, colnum = 1; + MSIQUERY *view; + MSIVIEW *columns; + + static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0}; + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ', + '`','T','a','b','l','e','`','=','\'','%','s','\'',' ','O','R','D','E','R',' ', + 'B','Y',' ','`','N','u','m','b','e','r','`',0 + }; + + r = TABLE_CreateView(av->db, szColumns, &columns); + if (r != ERROR_SUCCESS) + return r; + + if (check_column_exists(av->db, av->colinfo->table, av->colinfo->column)) + { + columns->ops->delete(columns); + return ERROR_BAD_QUERY_SYNTAX; + } + + r = MSI_OpenQuery(av->db, &view, query, av->colinfo->table, av->colinfo->column); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, NULL, ITERATE_columns, &colnum); + msiobj_release(&view->hdr); + if (r != ERROR_SUCCESS) + { + columns->ops->delete(columns); + return r; + } + } + r = columns->ops->add_column(columns, av->colinfo->table, + colnum, av->colinfo->column, + av->colinfo->type, (av->hold == 1)); + + columns->ops->delete(columns); + return r; +} + +static UINT ALTER_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + UINT ref; + + TRACE("%p %p\n", av, record); + + if (av->hold == 1) + av->table->ops->add_ref(av->table); + else if (av->hold == -1) + { + ref = av->table->ops->release(av->table); + if (ref == 0) + av->table = NULL; + } + + if (av->colinfo) + return alter_add_column(av); + + return ERROR_SUCCESS; +} + +static UINT ALTER_close( struct tagMSIVIEW *view ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p\n", av ); + + return ERROR_SUCCESS; +} + +static UINT ALTER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %p %p\n", av, rows, cols ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT ALTER_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %d %p %p %p %p\n", av, n, name, type, temporary, table_name ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT ALTER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p %d %p\n", av, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT ALTER_delete( struct tagMSIVIEW *view ) +{ + MSIALTERVIEW *av = (MSIALTERVIEW*)view; + + TRACE("%p\n", av ); + if (av->table) + av->table->ops->delete( av->table ); + msi_free( av ); + + return ERROR_SUCCESS; +} + +static UINT ALTER_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + return ERROR_FUNCTION_FAILED; +} + +static const MSIVIEWOPS alter_ops = +{ + ALTER_fetch_int, + ALTER_fetch_stream, + ALTER_get_row, + NULL, + NULL, + NULL, + ALTER_execute, + ALTER_close, + ALTER_get_dimensions, + ALTER_get_column_info, + ALTER_modify, + ALTER_delete, + ALTER_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold ) +{ + MSIALTERVIEW *av; + UINT r; + + TRACE("%p %p %s %d\n", view, colinfo, debugstr_w(name), hold ); + + av = msi_alloc_zero( sizeof *av ); + if( !av ) + return ERROR_FUNCTION_FAILED; + + r = TABLE_CreateView( db, name, &av->table ); + if (r != ERROR_SUCCESS) + { + msi_free( av ); + return r; + } + + if (colinfo) + colinfo->table = name; + + /* fill the structure */ + av->view.ops = &alter_ops; + av->db = db; + av->hold = hold; + av->colinfo = colinfo; + + *view = &av->view; + + return ERROR_SUCCESS; +} diff --git a/libmsi/appsearch.c b/libmsi/appsearch.c new file mode 100644 index 0000000..21daf43 --- /dev/null +++ b/libmsi/appsearch.c @@ -0,0 +1,1189 @@ +/* + * Implementation of the AppSearch action of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Juan Lang + * + * 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 "winreg.h" +#include "msi.h" +#include "msiquery.h" +#include "msidefs.h" +#include "winver.h" +#include "shlwapi.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tagMSISIGNATURE +{ + LPCWSTR Name; /* NOT owned by this structure */ + LPWSTR File; + DWORD MinVersionMS; + DWORD MinVersionLS; + DWORD MaxVersionMS; + DWORD MaxVersionLS; + DWORD MinSize; + DWORD MaxSize; + FILETIME MinTime; + FILETIME MaxTime; + LPWSTR Languages; +}MSISIGNATURE; + +void msi_parse_version_string(LPCWSTR verStr, PDWORD ms, PDWORD ls) +{ + const WCHAR *ptr; + int x1 = 0, x2 = 0, x3 = 0, x4 = 0; + + x1 = atoiW(verStr); + ptr = strchrW(verStr, '.'); + if (ptr) + { + x2 = atoiW(ptr + 1); + ptr = strchrW(ptr + 1, '.'); + } + if (ptr) + { + x3 = atoiW(ptr + 1); + ptr = strchrW(ptr + 1, '.'); + } + if (ptr) + x4 = atoiW(ptr + 1); + /* FIXME: byte-order dependent? */ + *ms = x1 << 16 | x2; + if (ls) *ls = x3 << 16 | x4; +} + +/* Fills in sig with the values from the Signature table, where name is the + * signature to find. Upon return, sig->File will be NULL if the record is not + * found, and not NULL if it is found. + * Warning: clears all fields in sig! + * Returns ERROR_SUCCESS upon success (where not finding the record counts as + * success), something else on error. + */ +static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, LPCWSTR name) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'S','i','g','n','a','t','u','r','e',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ', + '\'','%','s','\'',0}; + LPWSTR minVersion, maxVersion, p; + MSIRECORD *row; + DWORD time; + + TRACE("package %p, sig %p\n", package, sig); + + memset(sig, 0, sizeof(*sig)); + sig->Name = name; + row = MSI_QueryGetRecord( package->db, query, name ); + if (!row) + { + TRACE("failed to query signature for %s\n", debugstr_w(name)); + return ERROR_SUCCESS; + } + + /* get properties */ + sig->File = msi_dup_record_field(row,2); + if ((p = strchrW(sig->File, '|'))) + { + p++; + memmove(sig->File, p, (strlenW(p) + 1) * sizeof(WCHAR)); + } + + minVersion = msi_dup_record_field(row,3); + if (minVersion) + { + msi_parse_version_string( minVersion, &sig->MinVersionMS, &sig->MinVersionLS ); + msi_free( minVersion ); + } + maxVersion = msi_dup_record_field(row,4); + if (maxVersion) + { + msi_parse_version_string( maxVersion, &sig->MaxVersionMS, &sig->MaxVersionLS ); + msi_free( maxVersion ); + } + sig->MinSize = MSI_RecordGetInteger(row,5); + if (sig->MinSize == MSI_NULL_INTEGER) + sig->MinSize = 0; + sig->MaxSize = MSI_RecordGetInteger(row,6); + if (sig->MaxSize == MSI_NULL_INTEGER) + sig->MaxSize = 0; + sig->Languages = msi_dup_record_field(row,9); + time = MSI_RecordGetInteger(row,7); + if (time != MSI_NULL_INTEGER) + DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime); + time = MSI_RecordGetInteger(row,8); + if (time != MSI_NULL_INTEGER) + DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime); + + TRACE("Found file name %s for Signature_ %s;\n", + debugstr_w(sig->File), debugstr_w(name)); + TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS), + LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS), + LOWORD(sig->MinVersionLS)); + TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS), + LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS), + LOWORD(sig->MaxVersionLS)); + TRACE("MinSize is %d, MaxSize is %d;\n", sig->MinSize, sig->MaxSize); + TRACE("Languages is %s\n", debugstr_w(sig->Languages)); + + msiobj_release( &row->hdr ); + + return ERROR_SUCCESS; +} + +/* Frees any memory allocated in sig */ +static void ACTION_FreeSignature(MSISIGNATURE *sig) +{ + msi_free(sig->File); + msi_free(sig->Languages); +} + +static LPWSTR app_search_file(LPWSTR path, MSISIGNATURE *sig) +{ + VS_FIXEDFILEINFO *info; + DWORD attr, handle, size; + LPWSTR val = NULL; + LPBYTE buffer; + + if (!sig->File) + { + PathRemoveFileSpecW(path); + PathAddBackslashW(path); + + attr = GetFileAttributesW(path); + if (attr != INVALID_FILE_ATTRIBUTES && + (attr & FILE_ATTRIBUTE_DIRECTORY)) + return strdupW(path); + + return NULL; + } + + attr = GetFileAttributesW(path); + if (attr == INVALID_FILE_ATTRIBUTES || + (attr & FILE_ATTRIBUTE_DIRECTORY)) + return NULL; + + size = GetFileVersionInfoSizeW(path, &handle); + if (!size) + return strdupW(path); + + buffer = msi_alloc(size); + if (!buffer) + return NULL; + + if (!GetFileVersionInfoW(path, 0, size, buffer)) + goto done; + + if (!VerQueryValueW(buffer, szBackSlash, (LPVOID)&info, &size) || !info) + goto done; + + if (sig->MinVersionLS || sig->MinVersionMS) + { + if (info->dwFileVersionMS < sig->MinVersionMS) + goto done; + + if (info->dwFileVersionMS == sig->MinVersionMS && + info->dwFileVersionLS < sig->MinVersionLS) + goto done; + } + + if (sig->MaxVersionLS || sig->MaxVersionMS) + { + if (info->dwFileVersionMS > sig->MaxVersionMS) + goto done; + + if (info->dwFileVersionMS == sig->MaxVersionMS && + info->dwFileVersionLS > sig->MaxVersionLS) + goto done; + } + + val = strdupW(path); + +done: + msi_free(buffer); + return val; +} + +static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ', + '`','C','o','m','p','L','o','c','a','t','o','r','`',' ', + 'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','_','`',' ','=',' ', + '\'','%','s','\'',0}; + static const WCHAR sigquery[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','i','g','n','a','t','u','r','e','`',' ', + 'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','`',' ','=',' ', + '\'','%','s','\'',0}; + + MSIRECORD *row, *rec; + LPCWSTR signature, guid; + BOOL sigpresent = TRUE; + BOOL isdir; + UINT type; + WCHAR path[MAX_PATH]; + DWORD size = MAX_PATH; + LPWSTR ptr; + DWORD attr; + + TRACE("%s\n", debugstr_w(sig->Name)); + + *appValue = NULL; + + row = MSI_QueryGetRecord(package->db, query, sig->Name); + if (!row) + { + TRACE("failed to query CompLocator for %s\n", debugstr_w(sig->Name)); + return ERROR_SUCCESS; + } + + signature = MSI_RecordGetString(row, 1); + guid = MSI_RecordGetString(row, 2); + type = MSI_RecordGetInteger(row, 3); + + rec = MSI_QueryGetRecord(package->db, sigquery, signature); + if (!rec) + sigpresent = FALSE; + + *path = '\0'; + MsiLocateComponentW(guid, path, &size); + if (!*path) + goto done; + + attr = GetFileAttributesW(path); + if (attr == INVALID_FILE_ATTRIBUTES) + goto done; + + isdir = (attr & FILE_ATTRIBUTE_DIRECTORY); + + if (type != msidbLocatorTypeDirectory && sigpresent && !isdir) + { + *appValue = app_search_file(path, sig); + } + else if (!sigpresent && (type != msidbLocatorTypeDirectory || isdir)) + { + if (type == msidbLocatorTypeFileName) + { + ptr = strrchrW(path, '\\'); + *(ptr + 1) = '\0'; + } + else + PathAddBackslashW(path); + + *appValue = strdupW(path); + } + else if (sigpresent) + { + PathAddBackslashW(path); + lstrcatW(path, MSI_RecordGetString(rec, 2)); + + attr = GetFileAttributesW(path); + if (attr != INVALID_FILE_ATTRIBUTES && + !(attr & FILE_ATTRIBUTE_DIRECTORY)) + *appValue = strdupW(path); + } + +done: + if (rec) msiobj_release(&rec->hdr); + msiobj_release(&row->hdr); + return ERROR_SUCCESS; +} + +static void ACTION_ConvertRegValue(DWORD regType, const BYTE *value, DWORD sz, + LPWSTR *appValue) +{ + static const WCHAR dwordFmt[] = { '#','%','d','\0' }; + static const WCHAR binPre[] = { '#','x','\0' }; + static const WCHAR binFmt[] = { '%','0','2','X','\0' }; + LPWSTR ptr; + DWORD i; + + switch (regType) + { + case REG_SZ: + if (*(LPCWSTR)value == '#') + { + /* escape leading pound with another */ + *appValue = msi_alloc(sz + sizeof(WCHAR)); + (*appValue)[0] = '#'; + strcpyW(*appValue + 1, (LPCWSTR)value); + } + else + { + *appValue = msi_alloc(sz); + strcpyW(*appValue, (LPCWSTR)value); + } + break; + case REG_DWORD: + /* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign + * char if needed + */ + *appValue = msi_alloc(10 * sizeof(WCHAR)); + sprintfW(*appValue, dwordFmt, *(const DWORD *)value); + break; + case REG_EXPAND_SZ: + sz = ExpandEnvironmentStringsW((LPCWSTR)value, NULL, 0); + *appValue = msi_alloc(sz * sizeof(WCHAR)); + ExpandEnvironmentStringsW((LPCWSTR)value, *appValue, sz); + break; + case REG_BINARY: + /* #x<nibbles>\0 */ + *appValue = msi_alloc((sz * 2 + 3) * sizeof(WCHAR)); + lstrcpyW(*appValue, binPre); + ptr = *appValue + lstrlenW(binPre); + for (i = 0; i < sz; i++, ptr += 2) + sprintfW(ptr, binFmt, value[i]); + break; + default: + WARN("unimplemented for values of type %d\n", regType); + *appValue = NULL; + } +} + +static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, + LPCWSTR path, int depth, LPWSTR *appValue); + +static UINT ACTION_AppSearchReg(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'R','e','g','L','o','c','a','t','o','r',' ','W','H','E','R','E',' ', + 'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0}; + const WCHAR *keyPath, *valueName; + WCHAR *deformatted = NULL, *ptr = NULL, *end; + int root, type; + HKEY rootKey, key = NULL; + DWORD sz = 0, regType; + LPBYTE value = NULL; + MSIRECORD *row; + UINT rc; + + TRACE("%s\n", debugstr_w(sig->Name)); + + *appValue = NULL; + + row = MSI_QueryGetRecord( package->db, query, sig->Name ); + if (!row) + { + TRACE("failed to query RegLocator for %s\n", debugstr_w(sig->Name)); + return ERROR_SUCCESS; + } + + root = MSI_RecordGetInteger(row, 2); + keyPath = MSI_RecordGetString(row, 3); + valueName = MSI_RecordGetString(row, 4); + type = MSI_RecordGetInteger(row, 5); + + deformat_string(package, keyPath, &deformatted); + + switch (root) + { + case msidbRegistryRootClassesRoot: + rootKey = HKEY_CLASSES_ROOT; + break; + case msidbRegistryRootCurrentUser: + rootKey = HKEY_CURRENT_USER; + break; + case msidbRegistryRootLocalMachine: + rootKey = HKEY_LOCAL_MACHINE; + break; + case msidbRegistryRootUsers: + rootKey = HKEY_USERS; + break; + default: + WARN("Unknown root key %d\n", root); + goto end; + } + + rc = RegOpenKeyW(rootKey, deformatted, &key); + if (rc) + { + TRACE("RegOpenKeyW returned %d\n", rc); + goto end; + } + + msi_free(deformatted); + deformat_string(package, valueName, &deformatted); + + rc = RegQueryValueExW(key, deformatted, NULL, NULL, NULL, &sz); + if (rc) + { + TRACE("RegQueryValueExW returned %d\n", rc); + goto end; + } + /* FIXME: sanity-check sz before allocating (is there an upper-limit + * on the value of a property?) + */ + value = msi_alloc( sz ); + rc = RegQueryValueExW(key, deformatted, NULL, ®Type, value, &sz); + if (rc) + { + TRACE("RegQueryValueExW returned %d\n", rc); + goto end; + } + + /* bail out if the registry key is empty */ + if (sz == 0) + goto end; + + if ((regType == REG_SZ || regType == REG_EXPAND_SZ) && + (ptr = strchrW((LPWSTR)value, '"')) && (end = strchrW(++ptr, '"'))) + *end = '\0'; + else + ptr = (LPWSTR)value; + + switch (type & 0x0f) + { + case msidbLocatorTypeDirectory: + ACTION_SearchDirectory(package, sig, ptr, 0, appValue); + break; + case msidbLocatorTypeFileName: + *appValue = app_search_file(ptr, sig); + break; + case msidbLocatorTypeRawValue: + ACTION_ConvertRegValue(regType, value, sz, appValue); + break; + default: + FIXME("unimplemented for type %d (key path %s, value %s)\n", + type, debugstr_w(keyPath), debugstr_w(valueName)); + } +end: + msi_free( value ); + RegCloseKey( key ); + msi_free( deformatted ); + + msiobj_release(&row->hdr); + return ERROR_SUCCESS; +} + +static LPWSTR get_ini_field(LPWSTR buf, int field) +{ + LPWSTR beg, end; + int i = 1; + + if (field == 0) + return strdupW(buf); + + beg = buf; + while ((end = strchrW(beg, ',')) && i < field) + { + beg = end + 1; + while (*beg && *beg == ' ') + beg++; + + i++; + } + + end = strchrW(beg, ','); + if (!end) + end = beg + lstrlenW(beg); + + *end = '\0'; + return strdupW(beg); +} + +static UINT ACTION_AppSearchIni(MSIPACKAGE *package, LPWSTR *appValue, + MSISIGNATURE *sig) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'I','n','i','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ', + 'S','i','g','n','a','t','u','r','e','_',' ','=',' ','\'','%','s','\'',0}; + MSIRECORD *row; + LPWSTR fileName, section, key; + int field, type; + WCHAR buf[MAX_PATH]; + + TRACE("%s\n", debugstr_w(sig->Name)); + + *appValue = NULL; + + row = MSI_QueryGetRecord( package->db, query, sig->Name ); + if (!row) + { + TRACE("failed to query IniLocator for %s\n", debugstr_w(sig->Name)); + return ERROR_SUCCESS; + } + + fileName = msi_dup_record_field(row, 2); + section = msi_dup_record_field(row, 3); + key = msi_dup_record_field(row, 4); + field = MSI_RecordGetInteger(row, 5); + type = MSI_RecordGetInteger(row, 6); + if (field == MSI_NULL_INTEGER) + field = 0; + if (type == MSI_NULL_INTEGER) + type = 0; + + GetPrivateProfileStringW(section, key, NULL, buf, MAX_PATH, fileName); + if (buf[0]) + { + switch (type & 0x0f) + { + case msidbLocatorTypeDirectory: + ACTION_SearchDirectory(package, sig, buf, 0, appValue); + break; + case msidbLocatorTypeFileName: + *appValue = app_search_file(buf, sig); + break; + case msidbLocatorTypeRawValue: + *appValue = get_ini_field(buf, field); + break; + } + } + + msi_free(fileName); + msi_free(section); + msi_free(key); + + msiobj_release(&row->hdr); + + return ERROR_SUCCESS; +} + +/* Expands the value in src into a path without property names and only + * containing long path names into dst. Replaces at most len characters of dst, + * and always NULL-terminates dst if dst is not NULL and len >= 1. + * May modify src. + * Assumes src and dst are non-overlapping. + * FIXME: return code probably needed: + * - what does AppSearch return if the table values are invalid? + * - what if dst is too small? + */ +static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst, + size_t len) +{ + WCHAR *ptr, *deformatted; + + if (!src || !dst || !len) + { + if (dst) *dst = '\0'; + return; + } + + dst[0] = '\0'; + + /* Ignore the short portion of the path */ + if ((ptr = strchrW(src, '|'))) + ptr++; + else + ptr = src; + + deformat_string(package, ptr, &deformatted); + if (!deformatted || strlenW(deformatted) > len - 1) + { + msi_free(deformatted); + return; + } + + lstrcpyW(dst, deformatted); + dst[lstrlenW(deformatted)] = '\0'; + msi_free(deformatted); +} + +static LANGID *parse_languages( const WCHAR *languages, DWORD *num_ids ) +{ + UINT i, count = 1; + WCHAR *str = strdupW( languages ), *p, *q; + LANGID *ret; + + if (!str) return NULL; + for (p = q = str; (q = strchrW( q, ',' )); q++) count++; + + if (!(ret = msi_alloc( count * sizeof(LANGID) ))) + { + msi_free( str ); + return NULL; + } + i = 0; + while (*p) + { + q = strchrW( p, ',' ); + if (q) *q = 0; + ret[i] = atoiW( p ); + if (!q) break; + p = q + 1; + i++; + } + msi_free( str ); + *num_ids = count; + return ret; +} + +static BOOL match_languages( const void *version, const WCHAR *languages ) +{ + struct lang + { + USHORT id; + USHORT codepage; + } *lang; + DWORD len, num_ids, i, j; + BOOL found = FALSE; + LANGID *ids; + + if (!languages || !languages[0]) return TRUE; + if (!VerQueryValueW( version, szLangResource, (void **)&lang, &len )) return FALSE; + if (!(ids = parse_languages( languages, &num_ids ))) return FALSE; + + for (i = 0; i < num_ids; i++) + { + found = FALSE; + for (j = 0; j < len / sizeof(struct lang); j++) + { + if (!ids[i] || ids[i] == lang[j].id) found = TRUE; + } + if (!found) goto done; + } + +done: + msi_free( ids ); + return found; +} + +/* Sets *matches to whether the file (whose path is filePath) matches the + * versions set in sig. + * Return ERROR_SUCCESS in case of success (whether or not the file matches), + * something else if an install-halting error occurs. + */ +static UINT ACTION_FileVersionMatches(const MSISIGNATURE *sig, LPCWSTR filePath, + BOOL *matches) +{ + UINT len; + void *version; + VS_FIXEDFILEINFO *info = NULL; + DWORD zero, size = GetFileVersionInfoSizeW( filePath, &zero ); + + *matches = FALSE; + + if (!size) return ERROR_SUCCESS; + if (!(version = msi_alloc( size ))) return ERROR_OUTOFMEMORY; + + if (GetFileVersionInfoW( filePath, 0, size, version )) + VerQueryValueW( version, szBackSlash, (void **)&info, &len ); + + if (info) + { + TRACE("comparing file version %d.%d.%d.%d:\n", + HIWORD(info->dwFileVersionMS), + LOWORD(info->dwFileVersionMS), + HIWORD(info->dwFileVersionLS), + LOWORD(info->dwFileVersionLS)); + if (info->dwFileVersionMS < sig->MinVersionMS + || (info->dwFileVersionMS == sig->MinVersionMS && + info->dwFileVersionLS < sig->MinVersionLS)) + { + TRACE("less than minimum version %d.%d.%d.%d\n", + HIWORD(sig->MinVersionMS), + LOWORD(sig->MinVersionMS), + HIWORD(sig->MinVersionLS), + LOWORD(sig->MinVersionLS)); + } + else if ((sig->MaxVersionMS || sig->MaxVersionLS) && + (info->dwFileVersionMS > sig->MaxVersionMS || + (info->dwFileVersionMS == sig->MaxVersionMS && + info->dwFileVersionLS > sig->MaxVersionLS))) + { + TRACE("greater than maximum version %d.%d.%d.%d\n", + HIWORD(sig->MaxVersionMS), + LOWORD(sig->MaxVersionMS), + HIWORD(sig->MaxVersionLS), + LOWORD(sig->MaxVersionLS)); + } + else if (!match_languages( version, sig->Languages )) + { + TRACE("languages %s not supported\n", debugstr_w( sig->Languages )); + } + else *matches = TRUE; + } + msi_free( version ); + return ERROR_SUCCESS; +} + +/* Sets *matches to whether the file in findData matches that in sig. + * fullFilePath is assumed to be the full path of the file specified in + * findData, which may be necessary to compare the version. + * Return ERROR_SUCCESS in case of success (whether or not the file matches), + * something else if an install-halting error occurs. + */ +static UINT ACTION_FileMatchesSig(const MSISIGNATURE *sig, + const WIN32_FIND_DATAW *findData, LPCWSTR fullFilePath, BOOL *matches) +{ + UINT rc = ERROR_SUCCESS; + + *matches = TRUE; + /* assumes the caller has already ensured the filenames match, so check + * the other fields.. + */ + if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime) + { + if (findData->ftCreationTime.dwHighDateTime < + sig->MinTime.dwHighDateTime || + (findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime + && findData->ftCreationTime.dwLowDateTime < + sig->MinTime.dwLowDateTime)) + *matches = FALSE; + } + if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime)) + { + if (findData->ftCreationTime.dwHighDateTime > + sig->MaxTime.dwHighDateTime || + (findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime + && findData->ftCreationTime.dwLowDateTime > + sig->MaxTime.dwLowDateTime)) + *matches = FALSE; + } + if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize) + *matches = FALSE; + if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize) + *matches = FALSE; + if (*matches && (sig->MinVersionMS || sig->MinVersionLS || + sig->MaxVersionMS || sig->MaxVersionLS)) + rc = ACTION_FileVersionMatches(sig, fullFilePath, matches); + return rc; +} + +/* Recursively searches the directory dir for files that match the signature + * sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir + * (and only dir). If depth is 1, searches dir and its immediate + * subdirectories. + * Assumes sig->File is not NULL. + * Returns ERROR_SUCCESS on success (which may include non-critical errors), + * something else on failures which should halt the install. + */ +static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, LPWSTR *appValue, + MSISIGNATURE *sig, LPCWSTR dir, int depth) +{ + HANDLE hFind; + WIN32_FIND_DATAW findData; + UINT rc = ERROR_SUCCESS; + size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File); + WCHAR subpath[MAX_PATH]; + WCHAR *buf; + DWORD len; + + static const WCHAR starDotStarW[] = { '*','.','*',0 }; + + TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir), + debugstr_w(sig->File), depth); + + if (depth < 0) + return ERROR_SUCCESS; + + *appValue = NULL; + /* We need the buffer in both paths below, so go ahead and allocate it + * here. Add two because we might need to add a backslash if the dir name + * isn't backslash-terminated. + */ + len = dirLen + max(fileLen, strlenW(starDotStarW)) + 2; + buf = msi_alloc(len * sizeof(WCHAR)); + if (!buf) + return ERROR_OUTOFMEMORY; + + lstrcpyW(buf, dir); + PathAddBackslashW(buf); + lstrcatW(buf, sig->File); + + hFind = FindFirstFileW(buf, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + BOOL matches; + + rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches); + if (rc == ERROR_SUCCESS && matches) + { + TRACE("found file, returning %s\n", debugstr_w(buf)); + *appValue = buf; + } + } + FindClose(hFind); + } + + if (rc == ERROR_SUCCESS && !*appValue) + { + lstrcpyW(buf, dir); + PathAddBackslashW(buf); + lstrcatW(buf, starDotStarW); + + hFind = FindFirstFileW(buf, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + strcmpW( findData.cFileName, szDot ) && + strcmpW( findData.cFileName, szDotDot )) + { + lstrcpyW(subpath, dir); + PathAppendW(subpath, findData.cFileName); + rc = ACTION_RecurseSearchDirectory(package, appValue, sig, + subpath, depth - 1); + } + + while (rc == ERROR_SUCCESS && !*appValue && + FindNextFileW(hFind, &findData) != 0) + { + if (!strcmpW( findData.cFileName, szDot ) || + !strcmpW( findData.cFileName, szDotDot )) + continue; + + lstrcpyW(subpath, dir); + PathAppendW(subpath, findData.cFileName); + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + rc = ACTION_RecurseSearchDirectory(package, appValue, + sig, subpath, depth - 1); + } + + FindClose(hFind); + } + } + + if (*appValue != buf) + msi_free(buf); + + return rc; +} + +static UINT ACTION_CheckDirectory(MSIPACKAGE *package, LPCWSTR dir, + LPWSTR *appValue) +{ + DWORD attr = GetFileAttributesW(dir); + + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) + { + TRACE("directory exists, returning %s\n", debugstr_w(dir)); + *appValue = strdupW(dir); + } + + return ERROR_SUCCESS; +} + +static BOOL ACTION_IsFullPath(LPCWSTR path) +{ + WCHAR first = toupperW(path[0]); + BOOL ret; + + if (first >= 'A' && first <= 'Z' && path[1] == ':') + ret = TRUE; + else if (path[0] == '\\' && path[1] == '\\') + ret = TRUE; + else + ret = FALSE; + return ret; +} + +static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, + LPCWSTR path, int depth, LPWSTR *appValue) +{ + UINT rc; + DWORD attr; + LPWSTR val = NULL; + + TRACE("%p, %p, %s, %d, %p\n", package, sig, debugstr_w(path), depth, + appValue); + + if (ACTION_IsFullPath(path)) + { + if (sig->File) + rc = ACTION_RecurseSearchDirectory(package, &val, sig, path, depth); + else + { + /* Recursively searching a directory makes no sense when the + * directory to search is the thing you're trying to find. + */ + rc = ACTION_CheckDirectory(package, path, &val); + } + } + else + { + WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 }; + DWORD drives = GetLogicalDrives(); + int i; + + rc = ERROR_SUCCESS; + for (i = 0; rc == ERROR_SUCCESS && !val && i < 26; i++) + { + if (!(drives & (1 << i))) + continue; + + pathWithDrive[0] = 'A' + i; + if (GetDriveTypeW(pathWithDrive) != DRIVE_FIXED) + continue; + + lstrcpynW(pathWithDrive + 3, path, + sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3); + + if (sig->File) + rc = ACTION_RecurseSearchDirectory(package, &val, sig, + pathWithDrive, depth); + else + rc = ACTION_CheckDirectory(package, pathWithDrive, &val); + } + } + + attr = GetFileAttributesW(val); + if (attr != INVALID_FILE_ATTRIBUTES && + (attr & FILE_ATTRIBUTE_DIRECTORY) && + val && val[lstrlenW(val) - 1] != '\\') + { + val = msi_realloc(val, (lstrlenW(val) + 2) * sizeof(WCHAR)); + if (!val) + rc = ERROR_OUTOFMEMORY; + else + PathAddBackslashW(val); + } + + *appValue = val; + + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName, + MSISIGNATURE *sig, LPWSTR *appValue); + +static UINT ACTION_AppSearchDr(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'D','r','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ', + 'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0}; + LPWSTR parent = NULL; + LPCWSTR parentName; + WCHAR path[MAX_PATH]; + WCHAR expanded[MAX_PATH]; + MSIRECORD *row; + int depth; + DWORD sz, attr; + UINT rc; + + TRACE("%s\n", debugstr_w(sig->Name)); + + *appValue = NULL; + + row = MSI_QueryGetRecord( package->db, query, sig->Name ); + if (!row) + { + TRACE("failed to query DrLocator for %s\n", debugstr_w(sig->Name)); + return ERROR_SUCCESS; + } + + /* check whether parent is set */ + parentName = MSI_RecordGetString(row, 2); + if (parentName) + { + MSISIGNATURE parentSig; + + ACTION_AppSearchSigName(package, parentName, &parentSig, &parent); + ACTION_FreeSignature(&parentSig); + if (!parent) + { + msiobj_release(&row->hdr); + return ERROR_SUCCESS; + } + } + + sz = MAX_PATH; + MSI_RecordGetStringW(row, 3, path, &sz); + + if (MSI_RecordIsNull(row,4)) + depth = 0; + else + depth = MSI_RecordGetInteger(row,4); + + if (sz) + ACTION_ExpandAnyPath(package, path, expanded, MAX_PATH); + else + strcpyW(expanded, path); + + if (parent) + { + attr = GetFileAttributesW(parent); + if (attr != INVALID_FILE_ATTRIBUTES && + !(attr & FILE_ATTRIBUTE_DIRECTORY)) + { + PathRemoveFileSpecW(parent); + PathAddBackslashW(parent); + } + + strcpyW(path, parent); + strcatW(path, expanded); + } + else if (sz) + strcpyW(path, expanded); + + PathAddBackslashW(path); + + rc = ACTION_SearchDirectory(package, sig, path, depth, appValue); + + msi_free(parent); + msiobj_release(&row->hdr); + + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName, + MSISIGNATURE *sig, LPWSTR *appValue) +{ + UINT rc; + + *appValue = NULL; + rc = ACTION_AppSearchGetSignature(package, sig, sigName); + if (rc == ERROR_SUCCESS) + { + rc = ACTION_AppSearchComponents(package, appValue, sig); + if (rc == ERROR_SUCCESS && !*appValue) + { + rc = ACTION_AppSearchReg(package, appValue, sig); + if (rc == ERROR_SUCCESS && !*appValue) + { + rc = ACTION_AppSearchIni(package, appValue, sig); + if (rc == ERROR_SUCCESS && !*appValue) + rc = ACTION_AppSearchDr(package, appValue, sig); + } + } + } + return rc; +} + +static UINT iterate_appsearch(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR propName, sigName; + LPWSTR value = NULL; + MSISIGNATURE sig; + MSIRECORD *uirow; + UINT r; + + /* get property and signature */ + propName = MSI_RecordGetString(row, 1); + sigName = MSI_RecordGetString(row, 2); + + TRACE("%s %s\n", debugstr_w(propName), debugstr_w(sigName)); + + r = ACTION_AppSearchSigName(package, sigName, &sig, &value); + if (value) + { + r = msi_set_property( package->db, propName, value ); + if (r == ERROR_SUCCESS && !strcmpW( propName, szSourceDir )) + msi_reset_folders( package, TRUE ); + + msi_free(value); + } + ACTION_FreeSignature(&sig); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, propName ); + MSI_RecordSetStringW( uirow, 2, sigName ); + msi_ui_actiondata( package, szAppSearch, uirow ); + msiobj_release( &uirow->hdr ); + + return r; +} + +UINT ACTION_AppSearch(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'A','p','p','S','e','a','r','c','h',0}; + MSIQUERY *view; + UINT r; + + if (msi_action_is_unique(package, szAppSearch)) + { + TRACE("Skipping AppSearch action: already done in UI sequence\n"); + return ERROR_SUCCESS; + } + else + msi_register_unique_action(package, szAppSearch); + + r = MSI_OpenQuery( package->db, &view, query ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = MSI_IterateRecords( view, NULL, iterate_appsearch, package ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT ITERATE_CCPSearch(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPCWSTR signature; + LPWSTR value = NULL; + MSISIGNATURE sig; + UINT r = ERROR_SUCCESS; + + static const WCHAR success[] = {'C','C','P','_','S','u','c','c','e','s','s',0}; + + signature = MSI_RecordGetString(row, 1); + + TRACE("%s\n", debugstr_w(signature)); + + ACTION_AppSearchSigName(package, signature, &sig, &value); + if (value) + { + TRACE("Found signature %s\n", debugstr_w(signature)); + msi_set_property(package->db, success, szOne); + msi_free(value); + r = ERROR_NO_MORE_ITEMS; + } + + ACTION_FreeSignature(&sig); + + return r; +} + +UINT ACTION_CCPSearch(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'C','C','P','S','e','a','r','c','h',0}; + MSIQUERY *view; + UINT r; + + if (msi_action_is_unique(package, szCCPSearch)) + { + TRACE("Skipping AppSearch action: already done in UI sequence\n"); + return ERROR_SUCCESS; + } + else + msi_register_unique_action(package, szCCPSearch); + + r = MSI_OpenQuery(package->db, &view, query); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = MSI_IterateRecords(view, NULL, ITERATE_CCPSearch, package); + msiobj_release(&view->hdr); + return r; +} diff --git a/libmsi/assembly.c b/libmsi/assembly.c new file mode 100644 index 0000000..2f321db --- /dev/null +++ b/libmsi/assembly.c @@ -0,0 +1,694 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2010 Hans Leidekker 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 "winreg.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static HRESULT (WINAPI *pCreateAssemblyCacheNet10)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pCreateAssemblyCacheNet11)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pCreateAssemblyCacheNet20)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pCreateAssemblyCacheNet40)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pCreateAssemblyCacheSxs)( IAssemblyCache **, DWORD ); +static HRESULT (WINAPI *pLoadLibraryShim)( LPCWSTR, LPCWSTR, LPVOID, HMODULE * ); +static HRESULT (WINAPI *pGetFileVersion)( LPCWSTR, LPWSTR, DWORD, DWORD * ); + +static HMODULE hfusion10, hfusion11, hfusion20, hfusion40, hmscoree, hsxs; + +static BOOL init_function_pointers( void ) +{ + static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; + static const WCHAR szMscoree[] = {'\\','m','s','c','o','r','e','e','.','d','l','l',0}; + static const WCHAR szSxs[] = {'s','x','s','.','d','l','l',0}; + static const WCHAR szVersion10[] = {'v','1','.','0','.','3','7','0','5',0}; + static const WCHAR szVersion11[] = {'v','1','.','1','.','4','3','2','2',0}; + static const WCHAR szVersion20[] = {'v','2','.','0','.','5','0','7','2','7',0}; + static const WCHAR szVersion40[] = {'v','4','.','0','.','3','0','3','1','9',0}; + WCHAR path[MAX_PATH]; + DWORD len = GetSystemDirectoryW( path, MAX_PATH ); + + if (!hsxs && !(hsxs = LoadLibraryW( szSxs ))) return FALSE; + if (!(pCreateAssemblyCacheSxs = (void *)GetProcAddress( hsxs, "CreateAssemblyCache" ))) + { + FreeLibrary( hsxs ); + hsxs = NULL; + return FALSE; + } + strcpyW( path + len, szMscoree ); + if (hmscoree || !(hmscoree = LoadLibraryW( path ))) return TRUE; + pGetFileVersion = (void *)GetProcAddress( hmscoree, "GetFileVersion" ); /* missing from v1.0.3705 */ + if (!(pLoadLibraryShim = (void *)GetProcAddress( hmscoree, "LoadLibraryShim" ))) + { + FreeLibrary( hmscoree ); + hmscoree = NULL; + return TRUE; + } + if (!pLoadLibraryShim( szFusion, szVersion10, NULL, &hfusion10 )) + pCreateAssemblyCacheNet10 = (void *)GetProcAddress( hfusion10, "CreateAssemblyCache" ); + + if (!pLoadLibraryShim( szFusion, szVersion11, NULL, &hfusion11 )) + pCreateAssemblyCacheNet11 = (void *)GetProcAddress( hfusion11, "CreateAssemblyCache" ); + + if (!pLoadLibraryShim( szFusion, szVersion20, NULL, &hfusion20 )) + pCreateAssemblyCacheNet20 = (void *)GetProcAddress( hfusion20, "CreateAssemblyCache" ); + + if (!pLoadLibraryShim( szFusion, szVersion40, NULL, &hfusion40 )) + pCreateAssemblyCacheNet40 = (void *)GetProcAddress( hfusion40, "CreateAssemblyCache" ); + + return TRUE; +} + +BOOL msi_init_assembly_caches( MSIPACKAGE *package ) +{ + if (!init_function_pointers()) return FALSE; + if (pCreateAssemblyCacheSxs( &package->cache_sxs, 0 ) != S_OK) return FALSE; + if (pCreateAssemblyCacheNet10) pCreateAssemblyCacheNet10( &package->cache_net[CLR_VERSION_V10], 0 ); + if (pCreateAssemblyCacheNet11) pCreateAssemblyCacheNet11( &package->cache_net[CLR_VERSION_V11], 0 ); + if (pCreateAssemblyCacheNet20) pCreateAssemblyCacheNet20( &package->cache_net[CLR_VERSION_V20], 0 ); + if (pCreateAssemblyCacheNet40) pCreateAssemblyCacheNet40( &package->cache_net[CLR_VERSION_V40], 0 ); + return TRUE; +} + +void msi_destroy_assembly_caches( MSIPACKAGE *package ) +{ + UINT i; + + if (package->cache_sxs) + { + IAssemblyCache_Release( package->cache_sxs ); + package->cache_sxs = NULL; + } + for (i = 0; i < CLR_VERSION_MAX; i++) + { + if (package->cache_net[i]) + { + IAssemblyCache_Release( package->cache_net[i] ); + package->cache_net[i] = NULL; + } + } + pCreateAssemblyCacheNet10 = NULL; + pCreateAssemblyCacheNet11 = NULL; + pCreateAssemblyCacheNet20 = NULL; + pCreateAssemblyCacheNet40 = NULL; + FreeLibrary( hfusion10 ); + FreeLibrary( hfusion11 ); + FreeLibrary( hfusion20 ); + FreeLibrary( hfusion40 ); + FreeLibrary( hmscoree ); + FreeLibrary( hsxs ); + hfusion10 = NULL; + hfusion11 = NULL; + hfusion20 = NULL; + hfusion40 = NULL; + hmscoree = NULL; + hsxs = NULL; +} + +static MSIRECORD *get_assembly_record( MSIPACKAGE *package, const WCHAR *comp ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','`',' ', + 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', + ' ','=',' ','\'','%','s','\'',0}; + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_OpenQuery( package->db, &view, query, comp ); + if (r != ERROR_SUCCESS) + return NULL; + + r = MSI_ViewExecute( view, NULL ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return NULL; + } + r = MSI_ViewFetch( view, &rec ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return NULL; + } + if (!MSI_RecordGetString( rec, 4 )) + TRACE("component is a global assembly\n"); + + msiobj_release( &view->hdr ); + return rec; +} + +struct assembly_name +{ + UINT count; + UINT index; + WCHAR **attrs; +}; + +static UINT get_assembly_name_attribute( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR fmtW[] = {'%','s','=','"','%','s','"',0}; + static const WCHAR nameW[] = {'n','a','m','e',0}; + struct assembly_name *name = param; + const WCHAR *attr = MSI_RecordGetString( rec, 2 ); + const WCHAR *value = MSI_RecordGetString( rec, 3 ); + int len = strlenW( fmtW ) + strlenW( attr ) + strlenW( value ); + + if (!(name->attrs[name->index] = msi_alloc( len * sizeof(WCHAR) ))) + return ERROR_OUTOFMEMORY; + + if (!strcmpiW( attr, nameW )) strcpyW( name->attrs[name->index++], value ); + else sprintfW( name->attrs[name->index++], fmtW, attr, value ); + return ERROR_SUCCESS; +} + +static WCHAR *get_assembly_display_name( MSIDATABASE *db, const WCHAR *comp, MSIASSEMBLY *assembly ) +{ + static const WCHAR commaW[] = {',',0}; + static const WCHAR queryW[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ', + 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`', + ' ','=',' ','\'','%','s','\'',0}; + struct assembly_name name; + WCHAR *display_name = NULL; + MSIQUERY *view; + UINT i, r; + int len; + + r = MSI_OpenQuery( db, &view, queryW, comp ); + if (r != ERROR_SUCCESS) + return NULL; + + name.count = 0; + name.index = 0; + name.attrs = NULL; + MSI_IterateRecords( view, &name.count, NULL, NULL ); + if (!name.count) goto done; + + name.attrs = msi_alloc( name.count * sizeof(WCHAR *) ); + if (!name.attrs) goto done; + + MSI_IterateRecords( view, NULL, get_assembly_name_attribute, &name ); + + len = 0; + for (i = 0; i < name.count; i++) len += strlenW( name.attrs[i] ) + 1; + + display_name = msi_alloc( (len + 1) * sizeof(WCHAR) ); + if (display_name) + { + display_name[0] = 0; + for (i = 0; i < name.count; i++) + { + strcatW( display_name, name.attrs[i] ); + if (i < name.count - 1) strcatW( display_name, commaW ); + } + } + +done: + msiobj_release( &view->hdr ); + if (name.attrs) + { + for (i = 0; i < name.count; i++) msi_free( name.attrs[i] ); + msi_free( name.attrs ); + } + return display_name; +} + +static BOOL is_assembly_installed( IAssemblyCache *cache, const WCHAR *display_name ) +{ + HRESULT hr; + ASSEMBLY_INFO info; + + if (!cache) return FALSE; + + memset( &info, 0, sizeof(info) ); + info.cbAssemblyInfo = sizeof(info); + hr = IAssemblyCache_QueryAssemblyInfo( cache, 0, display_name, &info ); + if (hr == S_OK /* sxs version */ || hr == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER )) + { + return (info.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED); + } + TRACE("QueryAssemblyInfo returned 0x%08x\n", hr); + return FALSE; +} + +static const WCHAR clr_version_v10[] = {'v','1','.','0','.','3','7','0','5',0}; +static const WCHAR clr_version_v11[] = {'v','1','.','1','.','4','3','2','2',0}; +static const WCHAR clr_version_v20[] = {'v','2','.','0','.','5','0','7','2','7',0}; +static const WCHAR clr_version_v40[] = {'v','4','.','0','.','3','0','3','1','9',0}; +static const WCHAR clr_version_unknown[] = {'u','n','k','n','o','w','n',0}; + +static const WCHAR *clr_version[] = +{ + clr_version_v10, + clr_version_v11, + clr_version_v20, + clr_version_v40 +}; + +static const WCHAR *get_clr_version_str( enum clr_version version ) +{ + if (version >= sizeof(clr_version)/sizeof(clr_version[0])) return clr_version_unknown; + return clr_version[version]; +} + +/* assembly caches must be initialized */ +MSIASSEMBLY *msi_load_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + MSIRECORD *rec; + MSIASSEMBLY *a; + + if (!(rec = get_assembly_record( package, comp->Component ))) return NULL; + if (!(a = msi_alloc_zero( sizeof(MSIASSEMBLY) ))) + { + msiobj_release( &rec->hdr ); + return NULL; + } + a->feature = strdupW( MSI_RecordGetString( rec, 2 ) ); + TRACE("feature %s\n", debugstr_w(a->feature)); + + a->manifest = strdupW( MSI_RecordGetString( rec, 3 ) ); + TRACE("manifest %s\n", debugstr_w(a->manifest)); + + a->application = strdupW( MSI_RecordGetString( rec, 4 ) ); + TRACE("application %s\n", debugstr_w(a->application)); + + a->attributes = MSI_RecordGetInteger( rec, 5 ); + TRACE("attributes %u\n", a->attributes); + + if (!(a->display_name = get_assembly_display_name( package->db, comp->Component, a ))) + { + WARN("can't get display name\n"); + msiobj_release( &rec->hdr ); + msi_free( a->feature ); + msi_free( a->manifest ); + msi_free( a->application ); + msi_free( a ); + return NULL; + } + TRACE("display name %s\n", debugstr_w(a->display_name)); + + if (a->application) + { + /* We can't check the manifest here because the target path may still change. + So we assume that the assembly is not installed and lean on the InstallFiles + action to determine which files need to be installed. + */ + a->installed = FALSE; + } + else + { + if (a->attributes == msidbAssemblyAttributesWin32) + a->installed = is_assembly_installed( package->cache_sxs, a->display_name ); + else + { + UINT i; + for (i = 0; i < CLR_VERSION_MAX; i++) + { + a->clr_version[i] = is_assembly_installed( package->cache_net[i], a->display_name ); + if (a->clr_version[i]) + { + TRACE("runtime version %s\n", debugstr_w(get_clr_version_str( i ))); + a->installed = TRUE; + break; + } + } + } + } + TRACE("assembly is %s\n", a->installed ? "installed" : "not installed"); + msiobj_release( &rec->hdr ); + return a; +} + +static enum clr_version get_clr_version( const WCHAR *filename ) +{ + DWORD len; + HRESULT hr; + enum clr_version version = CLR_VERSION_V11; + WCHAR *strW; + + if (!pGetFileVersion) return CLR_VERSION_V10; + + hr = pGetFileVersion( filename, NULL, 0, &len ); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) return CLR_VERSION_V11; + if ((strW = msi_alloc( len * sizeof(WCHAR) ))) + { + hr = pGetFileVersion( filename, strW, len, &len ); + if (hr == S_OK) + { + UINT i; + for (i = 0; i < CLR_VERSION_MAX; i++) + if (!strcmpW( strW, clr_version[i] )) version = i; + } + msi_free( strW ); + } + return version; +} + +UINT msi_install_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + HRESULT hr; + const WCHAR *manifest; + IAssemblyCache *cache; + MSIASSEMBLY *assembly = comp->assembly; + MSIFEATURE *feature = NULL; + + if (comp->assembly->feature) + feature = msi_get_loaded_feature( package, comp->assembly->feature ); + + if (assembly->application) + { + if (feature) feature->Action = INSTALLSTATE_LOCAL; + return ERROR_SUCCESS; + } + if (assembly->attributes == msidbAssemblyAttributesWin32) + { + if (!assembly->manifest) + { + WARN("no manifest\n"); + return ERROR_FUNCTION_FAILED; + } + manifest = msi_get_loaded_file( package, assembly->manifest )->TargetPath; + cache = package->cache_sxs; + } + else + { + manifest = msi_get_loaded_file( package, comp->KeyPath )->TargetPath; + cache = package->cache_net[get_clr_version( manifest )]; + if (!cache) return ERROR_SUCCESS; + } + TRACE("installing assembly %s\n", debugstr_w(manifest)); + + hr = IAssemblyCache_InstallAssembly( cache, 0, manifest, NULL ); + if (hr != S_OK) + { + ERR("Failed to install assembly %s (0x%08x)\n", debugstr_w(manifest), hr); + return ERROR_FUNCTION_FAILED; + } + if (feature) feature->Action = INSTALLSTATE_LOCAL; + assembly->installed = TRUE; + return ERROR_SUCCESS; +} + +UINT msi_uninstall_assembly( MSIPACKAGE *package, MSICOMPONENT *comp ) +{ + HRESULT hr; + IAssemblyCache *cache; + MSIASSEMBLY *assembly = comp->assembly; + MSIFEATURE *feature = NULL; + + if (comp->assembly->feature) + feature = msi_get_loaded_feature( package, comp->assembly->feature ); + + if (assembly->application) + { + if (feature) feature->Action = INSTALLSTATE_ABSENT; + return ERROR_SUCCESS; + } + TRACE("removing %s\n", debugstr_w(assembly->display_name)); + + if (assembly->attributes == msidbAssemblyAttributesWin32) + { + cache = package->cache_sxs; + hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); + if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr); + } + else + { + unsigned int i; + for (i = 0; i < CLR_VERSION_MAX; i++) + { + if (!assembly->clr_version[i]) continue; + cache = package->cache_net[i]; + if (cache) + { + hr = IAssemblyCache_UninstallAssembly( cache, 0, assembly->display_name, NULL, NULL ); + if (FAILED( hr )) WARN("failed to uninstall assembly 0x%08x\n", hr); + } + } + } + if (feature) feature->Action = INSTALLSTATE_ABSENT; + assembly->installed = FALSE; + return ERROR_SUCCESS; +} + +static WCHAR *build_local_assembly_path( const WCHAR *filename ) +{ + UINT i; + WCHAR *ret; + + if (!(ret = msi_alloc( (strlenW( filename ) + 1) * sizeof(WCHAR) ))) + return NULL; + + for (i = 0; filename[i]; i++) + { + if (filename[i] == '\\' || filename[i] == '/') ret[i] = '|'; + else ret[i] = filename[i]; + } + ret[i] = 0; + return ret; +} + +static LONG open_assemblies_key( UINT context, BOOL win32, HKEY *hkey ) +{ + static const WCHAR path_win32[] = + {'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','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR path_dotnet[] = + {'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','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR classes_path_win32[] = + {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\',0}; + static const WCHAR classes_path_dotnet[] = + {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\',0}; + HKEY root; + const WCHAR *path; + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + root = HKEY_CLASSES_ROOT; + if (win32) path = classes_path_win32; + else path = classes_path_dotnet; + } + else + { + root = HKEY_CURRENT_USER; + if (win32) path = path_win32; + else path = path_dotnet; + } + return RegCreateKeyW( root, path, hkey ); +} + +static LONG open_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename, HKEY *hkey ) +{ + LONG res; + HKEY root; + WCHAR *path; + + if (!(path = build_local_assembly_path( filename ))) + return ERROR_OUTOFMEMORY; + + if ((res = open_assemblies_key( context, win32, &root ))) + { + msi_free( path ); + return res; + } + res = RegCreateKeyW( root, path, hkey ); + RegCloseKey( root ); + msi_free( path ); + return res; +} + +static LONG delete_local_assembly_key( UINT context, BOOL win32, const WCHAR *filename ) +{ + LONG res; + HKEY root; + WCHAR *path; + + if (!(path = build_local_assembly_path( filename ))) + return ERROR_OUTOFMEMORY; + + if ((res = open_assemblies_key( context, win32, &root ))) + { + msi_free( path ); + return res; + } + res = RegDeleteKeyW( root, path ); + RegCloseKey( root ); + msi_free( path ); + return res; +} + +static LONG open_global_assembly_key( UINT context, BOOL win32, HKEY *hkey ) +{ + static const WCHAR path_win32[] = + {'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','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR path_dotnet[] = + {'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','\\','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR classes_path_win32[] = + {'I','n','s','t','a','l','l','e','r','\\','W','i','n','3','2','A','s','s','e','m','b','l','i','e','s','\\', + 'G','l','o','b','a','l',0}; + static const WCHAR classes_path_dotnet[] = + {'I','n','s','t','a','l','l','e','r','\\','A','s','s','e','m','b','l','i','e','s','\\','G','l','o','b','a','l',0}; + HKEY root; + const WCHAR *path; + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + root = HKEY_CLASSES_ROOT; + if (win32) path = classes_path_win32; + else path = classes_path_dotnet; + } + else + { + root = HKEY_CURRENT_USER; + if (win32) path = path_win32; + else path = path_dotnet; + } + return RegCreateKeyW( root, path, hkey ); +} + +UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) +{ + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) + { + LONG res; + HKEY hkey; + GUID guid; + DWORD size; + WCHAR buffer[43]; + MSIRECORD *uirow; + MSIASSEMBLY *assembly = comp->assembly; + BOOL win32; + + if (!assembly || !comp->ComponentId) continue; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL) + { + TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component)); + continue; + } + TRACE("publishing %s\n", debugstr_w(comp->Component)); + + CLSIDFromString( package->ProductCode, &guid ); + encode_base85_guid( &guid, buffer ); + buffer[20] = '>'; + CLSIDFromString( comp->ComponentId, &guid ); + encode_base85_guid( &guid, buffer + 21 ); + buffer[42] = 0; + + win32 = assembly->attributes & msidbAssemblyAttributesWin32; + if (assembly->application) + { + MSIFILE *file = msi_get_loaded_file( package, assembly->application ); + if ((res = open_local_assembly_key( package->Context, win32, file->TargetPath, &hkey ))) + { + WARN("failed to open local assembly key %d\n", res); + return ERROR_FUNCTION_FAILED; + } + } + else + { + if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) + { + WARN("failed to open global assembly key %d\n", res); + return ERROR_FUNCTION_FAILED; + } + } + size = sizeof(buffer); + if ((res = RegSetValueExW( hkey, assembly->display_name, 0, REG_MULTI_SZ, (const BYTE *)buffer, size ))) + { + WARN("failed to set assembly value %d\n", res); + } + RegCloseKey( hkey ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 2, assembly->display_name ); + msi_ui_actiondata( package, szMsiPublishAssemblies, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) +{ + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry) + { + LONG res; + MSIRECORD *uirow; + MSIASSEMBLY *assembly = comp->assembly; + BOOL win32; + + if (!assembly || !comp->ComponentId) continue; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) + { + TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component)); + continue; + } + TRACE("unpublishing %s\n", debugstr_w(comp->Component)); + + win32 = assembly->attributes & msidbAssemblyAttributesWin32; + if (assembly->application) + { + MSIFILE *file = msi_get_loaded_file( package, assembly->application ); + if ((res = delete_local_assembly_key( package->Context, win32, file->TargetPath ))) + WARN("failed to delete local assembly key %d\n", res); + } + else + { + HKEY hkey; + if ((res = open_global_assembly_key( package->Context, win32, &hkey ))) + WARN("failed to delete global assembly key %d\n", res); + else + { + if ((res = RegDeleteValueW( hkey, assembly->display_name ))) + WARN("failed to delete global assembly value %d\n", res); + RegCloseKey( hkey ); + } + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 2, assembly->display_name ); + msi_ui_actiondata( package, szMsiPublishAssemblies, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} diff --git a/libmsi/automation.c b/libmsi/automation.c new file mode 100644 index 0000000..e55717a --- /dev/null +++ b/libmsi/automation.c @@ -0,0 +1,2461 @@ +/* + * Implementation of OLE Automation for Microsoft Installer (msi.dll) + * + * Copyright 2007 Misha Koshelev + * + * 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 + */ + +#define COBJMACROS + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "winreg.h" +#include "msidefs.h" +#include "msipriv.h" +#include "activscp.h" +#include "oleauto.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "msiserver.h" +#include "msiserver_dispids.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#define REG_INDEX_CLASSES_ROOT 0 +#define REG_INDEX_DYN_DATA 6 + +/* + * AutomationObject - "base" class for all automation objects. For each interface, we implement Invoke function + * called from AutomationObject::Invoke. + */ + +typedef struct AutomationObject AutomationObject; + +typedef HRESULT (*autoInvokeFunc)(AutomationObject* This, + DISPID dispIdMember, REFIID riid, LCID lcid, WORD flags, DISPPARAMS* pDispParams, + VARIANT* result, EXCEPINFO* ei, UINT* arg_err); + +typedef void (*autoFreeFunc)(AutomationObject* This); + +struct AutomationObject { + IDispatch IDispatch_iface; + IProvideMultipleClassInfo IProvideMultipleClassInfo_iface; + LONG ref; + + /* Clsid for this class and it's appropriate ITypeInfo object */ + LPCLSID clsid; + ITypeInfo *iTypeInfo; + + /* The MSI handle of the current object */ + MSIHANDLE msiHandle; + + /* A function that is called from AutomationObject::Invoke, specific to this type of object. */ + autoInvokeFunc funcInvoke; + /* A function that is called from AutomationObject::Release when the object is being freed to free any private + * data structures (or NULL) */ + autoFreeFunc funcFree; +}; + +typedef struct { + AutomationObject autoobj; + int count; + VARIANT *data; +} ListObject; + +static HRESULT create_database(MSIHANDLE, IDispatch**); +static HRESULT create_list_enumerator(ListObject*, void**); +static HRESULT create_summaryinfo(MSIHANDLE, IDispatch**); +static HRESULT create_view(MSIHANDLE, IDispatch**); + +/* ListEnumerator - IEnumVARIANT implementation for MSI automation lists */ +typedef struct { + IEnumVARIANT IEnumVARIANT_iface; + LONG ref; + + /* Current position and pointer to AutomationObject that stores actual data */ + ULONG pos; + ListObject *list; +} ListEnumerator; + +typedef struct { + AutomationObject autoobj; + IDispatch *installer; +} SessionObject; + +static inline AutomationObject *impl_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface ) +{ + return CONTAINING_RECORD(iface, AutomationObject, IProvideMultipleClassInfo_iface); +} + +static inline AutomationObject *impl_from_IDispatch( IDispatch *iface ) +{ + return CONTAINING_RECORD(iface, AutomationObject, IDispatch_iface); +} + +/* Load type info so we don't have to process GetIDsOfNames */ +HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid) +{ + static const WCHAR msiserverW[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b',0}; + ITypeInfo *ti = NULL; + ITypeLib *lib = NULL; + HRESULT hr; + + TRACE("(%p)->(%s, %d)\n", iface, debugstr_guid(clsid), lcid); + + /* Load registered type library */ + hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &lib); + if (FAILED(hr)) { + hr = LoadTypeLib(msiserverW, &lib); + if (FAILED(hr)) { + ERR("Could not load msiserver.tlb\n"); + return hr; + } + } + + /* Get type information for object */ + hr = ITypeLib_GetTypeInfoOfGuid(lib, clsid, &ti); + ITypeLib_Release(lib); + if (FAILED(hr)) { + ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid)); + return hr; + } + *pptinfo = ti; + return S_OK; +} + +/* AutomationObject methods */ +static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject) +{ + AutomationObject *This = impl_from_IDispatch(iface); + + TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); + + if (ppvObject == NULL) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IDispatch) || + IsEqualGUID(riid, This->clsid)) + *ppvObject = &This->IDispatch_iface; + else if (IsEqualGUID(riid, &IID_IProvideClassInfo) || + IsEqualGUID(riid, &IID_IProvideClassInfo2) || + IsEqualGUID(riid, &IID_IProvideMultipleClassInfo)) + *ppvObject = &This->IProvideMultipleClassInfo_iface; + else + { + TRACE("() : asking for unsupported interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + IDispatch_AddRef(iface); + + return S_OK; +} + +static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface) +{ + AutomationObject *This = impl_from_IDispatch(iface); + + TRACE("(%p/%p)\n", iface, This); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI AutomationObject_Release(IDispatch* iface) +{ + AutomationObject *This = impl_from_IDispatch(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)\n", iface, This); + + if (!ref) + { + if (This->funcFree) This->funcFree(This); + ITypeInfo_Release(This->iTypeInfo); + MsiCloseHandle(This->msiHandle); + msi_free(This); + } + + return ref; +} + +static HRESULT WINAPI AutomationObject_GetTypeInfoCount( + IDispatch* iface, + UINT* pctinfo) +{ + AutomationObject *This = impl_from_IDispatch(iface); + + TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo); + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI AutomationObject_GetTypeInfo( + IDispatch* iface, + UINT iTInfo, + LCID lcid, + ITypeInfo** ppTInfo) +{ + AutomationObject *This = impl_from_IDispatch(iface); + TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo); + + ITypeInfo_AddRef(This->iTypeInfo); + *ppTInfo = This->iTypeInfo; + return S_OK; +} + +static HRESULT WINAPI AutomationObject_GetIDsOfNames( + IDispatch* iface, + REFIID riid, + LPOLESTR* rgszNames, + UINT cNames, + LCID lcid, + DISPID* rgDispId) +{ + AutomationObject *This = impl_from_IDispatch(iface); + HRESULT hr; + TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId); + + if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG; + hr = ITypeInfo_GetIDsOfNames(This->iTypeInfo, rgszNames, cNames, rgDispId); + if (hr == DISP_E_UNKNOWNNAME) + { + UINT idx; + for (idx=0; idx<cNames; idx++) + { + if (rgDispId[idx] == DISPID_UNKNOWN) + FIXME("Unknown member %s, clsid %s\n", debugstr_w(rgszNames[idx]), debugstr_guid(This->clsid)); + } + } + return hr; +} + +/* Maximum number of allowed function parameters+1 */ +#define MAX_FUNC_PARAMS 20 + +/* Some error checking is done here to simplify individual object function invocation */ +static HRESULT WINAPI AutomationObject_Invoke( + IDispatch* iface, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + AutomationObject *This = impl_from_IDispatch(iface); + HRESULT hr; + unsigned int uArgErr; + VARIANT varResultDummy; + BSTR bstrName = NULL; + + TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + if (!IsEqualIID(riid, &IID_NULL)) + { + ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid)); + return DISP_E_UNKNOWNNAME; + } + + if (wFlags & DISPATCH_PROPERTYGET && !pVarResult) + { + ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n"); + return DISP_E_PARAMNOTOPTIONAL; + } + + /* This simplifies our individual object invocation functions */ + if (puArgErr == NULL) puArgErr = &uArgErr; + if (pVarResult == NULL) pVarResult = &varResultDummy; + + /* Assume return type is void unless determined otherwise */ + VariantInit(pVarResult); + + /* If we are tracing, we want to see the name of the member we are invoking */ + if (TRACE_ON(msi)) + { + ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL); + TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName)); + } + + hr = This->funcInvoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr); + + if (hr == DISP_E_MEMBERNOTFOUND) { + if (bstrName == NULL) ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL); + FIXME("Method %d, %s wflags %d not implemented, clsid %s\n", dispIdMember, debugstr_w(bstrName), wFlags, debugstr_guid(This->clsid)); + } + else if (pExcepInfo && + (hr == DISP_E_PARAMNOTFOUND || + hr == DISP_E_EXCEPTION)) { + static const WCHAR szComma[] = { ',',0 }; + static const WCHAR szExceptionSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0}; + WCHAR szExceptionDescription[MAX_PATH]; + BSTR bstrParamNames[MAX_FUNC_PARAMS]; + unsigned namesNo, i; + BOOL bFirst = TRUE; + + if (FAILED(ITypeInfo_GetNames(This->iTypeInfo, dispIdMember, bstrParamNames, + MAX_FUNC_PARAMS, &namesNo))) + { + TRACE("Failed to retrieve names for dispIdMember %d\n", dispIdMember); + } + else + { + memset(szExceptionDescription, 0, sizeof(szExceptionDescription)); + for (i=0; i<namesNo; i++) + { + if (bFirst) bFirst = FALSE; + else { + lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], szComma); + } + lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], bstrParamNames[i]); + SysFreeString(bstrParamNames[i]); + } + + memset(pExcepInfo, 0, sizeof(EXCEPINFO)); + pExcepInfo->wCode = 1000; + pExcepInfo->bstrSource = SysAllocString(szExceptionSource); + pExcepInfo->bstrDescription = SysAllocString(szExceptionDescription); + hr = DISP_E_EXCEPTION; + } + } + + /* Make sure we free the return variant if it is our dummy variant */ + if (pVarResult == &varResultDummy) VariantClear(pVarResult); + + /* Free function name if we retrieved it */ + SysFreeString(bstrName); + + TRACE("Returning 0x%08x, %s\n", hr, SUCCEEDED(hr) ? "ok" : "not ok"); + + return hr; +} + +static const struct IDispatchVtbl AutomationObjectVtbl = +{ + AutomationObject_QueryInterface, + AutomationObject_AddRef, + AutomationObject_Release, + AutomationObject_GetTypeInfoCount, + AutomationObject_GetTypeInfo, + AutomationObject_GetIDsOfNames, + AutomationObject_Invoke +}; + +/* + * IProvideMultipleClassInfo methods + */ + +static HRESULT WINAPI ProvideMultipleClassInfo_QueryInterface( + IProvideMultipleClassInfo* iface, + REFIID riid, + VOID** ppvoid) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + return IDispatch_QueryInterface(&This->IDispatch_iface, riid, ppvoid); +} + +static ULONG WINAPI ProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + return IDispatch_AddRef(&This->IDispatch_iface); +} + +static ULONG WINAPI ProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + return IDispatch_Release(&This->IDispatch_iface); +} + +static HRESULT WINAPI ProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + TRACE("(%p/%p)->(%p)\n", iface, This, ppTI); + return load_type_info(&This->IDispatch_iface, ppTI, This->clsid, 0); +} + +static HRESULT WINAPI ProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID)); + + if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID) + return E_INVALIDARG; + else { + *pGUID = *This->clsid; + return S_OK; + } +} + +static HRESULT WINAPI ProvideMultipleClassInfo_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + + TRACE("(%p/%p)->(%p)\n", iface, This, pcti); + *pcti = 1; + return S_OK; +} + +static HRESULT WINAPI ProvideMultipleClassInfo_GetInfoOfIndex(IProvideMultipleClassInfo* iface, + ULONG iti, + DWORD dwFlags, + ITypeInfo** pptiCoClass, + DWORD* pdwTIFlags, + ULONG* pcdispidReserved, + IID* piidPrimary, + IID* piidSource) +{ + AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface); + + TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource); + + if (iti != 0) + return E_INVALIDARG; + + if (dwFlags & MULTICLASSINFO_GETTYPEINFO) + load_type_info(&This->IDispatch_iface, pptiCoClass, This->clsid, 0); + + if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS) + { + *pdwTIFlags = 0; + *pcdispidReserved = 0; + } + + if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY){ + *piidPrimary = *This->clsid; + } + + if (dwFlags & MULTICLASSINFO_GETIIDSOURCE){ + *piidSource = *This->clsid; + } + + return S_OK; +} + +static const IProvideMultipleClassInfoVtbl ProvideMultipleClassInfoVtbl = +{ + ProvideMultipleClassInfo_QueryInterface, + ProvideMultipleClassInfo_AddRef, + ProvideMultipleClassInfo_Release, + ProvideMultipleClassInfo_GetClassInfo, + ProvideMultipleClassInfo_GetGUID, + ProvideMultipleClassInfo_GetMultiTypeInfoCount, + ProvideMultipleClassInfo_GetInfoOfIndex +}; + +static HRESULT init_automation_object(AutomationObject *This, MSIHANDLE msiHandle, REFIID clsid, + autoInvokeFunc invokeFunc, autoFreeFunc freeFunc) +{ + TRACE("(%p, %d, %s, %p, %p)\n", This, msiHandle, debugstr_guid(clsid), invokeFunc, freeFunc); + + This->IDispatch_iface.lpVtbl = &AutomationObjectVtbl; + This->IProvideMultipleClassInfo_iface.lpVtbl = &ProvideMultipleClassInfoVtbl; + This->ref = 1; + + This->msiHandle = msiHandle; + This->clsid = (LPCLSID)clsid; + This->funcInvoke = invokeFunc; + This->funcFree = freeFunc; + + /* Load our TypeInfo so we don't have to process GetIDsOfNames */ + This->iTypeInfo = NULL; + return load_type_info(&This->IDispatch_iface, &This->iTypeInfo, clsid, 0); +} + +/* + * ListEnumerator methods + */ + +static inline ListEnumerator *impl_from_IEnumVARIANT(IEnumVARIANT* iface) +{ + return CONTAINING_RECORD(iface, ListEnumerator, IEnumVARIANT_iface); +} + +static HRESULT WINAPI ListEnumerator_QueryInterface(IEnumVARIANT* iface, REFIID riid, + void** ppvObject) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); + + if (ppvObject == NULL) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IEnumVARIANT)) + { + *ppvObject = &This->IEnumVARIANT_iface; + } + else + { + TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid)); + return E_NOINTERFACE; + } + + IEnumVARIANT_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI ListEnumerator_AddRef(IEnumVARIANT* iface) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p/%p)\n", iface, This); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI ListEnumerator_Release(IEnumVARIANT* iface) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)\n", iface, This); + + if (!ref) + { + if (This->list) IDispatch_Release(&This->list->autoobj.IDispatch_iface); + msi_free(This); + } + + return ref; +} + +static HRESULT WINAPI ListEnumerator_Next(IEnumVARIANT* iface, ULONG celt, VARIANT* rgVar, + ULONG* fetched) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + ULONG i, local; + + TRACE("(%p, %uld, %p, %p)\n", iface, celt, rgVar, fetched); + + if (fetched) *fetched = 0; + + if (!rgVar) + return S_FALSE; + + for (local = 0; local < celt; local++) + VariantInit(&rgVar[local]); + + for (i = This->pos, local = 0; i < This->list->count && local < celt; i++, local++) + VariantCopy(&rgVar[local], &This->list->data[i]); + + if (fetched) *fetched = local; + This->pos = i; + + return (local < celt) ? S_FALSE : S_OK; +} + +static HRESULT WINAPI ListEnumerator_Skip(IEnumVARIANT* iface, ULONG celt) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p,%uld)\n", iface, celt); + + This->pos += celt; + if (This->pos >= This->list->count) + { + This->pos = This->list->count; + return S_FALSE; + } + + return S_OK; +} + +static HRESULT WINAPI ListEnumerator_Reset(IEnumVARIANT* iface) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + + TRACE("(%p)\n", iface); + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI ListEnumerator_Clone(IEnumVARIANT* iface, IEnumVARIANT **ppEnum) +{ + ListEnumerator *This = impl_from_IEnumVARIANT(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, ppEnum); + + if (ppEnum == NULL) + return S_FALSE; + + *ppEnum = NULL; + hr = create_list_enumerator(This->list, (LPVOID *)ppEnum); + if (FAILED(hr)) + { + if (*ppEnum) IEnumVARIANT_Release(*ppEnum); + return hr; + } + + return S_OK; +} + +static const struct IEnumVARIANTVtbl ListEnumerator_Vtbl = +{ + ListEnumerator_QueryInterface, + ListEnumerator_AddRef, + ListEnumerator_Release, + ListEnumerator_Next, + ListEnumerator_Skip, + ListEnumerator_Reset, + ListEnumerator_Clone +}; + +/* Create a list enumerator, placing the result in the pointer ppObj. */ +static HRESULT create_list_enumerator(ListObject *list, void **ppObj) +{ + ListEnumerator *object; + + TRACE("(%p, %p)\n", list, ppObj); + + object = msi_alloc(sizeof(ListEnumerator)); + + /* Set all the VTable references */ + object->IEnumVARIANT_iface.lpVtbl = &ListEnumerator_Vtbl; + object->ref = 1; + + /* Store data that was passed */ + object->pos = 0; + object->list = list; + if (list) IDispatch_AddRef(&list->autoobj.IDispatch_iface); + + *ppObj = object; + return S_OK; +} + +/* + * Individual Object Invocation Functions + */ + +/* Helper function that copies a passed parameter instead of using VariantChangeType like the actual DispGetParam. + This function is only for VARIANT type parameters that have several types that cannot be properly discriminated + using DispGetParam/VariantChangeType. */ +static HRESULT DispGetParam_CopyOnly( + DISPPARAMS *pdispparams, /* [in] Parameter list */ + UINT *position, /* [in] Position of parameter to copy in pdispparams; on return will contain calculated position */ + VARIANT *pvarResult) /* [out] Destination for resulting variant */ +{ + /* position is counted backwards */ + UINT pos; + + TRACE("position=%d, cArgs=%d, cNamedArgs=%d\n", + *position, pdispparams->cArgs, pdispparams->cNamedArgs); + if (*position < pdispparams->cArgs) { + /* positional arg? */ + pos = pdispparams->cArgs - *position - 1; + } else { + /* FIXME: is this how to handle named args? */ + for (pos=0; pos<pdispparams->cNamedArgs; pos++) + if (pdispparams->rgdispidNamedArgs[pos] == *position) break; + + if (pos==pdispparams->cNamedArgs) + return DISP_E_PARAMNOTFOUND; + } + *position = pos; + return VariantCopyInd(pvarResult, + &pdispparams->rgvarg[pos]); +} + +static HRESULT SummaryInfoImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + VARIANTARG varg0, varg1; + FILETIME ft, ftlocal; + SYSTEMTIME st; + HRESULT hr; + + VariantInit(&varg0); + VariantInit(&varg1); + + switch (dispIdMember) + { + case DISPID_SUMMARYINFO_PROPERTY: + if (wFlags & DISPATCH_PROPERTYGET) + { + UINT type; + INT value; + DWORD size = 0; + DATE date; + LPWSTR str; + + static WCHAR szEmpty[] = {0}; + + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + ret = MsiSummaryInfoGetPropertyW(This->msiHandle, V_I4(&varg0), &type, &value, + &ft, szEmpty, &size); + if (ret != ERROR_SUCCESS && + ret != ERROR_MORE_DATA) + { + ERR("MsiSummaryInfoGetProperty returned %d\n", ret); + return DISP_E_EXCEPTION; + } + + switch (type) + { + case VT_EMPTY: + break; + + case VT_I2: + case VT_I4: + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = value; + break; + + case VT_LPSTR: + if (!(str = msi_alloc(++size * sizeof(WCHAR)))) + ERR("Out of memory\n"); + else if ((ret = MsiSummaryInfoGetPropertyW(This->msiHandle, V_I4(&varg0), &type, NULL, + NULL, str, &size)) != ERROR_SUCCESS) + ERR("MsiSummaryInfoGetProperty returned %d\n", ret); + else + { + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocString(str); + } + msi_free(str); + break; + + case VT_FILETIME: + FileTimeToLocalFileTime(&ft, &ftlocal); + FileTimeToSystemTime(&ftlocal, &st); + SystemTimeToVariantTime(&st, &date); + + V_VT(pVarResult) = VT_DATE; + V_DATE(pVarResult) = date; + break; + + default: + ERR("Unhandled variant type %d\n", type); + } + } + else if (wFlags & DISPATCH_PROPERTYPUT) + { + UINT posValue = DISPID_PROPERTYPUT; + + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg1); + if (FAILED(hr)) + { + *puArgErr = posValue; + return hr; + } + + switch (V_VT(&varg1)) + { + case VT_I2: + case VT_I4: + ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), V_VT(&varg1), V_I4(&varg1), NULL, NULL); + break; + + case VT_DATE: + VariantTimeToSystemTime(V_DATE(&varg1), &st); + SystemTimeToFileTime(&st, &ftlocal); + LocalFileTimeToFileTime(&ftlocal, &ft); + ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), VT_FILETIME, 0, &ft, NULL); + break; + + case VT_BSTR: + ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), VT_LPSTR, 0, NULL, V_BSTR(&varg1)); + break; + + default: + FIXME("Unhandled variant type %d\n", V_VT(&varg1)); + VariantClear(&varg1); + return DISP_E_EXCEPTION; + } + + if (ret != ERROR_SUCCESS) + { + ERR("MsiSummaryInfoSetPropertyW returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SUMMARYINFO_PROPERTYCOUNT: + if (wFlags & DISPATCH_PROPERTYGET) { + UINT count; + if ((ret = MsiSummaryInfoGetPropertyCount(This->msiHandle, &count)) != ERROR_SUCCESS) + ERR("MsiSummaryInfoGetPropertyCount returned %d\n", ret); + else + { + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = count; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + default: + return DISP_E_MEMBERNOTFOUND; + } + + VariantClear(&varg1); + VariantClear(&varg0); + + return S_OK; +} + +static HRESULT RecordImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + WCHAR *szString; + DWORD dwLen; + UINT ret; + VARIANTARG varg0, varg1; + HRESULT hr; + + VariantInit(&varg0); + VariantInit(&varg1); + + switch (dispIdMember) + { + case DISPID_RECORD_FIELDCOUNT: + if (wFlags & DISPATCH_PROPERTYGET) { + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = MsiRecordGetFieldCount(This->msiHandle); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_RECORD_STRINGDATA: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = NULL; + if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), NULL, &dwLen)) == ERROR_SUCCESS) + { + if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR)))) + ERR("Out of memory\n"); + else if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), szString, &dwLen)) == ERROR_SUCCESS) + V_BSTR(pVarResult) = SysAllocString(szString); + msi_free(szString); + } + if (ret != ERROR_SUCCESS) + ERR("MsiRecordGetString returned %d\n", ret); + } else if (wFlags & DISPATCH_PROPERTYPUT) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) return hr; + if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS) + { + VariantClear(&varg1); + ERR("MsiRecordSetString returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_RECORD_INTEGERDATA: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = MsiRecordGetInteger(This->msiHandle, V_I4(&varg0)); + } else if (wFlags & DISPATCH_PROPERTYPUT) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr); + if (FAILED(hr)) return hr; + if ((ret = MsiRecordSetInteger(This->msiHandle, V_I4(&varg0), V_I4(&varg1))) != ERROR_SUCCESS) + { + ERR("MsiRecordSetInteger returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + default: + return DISP_E_MEMBERNOTFOUND; + } + + VariantClear(&varg1); + VariantClear(&varg0); + + return S_OK; +} + +static HRESULT create_record(MSIHANDLE msiHandle, IDispatch **disp) +{ + AutomationObject *record; + HRESULT hr; + + record = msi_alloc(sizeof(*record)); + if (!record) return E_OUTOFMEMORY; + + hr = init_automation_object(record, msiHandle, &DIID_Record, RecordImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(record); + return hr; + } + + *disp = &record->IDispatch_iface; + + return hr; +} + +static HRESULT ListImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + ListObject *list = (ListObject*)This; + IUnknown *pUnk = NULL; + HRESULT hr; + + switch (dispIdMember) + { + case DISPID_LIST__NEWENUM: + if (wFlags & DISPATCH_METHOD) { + V_VT(pVarResult) = VT_UNKNOWN; + if (SUCCEEDED(hr = create_list_enumerator(list, (LPVOID *)&pUnk))) + V_UNKNOWN(pVarResult) = pUnk; + else + ERR("Failed to create IEnumVARIANT object, hresult 0x%08x\n", hr); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_LIST_ITEM: + if (wFlags & DISPATCH_PROPERTYGET) { + VARIANTARG index; + + VariantInit(&index); + hr = DispGetParam(pDispParams, 0, VT_I4, &index, puArgErr); + if (FAILED(hr)) return hr; + if (V_I4(&index) < 0 || V_I4(&index) >= list->count) + return DISP_E_BADINDEX; + VariantCopy(pVarResult, &list->data[V_I4(&index)]); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_LIST_COUNT: + if (wFlags & DISPATCH_PROPERTYGET) { + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = list->count; + } + else return DISP_E_MEMBERNOTFOUND; + break; + + default: + return DISP_E_MEMBERNOTFOUND; + } + + return S_OK; +} + +static void ListImpl_Free(AutomationObject *This) +{ + ListObject *list = (ListObject*)This; + int i; + + for (i = 0; i < list->count; i++) + VariantClear(&list->data[i]); + msi_free(list->data); +} + +static HRESULT get_products_count(const WCHAR *product, int *len) +{ + int i = 0; + + while (1) + { + WCHAR dataW[GUID_SIZE]; + UINT ret; + + /* all or related only */ + if (product) + ret = MsiEnumRelatedProductsW(product, 0, i, dataW); + else + ret = MsiEnumProductsW(i, dataW); + + if (ret == ERROR_NO_MORE_ITEMS) break; + + if (ret != ERROR_SUCCESS) + return DISP_E_EXCEPTION; + + i++; + } + + *len = i; + + return S_OK; +} + +static HRESULT create_list(const WCHAR *product, IDispatch **dispatch) +{ + ListObject *list; + HRESULT hr; + int i; + + list = msi_alloc_zero(sizeof(ListObject)); + if (!list) return E_OUTOFMEMORY; + + hr = init_automation_object(&list->autoobj, 0, &DIID_StringList, ListImpl_Invoke, ListImpl_Free); + if (hr != S_OK) + { + msi_free(list); + return hr; + } + + *dispatch = &list->autoobj.IDispatch_iface; + + hr = get_products_count(product, &list->count); + if (hr != S_OK) + { + IDispatch_Release(*dispatch); + return hr; + } + + list->data = msi_alloc(list->count*sizeof(VARIANT)); + if (!list->data) + { + IDispatch_Release(*dispatch); + return E_OUTOFMEMORY; + } + + for (i = 0; i < list->count; i++) + { + WCHAR dataW[GUID_SIZE]; + UINT ret; + + /* all or related only */ + if (product) + ret = MsiEnumRelatedProductsW(product, 0, i, dataW); + else + ret = MsiEnumProductsW(i, dataW); + + if (ret == ERROR_NO_MORE_ITEMS) break; + + V_VT(&list->data[i]) = VT_BSTR; + V_BSTR(&list->data[i]) = SysAllocString(dataW); + } + + return S_OK; +} + +static HRESULT ViewImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + MSIHANDLE msiHandle; + UINT ret; + VARIANTARG varg0, varg1; + HRESULT hr; + + VariantInit(&varg0); + VariantInit(&varg1); + + switch (dispIdMember) + { + case DISPID_VIEW_EXECUTE: + if (wFlags & DISPATCH_METHOD) + { + hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &varg0, puArgErr); + if (SUCCEEDED(hr) && V_DISPATCH(&varg0) != NULL) + MsiViewExecute(This->msiHandle, ((AutomationObject *)V_DISPATCH(&varg0))->msiHandle); + else + MsiViewExecute(This->msiHandle, 0); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_VIEW_FETCH: + if (wFlags & DISPATCH_METHOD) + { + V_VT(pVarResult) = VT_DISPATCH; + if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS) + { + IDispatch *dispatch = NULL; + + if (SUCCEEDED(hr = create_record(msiHandle, &dispatch))) + V_DISPATCH(pVarResult) = dispatch; + else + ERR("Failed to create Record object, hresult 0x%08x\n", hr); + } + else if (ret == ERROR_NO_MORE_ITEMS) + V_DISPATCH(pVarResult) = NULL; + else + { + ERR("MsiViewFetch returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_VIEW_MODIFY: + if (wFlags & DISPATCH_METHOD) + { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_DISPATCH, &varg1, puArgErr); + if (FAILED(hr)) return hr; + if (!V_DISPATCH(&varg1)) return DISP_E_EXCEPTION; + if ((ret = MsiViewModify(This->msiHandle, V_I4(&varg0), ((AutomationObject *)V_DISPATCH(&varg1))->msiHandle)) != ERROR_SUCCESS) + { + VariantClear(&varg1); + ERR("MsiViewModify returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_VIEW_CLOSE: + if (wFlags & DISPATCH_METHOD) + { + MsiViewClose(This->msiHandle); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + default: + return DISP_E_MEMBERNOTFOUND; + } + + VariantClear(&varg1); + VariantClear(&varg0); + + return S_OK; +} + +static HRESULT DatabaseImpl_LastErrorRecord(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT DatabaseImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + IDispatch *dispatch = NULL; + MSIHANDLE msiHandle; + UINT ret; + VARIANTARG varg0, varg1; + HRESULT hr; + + VariantInit(&varg0); + VariantInit(&varg1); + + switch (dispIdMember) + { + case DISPID_DATABASE_SUMMARYINFORMATION: + if (wFlags & DISPATCH_PROPERTYGET) + { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) + V_I4(&varg0) = 0; + + V_VT(pVarResult) = VT_DISPATCH; + if ((ret = MsiGetSummaryInformationW(This->msiHandle, NULL, V_I4(&varg0), &msiHandle)) == ERROR_SUCCESS) + { + hr = create_summaryinfo(msiHandle, &dispatch); + if (SUCCEEDED(hr)) + V_DISPATCH(pVarResult) = dispatch; + else + ERR("Failed to create SummaryInfo object: 0x%08x\n", hr); + } + else + { + ERR("MsiGetSummaryInformation returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_DATABASE_OPENVIEW: + if (wFlags & DISPATCH_METHOD) + { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_DISPATCH; + if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS) + { + if (SUCCEEDED(hr = create_view(msiHandle, &dispatch))) + V_DISPATCH(pVarResult) = dispatch; + else + ERR("Failed to create View object, hresult 0x%08x\n", hr); + } + else + { + VariantClear(&varg0); + ERR("MsiDatabaseOpenView returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_INSTALLER_LASTERRORRECORD: + return DatabaseImpl_LastErrorRecord(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + default: + return DISP_E_MEMBERNOTFOUND; + } + + VariantClear(&varg1); + VariantClear(&varg0); + + return S_OK; +} + +static HRESULT SessionImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + SessionObject *session = (SessionObject*)This; + WCHAR *szString; + DWORD dwLen; + MSIHANDLE msiHandle; + LANGID langId; + UINT ret; + INSTALLSTATE iInstalled, iAction; + VARIANTARG varg0, varg1; + HRESULT hr; + + VariantInit(&varg0); + VariantInit(&varg1); + + switch (dispIdMember) + { + case DISPID_SESSION_INSTALLER: + if (wFlags & DISPATCH_PROPERTYGET) { + V_VT(pVarResult) = VT_DISPATCH; + IDispatch_AddRef(session->installer); + V_DISPATCH(pVarResult) = session->installer; + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_PROPERTY: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = NULL; + if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), NULL, &dwLen)) == ERROR_SUCCESS) + { + if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR)))) + ERR("Out of memory\n"); + else if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), szString, &dwLen)) == ERROR_SUCCESS) + V_BSTR(pVarResult) = SysAllocString(szString); + msi_free(szString); + } + if (ret != ERROR_SUCCESS) + ERR("MsiGetProperty returned %d\n", ret); + } else if (wFlags & DISPATCH_PROPERTYPUT) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) { + VariantClear(&varg0); + return hr; + } + if ((ret = MsiSetPropertyW(This->msiHandle, V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS) + { + VariantClear(&varg0); + VariantClear(&varg1); + ERR("MsiSetProperty returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_LANGUAGE: + if (wFlags & DISPATCH_PROPERTYGET) { + langId = MsiGetLanguage(This->msiHandle); + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = langId; + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_MODE: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_BOOL; + V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&varg0)); + } else if (wFlags & DISPATCH_PROPERTYPUT) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_BOOL, &varg1, puArgErr); + if (FAILED(hr)) return hr; + if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS) + { + ERR("MsiSetMode returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_DATABASE: + if (wFlags & DISPATCH_PROPERTYGET) { + V_VT(pVarResult) = VT_DISPATCH; + if ((msiHandle = MsiGetActiveDatabase(This->msiHandle))) + { + IDispatch *dispatch; + + if (SUCCEEDED(hr = create_database(msiHandle, &dispatch))) + V_DISPATCH(pVarResult) = dispatch; + else + ERR("Failed to create Database object, hresult 0x%08x\n", hr); + } + else + { + ERR("MsiGetActiveDatabase failed\n"); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_DOACTION: + if (wFlags & DISPATCH_METHOD) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + ret = MsiDoActionW(This->msiHandle, V_BSTR(&varg0)); + V_VT(pVarResult) = VT_I4; + switch (ret) + { + case ERROR_FUNCTION_NOT_CALLED: + V_I4(pVarResult) = msiDoActionStatusNoAction; + break; + case ERROR_SUCCESS: + V_I4(pVarResult) = msiDoActionStatusSuccess; + break; + case ERROR_INSTALL_USEREXIT: + V_I4(pVarResult) = msiDoActionStatusUserExit; + break; + case ERROR_INSTALL_FAILURE: + V_I4(pVarResult) = msiDoActionStatusFailure; + break; + case ERROR_INSTALL_SUSPEND: + V_I4(pVarResult) = msiDoActionStatusSuspend; + break; + case ERROR_MORE_DATA: + V_I4(pVarResult) = msiDoActionStatusFinished; + break; + case ERROR_INVALID_HANDLE_STATE: + V_I4(pVarResult) = msiDoActionStatusWrongState; + break; + case ERROR_INVALID_DATA: + V_I4(pVarResult) = msiDoActionStatusBadActionData; + break; + default: + VariantClear(&varg0); + FIXME("MsiDoAction returned unhandled value %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_EVALUATECONDITION: + if (wFlags & DISPATCH_METHOD) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = MsiEvaluateConditionW(This->msiHandle, V_BSTR(&varg0)); + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_MESSAGE: + if(!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_DISPATCH, &varg1, puArgErr); + if (FAILED(hr)) return hr; + + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = + MsiProcessMessage(This->msiHandle, V_I4(&varg0), ((AutomationObject *)V_DISPATCH(&varg1))->msiHandle); + break; + + case DISPID_SESSION_SETINSTALLLEVEL: + if (wFlags & DISPATCH_METHOD) { + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) return hr; + if ((ret = MsiSetInstallLevel(This->msiHandle, V_I4(&varg0))) != ERROR_SUCCESS) + { + ERR("MsiSetInstallLevel returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_FEATURECURRENTSTATE: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_I4; + if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS) + V_I4(pVarResult) = iInstalled; + else + { + ERR("MsiGetFeatureState returned %d\n", ret); + V_I4(pVarResult) = msiInstallStateUnknown; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + case DISPID_SESSION_FEATUREREQUESTSTATE: + if (wFlags & DISPATCH_PROPERTYGET) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + V_VT(pVarResult) = VT_I4; + if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS) + V_I4(pVarResult) = iAction; + else + { + ERR("MsiGetFeatureState returned %d\n", ret); + V_I4(pVarResult) = msiInstallStateUnknown; + } + } else if (wFlags & DISPATCH_PROPERTYPUT) { + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) return hr; + hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr); + if (FAILED(hr)) { + VariantClear(&varg0); + return hr; + } + if ((ret = MsiSetFeatureStateW(This->msiHandle, V_BSTR(&varg0), V_I4(&varg1))) != ERROR_SUCCESS) + { + VariantClear(&varg0); + ERR("MsiSetFeatureState returned %d\n", ret); + return DISP_E_EXCEPTION; + } + } + else return DISP_E_MEMBERNOTFOUND; + break; + + default: + return DISP_E_MEMBERNOTFOUND; + } + + VariantClear(&varg1); + VariantClear(&varg0); + + return S_OK; +} + +/* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the + * registry value type. Used by Installer::RegistryValue. */ +static void variant_from_registry_value(VARIANT *pVarResult, DWORD dwType, LPBYTE lpData, DWORD dwSize) +{ + static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 }; + static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 }; + WCHAR *szString = (WCHAR *)lpData; + LPWSTR szNewString = NULL; + DWORD dwNewSize = 0; + int idx; + + switch (dwType) + { + /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */ + case REG_MULTI_SZ: /* Multi SZ change internal null characters to newlines */ + idx = (dwSize/sizeof(WCHAR))-1; + while (idx >= 0 && !szString[idx]) idx--; + for (; idx >= 0; idx--) + if (!szString[idx]) szString[idx] = '\n'; + /* fall through */ + case REG_SZ: + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocStringByteLen((LPCSTR)szString, dwSize); + break; + + case REG_EXPAND_SZ: + if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize))) + ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError()); + else if (!(szNewString = msi_alloc(dwNewSize * sizeof(WCHAR)))) + ERR("Out of memory\n"); + else if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize))) + ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError()); + else + { + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocStringLen(szNewString, dwNewSize); + } + msi_free(szNewString); + break; + + case REG_DWORD: + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = *((DWORD *)lpData); + break; + + case REG_QWORD: + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocString(szREG_); /* Weird string, don't know why native returns it */ + break; + + case REG_BINARY: + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocString(szREG_BINARY); + break; + + case REG_NONE: + V_VT(pVarResult) = VT_EMPTY; + break; + + default: + FIXME("Unhandled registry value type %d\n", dwType); + } +} + +static HRESULT InstallerImpl_CreateRecord(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + HRESULT hr; + VARIANTARG varg0; + MSIHANDLE hrec; + IDispatch* dispatch; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + V_VT(pVarResult) = VT_DISPATCH; + + hrec = MsiCreateRecord(V_I4(&varg0)); + if (!hrec) + return DISP_E_EXCEPTION; + + hr = create_record(hrec, &dispatch); + if (SUCCEEDED(hr)) + V_DISPATCH(pVarResult) = dispatch; + + return hr; +} + +static HRESULT InstallerImpl_OpenPackage(AutomationObject* This, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + HRESULT hr; + MSIHANDLE hpkg; + IDispatch* dispatch; + VARIANTARG varg0, varg1; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + if (pDispParams->cArgs == 0) + return DISP_E_TYPEMISMATCH; + + if (V_VT(&pDispParams->rgvarg[pDispParams->cArgs - 1]) != VT_BSTR) + return DISP_E_TYPEMISMATCH; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + VariantInit(&varg1); + if (pDispParams->cArgs == 2) + { + hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr); + if (FAILED(hr)) + goto done; + } + else + { + V_VT(&varg1) = VT_I4; + V_I4(&varg1) = 0; + } + + V_VT(pVarResult) = VT_DISPATCH; + + ret = MsiOpenPackageExW(V_BSTR(&varg0), V_I4(&varg1), &hpkg); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_EXCEPTION; + goto done; + } + + hr = create_session(hpkg, &This->IDispatch_iface, &dispatch); + if (SUCCEEDED(hr)) + V_DISPATCH(pVarResult) = dispatch; + +done: + VariantClear(&varg0); + VariantClear(&varg1); + return hr; +} + +static HRESULT InstallerImpl_OpenProduct(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + HRESULT hr; + VARIANTARG varg0; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + FIXME("%s\n", debugstr_w(V_BSTR(&varg0))); + + VariantInit(pVarResult); + + VariantClear(&varg0); + return S_OK; +} + +static HRESULT InstallerImpl_OpenDatabase(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + HRESULT hr; + MSIHANDLE hdb; + IDispatch* dispatch; + VARIANTARG varg0, varg1; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + VariantInit(&varg1); + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) + goto done; + + V_VT(pVarResult) = VT_DISPATCH; + + ret = MsiOpenDatabaseW(V_BSTR(&varg0), V_BSTR(&varg1), &hdb); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_EXCEPTION; + goto done; + } + + hr = create_database(hdb, &dispatch); + if (SUCCEEDED(hr)) + V_DISPATCH(pVarResult) = dispatch; + +done: + VariantClear(&varg0); + VariantClear(&varg1); + return hr; +} + +static HRESULT InstallerImpl_SummaryInformation(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_UILevel(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + HRESULT hr; + VARIANTARG varg0; + INSTALLUILEVEL ui; + + if (!(wFlags & DISPATCH_PROPERTYPUT) && !(wFlags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + if (wFlags & DISPATCH_PROPERTYPUT) + { + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + ui = MsiSetInternalUI(V_I4(&varg0), NULL); + if (ui == INSTALLUILEVEL_NOCHANGE) + return DISP_E_EXCEPTION; + } + else if (wFlags & DISPATCH_PROPERTYGET) + { + ui = MsiSetInternalUI(INSTALLUILEVEL_NOCHANGE, NULL); + if (ui == INSTALLUILEVEL_NOCHANGE) + return DISP_E_EXCEPTION; + + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = ui; + } + + return S_OK; +} + +static HRESULT InstallerImpl_EnableLog(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_InstallProduct(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + HRESULT hr; + VARIANTARG varg0, varg1; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + VariantInit(&varg1); + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) + goto done; + + ret = MsiInstallProductW(V_BSTR(&varg0), V_BSTR(&varg1)); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_EXCEPTION; + goto done; + } + +done: + VariantClear(&varg0); + VariantClear(&varg1); + return hr; +} + +static HRESULT InstallerImpl_Version(WORD wFlags, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + HRESULT hr; + DLLVERSIONINFO verinfo; + WCHAR version[MAX_PATH]; + + static const WCHAR format[] = { + '%','d','.','%','d','.','%','d','.','%','d',0}; + + if (!(wFlags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + verinfo.cbSize = sizeof(DLLVERSIONINFO); + hr = DllGetVersion(&verinfo); + if (FAILED(hr)) + return hr; + + sprintfW(version, format, verinfo.dwMajorVersion, verinfo.dwMinorVersion, + verinfo.dwBuildNumber, verinfo.dwPlatformID); + + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocString(version); + return S_OK; +} + +static HRESULT InstallerImpl_LastErrorRecord(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_RegistryValue(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + HKEY hkey = NULL; + HRESULT hr; + UINT posValue; + DWORD type, size; + LPWSTR szString = NULL; + VARIANTARG varg0, varg1, varg2; + + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + VariantInit(&varg1); + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) + goto done; + + /* Save valuePos so we can save puArgErr if we are unable to do our type + * conversions. + */ + posValue = 2; + VariantInit(&varg2); + hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg2); + if (FAILED(hr)) + goto done; + + if (V_I4(&varg0) >= REG_INDEX_CLASSES_ROOT && + V_I4(&varg0) <= REG_INDEX_DYN_DATA) + { + V_I4(&varg0) |= (UINT_PTR)HKEY_CLASSES_ROOT; + } + + ret = RegOpenKeyW((HKEY)(UINT_PTR)V_I4(&varg0), V_BSTR(&varg1), &hkey); + + /* Only VT_EMPTY case can do anything if the key doesn't exist. */ + if (ret != ERROR_SUCCESS && V_VT(&varg2) != VT_EMPTY) + { + hr = DISP_E_BADINDEX; + goto done; + } + + /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */ + switch (V_VT(&varg2)) + { + /* Return VT_BOOL clarifying whether registry key exists or not. */ + case VT_EMPTY: + V_VT(pVarResult) = VT_BOOL; + V_BOOL(pVarResult) = (ret == ERROR_SUCCESS); + break; + + /* Return the value of specified key if it exists. */ + case VT_BSTR: + ret = RegQueryValueExW(hkey, V_BSTR(&varg2), + NULL, NULL, NULL, &size); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_BADINDEX; + goto done; + } + + szString = msi_alloc(size); + if (!szString) + { + hr = E_OUTOFMEMORY; + goto done; + } + + ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, + &type, (LPBYTE)szString, &size); + if (ret != ERROR_SUCCESS) + { + msi_free(szString); + hr = DISP_E_BADINDEX; + goto done; + } + + variant_from_registry_value(pVarResult, type, + (LPBYTE)szString, size); + msi_free(szString); + break; + + /* Try to make it into VT_I4, can use VariantChangeType for this. */ + default: + hr = VariantChangeType(&varg2, &varg2, 0, VT_I4); + if (FAILED(hr)) + { + if (hr == DISP_E_TYPEMISMATCH) + *puArgErr = posValue; + + goto done; + } + + /* Retrieve class name or maximum value name or subkey name size. */ + if (!V_I4(&varg2)) + ret = RegQueryInfoKeyW(hkey, NULL, &size, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL); + else if (V_I4(&varg2) > 0) + ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &size, NULL, NULL, NULL); + else /* V_I4(&varg2) < 0 */ + ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, &size, + NULL, NULL, NULL, NULL, NULL, NULL); + + if (ret != ERROR_SUCCESS) + goto done; + + szString = msi_alloc(++size * sizeof(WCHAR)); + if (!szString) + { + hr = E_OUTOFMEMORY; + goto done; + } + + if (!V_I4(&varg2)) + ret = RegQueryInfoKeyW(hkey, szString, &size,NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL); + else if (V_I4(&varg2) > 0) + ret = RegEnumValueW(hkey, V_I4(&varg2)-1, szString, + &size, 0, 0, NULL, NULL); + else /* V_I4(&varg2) < 0 */ + ret = RegEnumKeyW(hkey, -1 - V_I4(&varg2), szString, size); + + if (ret == ERROR_SUCCESS) + { + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = SysAllocString(szString); + } + + msi_free(szString); + } + +done: + VariantClear(&varg0); + VariantClear(&varg1); + VariantClear(&varg2); + RegCloseKey(hkey); + return hr; +} + +static HRESULT InstallerImpl_Environment(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_FileAttributes(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_FileSize(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_FileVersion(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + if (!(wFlags & DISPATCH_METHOD)) + return DISP_E_MEMBERNOTFOUND; + + FIXME("\n"); + + VariantInit(pVarResult); + return S_OK; +} + +static HRESULT InstallerImpl_ProductState(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + HRESULT hr; + VARIANTARG varg0; + + if (!(wFlags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + V_VT(pVarResult) = VT_I4; + V_I4(pVarResult) = MsiQueryProductStateW(V_BSTR(&varg0)); + + VariantClear(&varg0); + return S_OK; +} + +static HRESULT InstallerImpl_ProductInfo(WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + UINT ret; + HRESULT hr; + DWORD size; + LPWSTR str = NULL; + VARIANTARG varg0, varg1; + + if (!(wFlags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&varg0); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr); + if (FAILED(hr)) + return hr; + + VariantInit(&varg1); + hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr); + if (FAILED(hr)) + goto done; + + V_VT(pVarResult) = VT_BSTR; + V_BSTR(pVarResult) = NULL; + + ret = MsiGetProductInfoW(V_BSTR(&varg0), V_BSTR(&varg1), NULL, &size); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_EXCEPTION; + goto done; + } + + str = msi_alloc(++size * sizeof(WCHAR)); + if (!str) + { + hr = E_OUTOFMEMORY; + goto done; + } + + ret = MsiGetProductInfoW(V_BSTR(&varg0), V_BSTR(&varg1), str, &size); + if (ret != ERROR_SUCCESS) + { + hr = DISP_E_EXCEPTION; + goto done; + } + + V_BSTR(pVarResult) = SysAllocString(str); + hr = S_OK; + +done: + msi_free(str); + VariantClear(&varg0); + VariantClear(&varg1); + return hr; +} + +static HRESULT InstallerImpl_Products(WORD flags, + DISPPARAMS* pDispParams, + VARIANT* result, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + IDispatch *dispatch; + HRESULT hr; + + if (!(flags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + hr = create_list(NULL, &dispatch); + if (FAILED(hr)) + return hr; + + V_VT(result) = VT_DISPATCH; + V_DISPATCH(result) = dispatch; + + return hr; +} + +static HRESULT InstallerImpl_RelatedProducts(WORD flags, + DISPPARAMS* pDispParams, + VARIANT* result, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + IDispatch* dispatch; + VARIANTARG related; + HRESULT hr; + + if (!(flags & DISPATCH_PROPERTYGET)) + return DISP_E_MEMBERNOTFOUND; + + VariantInit(&related); + hr = DispGetParam(pDispParams, 0, VT_BSTR, &related, puArgErr); + if (FAILED(hr)) + return hr; + + hr = create_list(V_BSTR(&related), &dispatch); + VariantClear(&related); + + V_VT(result) = VT_DISPATCH; + V_DISPATCH(result) = dispatch; + + return hr; +} + +static HRESULT InstallerImpl_Invoke( + AutomationObject* This, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr) +{ + switch (dispIdMember) + { + case DISPID_INSTALLER_CREATERECORD: + return InstallerImpl_CreateRecord(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_OPENPACKAGE: + return InstallerImpl_OpenPackage(This, wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_OPENPRODUCT: + return InstallerImpl_OpenProduct(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_OPENDATABASE: + return InstallerImpl_OpenDatabase(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_SUMMARYINFORMATION: + return InstallerImpl_SummaryInformation(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + case DISPID_INSTALLER_UILEVEL: + return InstallerImpl_UILevel(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_ENABLELOG: + return InstallerImpl_EnableLog(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_INSTALLPRODUCT: + return InstallerImpl_InstallProduct(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + case DISPID_INSTALLER_VERSION: + return InstallerImpl_Version(wFlags, pVarResult, + pExcepInfo, puArgErr); + + case DISPID_INSTALLER_LASTERRORRECORD: + return InstallerImpl_LastErrorRecord(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + case DISPID_INSTALLER_REGISTRYVALUE: + return InstallerImpl_RegistryValue(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + case DISPID_INSTALLER_ENVIRONMENT: + return InstallerImpl_Environment(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_FILEATTRIBUTES: + return InstallerImpl_FileAttributes(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + case DISPID_INSTALLER_FILESIZE: + return InstallerImpl_FileSize(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_FILEVERSION: + return InstallerImpl_FileVersion(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_PRODUCTSTATE: + return InstallerImpl_ProductState(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_PRODUCTINFO: + return InstallerImpl_ProductInfo(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_PRODUCTS: + return InstallerImpl_Products(wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + + case DISPID_INSTALLER_RELATEDPRODUCTS: + return InstallerImpl_RelatedProducts(wFlags, pDispParams, + pVarResult, pExcepInfo, + puArgErr); + + default: + return DISP_E_MEMBERNOTFOUND; + } +} + +HRESULT create_msiserver(IUnknown *outer, void **ppObj) +{ + AutomationObject *installer; + HRESULT hr; + + TRACE("(%p %p)\n", outer, ppObj); + + if (outer) + return CLASS_E_NOAGGREGATION; + + installer = msi_alloc(sizeof(AutomationObject)); + if (!installer) return E_OUTOFMEMORY; + + hr = init_automation_object(installer, 0, &DIID_Installer, InstallerImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(installer); + return hr; + } + + *ppObj = &installer->IDispatch_iface; + + return hr; +} + +HRESULT create_session(MSIHANDLE msiHandle, IDispatch *installer, IDispatch **disp) +{ + SessionObject *session; + HRESULT hr; + + session = msi_alloc(sizeof(SessionObject)); + if (!session) return E_OUTOFMEMORY; + + hr = init_automation_object(&session->autoobj, msiHandle, &DIID_Session, SessionImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(session); + return hr; + } + + session->installer = installer; + *disp = &session->autoobj.IDispatch_iface; + + return hr; +} + +static HRESULT create_database(MSIHANDLE msiHandle, IDispatch **dispatch) +{ + AutomationObject *database; + HRESULT hr; + + TRACE("(%d %p)\n", msiHandle, dispatch); + + database = msi_alloc(sizeof(AutomationObject)); + if (!database) return E_OUTOFMEMORY; + + hr = init_automation_object(database, msiHandle, &DIID_Database, DatabaseImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(database); + return hr; + } + + *dispatch = &database->IDispatch_iface; + + return hr; +} + +static HRESULT create_view(MSIHANDLE msiHandle, IDispatch **dispatch) +{ + AutomationObject *view; + HRESULT hr; + + TRACE("(%d %p)\n", msiHandle, dispatch); + + view = msi_alloc(sizeof(AutomationObject)); + if (!view) return E_OUTOFMEMORY; + + hr = init_automation_object(view, msiHandle, &DIID_View, ViewImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(view); + return hr; + } + + *dispatch = &view->IDispatch_iface; + + return hr; +} + +static HRESULT create_summaryinfo(MSIHANDLE msiHandle, IDispatch **disp) +{ + AutomationObject *info; + HRESULT hr; + + info = msi_alloc(sizeof(*info)); + if (!info) return E_OUTOFMEMORY; + + hr = init_automation_object(info, msiHandle, &DIID_SummaryInfo, SummaryInfoImpl_Invoke, NULL); + if (hr != S_OK) + { + msi_free(info); + return hr; + } + + *disp = &info->IDispatch_iface; + + return hr; +} diff --git a/libmsi/classes.c b/libmsi/classes.c new file mode 100644 index 0000000..afc1b1c --- /dev/null +++ b/libmsi/classes.c @@ -0,0 +1,1564 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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 + */ + +/* Actions handled in this module: + * + * RegisterClassInfo + * RegisterProgIdInfo + * RegisterExtensionInfo + * RegisterMIMEInfo + * UnregisterClassInfo + * UnregisterProgIdInfo + * UnregisterExtensionInfo + * UnregisterMIMEInfo + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static MSIAPPID *load_appid( MSIPACKAGE* package, MSIRECORD *row ) +{ + LPCWSTR buffer; + MSIAPPID *appid; + + /* fill in the data */ + + appid = msi_alloc_zero( sizeof(MSIAPPID) ); + if (!appid) + return NULL; + + appid->AppID = msi_dup_record_field( row, 1 ); + TRACE("loading appid %s\n", debugstr_w( appid->AppID )); + + buffer = MSI_RecordGetString(row,2); + deformat_string( package, buffer, &appid->RemoteServerName ); + + appid->LocalServer = msi_dup_record_field(row,3); + appid->ServiceParameters = msi_dup_record_field(row,4); + appid->DllSurrogate = msi_dup_record_field(row,5); + + appid->ActivateAtStorage = !MSI_RecordIsNull(row,6); + appid->RunAsInteractiveUser = !MSI_RecordIsNull(row,7); + + list_add_tail( &package->appids, &appid->entry ); + + return appid; +} + +static MSIAPPID *load_given_appid( MSIPACKAGE *package, LPCWSTR name ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ', + '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0}; + MSIRECORD *row; + MSIAPPID *appid; + + if (!name) + return NULL; + + /* check for appids already loaded */ + LIST_FOR_EACH_ENTRY( appid, &package->appids, MSIAPPID, entry ) + { + if (!strcmpiW( appid->AppID, name )) + { + TRACE("found appid %s %p\n", debugstr_w(name), appid); + return appid; + } + } + + row = MSI_QueryGetRecord(package->db, query, name); + if (!row) + return NULL; + + appid = load_appid(package, row); + msiobj_release(&row->hdr); + return appid; +} + +static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR progid); +static MSICLASS *load_given_class( MSIPACKAGE *package, LPCWSTR classid ); + +static MSIPROGID *load_progid( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSIPROGID *progid; + LPCWSTR buffer; + + /* fill in the data */ + + progid = msi_alloc_zero( sizeof(MSIPROGID) ); + if (!progid) + return NULL; + + list_add_tail( &package->progids, &progid->entry ); + + progid->ProgID = msi_dup_record_field(row,1); + TRACE("loading progid %s\n",debugstr_w(progid->ProgID)); + + buffer = MSI_RecordGetString(row,2); + progid->Parent = load_given_progid(package,buffer); + if (progid->Parent == NULL && buffer) + FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer)); + + buffer = MSI_RecordGetString(row,3); + progid->Class = load_given_class(package,buffer); + if (progid->Class == NULL && buffer) + FIXME("Unknown class %s\n",debugstr_w(buffer)); + + progid->Description = msi_dup_record_field(row,4); + + if (!MSI_RecordIsNull(row,6)) + { + INT icon_index = MSI_RecordGetInteger(row,6); + LPCWSTR FileName = MSI_RecordGetString(row,5); + LPWSTR FilePath; + static const WCHAR fmt[] = {'%','s',',','%','i',0}; + + FilePath = msi_build_icon_path(package, FileName); + + progid->IconPath = msi_alloc( (strlenW(FilePath)+10)* sizeof(WCHAR) ); + + sprintfW(progid->IconPath,fmt,FilePath,icon_index); + + msi_free(FilePath); + } + else + { + buffer = MSI_RecordGetString(row,5); + if (buffer) + progid->IconPath = msi_build_icon_path(package, buffer); + } + + progid->CurVer = NULL; + progid->VersionInd = NULL; + + /* if we have a parent then we may be that parents CurVer */ + if (progid->Parent && progid->Parent != progid) + { + MSIPROGID *parent = progid->Parent; + + while (parent->Parent && parent->Parent != parent) + parent = parent->Parent; + + /* FIXME: need to determine if we are really the CurVer */ + + progid->CurVer = parent; + parent->VersionInd = progid; + } + + return progid; +} + +static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR name) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ', + '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0}; + MSIPROGID *progid; + MSIRECORD *row; + + if (!name) + return NULL; + + /* check for progids already loaded */ + LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) + { + if (!strcmpiW( progid->ProgID, name )) + { + TRACE("found progid %s (%p)\n",debugstr_w(name), progid ); + return progid; + } + } + + row = MSI_QueryGetRecord( package->db, query, name ); + if (!row) + return NULL; + + progid = load_progid(package, row); + msiobj_release(&row->hdr); + return progid; +} + +static MSICLASS *load_class( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSICLASS *cls; + DWORD i; + LPCWSTR buffer; + + /* fill in the data */ + + cls = msi_alloc_zero( sizeof(MSICLASS) ); + if (!cls) + return NULL; + + list_add_tail( &package->classes, &cls->entry ); + + cls->clsid = msi_dup_record_field( row, 1 ); + TRACE("loading class %s\n",debugstr_w(cls->clsid)); + cls->Context = msi_dup_record_field( row, 2 ); + buffer = MSI_RecordGetString(row,3); + cls->Component = msi_get_loaded_component( package, buffer ); + + cls->ProgIDText = msi_dup_record_field(row,4); + cls->ProgID = load_given_progid(package, cls->ProgIDText); + + cls->Description = msi_dup_record_field(row,5); + + buffer = MSI_RecordGetString(row,6); + if (buffer) + cls->AppID = load_given_appid(package, buffer); + + cls->FileTypeMask = msi_dup_record_field(row,7); + + if (!MSI_RecordIsNull(row,9)) + { + + INT icon_index = MSI_RecordGetInteger(row,9); + LPCWSTR FileName = MSI_RecordGetString(row,8); + LPWSTR FilePath; + static const WCHAR fmt[] = {'%','s',',','%','i',0}; + + FilePath = msi_build_icon_path(package, FileName); + + cls->IconPath = msi_alloc( (strlenW(FilePath)+5)* sizeof(WCHAR) ); + + sprintfW(cls->IconPath,fmt,FilePath,icon_index); + + msi_free(FilePath); + } + else + { + buffer = MSI_RecordGetString(row,8); + if (buffer) + cls->IconPath = msi_build_icon_path(package, buffer); + } + + if (!MSI_RecordIsNull(row,10)) + { + i = MSI_RecordGetInteger(row,10); + if (i != MSI_NULL_INTEGER && i > 0 && i < 4) + { + static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0}; + static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0}; + + switch(i) + { + case 1: + cls->DefInprocHandler = strdupW(ole2); + break; + case 2: + cls->DefInprocHandler32 = strdupW(ole32); + break; + case 3: + cls->DefInprocHandler = strdupW(ole2); + cls->DefInprocHandler32 = strdupW(ole32); + break; + } + } + else + { + cls->DefInprocHandler32 = msi_dup_record_field( row, 10 ); + msi_reduce_to_long_filename( cls->DefInprocHandler32 ); + } + } + buffer = MSI_RecordGetString(row,11); + deformat_string(package,buffer,&cls->Argument); + + buffer = MSI_RecordGetString(row,12); + cls->Feature = msi_get_loaded_feature(package, buffer); + + cls->Attributes = MSI_RecordGetInteger(row,13); + + return cls; +} + +/* + * the Class table has 3 primary keys. Generally it is only + * referenced through the first CLSID key. However when loading + * all of the classes we need to make sure we do not ignore rows + * with other Context and ComponentIndexs + */ +static MSICLASS *load_given_class(MSIPACKAGE *package, LPCWSTR classid) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ', + '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0}; + MSICLASS *cls; + MSIRECORD *row; + + if (!classid) + return NULL; + + /* check for classes already loaded */ + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + if (!strcmpiW( cls->clsid, classid )) + { + TRACE("found class %s (%p)\n",debugstr_w(classid), cls); + return cls; + } + } + + row = MSI_QueryGetRecord(package->db, query, classid); + if (!row) + return NULL; + + cls = load_class(package, row); + msiobj_release(&row->hdr); + return cls; +} + +static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR extension ); + +static MSIMIME *load_mime( MSIPACKAGE* package, MSIRECORD *row ) +{ + LPCWSTR extension; + MSIMIME *mt; + + /* fill in the data */ + + mt = msi_alloc_zero( sizeof(MSIMIME) ); + if (!mt) + return mt; + + mt->ContentType = msi_dup_record_field( row, 1 ); + TRACE("loading mime %s\n", debugstr_w(mt->ContentType)); + + extension = MSI_RecordGetString( row, 2 ); + mt->Extension = load_given_extension( package, extension ); + mt->suffix = strdupW( extension ); + + mt->clsid = msi_dup_record_field( row, 3 ); + mt->Class = load_given_class( package, mt->clsid ); + + list_add_tail( &package->mimes, &mt->entry ); + + return mt; +} + +static MSIMIME *load_given_mime( MSIPACKAGE *package, LPCWSTR mime ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','I','M','E','`',' ','W','H','E','R','E',' ', + '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ','\'','%','s','\'',0}; + MSIRECORD *row; + MSIMIME *mt; + + if (!mime) + return NULL; + + /* check for mime already loaded */ + LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) + { + if (!strcmpiW( mt->ContentType, mime )) + { + TRACE("found mime %s (%p)\n",debugstr_w(mime), mt); + return mt; + } + } + + row = MSI_QueryGetRecord(package->db, query, mime); + if (!row) + return NULL; + + mt = load_mime(package, row); + msiobj_release(&row->hdr); + return mt; +} + +static MSIEXTENSION *load_extension( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSIEXTENSION *ext; + LPCWSTR buffer; + + /* fill in the data */ + + ext = msi_alloc_zero( sizeof(MSIEXTENSION) ); + if (!ext) + return NULL; + + list_init( &ext->verbs ); + + list_add_tail( &package->extensions, &ext->entry ); + + ext->Extension = msi_dup_record_field( row, 1 ); + TRACE("loading extension %s\n", debugstr_w(ext->Extension)); + + buffer = MSI_RecordGetString( row, 2 ); + ext->Component = msi_get_loaded_component( package, buffer ); + + ext->ProgIDText = msi_dup_record_field( row, 3 ); + ext->ProgID = load_given_progid( package, ext->ProgIDText ); + + buffer = MSI_RecordGetString( row, 4 ); + ext->Mime = load_given_mime( package, buffer ); + + buffer = MSI_RecordGetString(row,5); + ext->Feature = msi_get_loaded_feature( package, buffer ); + + return ext; +} + +/* + * While the extension table has 2 primary keys, this function is only looking + * at the Extension key which is what is referenced as a foreign key + */ +static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR name ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','x','t','e','n','s','i','o','n','`',' ','W','H','E','R','E',' ', + '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ','\'','%','s','\'',0}; + MSIEXTENSION *ext; + MSIRECORD *row; + + if (!name) + return NULL; + + if (name[0] == '.') + name++; + + /* check for extensions already loaded */ + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + if (!strcmpiW( ext->Extension, name )) + { + TRACE("extension %s already loaded %p\n", debugstr_w(name), ext); + return ext; + } + } + + row = MSI_QueryGetRecord( package->db, query, name ); + if (!row) + return NULL; + + ext = load_extension(package, row); + msiobj_release(&row->hdr); + return ext; +} + +static UINT iterate_load_verb(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = param; + MSIVERB *verb; + LPCWSTR buffer; + MSIEXTENSION *extension; + + buffer = MSI_RecordGetString(row,1); + extension = load_given_extension( package, buffer ); + if (!extension) + { + ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer)); + return ERROR_SUCCESS; + } + + /* fill in the data */ + + verb = msi_alloc_zero( sizeof(MSIVERB) ); + if (!verb) + return ERROR_OUTOFMEMORY; + + verb->Verb = msi_dup_record_field(row,2); + TRACE("loading verb %s\n",debugstr_w(verb->Verb)); + verb->Sequence = MSI_RecordGetInteger(row,3); + + buffer = MSI_RecordGetString(row,4); + deformat_string(package,buffer,&verb->Command); + + buffer = MSI_RecordGetString(row,5); + deformat_string(package,buffer,&verb->Argument); + + /* associate the verb with the correct extension */ + list_add_tail( &extension->verbs, &verb->entry ); + + return ERROR_SUCCESS; +} + +static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param) +{ + MSICOMPONENT *comp; + LPCWSTR clsid; + LPCWSTR context; + LPCWSTR buffer; + MSIPACKAGE* package = param; + MSICLASS *cls; + BOOL match = FALSE; + + clsid = MSI_RecordGetString(rec,1); + context = MSI_RecordGetString(rec,2); + buffer = MSI_RecordGetString(rec,3); + comp = msi_get_loaded_component(package, buffer); + + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + if (strcmpiW( clsid, cls->clsid )) + continue; + if (strcmpW( context, cls->Context )) + continue; + if (comp == cls->Component) + { + match = TRUE; + break; + } + } + + if (!match) + load_class(package, rec); + + return ERROR_SUCCESS; +} + +static UINT load_all_classes( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ','`','C','l','a','s','s','`',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_all_classes, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param) +{ + MSICOMPONENT *comp; + LPCWSTR buffer; + LPCWSTR extension; + MSIPACKAGE* package = param; + BOOL match = FALSE; + MSIEXTENSION *ext; + + extension = MSI_RecordGetString(rec,1); + buffer = MSI_RecordGetString(rec,2); + comp = msi_get_loaded_component(package, buffer); + + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + if (strcmpiW(extension, ext->Extension)) + continue; + if (comp == ext->Component) + { + match = TRUE; + break; + } + } + + if (!match) + load_extension(package, rec); + + return ERROR_SUCCESS; +} + +static UINT load_all_extensions( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','E','x','t','e','n','s','i','o','n','`',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_all_extensions, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param) +{ + LPCWSTR buffer; + MSIPACKAGE* package = param; + + buffer = MSI_RecordGetString(rec,1); + load_given_progid(package,buffer); + return ERROR_SUCCESS; +} + +static UINT load_all_progids( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ','F','R','O','M',' ', + '`','P','r','o','g','I','d','`',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_all_progids, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT load_all_verbs( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','V','e','r','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_load_verb, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param) +{ + LPCWSTR buffer; + MSIPACKAGE* package = param; + + buffer = MSI_RecordGetString(rec,1); + load_given_mime(package,buffer); + return ERROR_SUCCESS; +} + +static UINT load_all_mimes( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','C','o','n','t','e','n','t','T','y','p','e','`',' ', + 'F','R','O','M',' ','`','M','I','M','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_all_mimes, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT load_classes_and_such( MSIPACKAGE *package ) +{ + UINT r; + + TRACE("Loading all the class info and related tables\n"); + + /* check if already loaded */ + if (!list_empty( &package->classes ) || + !list_empty( &package->mimes ) || + !list_empty( &package->extensions ) || + !list_empty( &package->progids )) return ERROR_SUCCESS; + + r = load_all_classes( package ); + if (r != ERROR_SUCCESS) return r; + + r = load_all_extensions( package ); + if (r != ERROR_SUCCESS) return r; + + r = load_all_progids( package ); + if (r != ERROR_SUCCESS) return r; + + /* these loads must come after the other loads */ + r = load_all_verbs( package ); + if (r != ERROR_SUCCESS) return r; + + return load_all_mimes( package ); +} + +static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid ) +{ + MSIPROGID *child; + + if (!progid) + return; + + if (progid->InstallMe) + return; + + progid->InstallMe = TRUE; + + /* all children if this is a parent also install */ + LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) + { + if (child->Parent == progid) + mark_progid_for_install( package, child ); + } +} + +static void mark_progid_for_uninstall( MSIPACKAGE *package, MSIPROGID *progid ) +{ + MSIPROGID *child; + + if (!progid) + return; + + if (!progid->InstallMe) + return; + + progid->InstallMe = FALSE; + + LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) + { + if (child->Parent == progid) + mark_progid_for_uninstall( package, child ); + } +} + +static void mark_mime_for_install( MSIMIME *mime ) +{ + if (!mime) + return; + mime->InstallMe = TRUE; +} + +static void mark_mime_for_uninstall( MSIMIME *mime ) +{ + if (!mime) + return; + mime->InstallMe = FALSE; +} + +static UINT register_appid(const MSIAPPID *appid, LPCWSTR app ) +{ + static const WCHAR szRemoteServerName[] = + {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0}; + static const WCHAR szLocalService[] = + {'L','o','c','a','l','S','e','r','v','i','c','e',0}; + static const WCHAR szService[] = + {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0}; + static const WCHAR szDLL[] = + {'D','l','l','S','u','r','r','o','g','a','t','e',0}; + static const WCHAR szActivate[] = + {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0}; + static const WCHAR szY[] = {'Y',0}; + static const WCHAR szRunAs[] = {'R','u','n','A','s',0}; + static const WCHAR szUser[] = + {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0}; + + HKEY hkey2,hkey3; + + RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2); + RegCreateKeyW( hkey2, appid->AppID, &hkey3 ); + RegCloseKey(hkey2); + msi_reg_set_val_str( hkey3, NULL, app ); + + if (appid->RemoteServerName) + msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName ); + + if (appid->LocalServer) + msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer ); + + if (appid->ServiceParameters) + msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters ); + + if (appid->DllSurrogate) + msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate ); + + if (appid->ActivateAtStorage) + msi_reg_set_val_str( hkey3, szActivate, szY ); + + if (appid->RunAsInteractiveUser) + msi_reg_set_val_str( hkey3, szRunAs, szUser ); + + RegCloseKey(hkey3); + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) +{ + static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0}; + const WCHAR *keypath; + MSIRECORD *uirow; + HKEY hkey, hkey2, hkey3; + MSICLASS *cls; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + if (is_64bit && package->platform == PLATFORM_INTEL) + keypath = szWow6432NodeCLSID; + else + keypath = szCLSID; + + if (RegCreateKeyW(HKEY_CLASSES_ROOT, keypath, &hkey) != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + MSICOMPONENT *comp; + MSIFILE *file; + DWORD size; + LPWSTR argument; + MSIFEATURE *feature; + + comp = cls->Component; + if ( !comp ) + continue; + + if (!comp->Enabled) + { + TRACE("component is disabled\n"); + continue; + } + + feature = cls->Feature; + if (!feature) + continue; + + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action != INSTALLSTATE_LOCAL && + feature->Action != INSTALLSTATE_ADVERTISED ) + { + TRACE("feature %s not scheduled for installation, skipping registration of class %s\n", + debugstr_w(feature->Feature), debugstr_w(cls->clsid)); + continue; + } + + if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath ))) + { + TRACE("COM server not provided, skipping class %s\n", debugstr_w(cls->clsid)); + continue; + } + TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls); + + cls->Installed = TRUE; + mark_progid_for_install( package, cls->ProgID ); + + RegCreateKeyW( hkey, cls->clsid, &hkey2 ); + + if (cls->Description) + msi_reg_set_val_str( hkey2, NULL, cls->Description ); + + RegCreateKeyW( hkey2, cls->Context, &hkey3 ); + + /* + * FIXME: Implement install on demand (advertised components). + * + * ole32.dll should call msi.MsiProvideComponentFromDescriptor() + * when it needs an InProcServer that doesn't exist. + * The component advertise string should be in the "InProcServer" value. + */ + size = lstrlenW( file->TargetPath )+1; + if (cls->Argument) + size += lstrlenW(cls->Argument)+1; + + argument = msi_alloc( size * sizeof(WCHAR) ); + lstrcpyW( argument, file->TargetPath ); + + if (cls->Argument) + { + lstrcatW( argument, szSpace ); + lstrcatW( argument, cls->Argument ); + } + + msi_reg_set_val_str( hkey3, NULL, argument ); + msi_free(argument); + + RegCloseKey(hkey3); + + if (cls->ProgID || cls->ProgIDText) + { + LPCWSTR progid; + + if (cls->ProgID) + progid = cls->ProgID->ProgID; + else + progid = cls->ProgIDText; + + msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid ); + + if (cls->ProgID && cls->ProgID->VersionInd) + { + msi_reg_set_subkey_val( hkey2, szVIProgID, NULL, + cls->ProgID->VersionInd->ProgID ); + } + } + + if (cls->AppID) + { + MSIAPPID *appid = cls->AppID; + msi_reg_set_val_str( hkey2, szAppID, appid->AppID ); + register_appid( appid, cls->Description ); + } + + if (cls->IconPath) + msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath ); + + if (cls->DefInprocHandler) + msi_reg_set_subkey_val( hkey2, szInprocHandler, NULL, cls->DefInprocHandler ); + + if (cls->DefInprocHandler32) + msi_reg_set_subkey_val( hkey2, szInprocHandler32, NULL, cls->DefInprocHandler32 ); + + RegCloseKey(hkey2); + + /* if there is a FileTypeMask, register the FileType */ + if (cls->FileTypeMask) + { + LPWSTR ptr, ptr2; + LPWSTR keyname; + INT index = 0; + ptr = cls->FileTypeMask; + while (ptr && *ptr) + { + ptr2 = strchrW(ptr,';'); + if (ptr2) + *ptr2 = 0; + keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR)); + sprintfW( keyname, szFileType_fmt, cls->clsid, index ); + + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr ); + msi_free(keyname); + + if (ptr2) + ptr = ptr2+1; + else + ptr = NULL; + + index ++; + } + } + + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW( uirow, 1, cls->clsid ); + msi_ui_actiondata( package, szRegisterClassInfo, uirow ); + msiobj_release(&uirow->hdr); + } + RegCloseKey(hkey); + return ERROR_SUCCESS; +} + +UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package ) +{ + static const WCHAR szFileType[] = {'F','i','l','e','T','y','p','e','\\',0}; + const WCHAR *keypath; + MSIRECORD *uirow; + MSICLASS *cls; + HKEY hkey, hkey2; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + if (is_64bit && package->platform == PLATFORM_INTEL) + keypath = szWow6432NodeCLSID; + else + keypath = szCLSID; + + if (RegOpenKeyW( HKEY_CLASSES_ROOT, keypath, &hkey ) != ERROR_SUCCESS) + return ERROR_SUCCESS; + + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + MSIFEATURE *feature; + MSICOMPONENT *comp; + LPWSTR filetype; + LONG res; + + comp = cls->Component; + if (!comp) + continue; + + if (!comp->Enabled) + { + TRACE("component is disabled\n"); + continue; + } + + feature = cls->Feature; + if (!feature) + continue; + + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action != INSTALLSTATE_ABSENT) + { + TRACE("feature %s not scheduled for removal, skipping unregistration of class %s\n", + debugstr_w(feature->Feature), debugstr_w(cls->clsid)); + continue; + } + TRACE("Unregistering class %s (%p)\n", debugstr_w(cls->clsid), cls); + + cls->Installed = FALSE; + mark_progid_for_uninstall( package, cls->ProgID ); + + res = RegDeleteTreeW( hkey, cls->clsid ); + if (res != ERROR_SUCCESS) + WARN("Failed to delete class key %d\n", res); + + if (cls->AppID) + { + res = RegOpenKeyW( HKEY_CLASSES_ROOT, szAppID, &hkey2 ); + if (res == ERROR_SUCCESS) + { + res = RegDeleteKeyW( hkey2, cls->AppID->AppID ); + if (res != ERROR_SUCCESS) + WARN("Failed to delete appid key %d\n", res); + RegCloseKey( hkey2 ); + } + } + if (cls->FileTypeMask) + { + filetype = msi_alloc( (strlenW( szFileType ) + strlenW( cls->clsid ) + 1) * sizeof(WCHAR) ); + if (filetype) + { + strcpyW( filetype, szFileType ); + strcatW( filetype, cls->clsid ); + res = RegDeleteTreeW( HKEY_CLASSES_ROOT, filetype ); + msi_free( filetype ); + + if (res != ERROR_SUCCESS) + WARN("Failed to delete file type %d\n", res); + } + } + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, cls->clsid ); + msi_ui_actiondata( package, szUnregisterClassInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + RegCloseKey( hkey ); + return ERROR_SUCCESS; +} + +static LPCWSTR get_clsid_of_progid( const MSIPROGID *progid ) +{ + while (progid) + { + if (progid->Class) + return progid->Class->clsid; + if (progid->Parent == progid) + break; + progid = progid->Parent; + } + return NULL; +} + +static UINT register_progid( const MSIPROGID* progid ) +{ + static const WCHAR szCurVer[] = {'C','u','r','V','e','r',0}; + HKEY hkey = 0; + UINT rc; + + rc = RegCreateKeyW( HKEY_CLASSES_ROOT, progid->ProgID, &hkey ); + if (rc == ERROR_SUCCESS) + { + LPCWSTR clsid = get_clsid_of_progid( progid ); + + if (clsid) + msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid ); + else + TRACE("%s has no class\n", debugstr_w( progid->ProgID ) ); + + if (progid->Description) + msi_reg_set_val_str( hkey, NULL, progid->Description ); + + if (progid->IconPath) + msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath ); + + /* write out the current version */ + if (progid->CurVer) + msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID ); + + RegCloseKey(hkey); + } + else + ERR("failed to create key %s\n", debugstr_w( progid->ProgID ) ); + + return rc; +} + +UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) +{ + MSIPROGID *progid; + MSIRECORD *uirow; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) + { + /* check if this progid is to be installed */ + if (progid->Class && progid->Class->Installed) + progid->InstallMe = TRUE; + + if (!progid->InstallMe) + { + TRACE("progid %s not scheduled to be installed\n", + debugstr_w(progid->ProgID)); + continue; + } + + TRACE("Registering progid %s\n", debugstr_w(progid->ProgID)); + + register_progid( progid ); + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, progid->ProgID ); + msi_ui_actiondata( package, szRegisterProgIdInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package ) +{ + MSIPROGID *progid; + MSIRECORD *uirow; + LONG res; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) + { + /* check if this progid is to be removed */ + if (progid->Class && !progid->Class->Installed) + progid->InstallMe = FALSE; + + if (progid->InstallMe) + { + TRACE("progid %s not scheduled to be removed\n", debugstr_w(progid->ProgID)); + continue; + } + + TRACE("Unregistering progid %s\n", debugstr_w(progid->ProgID)); + + res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid->ProgID ); + if (res != ERROR_SUCCESS) + TRACE("Failed to delete progid key %d\n", res); + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, progid->ProgID ); + msi_ui_actiondata( package, szUnregisterProgIdInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid, + MSICOMPONENT* component, const MSIEXTENSION* extension, + MSIVERB* verb, INT* Sequence ) +{ + LPWSTR keyname; + HKEY key; + static const WCHAR szShell[] = {'s','h','e','l','l',0}; + static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0}; + static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0}; + static const WCHAR fmt2[] = {'\"','%','s','\"',0}; + LPWSTR command; + DWORD size; + LPWSTR advertise; + + keyname = msi_build_directory_name(4, progid, szShell, verb->Verb, szCommand); + + TRACE("Making Key %s\n",debugstr_w(keyname)); + RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key); + size = strlenW(component->FullKeypath); + if (verb->Argument) + size += strlenW(verb->Argument); + size += 4; + + command = msi_alloc(size * sizeof (WCHAR)); + if (verb->Argument) + sprintfW(command, fmt, component->FullKeypath, verb->Argument); + else + sprintfW(command, fmt2, component->FullKeypath); + + msi_reg_set_val_str( key, NULL, command ); + msi_free(command); + + advertise = msi_create_component_advertise_string(package, component, + extension->Feature->Feature); + size = strlenW(advertise); + + if (verb->Argument) + size += strlenW(verb->Argument); + size += 4; + + command = msi_alloc_zero(size * sizeof (WCHAR)); + + strcpyW(command,advertise); + if (verb->Argument) + { + strcatW(command,szSpace); + strcatW(command,verb->Argument); + } + + msi_reg_set_val_multi_str( key, szCommand, command ); + + RegCloseKey(key); + msi_free(keyname); + msi_free(advertise); + msi_free(command); + + if (verb->Command) + { + keyname = msi_build_directory_name( 3, progid, szShell, verb->Verb ); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command ); + msi_free(keyname); + } + + if (verb->Sequence != MSI_NULL_INTEGER) + { + if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence) + { + *Sequence = verb->Sequence; + keyname = msi_build_directory_name( 2, progid, szShell ); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb ); + msi_free(keyname); + } + } + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package) +{ + static const WCHAR szContentType[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0}; + HKEY hkey = NULL; + MSIEXTENSION *ext; + MSIRECORD *uirow; + BOOL install_on_demand = TRUE; + LONG res; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + /* We need to set install_on_demand based on if the shell handles advertised + * shortcuts and the like. Because Mike McCormack is working on this i am + * going to default to TRUE + */ + + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + LPWSTR extension; + MSIFEATURE *feature; + + if (!ext->Component) + continue; + + if (!ext->Component->Enabled) + { + TRACE("component is disabled\n"); + continue; + } + + feature = ext->Feature; + if (!feature) + continue; + + /* + * yes. MSDN says that these are based on _Feature_ not on + * Component. So verify the feature is to be installed + */ + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action != INSTALLSTATE_LOCAL && + !(install_on_demand && feature->Action == INSTALLSTATE_ADVERTISED)) + { + TRACE("feature %s not scheduled for installation, skipping registration of extension %s\n", + debugstr_w(feature->Feature), debugstr_w(ext->Extension)); + continue; + } + TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext); + + ext->Installed = TRUE; + + /* this is only registered if the extension has at least 1 verb + * according to MSDN + */ + if (ext->ProgID && !list_empty( &ext->verbs ) ) + mark_progid_for_install( package, ext->ProgID ); + + mark_mime_for_install(ext->Mime); + + extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) ); + if (extension) + { + extension[0] = '.'; + strcpyW( extension + 1, ext->Extension ); + res = RegCreateKeyW( HKEY_CLASSES_ROOT, extension, &hkey ); + msi_free( extension ); + if (res != ERROR_SUCCESS) + WARN("Failed to create extension key %d\n", res); + } + + if (ext->Mime) + msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType ); + + if (ext->ProgID || ext->ProgIDText) + { + static const WCHAR szSN[] = + {'\\','S','h','e','l','l','N','e','w',0}; + HKEY hkey2; + LPWSTR newkey; + LPCWSTR progid; + MSIVERB *verb; + INT Sequence = MSI_NULL_INTEGER; + + if (ext->ProgID) + progid = ext->ProgID->ProgID; + else + progid = ext->ProgIDText; + + msi_reg_set_val_str( hkey, NULL, progid ); + + newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR)); + + strcpyW(newkey,progid); + strcatW(newkey,szSN); + RegCreateKeyW(hkey,newkey,&hkey2); + RegCloseKey(hkey2); + + msi_free(newkey); + + /* do all the verbs */ + LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry ) + { + register_verb( package, progid, ext->Component, + ext, verb, &Sequence); + } + } + + RegCloseKey(hkey); + + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW( uirow, 1, ext->Extension ); + msi_ui_actiondata( package, szRegisterExtensionInfo, uirow ); + msiobj_release(&uirow->hdr); + } + return ERROR_SUCCESS; +} + +UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package ) +{ + MSIEXTENSION *ext; + MSIRECORD *uirow; + LONG res; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + LPWSTR extension; + MSIFEATURE *feature; + + if (!ext->Component) + continue; + + if (!ext->Component->Enabled) + { + TRACE("component is disabled\n"); + continue; + } + + feature = ext->Feature; + if (!feature) + continue; + + feature->Action = msi_get_feature_action( package, feature ); + if (feature->Action != INSTALLSTATE_ABSENT) + { + TRACE("feature %s not scheduled for removal, skipping unregistration of extension %s\n", + debugstr_w(feature->Feature), debugstr_w(ext->Extension)); + continue; + } + TRACE("Unregistering extension %s\n", debugstr_w(ext->Extension)); + + ext->Installed = FALSE; + + if (ext->ProgID && !list_empty( &ext->verbs )) + mark_progid_for_uninstall( package, ext->ProgID ); + + mark_mime_for_uninstall( ext->Mime ); + + extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) ); + if (extension) + { + extension[0] = '.'; + strcpyW( extension + 1, ext->Extension ); + res = RegDeleteTreeW( HKEY_CLASSES_ROOT, extension ); + msi_free( extension ); + if (res != ERROR_SUCCESS) + WARN("Failed to delete extension key %d\n", res); + } + + if (ext->ProgID || ext->ProgIDText) + { + static const WCHAR shellW[] = {'\\','s','h','e','l','l',0}; + LPCWSTR progid; + LPWSTR progid_shell; + + if (ext->ProgID) + progid = ext->ProgID->ProgID; + else + progid = ext->ProgIDText; + + progid_shell = msi_alloc( (strlenW( progid ) + strlenW( shellW ) + 1) * sizeof(WCHAR) ); + if (progid_shell) + { + strcpyW( progid_shell, progid ); + strcatW( progid_shell, shellW ); + res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid_shell ); + msi_free( progid_shell ); + if (res != ERROR_SUCCESS) + WARN("Failed to delete shell key %d\n", res); + RegDeleteKeyW( HKEY_CLASSES_ROOT, progid ); + } + } + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, ext->Extension ); + msi_ui_actiondata( package, szUnregisterExtensionInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package) +{ + static const WCHAR szExtension[] = {'E','x','t','e','n','s','i','o','n',0}; + MSIRECORD *uirow; + MSIMIME *mt; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) + { + LPWSTR extension, key; + + /* + * check if the MIME is to be installed. Either as requested by an + * extension or Class + */ + mt->InstallMe = (mt->InstallMe || + (mt->Class && mt->Class->Installed) || + (mt->Extension && mt->Extension->Installed)); + + if (!mt->InstallMe) + { + TRACE("MIME %s not scheduled to be installed\n", debugstr_w(mt->ContentType)); + continue; + } + + TRACE("Registering MIME type %s\n", debugstr_w(mt->ContentType)); + + extension = msi_alloc( (strlenW( mt->Extension->Extension ) + 2) * sizeof(WCHAR) ); + key = msi_alloc( (strlenW( mt->ContentType ) + strlenW( szMIMEDatabase ) + 1) * sizeof(WCHAR) ); + + if (extension && key) + { + extension[0] = '.'; + strcpyW( extension + 1, mt->Extension->Extension ); + + strcpyW( key, szMIMEDatabase ); + strcatW( key, mt->ContentType ); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExtension, extension ); + + if (mt->clsid) + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szCLSID, mt->clsid ); + } + msi_free( extension ); + msi_free( key ); + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, mt->ContentType ); + MSI_RecordSetStringW( uirow, 2, mt->suffix ); + msi_ui_actiondata( package, szRegisterMIMEInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} + +UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package ) +{ + MSIRECORD *uirow; + MSIMIME *mime; + UINT r; + + r = load_classes_and_such( package ); + if (r != ERROR_SUCCESS) + return r; + + LIST_FOR_EACH_ENTRY( mime, &package->mimes, MSIMIME, entry ) + { + LONG res; + LPWSTR mime_key; + + mime->InstallMe = (mime->InstallMe || + (mime->Class && mime->Class->Installed) || + (mime->Extension && mime->Extension->Installed)); + + if (mime->InstallMe) + { + TRACE("MIME %s not scheduled to be removed\n", debugstr_w(mime->ContentType)); + continue; + } + + TRACE("Unregistering MIME type %s\n", debugstr_w(mime->ContentType)); + + mime_key = msi_alloc( (strlenW( szMIMEDatabase ) + strlenW( mime->ContentType ) + 1) * sizeof(WCHAR) ); + if (mime_key) + { + strcpyW( mime_key, szMIMEDatabase ); + strcatW( mime_key, mime->ContentType ); + res = RegDeleteKeyW( HKEY_CLASSES_ROOT, mime_key ); + if (res != ERROR_SUCCESS) + WARN("Failed to delete MIME key %d\n", res); + msi_free( mime_key ); + } + + uirow = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( uirow, 1, mime->ContentType ); + MSI_RecordSetStringW( uirow, 2, mime->suffix ); + msi_ui_actiondata( package, szUnregisterMIMEInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + return ERROR_SUCCESS; +} diff --git a/libmsi/cond.y b/libmsi/cond.y new file mode 100644 index 0000000..97bb002 --- /dev/null +++ b/libmsi/cond.y @@ -0,0 +1,898 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2003 Mike McCormack 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 + */ + +#define COBJMACROS + +#include "config.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "oleauto.h" + +#include "msipriv.h" +#include "msiserver.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/list.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +static int cond_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_yyinput +{ + MSIPACKAGE *package; + LPCWSTR str; + INT n; + MSICONDITION result; + struct list mem; +} COND_input; + +struct cond_str { + LPCWSTR data; + INT len; +}; + +static LPWSTR COND_GetString( COND_input *info, const struct cond_str *str ); +static LPWSTR COND_GetLiteral( COND_input *info, const struct cond_str *str ); +static int cond_lex( void *COND_lval, COND_input *info); + +static void *cond_alloc( COND_input *cond, unsigned int sz ); +static void *cond_track_mem( COND_input *cond, void *ptr, unsigned int sz ); +static void cond_free( void *ptr ); + +static INT compare_int( INT a, INT operator, INT b ); +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b, BOOL convert ); + +static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b, BOOL convert ) +{ + INT r; + + r = compare_string( a, op, b, convert ); + cond_free( a ); + cond_free( b ); + return r; +} + +static BOOL num_from_prop( LPCWSTR p, INT *val ) +{ + INT ret = 0, sign = 1; + + if (!p) + return FALSE; + if (*p == '-') + { + sign = -1; + p++; + } + if (!*p) + return FALSE; + while (*p) + { + if( *p < '0' || *p > '9' ) + return FALSE; + ret = ret*10 + (*p - '0'); + p++; + } + *val = ret*sign; + return TRUE; +} + +%} + +%pure-parser + +%union +{ + struct cond_str str; + LPWSTR string; + INT value; +} + +%token COND_SPACE COND_EOF +%token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV +%token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE +%token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE +%token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS +%token COND_ILHS COND_IRHS COND_LHS COND_RHS +%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM +%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER + +%nonassoc COND_ERROR COND_EOF + +%type <value> expression boolean_term boolean_factor +%type <value> value_i integer operator +%type <string> identifier symbol_s value_s literal + +%% + +condition: + expression + { + COND_input* cond = (COND_input*) info; + cond->result = $1; + } + | /* empty */ + { + COND_input* cond = (COND_input*) info; + cond->result = MSICONDITION_NONE; + } + ; + +expression: + boolean_term + { + $$ = $1; + } + | expression COND_OR boolean_term + { + $$ = $1 || $3; + } + | expression COND_IMP boolean_term + { + $$ = !$1 || $3; + } + | expression COND_XOR boolean_term + { + $$ = ( $1 || $3 ) && !( $1 && $3 ); + } + | expression COND_EQV boolean_term + { + $$ = ( $1 && $3 ) || ( !$1 && !$3 ); + } + ; + +boolean_term: + boolean_factor + { + $$ = $1; + } + | boolean_term COND_AND boolean_factor + { + $$ = $1 && $3; + } + ; + +boolean_factor: + COND_NOT boolean_factor + { + $$ = $2 ? 0 : 1; + } + | value_i + { + $$ = $1 ? 1 : 0; + } + | value_s + { + $$ = ($1 && $1[0]) ? 1 : 0; + cond_free( $1 ); + } + | value_i operator value_i + { + $$ = compare_int( $1, $2, $3 ); + } + | symbol_s operator value_i + { + int num; + if (num_from_prop( $1, &num )) + $$ = compare_int( num, $2, $3 ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + cond_free( $1 ); + } + | value_i operator symbol_s + { + int num; + if (num_from_prop( $3, &num )) + $$ = compare_int( $1, $2, num ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + cond_free( $3 ); + } + | symbol_s operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | symbol_s operator literal + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | literal operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3, TRUE ); + } + | literal operator literal + { + $$ = compare_and_free_strings( $1, $2, $3, FALSE ); + } + | literal operator value_i + { + $$ = 0; + cond_free( $1 ); + } + | value_i operator literal + { + $$ = 0; + cond_free( $3 ); + } + | COND_LPAR expression COND_RPAR + { + $$ = $2; + } + ; + +operator: + /* common functions */ + COND_EQ { $$ = COND_EQ; } + | COND_NE { $$ = COND_NE; } + | COND_LT { $$ = COND_LT; } + | COND_GT { $$ = COND_GT; } + | COND_LE { $$ = COND_LE; } + | COND_GE { $$ = COND_GE; } + | COND_SS { $$ = COND_SS; } + | COND_IEQ { $$ = COND_IEQ; } + | COND_INE { $$ = COND_INE; } + | COND_ILT { $$ = COND_ILT; } + | COND_IGT { $$ = COND_IGT; } + | COND_ILE { $$ = COND_ILE; } + | COND_IGE { $$ = COND_IGE; } + | COND_ISS { $$ = COND_ISS; } + | COND_LHS { $$ = COND_LHS; } + | COND_RHS { $$ = COND_RHS; } + | COND_ILHS { $$ = COND_ILHS; } + | COND_IRHS { $$ = COND_IRHS; } + ; + +value_s: + symbol_s + { + $$ = $1; + } + | literal + { + $$ = $1; + } + ; + +literal: + COND_LITER + { + COND_input* cond = (COND_input*) info; + $$ = COND_GetLiteral( cond, &$1 ); + if( !$$ ) + YYABORT; + } + ; + +value_i: + integer + { + $$ = $1; + } + | COND_DOLLARS identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = action; + cond_free( $2 ); + } + | COND_QUESTION identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = install; + cond_free( $2 ); + } + | COND_AMPER identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + if (action == INSTALLSTATE_UNKNOWN) + $$ = MSICONDITION_FALSE; + else + $$ = action; + + cond_free( $2 ); + } + | COND_EXCLAM identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + $$ = install; + cond_free( $2 ); + } + ; + +symbol_s: + identifier + { + COND_input* cond = (COND_input*) info; + UINT len; + + $$ = msi_dup_property( cond->package->db, $1 ); + if ($$) + { + len = (lstrlenW($$) + 1) * sizeof (WCHAR); + $$ = cond_track_mem( cond, $$, len ); + } + cond_free( $1 ); + } + | COND_PERCENT identifier + { + COND_input* cond = (COND_input*) info; + UINT len = GetEnvironmentVariableW( $2, NULL, 0 ); + $$ = NULL; + if (len++) + { + $$ = cond_alloc( cond, len*sizeof (WCHAR) ); + if( !$$ ) + YYABORT; + GetEnvironmentVariableW( $2, $$, len ); + } + cond_free( $2 ); + } + ; + +identifier: + COND_IDENT + { + COND_input* cond = (COND_input*) info; + $$ = COND_GetString( cond, &$1 ); + if( !$$ ) + YYABORT; + } + ; + +integer: + COND_NUMBER + { + COND_input* cond = (COND_input*) info; + LPWSTR szNum = COND_GetString( cond, &$1 ); + if( !szNum ) + YYABORT; + $$ = atoiW( szNum ); + cond_free( szNum ); + } + ; + +%% + + +static int COND_IsAlpha( WCHAR x ) +{ + return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) || + ( ( x >= 'a' ) && ( x <= 'z' ) ) || + ( ( x == '_' ) ) ); +} + +static int COND_IsNumber( WCHAR x ) +{ + return( (( x >= '0' ) && ( x <= '9' )) || (x =='-') || (x =='.') ); +} + +static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub ) +{ + LPWSTR strlower, sublower, r; + strlower = CharLowerW( strdupW( str ) ); + sublower = CharLowerW( strdupW( sub ) ); + r = strstrW( strlower, sublower ); + if (r) + r = (LPWSTR)str + (r - strlower); + msi_free( strlower ); + msi_free( sublower ); + return r; +} + +static BOOL str_is_number( LPCWSTR str ) +{ + int i; + + if (!*str) + return FALSE; + + for (i = 0; i < lstrlenW( str ); i++) + if (!isdigitW(str[i])) + return FALSE; + + return TRUE; +} + +static INT compare_substring( LPCWSTR a, INT operator, LPCWSTR b ) +{ + int lhs, rhs; + + /* substring operators return 0 if LHS is missing */ + if (!a || !*a) + return 0; + + /* substring operators return 1 if RHS is missing */ + if (!b || !*b) + return 1; + + /* if both strings contain only numbers, use integer comparison */ + lhs = atoiW(a); + rhs = atoiW(b); + if (str_is_number(a) && str_is_number(b)) + return compare_int( lhs, operator, rhs ); + + switch (operator) + { + case COND_SS: + return strstrW( a, b ) != 0; + case COND_ISS: + return strstriW( a, b ) != 0; + case COND_LHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpW( a, b, r ); + } + case COND_RHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpW( a + (l - r), b, r ); + } + case COND_ILHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpiW( a, b, r ); + } + case COND_IRHS: + { + int l = strlenW( a ); + int r = strlenW( b ); + if (r > l) return 0; + return !strncmpiW( a + (l - r), b, r ); + } + default: + ERR("invalid substring operator\n"); + return 0; + } + return 0; +} + +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b, BOOL convert ) +{ + if (operator >= COND_SS && operator <= COND_RHS) + return compare_substring( a, operator, b ); + + /* null and empty string are equivalent */ + if (!a) a = szEmpty; + if (!b) b = szEmpty; + + if (convert && str_is_number(a) && str_is_number(b)) + return compare_int( atoiW(a), operator, atoiW(b) ); + + /* a or b may be NULL */ + switch (operator) + { + case COND_LT: + return strcmpW( a, b ) < 0; + case COND_GT: + return strcmpW( a, b ) > 0; + case COND_EQ: + return strcmpW( a, b ) == 0; + case COND_NE: + return strcmpW( a, b ) != 0; + case COND_GE: + return strcmpW( a, b ) >= 0; + case COND_LE: + return strcmpW( a, b ) <= 0; + case COND_ILT: + return strcmpiW( a, b ) < 0; + case COND_IGT: + return strcmpiW( a, b ) > 0; + case COND_IEQ: + return strcmpiW( a, b ) == 0; + case COND_INE: + return strcmpiW( a, b ) != 0; + case COND_IGE: + return strcmpiW( a, b ) >= 0; + case COND_ILE: + return strcmpiW( a, b ) <= 0; + default: + ERR("invalid string operator\n"); + return 0; + } + return 0; +} + + +static INT compare_int( INT a, INT operator, INT b ) +{ + switch (operator) + { + case COND_LT: + case COND_ILT: + return a < b; + case COND_GT: + case COND_IGT: + return a > b; + case COND_EQ: + case COND_IEQ: + return a == b; + case COND_NE: + case COND_INE: + return a != b; + case COND_GE: + case COND_IGE: + return a >= b; + case COND_LE: + case COND_ILE: + return a <= b; + case COND_SS: + case COND_ISS: + return ( a & b ) ? 1 : 0; + case COND_RHS: + return ( ( a & 0xffff ) == b ) ? 1 : 0; + case COND_LHS: + return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0; + default: + ERR("invalid integer operator\n"); + return 0; + } + return 0; +} + + +static int COND_IsIdent( WCHAR x ) +{ + return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) + || ( x == '#' ) || (x == '.') ); +} + +static int COND_GetOperator( COND_input *cond ) +{ + static const struct { + const WCHAR str[4]; + int id; + } table[] = { + { {'~','<','=',0}, COND_ILE }, + { {'~','>','<',0}, COND_ISS }, + { {'~','>','>',0}, COND_IRHS }, + { {'~','<','>',0}, COND_INE }, + { {'~','>','=',0}, COND_IGE }, + { {'~','<','<',0}, COND_ILHS }, + { {'~','=',0}, COND_IEQ }, + { {'~','<',0}, COND_ILT }, + { {'~','>',0}, COND_IGT }, + { {'>','=',0}, COND_GE }, + { {'>','<',0}, COND_SS }, + { {'<','<',0}, COND_LHS }, + { {'<','>',0}, COND_NE }, + { {'<','=',0}, COND_LE }, + { {'>','>',0}, COND_RHS }, + { {'>',0}, COND_GT }, + { {'<',0}, COND_LT }, + { {0}, 0 } + }; + LPCWSTR p = &cond->str[cond->n]; + int i = 0, len; + + while ( 1 ) + { + len = lstrlenW( table[i].str ); + if ( !len || 0 == strncmpW( table[i].str, p, len ) ) + break; + i++; + } + cond->n += len; + return table[i].id; +} + +static int COND_GetOne( struct cond_str *str, COND_input *cond ) +{ + int rc, len = 1; + WCHAR ch; + + str->data = &cond->str[cond->n]; + + ch = str->data[0]; + + switch( ch ) + { + case 0: return 0; + case '(': rc = COND_LPAR; break; + case ')': rc = COND_RPAR; break; + case '&': rc = COND_AMPER; break; + case '!': rc = COND_EXCLAM; break; + case '$': rc = COND_DOLLARS; break; + case '?': rc = COND_QUESTION; break; + case '%': rc = COND_PERCENT; break; + case ' ': rc = COND_SPACE; break; + case '=': rc = COND_EQ; break; + + case '~': + case '<': + case '>': + rc = COND_GetOperator( cond ); + if (!rc) + rc = COND_ERROR; + return rc; + default: + rc = 0; + } + + if ( rc ) + { + cond->n += len; + return rc; + } + + if (ch == '"' ) + { + LPCWSTR p = strchrW( str->data + 1, '"' ); + if (!p) return COND_ERROR; + len = p - str->data + 1; + rc = COND_LITER; + } + else if( COND_IsAlpha( ch ) ) + { + static const WCHAR szNot[] = {'N','O','T',0}; + static const WCHAR szAnd[] = {'A','N','D',0}; + static const WCHAR szXor[] = {'X','O','R',0}; + static const WCHAR szEqv[] = {'E','Q','V',0}; + static const WCHAR szImp[] = {'I','M','P',0}; + static const WCHAR szOr[] = {'O','R',0}; + + while( COND_IsIdent( str->data[len] ) ) + len++; + rc = COND_IDENT; + + if ( len == 3 ) + { + if ( !strncmpiW( str->data, szNot, len ) ) + rc = COND_NOT; + else if( !strncmpiW( str->data, szAnd, len ) ) + rc = COND_AND; + else if( !strncmpiW( str->data, szXor, len ) ) + rc = COND_XOR; + else if( !strncmpiW( str->data, szEqv, len ) ) + rc = COND_EQV; + else if( !strncmpiW( str->data, szImp, len ) ) + rc = COND_IMP; + } + else if( (len == 2) && !strncmpiW( str->data, szOr, len ) ) + rc = COND_OR; + } + else if( COND_IsNumber( ch ) ) + { + while( COND_IsNumber( str->data[len] ) ) + len++; + rc = COND_NUMBER; + } + else + { + ERR("Got unknown character %c(%x)\n",ch,ch); + return COND_ERROR; + } + + cond->n += len; + str->len = len; + + return rc; +} + +static int cond_lex( void *COND_lval, COND_input *cond ) +{ + int rc; + struct cond_str *str = COND_lval; + + do { + rc = COND_GetOne( str, cond ); + } while (rc == COND_SPACE); + + return rc; +} + +static LPWSTR COND_GetString( COND_input *cond, const struct cond_str *str ) +{ + LPWSTR ret; + + ret = cond_alloc( cond, (str->len+1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data, str->len * sizeof(WCHAR)); + ret[str->len]=0; + } + TRACE("Got identifier %s\n",debugstr_w(ret)); + return ret; +} + +static LPWSTR COND_GetLiteral( COND_input *cond, const struct cond_str *str ) +{ + LPWSTR ret; + + ret = cond_alloc( cond, (str->len-1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) ); + ret[str->len - 2]=0; + } + TRACE("Got literal %s\n",debugstr_w(ret)); + return ret; +} + +static void *cond_alloc( COND_input *cond, unsigned int sz ) +{ + struct list *mem; + + mem = msi_alloc( sizeof (struct list) + sz ); + if( !mem ) + return NULL; + + list_add_head( &(cond->mem), mem ); + return mem + 1; +} + +static void *cond_track_mem( COND_input *cond, void *ptr, unsigned int sz ) +{ + void *new_ptr; + + if( !ptr ) + return ptr; + + new_ptr = cond_alloc( cond, sz ); + if( !new_ptr ) + { + msi_free( ptr ); + return NULL; + } + + memcpy( new_ptr, ptr, sz ); + msi_free( ptr ); + return new_ptr; +} + +static void cond_free( void *ptr ) +{ + struct list *mem = (struct list *)ptr - 1; + + if( ptr ) + { + list_remove( mem ); + msi_free( mem ); + } +} + +static int cond_error(const char *str) +{ + TRACE("%s\n", str ); + return 0; +} + +MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition ) +{ + COND_input cond; + MSICONDITION r; + struct list *mem, *safety; + + TRACE("%s\n", debugstr_w( szCondition ) ); + + if (szCondition == NULL) return MSICONDITION_NONE; + + cond.package = package; + cond.str = szCondition; + cond.n = 0; + cond.result = MSICONDITION_ERROR; + + list_init( &cond.mem ); + + if ( !cond_parse( &cond ) ) + r = cond.result; + else + r = MSICONDITION_ERROR; + + LIST_FOR_EACH_SAFE( mem, safety, &cond.mem ) + { + /* The tracked memory lives directly after the list struct */ + void *ptr = mem + 1; + if ( r != MSICONDITION_ERROR ) + WARN( "condition parser failed to free up some memory: %p\n", ptr ); + cond_free( ptr ); + } + + TRACE("%i <- %s\n", r, debugstr_w(szCondition)); + return r; +} + +MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition ) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package ) + { + HRESULT hr; + BSTR condition; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return MSICONDITION_ERROR; + + condition = SysAllocString( szCondition ); + if (!condition) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_EvaluateCondition( remote_package, condition ); + + SysFreeString( condition ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_EvaluateConditionW( package, szCondition ); + msiobj_release( &package->hdr ); + return ret; +} + +MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition ) +{ + LPWSTR szwCond = NULL; + MSICONDITION r; + + szwCond = strdupAtoW( szCondition ); + if( szCondition && !szwCond ) + return MSICONDITION_ERROR; + + r = MsiEvaluateConditionW( hInstall, szwCond ); + msi_free( szwCond ); + return r; +} diff --git a/libmsi/create.c b/libmsi/create.c new file mode 100644 index 0000000..c679d6b --- /dev/null +++ b/libmsi/create.c @@ -0,0 +1,208 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSICREATEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + LPCWSTR name; + BOOL bIsTemp; + BOOL hold; + column_info *col_info; +} MSICREATEVIEW; + +static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %d %p\n", cv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + BOOL persist = (cv->bIsTemp) ? MSICONDITION_FALSE : MSICONDITION_TRUE; + + TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name), + cv->bIsTemp?"temporary":"permanent"); + + if (cv->bIsTemp && !cv->hold) + return ERROR_SUCCESS; + + return msi_create_table( cv->db, cv->name, cv->col_info, persist ); +} + +static UINT CREATE_close( struct tagMSIVIEW *view ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p\n", cv); + + return ERROR_SUCCESS; +} + +static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %p %p\n", cv, rows, cols ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %p %p %p %p\n", cv, n, name, type, temporary, table_name ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %p\n", cv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_delete( struct tagMSIVIEW *view ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p\n", cv ); + + msiobj_release( &cv->db->hdr ); + msi_free( cv ); + + return ERROR_SUCCESS; +} + +static const MSIVIEWOPS create_ops = +{ + CREATE_fetch_int, + NULL, + NULL, + NULL, + NULL, + NULL, + CREATE_execute, + CREATE_close, + CREATE_get_dimensions, + CREATE_get_column_info, + CREATE_modify, + CREATE_delete, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static UINT check_columns( const column_info *col_info ) +{ + const column_info *c1, *c2; + + /* check for two columns with the same name */ + for( c1 = col_info; c1; c1 = c1->next ) + for( c2 = c1->next; c2; c2 = c2->next ) + if (!strcmpW( c1->column, c2->column )) + return ERROR_BAD_QUERY_SYNTAX; + + return ERROR_SUCCESS; +} + +UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, + column_info *col_info, BOOL hold ) +{ + MSICREATEVIEW *cv = NULL; + UINT r; + column_info *col; + BOOL temp = TRUE; + BOOL tempprim = FALSE; + + TRACE("%p\n", cv ); + + r = check_columns( col_info ); + if( r != ERROR_SUCCESS ) + return r; + + cv = msi_alloc_zero( sizeof *cv ); + if( !cv ) + return ERROR_FUNCTION_FAILED; + + for( col = col_info; col; col = col->next ) + { + if (!col->table) + col->table = table; + + if( !col->temporary ) + temp = FALSE; + else if ( col->type & MSITYPE_KEY ) + tempprim = TRUE; + } + + if ( !temp && tempprim ) + { + msi_free( cv ); + return ERROR_FUNCTION_FAILED; + } + + /* fill the structure */ + cv->view.ops = &create_ops; + msiobj_addref( &db->hdr ); + cv->db = db; + cv->name = table; + cv->col_info = col_info; + cv->bIsTemp = temp; + cv->hold = hold; + *view = (MSIVIEW*) cv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/custom.c b/libmsi/custom.c new file mode 100644 index 0000000..8f32e59 --- /dev/null +++ b/libmsi/custom.c @@ -0,0 +1,1508 @@ +/* + * Custom Action processing for the Microsoft Installer (msi.dll) + * + * Copyright 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 "config.h" +#include "wine/port.h" + +#define COBJMACROS + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "msidefs.h" +#include "winuser.h" +#include "objbase.h" +#include "oleauto.h" + +#include "msipriv.h" +#include "msiserver.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/exception.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#define CUSTOM_ACTION_TYPE_MASK 0x3F + +typedef struct tagMSIRUNNINGACTION +{ + struct list entry; + HANDLE handle; + BOOL process; + LPWSTR name; +} MSIRUNNINGACTION; + +typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE ); + +static CRITICAL_SECTION msi_custom_action_cs; +static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug = +{ + 0, 0, &msi_custom_action_cs, + { &msi_custom_action_cs_debug.ProcessLocksList, + &msi_custom_action_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") } +}; +static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 }; + +static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions ); + +UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action ) +{ + UINT count; + WCHAR **newbuf = NULL; + + if (script >= SCRIPT_MAX) + { + FIXME("Unknown script requested %u\n", script); + return ERROR_FUNCTION_FAILED; + } + TRACE("Scheduling action %s in script %u\n", debugstr_w(action), script); + + count = package->script->ActionCount[script]; + package->script->ActionCount[script]++; + if (count != 0) newbuf = msi_realloc( package->script->Actions[script], + package->script->ActionCount[script] * sizeof(WCHAR *) ); + else newbuf = msi_alloc( sizeof(WCHAR *) ); + + newbuf[count] = strdupW( action ); + package->script->Actions[script] = newbuf; + return ERROR_SUCCESS; +} + +UINT msi_register_unique_action( MSIPACKAGE *package, const WCHAR *action ) +{ + UINT count; + WCHAR **newbuf = NULL; + + if (!package->script) return FALSE; + + TRACE("Registering %s as unique action\n", debugstr_w(action)); + + count = package->script->UniqueActionsCount; + package->script->UniqueActionsCount++; + if (count != 0) newbuf = msi_realloc( package->script->UniqueActions, + package->script->UniqueActionsCount * sizeof(WCHAR *) ); + else newbuf = msi_alloc( sizeof(WCHAR *) ); + + newbuf[count] = strdupW( action ); + package->script->UniqueActions = newbuf; + return ERROR_SUCCESS; +} + +BOOL msi_action_is_unique( const MSIPACKAGE *package, const WCHAR *action ) +{ + UINT i; + + if (!package->script) return FALSE; + + for (i = 0; i < package->script->UniqueActionsCount; i++) + { + if (!strcmpW( package->script->UniqueActions[i], action )) return TRUE; + } + return FALSE; +} + +static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options) +{ + if (!package->script) + return TRUE; + + if ((options & msidbCustomActionTypeClientRepeat) == + msidbCustomActionTypeClientRepeat) + { + if (!(package->script->InWhatSequence & SEQUENCE_UI && + package->script->InWhatSequence & SEQUENCE_EXEC)) + { + TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n"); + return FALSE; + } + } + else if (options & msidbCustomActionTypeFirstSequence) + { + if (package->script->InWhatSequence & SEQUENCE_UI && + package->script->InWhatSequence & SEQUENCE_EXEC ) + { + TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n"); + return FALSE; + } + } + else if (options & msidbCustomActionTypeOncePerProcess) + { + if (msi_action_is_unique(package, action)) + { + TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n"); + return FALSE; + } + else + msi_register_unique_action(package, action); + } + + return TRUE; +} + +/* stores the following properties before the action: + * + * [CustomActionData<=>UserSID<=>ProductCode]Action + */ +static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata, + LPCWSTR usersid, LPCWSTR prodcode) +{ + LPWSTR deferred; + DWORD len; + + static const WCHAR format[] = { + '[','%','s','<','=','>','%','s','<','=','>','%','s',']','%','s',0 + }; + + if (!actiondata) + return strdupW(action); + + len = lstrlenW(action) + lstrlenW(actiondata) + + lstrlenW(usersid) + lstrlenW(prodcode) + + lstrlenW(format) - 7; + deferred = msi_alloc(len * sizeof(WCHAR)); + + sprintfW(deferred, format, actiondata, usersid, prodcode, action); + return deferred; +} + +static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data) +{ + LPWSTR end, beg = deferred_data + 1; + + static const WCHAR sep[] = {'<','=','>',0}; + + end = strstrW(beg, sep); + *end = '\0'; + msi_set_property(package->db, szCustomActionData, beg); + beg = end + 3; + + end = strstrW(beg, sep); + *end = '\0'; + msi_set_property(package->db, szUserSID, beg); + beg = end + 3; + + end = strchrW(beg, ']'); + *end = '\0'; + msi_set_property(package->db, szProductCode, beg); +} + +static MSIBINARY *create_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ', + '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0}; + MSIRECORD *row; + MSIBINARY *binary; + HANDLE file; + CHAR buffer[1024]; + WCHAR fmt[MAX_PATH], tmpfile[MAX_PATH]; + DWORD sz = MAX_PATH, write; + UINT r; + + if (msi_get_property(package->db, szTempFolder, fmt, &sz) != ERROR_SUCCESS) + GetTempPathW(MAX_PATH, fmt); + + if (!GetTempFileNameW( fmt, szMsi, 0, tmpfile )) + { + TRACE("unable to create temp file %s (%u)\n", debugstr_w(tmpfile), GetLastError()); + return NULL; + } + + row = MSI_QueryGetRecord(package->db, query, source); + if (!row) + return NULL; + + if (!(binary = msi_alloc_zero( sizeof(MSIBINARY) ))) + { + msiobj_release( &row->hdr ); + return NULL; + } + file = CreateFileW( tmpfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if (file == INVALID_HANDLE_VALUE) + { + msiobj_release( &row->hdr ); + msi_free( binary ); + return NULL; + } + do + { + sz = sizeof(buffer); + r = MSI_RecordReadStream( row, 2, buffer, &sz ); + if (r != ERROR_SUCCESS) + { + ERR("Failed to get stream\n"); + break; + } + WriteFile( file, buffer, sz, &write, NULL ); + } while (sz == sizeof buffer); + + CloseHandle( file ); + msiobj_release( &row->hdr ); + if (r != ERROR_SUCCESS) + { + DeleteFileW( tmpfile ); + msi_free( binary ); + return NULL; + } + + /* keep a reference to prevent the dll from being unloaded */ + if (dll && !(binary->module = LoadLibraryW( tmpfile ))) + { + WARN( "failed to load dll %s (%u)\n", debugstr_w( tmpfile ), GetLastError() ); + } + binary->source = strdupW( source ); + binary->tmpfile = strdupW( tmpfile ); + list_add_tail( &package->binaries, &binary->entry ); + return binary; +} + +static MSIBINARY *get_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll ) +{ + MSIBINARY *binary; + + LIST_FOR_EACH_ENTRY( binary, &package->binaries, MSIBINARY, entry ) + { + if (!strcmpW( binary->source, source )) + return binary; + } + + return create_temp_binary( package, source, dll ); +} + +static void file_running_action(MSIPACKAGE* package, HANDLE Handle, + BOOL process, LPCWSTR name) +{ + MSIRUNNINGACTION *action; + + action = msi_alloc( sizeof(MSIRUNNINGACTION) ); + + action->handle = Handle; + action->process = process; + action->name = strdupW(name); + + list_add_tail( &package->RunningActions, &action->entry ); +} + +static UINT custom_get_process_return( HANDLE process ) +{ + DWORD rc = 0; + + GetExitCodeProcess( process, &rc ); + TRACE("exit code is %u\n", rc); + if (rc != 0) + return ERROR_FUNCTION_FAILED; + return ERROR_SUCCESS; +} + +static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread ) +{ + DWORD rc = 0; + + GetExitCodeThread( thread, &rc ); + + switch (rc) + { + case ERROR_FUNCTION_NOT_CALLED: + case ERROR_SUCCESS: + case ERROR_INSTALL_USEREXIT: + case ERROR_INSTALL_FAILURE: + return rc; + case ERROR_NO_MORE_ITEMS: + return ERROR_SUCCESS; + case ERROR_INSTALL_SUSPEND: + ACTION_ForceReboot( package ); + return ERROR_SUCCESS; + default: + ERR("Invalid Return Code %d\n",rc); + return ERROR_INSTALL_FAILURE; + } +} + +static UINT wait_process_handle(MSIPACKAGE* package, UINT type, + HANDLE ProcessHandle, LPCWSTR name) +{ + UINT rc = ERROR_SUCCESS; + + if (!(type & msidbCustomActionTypeAsync)) + { + TRACE("waiting for %s\n", debugstr_w(name)); + + msi_dialog_check_messages(ProcessHandle); + + if (!(type & msidbCustomActionTypeContinue)) + rc = custom_get_process_return(ProcessHandle); + + CloseHandle(ProcessHandle); + } + else + { + TRACE("%s running in background\n", debugstr_w(name)); + + if (!(type & msidbCustomActionTypeContinue)) + file_running_action(package, ProcessHandle, TRUE, name); + else + CloseHandle(ProcessHandle); + } + + return rc; +} + +typedef struct _msi_custom_action_info { + struct list entry; + LONG refs; + MSIPACKAGE *package; + LPWSTR source; + LPWSTR target; + HANDLE handle; + LPWSTR action; + INT type; + GUID guid; +} msi_custom_action_info; + +static void release_custom_action_data( msi_custom_action_info *info ) +{ + EnterCriticalSection( &msi_custom_action_cs ); + + if (!--info->refs) + { + list_remove( &info->entry ); + if (info->handle) + CloseHandle( info->handle ); + msi_free( info->action ); + msi_free( info->source ); + msi_free( info->target ); + msiobj_release( &info->package->hdr ); + msi_free( info ); + } + + LeaveCriticalSection( &msi_custom_action_cs ); +} + +/* must be called inside msi_custom_action_cs if info is in the pending custom actions list */ +static void addref_custom_action_data( msi_custom_action_info *info ) +{ + info->refs++; + } + +static UINT wait_thread_handle( msi_custom_action_info *info ) +{ + UINT rc = ERROR_SUCCESS; + + if (!(info->type & msidbCustomActionTypeAsync)) + { + TRACE("waiting for %s\n", debugstr_w( info->action )); + + msi_dialog_check_messages( info->handle ); + + if (!(info->type & msidbCustomActionTypeContinue)) + rc = custom_get_thread_return( info->package, info->handle ); + + release_custom_action_data( info ); + } + else + { + TRACE("%s running in background\n", debugstr_w( info->action )); + } + + return rc; +} + +static msi_custom_action_info *find_action_by_guid( const GUID *guid ) +{ + msi_custom_action_info *info; + BOOL found = FALSE; + + EnterCriticalSection( &msi_custom_action_cs ); + + LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry ) + { + if (IsEqualGUID( &info->guid, guid )) + { + addref_custom_action_data( info ); + found = TRUE; + break; + } + } + + LeaveCriticalSection( &msi_custom_action_cs ); + + if (!found) + return NULL; + + return info; +} + +static void handle_msi_break( LPCWSTR target ) +{ + LPWSTR msg; + WCHAR val[MAX_PATH]; + + static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 }; + static const WCHAR WindowsInstaller[] = { + 'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0 + }; + + static const WCHAR format[] = { + 'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ', + 'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ', + 'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ', + 't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ', + 'a','n','d',' ','p','r','e','s','s',' ','O','K',0 + }; + + if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH )) + return; + + if( strcmpiW( val, target )) + return; + + msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) ); + if (!msg) + return; + + wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId()); + MessageBoxW( NULL, msg, WindowsInstaller, MB_OK); + msi_free(msg); + DebugBreak(); +} + +static UINT get_action_info( const GUID *guid, INT *type, MSIHANDLE *handle, + BSTR *dll, BSTR *funcname, + IWineMsiRemotePackage **package ) +{ + IClassFactory *cf = NULL; + IWineMsiRemoteCustomAction *rca = NULL; + HRESULT r; + + r = DllGetClassObject( &CLSID_WineMsiRemoteCustomAction, + &IID_IClassFactory, (LPVOID *)&cf ); + if (FAILED(r)) + { + ERR("failed to get IClassFactory interface\n"); + return ERROR_FUNCTION_FAILED; + } + + r = IClassFactory_CreateInstance( cf, NULL, &IID_IWineMsiRemoteCustomAction, (LPVOID *)&rca ); + if (FAILED(r)) + { + ERR("failed to get IWineMsiRemoteCustomAction interface\n"); + return ERROR_FUNCTION_FAILED; + } + + r = IWineMsiRemoteCustomAction_GetActionInfo( rca, guid, type, handle, dll, funcname, package ); + IWineMsiRemoteCustomAction_Release( rca ); + if (FAILED(r)) + { + ERR("GetActionInfo failed\n"); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +#ifdef __i386__ +extern UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle ); +__ASM_GLOBAL_FUNC( CUSTOMPROC_wrapper, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp,%ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "pushl 12(%ebp)\n\t" + "movl 8(%ebp),%eax\n\t" + "call *%eax\n\t" + "leave\n\t" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret" ) +#else +static inline UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle ) +{ + return proc(handle); +} +#endif + +static DWORD ACTION_CallDllFunction( const GUID *guid ) +{ + MsiCustomActionEntryPoint fn; + MSIHANDLE hPackage, handle; + HANDLE hModule; + LPSTR proc; + UINT r = ERROR_FUNCTION_FAILED; + BSTR dll = NULL, function = NULL; + INT type; + IWineMsiRemotePackage *remote_package = NULL; + + TRACE("%s\n", debugstr_guid( guid )); + + r = get_action_info( guid, &type, &handle, &dll, &function, &remote_package ); + if (r != ERROR_SUCCESS) + return r; + + hModule = LoadLibraryW( dll ); + if (!hModule) + { + WARN( "failed to load dll %s (%u)\n", debugstr_w( dll ), GetLastError() ); + return ERROR_SUCCESS; + } + + proc = strdupWtoA( function ); + fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc ); + msi_free( proc ); + if (fn) + { + hPackage = alloc_msi_remote_handle( (IUnknown *)remote_package ); + if (hPackage) + { + IWineMsiRemotePackage_SetMsiHandle( remote_package, handle ); + TRACE("calling %s\n", debugstr_w( function ) ); + handle_msi_break( function ); + + __TRY + { + r = CUSTOMPROC_wrapper( fn, hPackage ); + } + __EXCEPT_PAGE_FAULT + { + ERR("Custom action (%s:%s) caused a page fault: %08x\n", + debugstr_w(dll), debugstr_w(function), GetExceptionCode()); + r = ERROR_SUCCESS; + } + __ENDTRY; + + MsiCloseHandle( hPackage ); + } + else + ERR("failed to create handle for %p\n", remote_package ); + } + else + ERR("GetProcAddress(%s) failed\n", debugstr_w( function ) ); + + FreeLibrary(hModule); + + IWineMsiRemotePackage_Release( remote_package ); + SysFreeString( dll ); + SysFreeString( function ); + MsiCloseHandle( handle ); + + return r; +} + +static DWORD WINAPI DllThread( LPVOID arg ) +{ + LPGUID guid = arg; + DWORD rc = 0; + + TRACE("custom action (%x) started\n", GetCurrentThreadId() ); + + rc = ACTION_CallDllFunction( guid ); + + TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc ); + + MsiCloseAllHandles(); + return rc; +} + +static DWORD ACTION_CAInstallPackage(const GUID *guid) +{ + msi_custom_action_info *info; + UINT r = ERROR_FUNCTION_FAILED; + INSTALLUILEVEL old_level; + + info = find_action_by_guid(guid); + if (!info) + { + ERR("failed to find action %s\n", debugstr_guid(guid)); + return r; + } + + old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL); + r = MsiInstallProductW(info->source, info->target); + MsiSetInternalUI(old_level, NULL); + + release_custom_action_data(info); + + return r; +} + +static DWORD WINAPI ConcurrentInstallThread(LPVOID arg) +{ + LPGUID guid = arg; + DWORD rc; + + TRACE("concurrent installation (%x) started\n", GetCurrentThreadId()); + + rc = ACTION_CAInstallPackage(guid); + + TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc); + + MsiCloseAllHandles(); + return rc; +} + +static msi_custom_action_info *do_msidbCustomActionTypeDll( + MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action ) +{ + msi_custom_action_info *info; + + info = msi_alloc( sizeof *info ); + if (!info) + return NULL; + + msiobj_addref( &package->hdr ); + info->refs = 2; /* 1 for our caller and 1 for thread we created */ + info->package = package; + info->type = type; + info->target = strdupW( target ); + info->source = strdupW( source ); + info->action = strdupW( action ); + CoCreateGuid( &info->guid ); + + EnterCriticalSection( &msi_custom_action_cs ); + list_add_tail( &msi_pending_custom_actions, &info->entry ); + LeaveCriticalSection( &msi_custom_action_cs ); + + info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL ); + if (!info->handle) + { + /* release both references */ + release_custom_action_data( info ); + release_custom_action_data( info ); + return NULL; + } + + return info; +} + +static msi_custom_action_info *do_msidbCAConcurrentInstall( + MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action) +{ + msi_custom_action_info *info; + + info = msi_alloc( sizeof *info ); + if (!info) + return NULL; + + msiobj_addref( &package->hdr ); + info->refs = 2; /* 1 for our caller and 1 for thread we created */ + info->package = package; + info->type = type; + info->target = strdupW( target ); + info->source = strdupW( source ); + info->action = strdupW( action ); + CoCreateGuid( &info->guid ); + + EnterCriticalSection( &msi_custom_action_cs ); + list_add_tail( &msi_pending_custom_actions, &info->entry ); + LeaveCriticalSection( &msi_custom_action_cs ); + + info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL ); + if (!info->handle) + { + /* release both references */ + release_custom_action_data( info ); + release_custom_action_data( info ); + return NULL; + } + + return info; +} + +static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + WCHAR package_path[MAX_PATH]; + DWORD size; + + size = MAX_PATH; + msi_get_property(package->db, szSourceDir, package_path, &size); + lstrcatW(package_path, szBackSlash); + lstrcatW(package_path, source); + + TRACE("Installing package %s concurrently\n", debugstr_w(package_path)); + + info = do_msidbCAConcurrentInstall(package, type, package_path, target, action); + return wait_thread_handle(info); +} + +static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + MSIBINARY *binary; + + if (!(binary = get_temp_binary( package, source, TRUE ))) + return ERROR_FUNCTION_FAILED; + + TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile)); + + info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action ); + return wait_thread_handle( info ); +} + +static HANDLE execute_command( const WCHAR *app, WCHAR *arg, const WCHAR *dir ) +{ + static const WCHAR dotexeW[] = {'.','e','x','e',0}; + STARTUPINFOW si; + PROCESS_INFORMATION info; + WCHAR *exe = NULL, *cmd = NULL, *p; + BOOL ret; + + if (app) + { + int len_arg = 0; + DWORD len_exe; + + if (!(exe = msi_alloc( MAX_PATH * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; + len_exe = SearchPathW( NULL, app, dotexeW, MAX_PATH, exe, NULL ); + if (len_exe >= MAX_PATH) + { + msi_free( exe ); + if (!(exe = msi_alloc( len_exe * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; + len_exe = SearchPathW( NULL, app, dotexeW, len_exe, exe, NULL ); + } + if (!len_exe) + { + WARN("can't find executable %u\n", GetLastError()); + msi_free( exe ); + return INVALID_HANDLE_VALUE; + } + + if (arg) len_arg = strlenW( arg ); + if (!(cmd = msi_alloc( (len_exe + len_arg + 4) * sizeof(WCHAR) ))) + { + msi_free( exe ); + return INVALID_HANDLE_VALUE; + } + p = cmd; + if (strchrW( exe, ' ' )) + { + *p++ = '\"'; + memcpy( p, exe, len_exe * sizeof(WCHAR) ); + p += len_exe; + *p++ = '\"'; + *p = 0; + } + else + { + strcpyW( p, exe ); + p += len_exe; + } + if (arg) + { + *p++ = ' '; + memcpy( p, arg, len_arg * sizeof(WCHAR) ); + p[len_arg] = 0; + } + } + memset( &si, 0, sizeof(STARTUPINFOW) ); + ret = CreateProcessW( exe, exe ? cmd : arg, NULL, NULL, FALSE, 0, NULL, dir, &si, &info ); + msi_free( cmd ); + msi_free( exe ); + if (!ret) + { + WARN("unable to execute command %u\n", GetLastError()); + return INVALID_HANDLE_VALUE; + } + CloseHandle( info.hThread ); + return info.hProcess; +} + +static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + MSIBINARY *binary; + HANDLE handle; + WCHAR *arg; + + if (!(binary = get_temp_binary( package, source, FALSE ))) return ERROR_FUNCTION_FAILED; + + deformat_string( package, target, &arg ); + TRACE("exe %s arg %s\n", debugstr_w(binary->tmpfile), debugstr_w(arg)); + + handle = execute_command( binary->tmpfile, arg, szCRoot ); + msi_free( arg ); + if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; + return wait_process_handle( package, type, handle, action ); +} + +static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + MSIFILE *file; + + TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); + + file = msi_get_loaded_file( package, source ); + if (!file) + { + ERR("invalid file key %s\n", debugstr_w( source )); + return ERROR_FUNCTION_FAILED; + } + + info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action ); + return wait_thread_handle( info ); +} + +static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + MSIFILE *file; + HANDLE handle; + WCHAR *arg; + + if (!(file = msi_get_loaded_file( package, source ))) return ERROR_FUNCTION_FAILED; + + deformat_string( package, target, &arg ); + TRACE("exe %s arg %s\n", debugstr_w(file->TargetPath), debugstr_w(arg)); + + handle = execute_command( file->TargetPath, arg, szCRoot ); + msi_free( arg ); + if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; + return wait_process_handle( package, type, handle, action ); +} + +static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ', + 'F','R','O','M',' ','`','E','r','r','o','r','`',' ', + 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ', + '%','s',0 + }; + MSIRECORD *row = 0; + LPWSTR deformated = NULL; + + deformat_string( package, target, &deformated ); + + /* first try treat the error as a number */ + row = MSI_QueryGetRecord( package->db, query, deformated ); + if( row ) + { + LPCWSTR error = MSI_RecordGetString( row, 1 ); + if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) + MessageBoxW( NULL, error, NULL, MB_OK ); + msiobj_release( &row->hdr ); + } + else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) + MessageBoxW( NULL, deformated, NULL, MB_OK ); + + msi_free( deformated ); + + return ERROR_INSTALL_FAILURE; +} + +static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + WCHAR *exe, *arg; + HANDLE handle; + + if (!(exe = msi_dup_property( package->db, source ))) return ERROR_SUCCESS; + + deformat_string( package, target, &arg ); + TRACE("exe %s arg %s\n", debugstr_w(exe), debugstr_w(arg)); + + handle = execute_command( exe, arg, szCRoot ); + msi_free( arg ); + if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; + return wait_process_handle( package, type, handle, action ); +} + +static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + const WCHAR *workingdir = NULL; + HANDLE handle; + WCHAR *cmd; + + if (source) + { + workingdir = msi_get_target_folder( package, source ); + if (!workingdir) return ERROR_FUNCTION_FAILED; + } + deformat_string( package, target, &cmd ); + if (!cmd) return ERROR_FUNCTION_FAILED; + + TRACE("cmd %s dir %s\n", debugstr_w(cmd), debugstr_w(workingdir)); + + handle = execute_command( NULL, cmd, workingdir ); + msi_free( cmd ); + if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; + return wait_process_handle( package, type, handle, action ); +} + +static DWORD ACTION_CallScript( const GUID *guid ) +{ + msi_custom_action_info *info; + MSIHANDLE hPackage; + UINT r = ERROR_FUNCTION_FAILED; + + info = find_action_by_guid( guid ); + if (!info) + { + ERR("failed to find action %s\n", debugstr_guid( guid) ); + return ERROR_FUNCTION_FAILED; + } + + TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) ); + + hPackage = alloc_msihandle( &info->package->hdr ); + if (hPackage) + { + r = call_script( hPackage, info->type, info->source, info->target, info->action ); + TRACE("script returned %u\n", r); + MsiCloseHandle( hPackage ); + } + else + ERR("failed to create handle for %p\n", info->package ); + + release_custom_action_data( info ); + return r; +} + +static DWORD WINAPI ScriptThread( LPVOID arg ) +{ + LPGUID guid = arg; + DWORD rc; + + TRACE("custom action (%x) started\n", GetCurrentThreadId() ); + + rc = ACTION_CallScript( guid ); + + TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc ); + + MsiCloseAllHandles(); + return rc; +} + +static msi_custom_action_info *do_msidbCustomActionTypeScript( + MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action ) +{ + msi_custom_action_info *info; + + info = msi_alloc( sizeof *info ); + if (!info) + return NULL; + + msiobj_addref( &package->hdr ); + info->refs = 2; /* 1 for our caller and 1 for thread we created */ + info->package = package; + info->type = type; + info->target = strdupW( function ); + info->source = strdupW( script ); + info->action = strdupW( action ); + CoCreateGuid( &info->guid ); + + EnterCriticalSection( &msi_custom_action_cs ); + list_add_tail( &msi_pending_custom_actions, &info->entry ); + LeaveCriticalSection( &msi_custom_action_cs ); + + info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL ); + if (!info->handle) + { + /* release both references */ + release_custom_action_data( info ); + release_custom_action_data( info ); + return NULL; + } + + return info; +} + +static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + + TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); + + info = do_msidbCustomActionTypeScript( package, type, target, NULL, action ); + return wait_thread_handle( info ); +} + +static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ', + '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0}; + MSIRECORD *row = 0; + msi_custom_action_info *info; + CHAR *buffer = NULL; + WCHAR *bufferw = NULL; + DWORD sz = 0; + UINT r; + + TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); + + row = MSI_QueryGetRecord(package->db, query, source); + if (!row) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordReadStream(row, 2, NULL, &sz); + if (r != ERROR_SUCCESS) return r; + + buffer = msi_alloc( sz + 1 ); + if (!buffer) return ERROR_FUNCTION_FAILED; + + r = MSI_RecordReadStream(row, 2, buffer, &sz); + if (r != ERROR_SUCCESS) + goto done; + + buffer[sz] = 0; + bufferw = strdupAtoW(buffer); + if (!bufferw) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); + r = wait_thread_handle( info ); + +done: + msi_free(bufferw); + msi_free(buffer); + return r; +} + +static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + MSIFILE *file; + HANDLE hFile; + DWORD sz, szHighWord = 0, read; + CHAR *buffer=NULL; + WCHAR *bufferw=NULL; + BOOL bRet; + UINT r; + + TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); + + file = msi_get_loaded_file(package, source); + if (!file) + { + ERR("invalid file key %s\n", debugstr_w(source)); + return ERROR_FUNCTION_FAILED; + } + + hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) return ERROR_FUNCTION_FAILED; + + sz = GetFileSize(hFile, &szHighWord); + if (sz == INVALID_FILE_SIZE || szHighWord != 0) + { + CloseHandle(hFile); + return ERROR_FUNCTION_FAILED; + } + buffer = msi_alloc( sz + 1 ); + if (!buffer) + { + CloseHandle(hFile); + return ERROR_FUNCTION_FAILED; + } + bRet = ReadFile(hFile, buffer, sz, &read, NULL); + CloseHandle(hFile); + if (!bRet) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + buffer[read] = 0; + bufferw = strdupAtoW(buffer); + if (!bufferw) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); + r = wait_thread_handle( info ); + +done: + msi_free(bufferw); + msi_free(buffer); + return r; +} + +static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + msi_custom_action_info *info; + WCHAR *prop; + + TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); + + prop = msi_dup_property( package->db, source ); + if (!prop) return ERROR_SUCCESS; + + info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action ); + msi_free(prop); + return wait_thread_handle( info ); +} + +static BOOL action_type_matches_script( MSIPACKAGE *package, UINT type, UINT script ) +{ + switch (script) + { + case SCRIPT_NONE: + case SCRIPT_INSTALL: + return !(type & msidbCustomActionTypeCommit) && !(type & msidbCustomActionTypeRollback); + case SCRIPT_COMMIT: + return (type & msidbCustomActionTypeCommit); + case SCRIPT_ROLLBACK: + return (type & msidbCustomActionTypeRollback); + default: + ERR("unhandled script %u\n", script); + } + return FALSE; +} + +static UINT defer_custom_action( MSIPACKAGE *package, const WCHAR *action, UINT type ) +{ + WCHAR *actiondata = msi_dup_property( package->db, action ); + WCHAR *usersid = msi_dup_property( package->db, szUserSID ); + WCHAR *prodcode = msi_dup_property( package->db, szProductCode ); + WCHAR *deferred = msi_get_deferred_action( action, actiondata, usersid, prodcode ); + + if (!deferred) + { + msi_free( actiondata ); + msi_free( usersid ); + msi_free( prodcode ); + return ERROR_OUTOFMEMORY; + } + if (type & msidbCustomActionTypeCommit) + { + TRACE("deferring commit action\n"); + msi_schedule_action( package, SCRIPT_COMMIT, deferred ); + } + else if (type & msidbCustomActionTypeRollback) + { + TRACE("deferring rollback action\n"); + msi_schedule_action( package, SCRIPT_ROLLBACK, deferred ); + } + else + { + TRACE("deferring install action\n"); + msi_schedule_action( package, SCRIPT_INSTALL, deferred ); + } + + msi_free( actiondata ); + msi_free( usersid ); + msi_free( prodcode ); + msi_free( deferred ); + return ERROR_SUCCESS; +} + +UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','u','s','t','o','m','A','c','t','i','o','n','`',' ','W','H','E','R','E',' ', + '`','A','c','t','i' ,'o','n','`',' ','=',' ','\'','%','s','\'',0}; + UINT rc = ERROR_SUCCESS; + MSIRECORD *row; + UINT type; + LPCWSTR source, target; + LPWSTR ptr, deferred_data = NULL; + LPWSTR deformated = NULL, action_copy = strdupW(action); + + /* deferred action: [properties]Action */ + if ((ptr = strrchrW(action_copy, ']'))) + { + deferred_data = action_copy; + action = ptr + 1; + } + + row = MSI_QueryGetRecord( package->db, query, action ); + if (!row) + { + msi_free(action_copy); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + type = MSI_RecordGetInteger(row,2); + source = MSI_RecordGetString(row,3); + target = MSI_RecordGetString(row,4); + + TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type, + debugstr_w(source), debugstr_w(target)); + + /* handle some of the deferred actions */ + if (type & msidbCustomActionTypeTSAware) + FIXME("msidbCustomActionTypeTSAware not handled\n"); + + if (type & msidbCustomActionTypeInScript) + { + if (type & msidbCustomActionTypeNoImpersonate) + WARN("msidbCustomActionTypeNoImpersonate not handled\n"); + + if (!execute || !action_type_matches_script( package, type, script )) + { + rc = defer_custom_action( package, action, type ); + goto end; + } + else + { + LPWSTR actiondata = msi_dup_property( package->db, action ); + + if (type & msidbCustomActionTypeInScript) + package->scheduled_action_running = TRUE; + + if (type & msidbCustomActionTypeCommit) + package->commit_action_running = TRUE; + + if (type & msidbCustomActionTypeRollback) + package->rollback_action_running = TRUE; + + if (deferred_data) + set_deferred_action_props(package, deferred_data); + else if (actiondata) + msi_set_property(package->db, szCustomActionData, actiondata); + else + msi_set_property(package->db, szCustomActionData, szEmpty); + + msi_free(actiondata); + } + } + else if (!check_execution_scheduling_options(package,action,type)) + { + rc = ERROR_SUCCESS; + goto end; + } + + switch (type & CUSTOM_ACTION_TYPE_MASK) + { + case 1: /* DLL file stored in a Binary table stream */ + rc = HANDLE_CustomType1(package,source,target,type,action); + break; + case 2: /* EXE file stored in a Binary table stream */ + rc = HANDLE_CustomType2(package,source,target,type,action); + break; + case 18: /*EXE file installed with package */ + rc = HANDLE_CustomType18(package,source,target,type,action); + break; + case 19: /* Error that halts install */ + rc = HANDLE_CustomType19(package,source,target,type,action); + break; + case 17: + rc = HANDLE_CustomType17(package,source,target,type,action); + break; + case 23: /* installs another package in the source tree */ + deformat_string(package,target,&deformated); + rc = HANDLE_CustomType23(package,source,deformated,type,action); + msi_free(deformated); + break; + case 50: /*EXE file specified by a property value */ + rc = HANDLE_CustomType50(package,source,target,type,action); + break; + case 34: /*EXE to be run in specified directory */ + rc = HANDLE_CustomType34(package,source,target,type,action); + break; + case 35: /* Directory set with formatted text. */ + deformat_string(package,target,&deformated); + MSI_SetTargetPathW(package, source, deformated); + msi_free(deformated); + break; + case 51: /* Property set with formatted text. */ + if (!source) + break; + + deformat_string(package,target,&deformated); + rc = msi_set_property( package->db, source, deformated ); + if (rc == ERROR_SUCCESS && !strcmpW( source, szSourceDir )) + msi_reset_folders( package, TRUE ); + msi_free(deformated); + break; + case 37: /* JScript/VBScript text stored in target column. */ + case 38: + rc = HANDLE_CustomType37_38(package,source,target,type,action); + break; + case 5: + case 6: /* JScript/VBScript file stored in a Binary table stream. */ + rc = HANDLE_CustomType5_6(package,source,target,type,action); + break; + case 21: /* JScript/VBScript file installed with the product. */ + case 22: + rc = HANDLE_CustomType21_22(package,source,target,type,action); + break; + case 53: /* JScript/VBScript text specified by a property value. */ + case 54: + rc = HANDLE_CustomType53_54(package,source,target,type,action); + break; + default: + FIXME("unhandled action type %u (%s %s)\n", type & CUSTOM_ACTION_TYPE_MASK, + debugstr_w(source), debugstr_w(target)); + } + +end: + package->scheduled_action_running = FALSE; + package->commit_action_running = FALSE; + package->rollback_action_running = FALSE; + msi_free(action_copy); + msiobj_release(&row->hdr); + return rc; +} + +void ACTION_FinishCustomActions(const MSIPACKAGE* package) +{ + struct list *item; + HANDLE *wait_handles; + unsigned int handle_count, i; + msi_custom_action_info *info, *cursor; + + while ((item = list_head( &package->RunningActions ))) + { + MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry ); + + list_remove( &action->entry ); + + TRACE("waiting for %s\n", debugstr_w( action->name ) ); + msi_dialog_check_messages( action->handle ); + + CloseHandle( action->handle ); + msi_free( action->name ); + msi_free( action ); + } + + EnterCriticalSection( &msi_custom_action_cs ); + + handle_count = list_count( &msi_pending_custom_actions ); + wait_handles = msi_alloc( handle_count * sizeof(HANDLE) ); + + handle_count = 0; + LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry ) + { + if (info->package == package ) + { + if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0)) + handle_count++; + } + } + + LeaveCriticalSection( &msi_custom_action_cs ); + + for (i = 0; i < handle_count; i++) + { + msi_dialog_check_messages( wait_handles[i] ); + CloseHandle( wait_handles[i] ); + } + msi_free( wait_handles ); + + EnterCriticalSection( &msi_custom_action_cs ); + LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry ) + { + if (info->package == package) release_custom_action_data( info ); + } + LeaveCriticalSection( &msi_custom_action_cs ); +} + +typedef struct _msi_custom_remote_impl { + IWineMsiRemoteCustomAction IWineMsiRemoteCustomAction_iface; + LONG refs; +} msi_custom_remote_impl; + +static inline msi_custom_remote_impl *impl_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction *iface ) +{ + return CONTAINING_RECORD(iface, msi_custom_remote_impl, IWineMsiRemoteCustomAction_iface); +} + +static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface, + REFIID riid,LPVOID *ppobj) +{ + if( IsEqualCLSID( riid, &IID_IUnknown ) || + IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) ) + { + IWineMsiRemoteCustomAction_AddRef( iface ); + *ppobj = iface; + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface ) +{ + msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface ); + + return InterlockedIncrement( &This->refs ); +} + +static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface ) +{ + msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface ); + ULONG r; + + r = InterlockedDecrement( &This->refs ); + if (r == 0) + msi_free( This ); + return r; +} + +static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid, + INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package ) +{ + msi_custom_action_info *info; + + info = find_action_by_guid( custom_action_guid ); + if (!info) + return E_FAIL; + + *type = info->type; + *handle = alloc_msihandle( &info->package->hdr ); + *dll = SysAllocString( info->source ); + *func = SysAllocString( info->target ); + + release_custom_action_data( info ); + return create_msi_remote_package( NULL, (LPVOID *)remote_package ); +} + +static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl = +{ + mcr_QueryInterface, + mcr_AddRef, + mcr_Release, + mcr_GetActionInfo, +}; + +HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj ) +{ + msi_custom_remote_impl* This; + + This = msi_alloc( sizeof *This ); + if (!This) + return E_OUTOFMEMORY; + + This->IWineMsiRemoteCustomAction_iface.lpVtbl = &msi_custom_remote_vtbl; + This->refs = 1; + + *ppObj = This; + + return S_OK; +} diff --git a/libmsi/database.c b/libmsi/database.c new file mode 100644 index 0000000..9c0d346 --- /dev/null +++ b/libmsi/database.c @@ -0,0 +1,2180 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002,2003,2004,2005 Mike McCormack 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> +#include <stdio.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" +#include "objbase.h" +#include "msiserver.h" +#include "query.h" + +#include "initguid.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * .MSI file format + * + * An .msi file is a structured storage file. + * It contains a number of streams. + * A stream for each table in the database. + * Two streams for the string table in the database. + * Any binary data in a table is a reference to a stream. + */ + +#define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0) + +typedef struct tagMSITRANSFORM { + struct list entry; + IStorage *stg; +} MSITRANSFORM; + +typedef struct tagMSISTREAM { + struct list entry; + IStorage *stg; + IStream *stm; +} MSISTREAM; + +static UINT find_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm ) +{ + MSISTREAM *stream; + + LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry ) + { + HRESULT r; + STATSTG stat; + + if (stream->stg != stg) continue; + + r = IStream_Stat( stream->stm, &stat, 0 ); + if( FAILED( r ) ) + { + WARN("failed to stat stream r = %08x!\n", r); + continue; + } + + if( !strcmpW( name, stat.pwcsName ) ) + { + TRACE("found %s\n", debugstr_w(name)); + *stm = stream->stm; + CoTaskMemFree( stat.pwcsName ); + return ERROR_SUCCESS; + } + + CoTaskMemFree( stat.pwcsName ); + } + + return ERROR_FUNCTION_FAILED; +} + +UINT msi_clone_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm ) +{ + IStream *stream; + + if (find_open_stream( db, stg, name, &stream ) == ERROR_SUCCESS) + { + HRESULT r; + LARGE_INTEGER pos; + + r = IStream_Clone( stream, stm ); + if( FAILED( r ) ) + { + WARN("failed to clone stream r = %08x!\n", r); + return ERROR_FUNCTION_FAILED; + } + + pos.QuadPart = 0; + r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL ); + if( FAILED( r ) ) + { + IStream_Release( *stm ); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + return ERROR_FUNCTION_FAILED; +} + +UINT msi_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm ) +{ + HRESULT r; + IStorage *stg; + WCHAR decoded[MAX_STREAM_NAME_LEN]; + + decode_streamname( stname, decoded ); + TRACE("%s -> %s\n", debugstr_w(stname), debugstr_w(decoded)); + + if (msi_clone_open_stream( db, db->storage, stname, stm ) == ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = IStorage_OpenStream( db->storage, stname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm ); + if( FAILED( r ) ) + { + MSITRANSFORM *transform; + + LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry ) + { + r = IStorage_OpenStream( transform->stg, stname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm ); + if (SUCCEEDED(r)) + { + stg = transform->stg; + break; + } + } + } + else stg = db->storage; + + if( SUCCEEDED(r) ) + { + MSISTREAM *stream; + + if (!(stream = msi_alloc( sizeof(MSISTREAM) ))) return ERROR_NOT_ENOUGH_MEMORY; + stream->stg = stg; + IStorage_AddRef( stg ); + stream->stm = *stm; + IStream_AddRef( *stm ); + list_add_tail( &db->streams, &stream->entry ); + } + + return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; +} + +static void free_transforms( MSIDATABASE *db ) +{ + while( !list_empty( &db->transforms ) ) + { + MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ), + MSITRANSFORM, entry ); + list_remove( &t->entry ); + IStorage_Release( t->stg ); + msi_free( t ); + } +} + +void msi_destroy_stream( MSIDATABASE *db, const WCHAR *stname ) +{ + MSISTREAM *stream, *stream2; + + LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry ) + { + HRESULT r; + STATSTG stat; + + r = IStream_Stat( stream->stm, &stat, 0 ); + if (FAILED(r)) + { + WARN("failed to stat stream r = %08x\n", r); + continue; + } + + if (!strcmpW( stname, stat.pwcsName )) + { + TRACE("destroying %s\n", debugstr_w(stname)); + + list_remove( &stream->entry ); + IStream_Release( stream->stm ); + IStorage_Release( stream->stg ); + IStorage_DestroyElement( stream->stg, stname ); + msi_free( stream ); + CoTaskMemFree( stat.pwcsName ); + break; + } + CoTaskMemFree( stat.pwcsName ); + } +} + +static void free_streams( MSIDATABASE *db ) +{ + while( !list_empty( &db->streams ) ) + { + MSISTREAM *s = LIST_ENTRY(list_head( &db->streams ), MSISTREAM, entry); + list_remove( &s->entry ); + IStream_Release( s->stm ); + IStorage_Release( s->stg ); + msi_free( s ); + } +} + +void append_storage_to_db( MSIDATABASE *db, IStorage *stg ) +{ + MSITRANSFORM *t; + + t = msi_alloc( sizeof *t ); + t->stg = stg; + IStorage_AddRef( stg ); + list_add_head( &db->transforms, &t->entry ); + + /* the transform may add or replace streams */ + free_streams( db ); +} + +static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) +{ + MSIDATABASE *db = (MSIDATABASE *) arg; + + msi_free(db->path); + free_cached_tables( db ); + free_streams( db ); + free_transforms( db ); + if (db->strings) msi_destroy_stringtable( db->strings ); + IStorage_Release( db->storage ); + if (db->deletefile) + { + DeleteFileW( db->deletefile ); + msi_free( db->deletefile ); + } +} + +static HRESULT db_initialize( IStorage *stg, const GUID *clsid ) +{ + static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; + HRESULT hr; + + hr = IStorage_SetClass( stg, clsid ); + if (FAILED( hr )) + { + WARN("failed to set class id 0x%08x\n", hr); + return hr; + } + + /* create the _Tables stream */ + hr = write_stream_data( stg, szTables, NULL, 0, TRUE ); + if (FAILED( hr )) + { + WARN("failed to create _Tables stream 0x%08x\n", hr); + return hr; + } + + hr = msi_init_string_table( stg ); + if (FAILED( hr )) + { + WARN("failed to initialize string table 0x%08x\n", hr); + return hr; + } + + hr = IStorage_Commit( stg, 0 ); + if (FAILED( hr )) + { + WARN("failed to commit changes 0x%08x\n", hr); + return hr; + } + + return S_OK; +} + +UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) +{ + IStorage *stg = NULL; + HRESULT r; + MSIDATABASE *db = NULL; + UINT ret = ERROR_FUNCTION_FAILED; + LPCWSTR szMode, save_path; + STATSTG stat; + BOOL created = FALSE, patch = FALSE; + WCHAR path[MAX_PATH]; + + TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) ); + + if( !pdb ) + return ERROR_INVALID_PARAMETER; + + if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY && + szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT) + { + TRACE("Database is a patch\n"); + szPersist -= MSIDBOPEN_PATCHFILE; + patch = TRUE; + } + + save_path = szDBPath; + szMode = szPersist; + if( !IS_INTMSIDBOPEN(szPersist) ) + { + if (!CopyFileW( szDBPath, szPersist, FALSE )) + return ERROR_OPEN_FAILED; + + szDBPath = szPersist; + szPersist = MSIDBOPEN_TRANSACT; + created = TRUE; + } + + if( szPersist == MSIDBOPEN_READONLY ) + { + r = StgOpenStorage( szDBPath, NULL, + STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + } + else if( szPersist == MSIDBOPEN_CREATE ) + { + r = StgCreateDocfile( szDBPath, + STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); + + if( SUCCEEDED(r) ) + r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); + created = TRUE; + } + else if( szPersist == MSIDBOPEN_CREATEDIRECT ) + { + r = StgCreateDocfile( szDBPath, + STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); + + if( SUCCEEDED(r) ) + r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); + created = TRUE; + } + else if( szPersist == MSIDBOPEN_TRANSACT ) + { + r = StgOpenStorage( szDBPath, NULL, + STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + } + else if( szPersist == MSIDBOPEN_DIRECT ) + { + r = StgOpenStorage( szDBPath, NULL, + STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + } + else + { + ERR("unknown flag %p\n",szPersist); + return ERROR_INVALID_PARAMETER; + } + + if( FAILED( r ) || !stg ) + { + WARN("open failed r = %08x for %s\n", r, debugstr_w(szDBPath)); + return ERROR_FUNCTION_FAILED; + } + + r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + FIXME("Failed to stat storage\n"); + goto end; + } + + if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) && + !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) && + !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) ) + { + ERR("storage GUID is not a MSI database GUID %s\n", + debugstr_guid(&stat.clsid) ); + goto end; + } + + if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) + { + ERR("storage GUID is not the MSI patch GUID %s\n", + debugstr_guid(&stat.clsid) ); + ret = ERROR_OPEN_FAILED; + goto end; + } + + db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), + MSI_CloseDatabase ); + if( !db ) + { + FIXME("Failed to allocate a handle\n"); + goto end; + } + + if (!strchrW( save_path, '\\' )) + { + GetCurrentDirectoryW( MAX_PATH, path ); + lstrcatW( path, szBackSlash ); + lstrcatW( path, save_path ); + } + else + lstrcpyW( path, save_path ); + + db->path = strdupW( path ); + db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; + db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID; + + if( TRACE_ON( msi ) ) + enum_stream_names( stg ); + + db->storage = stg; + db->mode = szMode; + if (created) + db->deletefile = strdupW( szDBPath ); + list_init( &db->tables ); + list_init( &db->transforms ); + list_init( &db->streams ); + + db->strings = msi_load_string_table( stg, &db->bytes_per_strref ); + if( !db->strings ) + goto end; + + ret = ERROR_SUCCESS; + + msiobj_addref( &db->hdr ); + IStorage_AddRef( stg ); + *pdb = db; + +end: + if( db ) + msiobj_release( &db->hdr ); + if( stg ) + IStorage_Release( stg ); + + return ret; +} + +UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB) +{ + MSIDATABASE *db; + UINT ret; + + TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB); + + ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db ); + if( ret == ERROR_SUCCESS ) + { + *phDB = alloc_msihandle( &db->hdr ); + if (! *phDB) + ret = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &db->hdr ); + } + + return ret; +} + +UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB) +{ + HRESULT r = ERROR_FUNCTION_FAILED; + LPWSTR szwDBPath = NULL, szwPersist = NULL; + + TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB); + + if( szDBPath ) + { + szwDBPath = strdupAtoW( szDBPath ); + if( !szwDBPath ) + goto end; + } + + if( !IS_INTMSIDBOPEN(szPersist) ) + { + szwPersist = strdupAtoW( szPersist ); + if( !szwPersist ) + goto end; + } + else + szwPersist = (LPWSTR)(DWORD_PTR)szPersist; + + r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB ); + +end: + if( !IS_INTMSIDBOPEN(szPersist) ) + msi_free( szwPersist ); + msi_free( szwDBPath ); + + return r; +} + +static LPWSTR msi_read_text_archive(LPCWSTR path, DWORD *len) +{ + HANDLE file; + LPSTR data = NULL; + LPWSTR wdata = NULL; + DWORD read, size = 0; + + file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + if (file == INVALID_HANDLE_VALUE) + return NULL; + + size = GetFileSize( file, NULL ); + if (!(data = msi_alloc( size ))) goto done; + + if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done; + + while (!data[size - 1]) size--; + *len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 ); + if ((wdata = msi_alloc( (*len + 1) * sizeof(WCHAR) ))) + { + MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len ); + wdata[*len] = 0; + } + +done: + CloseHandle( file ); + msi_free( data ); + return wdata; +} + +static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries, DWORD *len) +{ + LPWSTR ptr = *line, save; + DWORD i, count = 1, chars_left = *len; + + *entries = NULL; + + /* stay on this line */ + while (chars_left && *ptr != '\n') + { + /* entries are separated by tabs */ + if (*ptr == '\t') + count++; + + ptr++; + chars_left--; + } + + *entries = msi_alloc(count * sizeof(LPWSTR)); + if (!*entries) + return; + + /* store pointers into the data */ + chars_left = *len; + for (i = 0, ptr = *line; i < count; i++) + { + while (chars_left && *ptr == '\r') + { + ptr++; + chars_left--; + } + save = ptr; + + while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') + { + if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */ + if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11') + { + *ptr = '\n'; + *(ptr - 1) = '\r'; + } + ptr++; + chars_left--; + } + + /* NULL-separate the data */ + if (*ptr == '\n' || *ptr == '\r') + { + while (chars_left && (*ptr == '\n' || *ptr == '\r')) + { + *(ptr++) = 0; + chars_left--; + } + } + else if (*ptr) + { + *(ptr++) = 0; + chars_left--; + } + (*entries)[i] = save; + } + + /* move to the next line if there's more, else EOF */ + *line = ptr; + *len = chars_left; + if (num_entries) + *num_entries = count; +} + +static LPWSTR msi_build_createsql_prelude(LPWSTR table) +{ + LPWSTR prelude; + DWORD size; + + static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0}; + + size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2; + prelude = msi_alloc(size * sizeof(WCHAR)); + if (!prelude) + return NULL; + + sprintfW(prelude, create_fmt, table); + return prelude; +} + +static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns) +{ + LPWSTR columns, p; + LPCWSTR type; + DWORD sql_size = 1, i, len; + WCHAR expanded[128], *ptr; + WCHAR size[10], comma[2], extra[30]; + + static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0}; + static const WCHAR size_fmt[] = {'(','%','s',')',0}; + static const WCHAR type_char[] = {'C','H','A','R',0}; + static const WCHAR type_int[] = {'I','N','T',0}; + static const WCHAR type_long[] = {'L','O','N','G',0}; + static const WCHAR type_object[] = {'O','B','J','E','C','T',0}; + static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0}; + static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0}; + + columns = msi_alloc_zero(sql_size * sizeof(WCHAR)); + if (!columns) + return NULL; + + for (i = 0; i < num_columns; i++) + { + type = NULL; + comma[1] = size[0] = extra[0] = '\0'; + + if (i == num_columns - 1) + comma[0] = '\0'; + else + comma[0] = ','; + + ptr = &types[i][1]; + len = atolW(ptr); + extra[0] = '\0'; + + switch (types[i][0]) + { + case 'l': + lstrcpyW(extra, type_notnull); + /* fall through */ + case 'L': + lstrcatW(extra, localizable); + type = type_char; + sprintfW(size, size_fmt, ptr); + break; + case 's': + lstrcpyW(extra, type_notnull); + /* fall through */ + case 'S': + type = type_char; + sprintfW(size, size_fmt, ptr); + break; + case 'i': + lstrcpyW(extra, type_notnull); + /* fall through */ + case 'I': + if (len <= 2) + type = type_int; + else if (len == 4) + type = type_long; + else + { + WARN("invalid int width %u\n", len); + msi_free(columns); + return NULL; + } + break; + case 'v': + lstrcpyW(extra, type_notnull); + /* fall through */ + case 'V': + type = type_object; + break; + default: + ERR("Unknown type: %c\n", types[i][0]); + msi_free(columns); + return NULL; + } + + sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma); + sql_size += lstrlenW(expanded); + + p = msi_realloc(columns, sql_size * sizeof(WCHAR)); + if (!p) + { + msi_free(columns); + return NULL; + } + columns = p; + + lstrcatW(columns, expanded); + } + + return columns; +} + +static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys) +{ + LPWSTR postlude, keys, ptr; + DWORD size, key_size, i; + + static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0}; + static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0}; + + for (i = 0, size = 1; i < num_keys; i++) + size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2; + + keys = msi_alloc(size * sizeof(WCHAR)); + if (!keys) + return NULL; + + for (i = 0, ptr = keys; i < num_keys; i++) + { + key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2; + sprintfW(ptr, key_fmt, primary_keys[i]); + ptr += key_size; + } + + /* remove final ', ' */ + *(ptr - 2) = '\0'; + + size = lstrlenW(postlude_fmt) + size - 1; + postlude = msi_alloc(size * sizeof(WCHAR)); + if (!postlude) + goto done; + + sprintfW(postlude, postlude_fmt, keys); + +done: + msi_free(keys); + return postlude; +} + +static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns) +{ + UINT r = ERROR_OUTOFMEMORY; + DWORD size; + MSIQUERY *view; + LPWSTR create_sql = NULL; + LPWSTR prelude, columns_sql, postlude; + + prelude = msi_build_createsql_prelude(labels[0]); + columns_sql = msi_build_createsql_columns(columns, types, num_columns); + postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */ + + if (!prelude || !columns_sql || !postlude) + goto done; + + size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1; + create_sql = msi_alloc(size * sizeof(WCHAR)); + if (!create_sql) + goto done; + + lstrcpyW(create_sql, prelude); + lstrcatW(create_sql, columns_sql); + lstrcatW(create_sql, postlude); + + r = MSI_DatabaseOpenViewW( db, create_sql, &view ); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewExecute(view, NULL); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + +done: + msi_free(prelude); + msi_free(columns_sql); + msi_free(postlude); + msi_free(create_sql); + return r; +} + +static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name) +{ + DWORD len; + LPWSTR fullname, ptr; + + len = lstrlenW(path) + lstrlenW(name) + 1; + fullname = msi_alloc(len*sizeof(WCHAR)); + if (!fullname) + return NULL; + + lstrcpyW( fullname, path ); + + /* chop off extension from path */ + ptr = strrchrW(fullname, '.'); + if (!ptr) + { + msi_free (fullname); + return NULL; + } + *ptr++ = '\\'; + lstrcpyW( ptr, name ); + return fullname; +} + +static UINT construct_record(DWORD num_columns, LPWSTR *types, + LPWSTR *data, LPWSTR path, MSIRECORD **rec) +{ + UINT i; + + *rec = MSI_CreateRecord(num_columns); + if (!*rec) + return ERROR_OUTOFMEMORY; + + for (i = 0; i < num_columns; i++) + { + switch (types[i][0]) + { + case 'L': case 'l': case 'S': case 's': + MSI_RecordSetStringW(*rec, i + 1, data[i]); + break; + case 'I': case 'i': + if (*data[i]) + MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i])); + break; + case 'V': case 'v': + if (*data[i]) + { + UINT r; + LPWSTR file = msi_import_stream_filename(path, data[i]); + if (!file) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file); + msi_free (file); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + } + break; + default: + ERR("Unhandled column type: %c\n", types[i][0]); + msiobj_release(&(*rec)->hdr); + return ERROR_FUNCTION_FAILED; + } + } + + return ERROR_SUCCESS; +} + +static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, + LPWSTR *labels, LPWSTR **records, + int num_columns, int num_records, + LPWSTR path) +{ + UINT r; + int i; + MSIQUERY *view; + MSIRECORD *rec; + + static const WCHAR select[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','%','s','`',0 + }; + + r = MSI_OpenQuery(db, &view, select, labels[0]); + if (r != ERROR_SUCCESS) + return r; + + while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS) + { + r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec); + msiobj_release(&rec->hdr); + if (r != ERROR_SUCCESS) + goto done; + } + + for (i = 0; i < num_records; i++) + { + r = construct_record(num_columns, types, records[i], path, &rec); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec); + if (r != ERROR_SUCCESS) + { + msiobj_release(&rec->hdr); + goto done; + } + + msiobj_release(&rec->hdr); + } + +done: + msiobj_release(&view->hdr); + return r; +} + +static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file) +{ + UINT r; + DWORD len, i; + DWORD num_labels, num_types; + DWORD num_columns, num_records = 0; + LPWSTR *columns, *types, *labels; + LPWSTR path, ptr, data; + LPWSTR **records = NULL; + LPWSTR **temp_records; + + static const WCHAR suminfo[] = + {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0}; + static const WCHAR forcecodepage[] = + {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0}; + + TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) ); + + if( folder == NULL || file == NULL ) + return ERROR_INVALID_PARAMETER; + + len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1; + path = msi_alloc( len * sizeof(WCHAR) ); + if (!path) + return ERROR_OUTOFMEMORY; + + lstrcpyW( path, folder ); + lstrcatW( path, szBackSlash ); + lstrcatW( path, file ); + + data = msi_read_text_archive( path, &len ); + + ptr = data; + msi_parse_line( &ptr, &columns, &num_columns, &len ); + msi_parse_line( &ptr, &types, &num_types, &len ); + msi_parse_line( &ptr, &labels, &num_labels, &len ); + + if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] && + num_types == 2 && !strcmpW( types[1], forcecodepage )) + { + r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) ); + goto done; + } + + if (num_columns != num_types) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + records = msi_alloc(sizeof(LPWSTR *)); + if (!records) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + /* read in the table records */ + while (len) + { + msi_parse_line( &ptr, &records[num_records], NULL, &len ); + + num_records++; + temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *)); + if (!temp_records) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + records = temp_records; + } + + if (!strcmpW(labels[0], suminfo)) + { + r = msi_add_suminfo( db, records, num_records, num_columns ); + if (r != ERROR_SUCCESS) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + } + else + { + if (!TABLE_Exists(db, labels[0])) + { + r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns ); + if (r != ERROR_SUCCESS) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + } + + r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path ); + } + +done: + msi_free(path); + msi_free(data); + msi_free(columns); + msi_free(types); + msi_free(labels); + + for (i = 0; i < num_records; i++) + msi_free(records[i]); + + msi_free(records); + + return r; +} + +UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename)); + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiDatabaseImport not allowed during a custom action!\n"); + + return ERROR_SUCCESS; + } + + r = MSI_DatabaseImport( db, szFolder, szFilename ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, + LPCSTR szFolder, LPCSTR szFilename ) +{ + LPWSTR path = NULL, file = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename)); + + if( szFolder ) + { + path = strdupAtoW( szFolder ); + if( !path ) + goto end; + } + + if( szFilename ) + { + file = strdupAtoW( szFilename ); + if( !file ) + goto end; + } + + r = MsiDatabaseImportW( handle, path, file ); + +end: + msi_free( path ); + msi_free( file ); + + return r; +} + +static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start ) +{ + UINT i, count, len, r = ERROR_SUCCESS; + const char *sep; + char *buffer; + DWORD sz; + + len = 0x100; + buffer = msi_alloc( len ); + if ( !buffer ) + return ERROR_OUTOFMEMORY; + + count = MSI_RecordGetFieldCount( row ); + for ( i=start; i<=count; i++ ) + { + sz = len; + r = MSI_RecordGetStringA( row, i, buffer, &sz ); + if (r == ERROR_MORE_DATA) + { + char *p = msi_realloc( buffer, sz + 1 ); + if (!p) + break; + len = sz + 1; + buffer = p; + } + sz = len; + r = MSI_RecordGetStringA( row, i, buffer, &sz ); + if (r != ERROR_SUCCESS) + break; + + if (!WriteFile( handle, buffer, sz, &sz, NULL )) + { + r = ERROR_FUNCTION_FAILED; + break; + } + + sep = (i < count) ? "\t" : "\r\n"; + if (!WriteFile( handle, sep, strlen(sep), &sz, NULL )) + { + r = ERROR_FUNCTION_FAILED; + break; + } + } + msi_free( buffer ); + return r; +} + +static UINT msi_export_row( MSIRECORD *row, void *arg ) +{ + return msi_export_record( arg, row, 1 ); +} + +static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage ) +{ + static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n"; + char data[sizeof(fmt) + 10]; + DWORD sz; + + sprintf( data, fmt, codepage ); + + sz = lstrlenA(data) + 1; + if (!WriteFile(handle, data, sz, &sz, NULL)) + return ERROR_FUNCTION_FAILED; + + return ERROR_SUCCESS; +} + +static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, + LPCWSTR folder, LPCWSTR file ) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 }; + static const WCHAR forcecodepage[] = { + '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 }; + MSIRECORD *rec = NULL; + MSIQUERY *view = NULL; + LPWSTR filename; + HANDLE handle; + UINT len, r; + + TRACE("%p %s %s %s\n", db, debugstr_w(table), + debugstr_w(folder), debugstr_w(file) ); + + if( folder == NULL || file == NULL ) + return ERROR_INVALID_PARAMETER; + + len = lstrlenW(folder) + lstrlenW(file) + 2; + filename = msi_alloc(len * sizeof (WCHAR)); + if (!filename) + return ERROR_OUTOFMEMORY; + + lstrcpyW( filename, folder ); + lstrcatW( filename, szBackSlash ); + lstrcatW( filename, file ); + + handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + msi_free( filename ); + if (handle == INVALID_HANDLE_VALUE) + return ERROR_FUNCTION_FAILED; + + if (!strcmpW( table, forcecodepage )) + { + UINT codepage = msi_get_string_table_codepage( db->strings ); + r = msi_export_forcecodepage( handle, codepage ); + goto done; + } + + r = MSI_OpenQuery( db, &view, query, table ); + if (r == ERROR_SUCCESS) + { + /* write out row 1, the column names */ + r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + if (r == ERROR_SUCCESS) + { + msi_export_record( handle, rec, 1 ); + msiobj_release( &rec->hdr ); + } + + /* write out row 2, the column types */ + r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + if (r == ERROR_SUCCESS) + { + msi_export_record( handle, rec, 1 ); + msiobj_release( &rec->hdr ); + } + + /* write out row 3, the table name + keys */ + r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); + if (r == ERROR_SUCCESS) + { + MSI_RecordSetStringW( rec, 0, table ); + msi_export_record( handle, rec, 0 ); + msiobj_release( &rec->hdr ); + } + + /* write out row 4 onwards, the data */ + r = MSI_IterateRecords( view, 0, msi_export_row, handle ); + msiobj_release( &view->hdr ); + } + +done: + CloseHandle( handle ); + return r; +} + +/*********************************************************************** + * MsiExportDatabaseW [MSI.@] + * + * Writes a file containing the table data as tab separated ASCII. + * + * The format is as follows: + * + * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf> + * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf> + * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf> + * + * Followed by the data, starting at row 1 with one row per line + * + * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf> + */ +UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable, + LPCWSTR szFolder, LPCWSTR szFilename ) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%x %s %s %s\n", handle, debugstr_w(szTable), + debugstr_w(szFolder), debugstr_w(szFilename)); + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiDatabaseExport not allowed during a custom action!\n"); + + return ERROR_SUCCESS; + } + + r = MSI_DatabaseExport( db, szTable, szFolder, szFilename ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable, + LPCSTR szFolder, LPCSTR szFilename ) +{ + LPWSTR path = NULL, file = NULL, table = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%x %s %s %s\n", handle, debugstr_a(szTable), + debugstr_a(szFolder), debugstr_a(szFilename)); + + if( szTable ) + { + table = strdupAtoW( szTable ); + if( !table ) + goto end; + } + + if( szFolder ) + { + path = strdupAtoW( szFolder ); + if( !path ) + goto end; + } + + if( szFilename ) + { + file = strdupAtoW( szFilename ); + if( !file ) + goto end; + } + + r = MsiDatabaseExportW( handle, table, path, file ); + +end: + msi_free( table ); + msi_free( path ); + msi_free( file ); + + return r; +} + +UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, + LPCSTR szTableName) +{ + UINT r; + LPWSTR table; + + TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge, + debugstr_a(szTableName)); + + table = strdupAtoW(szTableName); + r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table); + + msi_free(table); + return r; +} + +typedef struct _tagMERGETABLE +{ + struct list entry; + struct list rows; + LPWSTR name; + DWORD numconflicts; + LPWSTR *columns; + DWORD numcolumns; + LPWSTR *types; + DWORD numtypes; + LPWSTR *labels; + DWORD numlabels; +} MERGETABLE; + +typedef struct _tagMERGEROW +{ + struct list entry; + MSIRECORD *data; +} MERGEROW; + +typedef struct _tagMERGEDATA +{ + MSIDATABASE *db; + MSIDATABASE *merge; + MERGETABLE *curtable; + MSIQUERY *curview; + struct list *tabledata; +} MERGEDATA; + +static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2) +{ + if (((type1[0] == 'l') || (type1[0] == 's')) && + ((type2[0] == 'l') || (type2[0] == 's'))) + return TRUE; + + if (((type1[0] == 'L') || (type1[0] == 'S')) && + ((type2[0] == 'L') || (type2[0] == 'S'))) + return TRUE; + + return !strcmpW( type1, type2 ); +} + +static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview) +{ + MSIRECORD *dbrec, *mergerec; + UINT r, i, count; + + r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec); + if (r != ERROR_SUCCESS) + return r; + + count = MSI_RecordGetFieldCount(dbrec); + for (i = 1; i <= count; i++) + { + if (!MSI_RecordGetString(mergerec, i)) + break; + + if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) )) + { + r = ERROR_DATATYPE_MISMATCH; + goto done; + } + } + + msiobj_release(&dbrec->hdr); + msiobj_release(&mergerec->hdr); + dbrec = mergerec = NULL; + + r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec); + if (r != ERROR_SUCCESS) + return r; + + count = MSI_RecordGetFieldCount(dbrec); + for (i = 1; i <= count; i++) + { + if (!MSI_RecordGetString(mergerec, i)) + break; + + if (!merge_type_match(MSI_RecordGetString(dbrec, i), + MSI_RecordGetString(mergerec, i))) + { + r = ERROR_DATATYPE_MISMATCH; + break; + } + } + +done: + msiobj_release(&dbrec->hdr); + msiobj_release(&mergerec->hdr); + + return r; +} + +static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb, + LPCWSTR table) +{ + MSIRECORD *dbrec, *mergerec = NULL; + UINT r, i, count; + + r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec); + if (r != ERROR_SUCCESS) + goto done; + + count = MSI_RecordGetFieldCount(dbrec); + if (count != MSI_RecordGetFieldCount(mergerec)) + { + r = ERROR_DATATYPE_MISMATCH; + goto done; + } + + for (i = 1; i <= count; i++) + { + if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) )) + { + r = ERROR_DATATYPE_MISMATCH; + goto done; + } + } + +done: + msiobj_release(&dbrec->hdr); + msiobj_release(&mergerec->hdr); + + return r; +} + +static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec) +{ + MSIRECORD *colnames; + LPWSTR str, val; + UINT r, i = 0, sz = 0; + int cmp; + + r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames); + if (r != ERROR_SUCCESS) + return NULL; + + do + { + str = msi_dup_record_field(colnames, ++i); + cmp = strcmpW( key, str ); + msi_free(str); + } while (cmp); + + msiobj_release(&colnames->hdr); + + r = MSI_RecordGetStringW(rec, i, NULL, &sz); + if (r != ERROR_SUCCESS) + return NULL; + sz++; + + if (MSI_RecordGetString(rec, i)) /* check record field is a string */ + { + /* quote string record fields */ + const WCHAR szQuote[] = {'\'', 0}; + sz += 2; + val = msi_alloc(sz*sizeof(WCHAR)); + if (!val) + return NULL; + + lstrcpyW(val, szQuote); + r = MSI_RecordGetStringW(rec, i, val+1, &sz); + lstrcpyW(val+1+sz, szQuote); + } + else + { + /* do not quote integer record fields */ + val = msi_alloc(sz*sizeof(WCHAR)); + if (!val) + return NULL; + + r = MSI_RecordGetStringW(rec, i, val, &sz); + } + + if (r != ERROR_SUCCESS) + { + ERR("failed to get string!\n"); + msi_free(val); + return NULL; + } + + return val; +} + +static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view, + LPWSTR table, MSIRECORD *rec) +{ + LPWSTR query = NULL, clause = NULL, val; + LPCWSTR setptr, key; + DWORD size, oldsize; + MSIRECORD *keys; + UINT r, i, count; + + static const WCHAR keyset[] = { + '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0}; + static const WCHAR lastkeyset[] = { + '`','%','s','`',' ','=',' ','%','s',' ',0}; + static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','%','s','`',' ', + 'W','H','E','R','E',' ','%','s',0}; + + r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys); + if (r != ERROR_SUCCESS) + return NULL; + + clause = msi_alloc_zero(sizeof(WCHAR)); + if (!clause) + goto done; + + size = 1; + count = MSI_RecordGetFieldCount(keys); + for (i = 1; i <= count; i++) + { + key = MSI_RecordGetString(keys, i); + val = get_key_value(view, key, rec); + + if (i == count) + setptr = lastkeyset; + else + setptr = keyset; + + oldsize = size; + size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4; + clause = msi_realloc(clause, size * sizeof (WCHAR)); + if (!clause) + { + msi_free(val); + goto done; + } + + sprintfW(clause + oldsize - 1, setptr, key, val); + msi_free(val); + } + + size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1; + query = msi_alloc(size * sizeof(WCHAR)); + if (!query) + goto done; + + sprintfW(query, fmt, table, clause); + +done: + msi_free(clause); + msiobj_release(&keys->hdr); + return query; +} + +static UINT merge_diff_row(MSIRECORD *rec, LPVOID param) +{ + MERGEDATA *data = param; + MERGETABLE *table = data->curtable; + MERGEROW *mergerow; + MSIQUERY *dbview = NULL; + MSIRECORD *row = NULL; + LPWSTR query = NULL; + UINT r = ERROR_SUCCESS; + + if (TABLE_Exists(data->db, table->name)) + { + query = create_diff_row_query(data->merge, data->curview, table->name, rec); + if (!query) + return ERROR_OUTOFMEMORY; + + r = MSI_DatabaseOpenViewW(data->db, query, &dbview); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewExecute(dbview, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(dbview, &row); + if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row)) + { + table->numconflicts++; + goto done; + } + else if (r != ERROR_NO_MORE_ITEMS) + goto done; + + r = ERROR_SUCCESS; + } + + mergerow = msi_alloc(sizeof(MERGEROW)); + if (!mergerow) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + mergerow->data = MSI_CloneRecord(rec); + if (!mergerow->data) + { + r = ERROR_OUTOFMEMORY; + msi_free(mergerow); + goto done; + } + + list_add_tail(&table->rows, &mergerow->entry); + +done: + msi_free(query); + msiobj_release(&row->hdr); + msiobj_release(&dbview->hdr); + return r; +} + +static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels) +{ + UINT r, i, count; + MSIRECORD *prec = NULL; + + r = MSI_DatabaseGetPrimaryKeys(db, table, &prec); + if (r != ERROR_SUCCESS) + return r; + + count = MSI_RecordGetFieldCount(prec); + *numlabels = count + 1; + *labels = msi_alloc((*numlabels)*sizeof(LPWSTR)); + if (!*labels) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + (*labels)[0] = strdupW(table); + for (i=1; i<=count; i++ ) + { + (*labels)[i] = strdupW(MSI_RecordGetString(prec, i)); + } + +end: + msiobj_release( &prec->hdr ); + return r; +} + +static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns) +{ + UINT r, i, count; + MSIRECORD *prec = NULL; + + r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec); + if (r != ERROR_SUCCESS) + return r; + + count = MSI_RecordGetFieldCount(prec); + *columns = msi_alloc(count*sizeof(LPWSTR)); + if (!*columns) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + for (i=1; i<=count; i++ ) + { + (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i)); + } + + *numcolumns = count; + +end: + msiobj_release( &prec->hdr ); + return r; +} + +static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes) +{ + UINT r, i, count; + MSIRECORD *prec = NULL; + + r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec); + if (r != ERROR_SUCCESS) + return r; + + count = MSI_RecordGetFieldCount(prec); + *types = msi_alloc(count*sizeof(LPWSTR)); + if (!*types) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + *numtypes = count; + for (i=1; i<=count; i++ ) + { + (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i)); + } + +end: + msiobj_release( &prec->hdr ); + return r; +} + +static void merge_free_rows(MERGETABLE *table) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE(item, cursor, &table->rows) + { + MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry); + + list_remove(&row->entry); + msiobj_release(&row->data->hdr); + msi_free(row); + } +} + +static void free_merge_table(MERGETABLE *table) +{ + UINT i; + + if (table->labels != NULL) + { + for (i = 0; i < table->numlabels; i++) + msi_free(table->labels[i]); + + msi_free(table->labels); + } + + if (table->columns != NULL) + { + for (i = 0; i < table->numcolumns; i++) + msi_free(table->columns[i]); + + msi_free(table->columns); + } + + if (table->types != NULL) + { + for (i = 0; i < table->numtypes; i++) + msi_free(table->types[i]); + + msi_free(table->types); + } + + msi_free(table->name); + merge_free_rows(table); + + msi_free(table); +} + +static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable) +{ + UINT r; + MERGETABLE *table; + MSIQUERY *mergeview = NULL; + + static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','%','s','`',0}; + + table = msi_alloc_zero(sizeof(MERGETABLE)); + if (!table) + { + *ptable = NULL; + return ERROR_OUTOFMEMORY; + } + + r = msi_get_table_labels(db, name, &table->labels, &table->numlabels); + if (r != ERROR_SUCCESS) + goto err; + + r = MSI_OpenQuery(db, &mergeview, query, name); + if (r != ERROR_SUCCESS) + goto err; + + r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns); + if (r != ERROR_SUCCESS) + goto err; + + r = msi_get_query_types(mergeview, &table->types, &table->numtypes); + if (r != ERROR_SUCCESS) + goto err; + + list_init(&table->rows); + + table->name = strdupW(name); + table->numconflicts = 0; + + msiobj_release(&mergeview->hdr); + *ptable = table; + return ERROR_SUCCESS; + +err: + msiobj_release(&mergeview->hdr); + free_merge_table(table); + *ptable = NULL; + return r; +} + +static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param) +{ + MERGEDATA *data = param; + MERGETABLE *table; + MSIQUERY *dbview = NULL; + MSIQUERY *mergeview = NULL; + LPCWSTR name; + UINT r; + + static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','%','s','`',0}; + + name = MSI_RecordGetString(rec, 1); + + r = MSI_OpenQuery(data->merge, &mergeview, query, name); + if (r != ERROR_SUCCESS) + goto done; + + if (TABLE_Exists(data->db, name)) + { + r = MSI_OpenQuery(data->db, &dbview, query, name); + if (r != ERROR_SUCCESS) + goto done; + + r = merge_verify_colnames(dbview, mergeview); + if (r != ERROR_SUCCESS) + goto done; + + r = merge_verify_primary_keys(data->db, data->merge, name); + if (r != ERROR_SUCCESS) + goto done; + } + + r = msi_get_merge_table(data->merge, name, &table); + if (r != ERROR_SUCCESS) + goto done; + + data->curtable = table; + data->curview = mergeview; + r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data); + if (r != ERROR_SUCCESS) + { + free_merge_table(table); + goto done; + } + + list_add_tail(data->tabledata, &table->entry); + +done: + msiobj_release(&dbview->hdr); + msiobj_release(&mergeview->hdr); + return r; +} + +static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge, + struct list *tabledata) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','_','T','a','b','l','e','s','`',0}; + MSIQUERY *view; + MERGEDATA data; + UINT r; + + r = MSI_DatabaseOpenViewW(merge, query, &view); + if (r != ERROR_SUCCESS) + return r; + + data.db = db; + data.merge = merge; + data.tabledata = tabledata; + r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data); + msiobj_release(&view->hdr); + return r; +} + +static UINT merge_table(MSIDATABASE *db, MERGETABLE *table) +{ + UINT r; + MERGEROW *row; + MSIVIEW *tv; + + if (!TABLE_Exists(db, table->name)) + { + r = msi_add_table_to_db(db, table->columns, table->types, + table->labels, table->numlabels, table->numcolumns); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + } + + LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry) + { + r = TABLE_CreateView(db, table->name, &tv); + if (r != ERROR_SUCCESS) + return r; + + r = tv->ops->insert_row(tv, row->data, -1, FALSE); + tv->ops->delete(tv); + + if (r != ERROR_SUCCESS) + return r; + } + + return ERROR_SUCCESS; +} + +static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error, + LPWSTR table, DWORD numconflicts) +{ + UINT r; + MSIQUERY *view; + + static const WCHAR create[] = { + 'C','R','E','A','T','E',' ','T','A','B','L','E',' ', + '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ', + 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ', + 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e', + 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ', + 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ', + 'K','E','Y',' ','`','T','a','b','l','e','`',')',0}; + static const WCHAR insert[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ', + '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ', + '`','N','u','m','R','o','w','M','e','r','g','e', + 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S', + ' ','(','\'','%','s','\'',',',' ','%','d',')',0}; + + if (!TABLE_Exists(db, error)) + { + r = MSI_OpenQuery(db, &view, create, error); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewExecute(view, NULL); + msiobj_release(&view->hdr); + if (r != ERROR_SUCCESS) + return r; + } + + r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewExecute(view, NULL); + msiobj_release(&view->hdr); + return r; +} + +UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, + LPCWSTR szTableName) +{ + struct list tabledata = LIST_INIT(tabledata); + struct list *item, *cursor; + MSIDATABASE *db, *merge; + MERGETABLE *table; + BOOL conflicts; + UINT r; + + TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge, + debugstr_w(szTableName)); + + if (szTableName && !*szTableName) + return ERROR_INVALID_TABLE; + + db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE); + merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE); + if (!db || !merge) + { + r = ERROR_INVALID_HANDLE; + goto done; + } + + r = gather_merge_data(db, merge, &tabledata); + if (r != ERROR_SUCCESS) + goto done; + + conflicts = FALSE; + LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry) + { + if (table->numconflicts) + { + conflicts = TRUE; + + r = update_merge_errors(db, szTableName, table->name, + table->numconflicts); + if (r != ERROR_SUCCESS) + break; + } + else + { + r = merge_table(db, table); + if (r != ERROR_SUCCESS) + break; + } + } + + LIST_FOR_EACH_SAFE(item, cursor, &tabledata) + { + MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry); + list_remove(&table->entry); + free_merge_table(table); + } + + if (conflicts) + r = ERROR_FUNCTION_FAILED; + +done: + msiobj_release(&db->hdr); + msiobj_release(&merge->hdr); + return r; +} + +MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle ) +{ + MSIDBSTATE ret = MSIDBSTATE_READ; + MSIDATABASE *db; + + TRACE("%d\n", handle); + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); + if ( !remote_database ) + return MSIDBSTATE_ERROR; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiGetDatabaseState not allowed during a custom action!\n"); + + return MSIDBSTATE_READ; + } + + if (db->mode != MSIDBOPEN_READONLY ) + ret = MSIDBSTATE_WRITE; + msiobj_release( &db->hdr ); + + return ret; +} + +typedef struct _msi_remote_database_impl { + IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface; + MSIHANDLE database; + LONG refs; +} msi_remote_database_impl; + +static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface ) +{ + return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface); +} + +static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface, + REFIID riid,LPVOID *ppobj) +{ + if( IsEqualCLSID( riid, &IID_IUnknown ) || + IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) ) + { + IWineMsiRemoteDatabase_AddRef( iface ); + *ppobj = iface; + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface ) +{ + msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); + + return InterlockedIncrement( &This->refs ); +} + +static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface ) +{ + msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); + ULONG r; + + r = InterlockedDecrement( &This->refs ); + if (r == 0) + { + MsiCloseHandle( This->database ); + msi_free( This ); + } + return r; +} + +static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface, + LPCWSTR table, MSICONDITION *persistent ) +{ + msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); + *persistent = MsiDatabaseIsTablePersistentW(This->database, table); + return S_OK; +} + +static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface, + LPCWSTR table, MSIHANDLE *keys ) +{ + msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); + UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface, + UINT updatecount, MSIHANDLE *suminfo ) +{ + msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); + UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface, + LPCWSTR query, MSIHANDLE *view ) +{ + msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); + UINT r = MsiDatabaseOpenViewW(This->database, query, view); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle ) +{ + msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); + This->database = handle; + return S_OK; +} + +static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl = +{ + mrd_QueryInterface, + mrd_AddRef, + mrd_Release, + mrd_IsTablePersistent, + mrd_GetPrimaryKeys, + mrd_GetSummaryInformation, + mrd_OpenView, + mrd_SetMsiHandle, +}; + +HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj ) +{ + msi_remote_database_impl *This; + + This = msi_alloc( sizeof *This ); + if (!This) + return E_OUTOFMEMORY; + + This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl; + This->database = 0; + This->refs = 1; + + *ppObj = This; + + return S_OK; +} diff --git a/libmsi/delete.c b/libmsi/delete.c new file mode 100644 index 0000000..4472409 --- /dev/null +++ b/libmsi/delete.c @@ -0,0 +1,217 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* + * Code to delete rows from a table. + * + * We delete rows by blanking them out rather than trying to remove the row. + * This appears to be what the native MSI does (or tries to do). For the query: + * + * delete from Property + * + * some non-zero entries are left in the table by native MSI. I'm not sure if + * that's a bug in the way I'm running the query, or a just a bug. + */ + +typedef struct tagMSIDELETEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; +} MSIDELETEVIEW; + +static UINT DELETE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, stm ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + UINT r, i, rows = 0, cols = 0; + + TRACE("%p %p\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute( dv->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = dv->table->ops->get_dimensions( dv->table, &rows, &cols ); + if( r != ERROR_SUCCESS ) + return r; + + TRACE("deleting %d rows\n", rows); + + /* blank out all the rows that match */ + for ( i=0; i<rows; i++ ) + dv->table->ops->delete_row( dv->table, i ); + + return ERROR_SUCCESS; +} + +static UINT DELETE_close( struct tagMSIVIEW *view ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->close( dv->table ); +} + +static UINT DELETE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + *rows = 0; + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DELETE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %p %p %p %p\n", dv, n, name, type, temporary, table_name ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, + type, temporary, table_name); +} + +static UINT DELETE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %p\n", dv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_delete( struct tagMSIVIEW *view ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + msi_free( dv ); + + return ERROR_SUCCESS; +} + +static UINT DELETE_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + return ERROR_FUNCTION_FAILED; +} + + +static const MSIVIEWOPS delete_ops = +{ + DELETE_fetch_int, + DELETE_fetch_stream, + NULL, + NULL, + NULL, + NULL, + DELETE_execute, + DELETE_close, + DELETE_get_dimensions, + DELETE_get_column_info, + DELETE_modify, + DELETE_delete, + DELETE_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIDELETEVIEW *dv = NULL; + + TRACE("%p\n", dv ); + + dv = msi_alloc_zero( sizeof *dv ); + if( !dv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + dv->view.ops = &delete_ops; + dv->db = db; + dv->table = table; + + *view = &dv->view; + + return ERROR_SUCCESS; +} diff --git a/libmsi/dialog.c b/libmsi/dialog.c new file mode 100644 index 0000000..66d8786 --- /dev/null +++ b/libmsi/dialog.c @@ -0,0 +1,4164 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack 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 + */ + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "msi.h" +#include "msipriv.h" +#include "msidefs.h" +#include "ocidl.h" +#include "olectl.h" +#include "richedit.h" +#include "commctrl.h" +#include "winreg.h" +#include "shlwapi.h" +#include "msiserver.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +extern HINSTANCE msi_hInstance; + +struct msi_control_tag; +typedef struct msi_control_tag msi_control; +typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM ); +typedef void (*msi_update)( msi_dialog *, msi_control * ); + +struct msi_control_tag +{ + struct list entry; + HWND hwnd; + msi_handler handler; + msi_update update; + LPWSTR property; + LPWSTR value; + HBITMAP hBitmap; + HICON hIcon; + LPWSTR tabnext; + LPWSTR type; + HMODULE hDll; + float progress_current; + float progress_max; + BOOL progress_backwards; + DWORD attributes; + WCHAR name[1]; +}; + +typedef struct msi_font_tag +{ + struct list entry; + HFONT hfont; + COLORREF color; + WCHAR name[1]; +} msi_font; + +struct msi_dialog_tag +{ + MSIPACKAGE *package; + msi_dialog *parent; + msi_dialog_event_handler event_handler; + BOOL finished; + INT scale; + DWORD attributes; + SIZE size; + HWND hwnd; + LPWSTR default_font; + struct list fonts; + struct list controls; + HWND hWndFocus; + LPWSTR control_default; + LPWSTR control_cancel; + WCHAR name[1]; +}; + +typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec ); +struct control_handler +{ + LPCWSTR control_type; + msi_dialog_control_func func; +}; + +typedef struct +{ + msi_dialog* dialog; + msi_control *parent; + DWORD attributes; + LPWSTR propval; +} radio_button_group_descr; + +static const WCHAR szMsiDialogClass[] = { 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0 }; +static const WCHAR szMsiHiddenWindow[] = { 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 }; +static const WCHAR szStatic[] = { 'S','t','a','t','i','c',0 }; +static const WCHAR szButton[] = { 'B','U','T','T','O','N', 0 }; +static const WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 }; +static const WCHAR szProgress[] = { 'P','r','o','g','r','e','s','s',0 }; +static const WCHAR szText[] = { 'T','e','x','t',0 }; +static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 }; +static const WCHAR szLine[] = { 'L','i','n','e',0 }; +static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 }; +static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 }; +static const WCHAR szScrollableText[] = { 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 }; +static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 }; +static const WCHAR szEdit[] = { 'E','d','i','t',0 }; +static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 }; +static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 }; +static const WCHAR szProgressBar[] = { 'P','r','o','g','r','e','s','s','B','a','r',0 }; +static const WCHAR szSetProgress[] = { 'S','e','t','P','r','o','g','r','e','s','s',0 }; +static const WCHAR szRadioButtonGroup[] = { 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 }; +static const WCHAR szIcon[] = { 'I','c','o','n',0 }; +static const WCHAR szSelectionTree[] = { 'S','e','l','e','c','t','i','o','n','T','r','e','e',0 }; +static const WCHAR szGroupBox[] = { 'G','r','o','u','p','B','o','x',0 }; +static const WCHAR szListBox[] = { 'L','i','s','t','B','o','x',0 }; +static const WCHAR szDirectoryCombo[] = { 'D','i','r','e','c','t','o','r','y','C','o','m','b','o',0 }; +static const WCHAR szDirectoryList[] = { 'D','i','r','e','c','t','o','r','y','L','i','s','t',0 }; +static const WCHAR szVolumeCostList[] = { 'V','o','l','u','m','e','C','o','s','t','L','i','s','t',0 }; +static const WCHAR szVolumeSelectCombo[] = { 'V','o','l','u','m','e','S','e','l','e','c','t','C','o','m','b','o',0 }; +static const WCHAR szSelectionDescription[] = {'S','e','l','e','c','t','i','o','n','D','e','s','c','r','i','p','t','i','o','n',0}; +static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0}; +static const WCHAR szProperty[] = {'P','r','o','p','e','r','t','y',0}; + +/* dialog sequencing */ + +#define WM_MSI_DIALOG_CREATE (WM_USER+0x100) +#define WM_MSI_DIALOG_DESTROY (WM_USER+0x101) + +#define USER_INSTALLSTATE_ALL 0x1000 + +static DWORD uiThreadId; +static HWND hMsiHiddenWindow; + +static LPWSTR msi_get_window_text( HWND hwnd ) +{ + UINT sz, r; + LPWSTR buf; + + sz = 0x20; + buf = msi_alloc( sz*sizeof(WCHAR) ); + while ( buf ) + { + r = GetWindowTextW( hwnd, buf, sz ); + if ( r < (sz - 1) ) + break; + sz *= 2; + buf = msi_realloc( buf, sz*sizeof(WCHAR) ); + } + + return buf; +} + +static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val ) +{ + return MulDiv( val, dialog->scale, 12 ); +} + +static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name ) +{ + msi_control *control; + + if( !name ) + return NULL; + if( !dialog->hwnd ) + return NULL; + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */ + return control; + return NULL; +} + +static msi_control *msi_dialog_find_control_by_type( msi_dialog *dialog, LPCWSTR type ) +{ + msi_control *control; + + if( !type ) + return NULL; + if( !dialog->hwnd ) + return NULL; + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + if( !strcmpW( control->type, type ) ) /* FIXME: case sensitive? */ + return control; + return NULL; +} + +static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd ) +{ + msi_control *control; + + if( !dialog->hwnd ) + return NULL; + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + if( hwnd == control->hwnd ) + return control; + return NULL; +} + +static LPWSTR msi_get_deformatted_field( MSIPACKAGE *package, MSIRECORD *rec, int field ) +{ + LPCWSTR str = MSI_RecordGetString( rec, field ); + LPWSTR ret = NULL; + + if (str) + deformat_string( package, str, &ret ); + return ret; +} + +static LPWSTR msi_dialog_dup_property( msi_dialog *dialog, LPCWSTR property, BOOL indirect ) +{ + LPWSTR prop = NULL; + + if (!property) + return NULL; + + if (indirect) + prop = msi_dup_property( dialog->package->db, property ); + + if (!prop) + prop = strdupW( property ); + + return prop; +} + +msi_dialog *msi_dialog_get_parent( msi_dialog *dialog ) +{ + return dialog->parent; +} + +LPWSTR msi_dialog_get_name( msi_dialog *dialog ) +{ + return dialog->name; +} + +/* + * msi_dialog_get_style + * + * Extract the {\style} string from the front of the text to display and + * update the pointer. Only the last style in a list is applied. + */ +static LPWSTR msi_dialog_get_style( LPCWSTR p, LPCWSTR *rest ) +{ + LPWSTR ret; + LPCWSTR q, i, first; + DWORD len; + + q = NULL; + *rest = p; + if( !p ) + return NULL; + + while ((first = strchrW( p, '{' )) && (q = strchrW( first + 1, '}' ))) + { + p = first + 1; + if( *p != '\\' && *p != '&' ) + return NULL; + + /* little bit of sanity checking to stop us getting confused with RTF */ + for( i=++p; i<q; i++ ) + if( *i == '}' || *i == '\\' ) + return NULL; + } + + if (!q) + return NULL; + + *rest = ++q; + len = q - p; + + ret = msi_alloc( len*sizeof(WCHAR) ); + if( !ret ) + return ret; + memcpy( ret, p, len*sizeof(WCHAR) ); + ret[len-1] = 0; + return ret; +} + +static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param ) +{ + msi_dialog *dialog = param; + msi_font *font; + LPCWSTR face, name; + LOGFONTW lf; + INT style; + HDC hdc; + + /* create a font and add it to the list */ + name = MSI_RecordGetString( rec, 1 ); + font = msi_alloc( sizeof *font + strlenW( name )*sizeof (WCHAR) ); + strcpyW( font->name, name ); + list_add_head( &dialog->fonts, &font->entry ); + + font->color = MSI_RecordGetInteger( rec, 4 ); + + memset( &lf, 0, sizeof lf ); + face = MSI_RecordGetString( rec, 2 ); + lf.lfHeight = MSI_RecordGetInteger( rec, 3 ); + style = MSI_RecordGetInteger( rec, 5 ); + if( style & msidbTextStyleStyleBitsBold ) + lf.lfWeight = FW_BOLD; + if( style & msidbTextStyleStyleBitsItalic ) + lf.lfItalic = TRUE; + if( style & msidbTextStyleStyleBitsUnderline ) + lf.lfUnderline = TRUE; + if( style & msidbTextStyleStyleBitsStrike ) + lf.lfStrikeOut = TRUE; + lstrcpynW( lf.lfFaceName, face, LF_FACESIZE ); + + /* adjust the height */ + hdc = GetDC( dialog->hwnd ); + if (hdc) + { + lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC( dialog->hwnd, hdc ); + } + + font->hfont = CreateFontIndirectW( &lf ); + + TRACE("Adding font style %s\n", debugstr_w(font->name) ); + + return ERROR_SUCCESS; +} + +static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name ) +{ + msi_font *font = NULL; + + LIST_FOR_EACH_ENTRY( font, &dialog->fonts, msi_font, entry ) + if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */ + break; + + return font; +} + +static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name ) +{ + msi_font *font; + + font = msi_dialog_find_font( dialog, name ); + if( font ) + SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE ); + else + ERR("No font entry for %s\n", debugstr_w(name)); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_build_font_list( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','T','e','x','t','S','t','y','l','e','`',0}; + MSIQUERY *view; + UINT r; + + TRACE("dialog %p\n", dialog ); + + r = MSI_OpenQuery( dialog->package->db, &view, query ); + if( r != ERROR_SUCCESS ) + return r; + + r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog ); + msiobj_release( &view->hdr ); + return r; +} + +static void msi_destroy_control( msi_control *t ) +{ + list_remove( &t->entry ); + /* leave dialog->hwnd - destroying parent destroys child windows */ + msi_free( t->property ); + msi_free( t->value ); + if( t->hBitmap ) + DeleteObject( t->hBitmap ); + if( t->hIcon ) + DestroyIcon( t->hIcon ); + msi_free( t->tabnext ); + msi_free( t->type ); + if (t->hDll) + FreeLibrary( t->hDll ); + msi_free( t ); +} + +static msi_control *msi_dialog_create_window( msi_dialog *dialog, + MSIRECORD *rec, DWORD exstyle, LPCWSTR szCls, LPCWSTR name, LPCWSTR text, + DWORD style, HWND parent ) +{ + DWORD x, y, width, height; + LPWSTR font = NULL, title_font = NULL; + LPCWSTR title = NULL; + msi_control *control; + + style |= WS_CHILD; + + control = msi_alloc( sizeof *control + strlenW(name)*sizeof(WCHAR) ); + if (!control) + return NULL; + + strcpyW( control->name, name ); + list_add_tail( &dialog->controls, &control->entry ); + control->handler = NULL; + control->update = NULL; + control->property = NULL; + control->value = NULL; + control->hBitmap = NULL; + control->hIcon = NULL; + control->hDll = NULL; + control->tabnext = strdupW( MSI_RecordGetString( rec, 11) ); + control->type = strdupW( MSI_RecordGetString( rec, 3 ) ); + control->progress_current = 0; + control->progress_max = 100; + control->progress_backwards = FALSE; + + x = MSI_RecordGetInteger( rec, 4 ); + y = MSI_RecordGetInteger( rec, 5 ); + width = MSI_RecordGetInteger( rec, 6 ); + height = MSI_RecordGetInteger( rec, 7 ); + + x = msi_dialog_scale_unit( dialog, x ); + y = msi_dialog_scale_unit( dialog, y ); + width = msi_dialog_scale_unit( dialog, width ); + height = msi_dialog_scale_unit( dialog, height ); + + if( text ) + { + deformat_string( dialog->package, text, &title_font ); + font = msi_dialog_get_style( title_font, &title ); + } + + control->hwnd = CreateWindowExW( exstyle, szCls, title, style, + x, y, width, height, parent, NULL, NULL, NULL ); + + TRACE("Dialog %s control %s hwnd %p\n", + debugstr_w(dialog->name), debugstr_w(text), control->hwnd ); + + msi_dialog_set_font( dialog, control->hwnd, + font ? font : dialog->default_font ); + + msi_free( title_font ); + msi_free( font ); + + return control; +} + +static LPWSTR msi_dialog_get_uitext( msi_dialog *dialog, LPCWSTR key ) +{ + MSIRECORD *rec; + LPWSTR text; + + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ','`','U','I','T','e','x','t','`',' ', + 'w','h','e','r','e',' ','`','K','e','y','`',' ','=',' ','\'','%','s','\'',0 + }; + + rec = MSI_QueryGetRecord( dialog->package->db, query, key ); + if (!rec) return NULL; + text = strdupW( MSI_RecordGetString( rec, 2 ) ); + msiobj_release( &rec->hdr ); + return text; +} + +static MSIRECORD *msi_get_binary_record( MSIDATABASE *db, LPCWSTR name ) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ','B','i','n','a','r','y',' ', + 'w','h','e','r','e',' ', + '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0 + }; + + return MSI_QueryGetRecord( db, query, name ); +} + +static LPWSTR msi_create_tmp_path(void) +{ + WCHAR tmp[MAX_PATH]; + LPWSTR path = NULL; + DWORD len, r; + + r = GetTempPathW( MAX_PATH, tmp ); + if( !r ) + return path; + len = lstrlenW( tmp ) + 20; + path = msi_alloc( len * sizeof (WCHAR) ); + if( path ) + { + r = GetTempFileNameW( tmp, szMsi, 0, path ); + if (!r) + { + msi_free( path ); + path = NULL; + } + } + return path; +} + +static HANDLE msi_load_image( MSIDATABASE *db, LPCWSTR name, UINT type, + UINT cx, UINT cy, UINT flags ) +{ + MSIRECORD *rec = NULL; + HANDLE himage = NULL; + LPWSTR tmp; + UINT r; + + TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags); + + tmp = msi_create_tmp_path(); + if( !tmp ) + return himage; + + rec = msi_get_binary_record( db, name ); + if( rec ) + { + r = MSI_RecordStreamToFile( rec, 2, tmp ); + if( r == ERROR_SUCCESS ) + { + himage = LoadImageW( 0, tmp, type, cx, cy, flags ); + } + msiobj_release( &rec->hdr ); + } + DeleteFileW( tmp ); + + msi_free( tmp ); + return himage; +} + +static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes ) +{ + DWORD cx = 0, cy = 0, flags; + + flags = LR_LOADFROMFILE | LR_DEFAULTSIZE; + if( attributes & msidbControlAttributesFixedSize ) + { + flags &= ~LR_DEFAULTSIZE; + if( attributes & msidbControlAttributesIconSize16 ) + { + cx += 16; + cy += 16; + } + if( attributes & msidbControlAttributesIconSize32 ) + { + cx += 32; + cy += 32; + } + /* msidbControlAttributesIconSize48 handled by above logic */ + } + return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags ); +} + +static void msi_dialog_update_controls( msi_dialog *dialog, LPCWSTR property ) +{ + msi_control *control; + + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + { + if ( control->property && !strcmpW( control->property, property ) && control->update ) + control->update( dialog, control ); + } +} + +static void msi_dialog_set_property( MSIPACKAGE *package, LPCWSTR property, LPCWSTR value ) +{ + UINT r = msi_set_property( package->db, property, value ); + if (r == ERROR_SUCCESS && !strcmpW( property, szSourceDir )) + msi_reset_folders( package, TRUE ); +} + +static MSIFEATURE *msi_seltree_feature_from_item( HWND hwnd, HTREEITEM hItem ) +{ + TVITEMW tvi; + + /* get the feature from the item */ + memset( &tvi, 0, sizeof tvi ); + tvi.hItem = hItem; + tvi.mask = TVIF_PARAM | TVIF_HANDLE; + SendMessageW( hwnd, TVM_GETITEMW, 0, (LPARAM)&tvi ); + return (MSIFEATURE *)tvi.lParam; +} + +struct msi_selection_tree_info +{ + msi_dialog *dialog; + HWND hwnd; + WNDPROC oldproc; + HTREEITEM selected; +}; + +static MSIFEATURE *msi_seltree_get_selected_feature( msi_control *control ) +{ + struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData ); + return msi_seltree_feature_from_item( control->hwnd, info->selected ); +} + +/* called from the Control Event subscription code */ +void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control, + LPCWSTR attribute, MSIRECORD *rec ) +{ + msi_control* ctrl; + LPCWSTR font_text, text = NULL; + LPWSTR font; + + ctrl = msi_dialog_find_control( dialog, control ); + if (!ctrl) + return; + if( !strcmpW( attribute, szText ) ) + { + font_text = MSI_RecordGetString( rec , 1 ); + font = msi_dialog_get_style( font_text, &text ); + if (!text) text = szEmpty; + SetWindowTextW( ctrl->hwnd, text ); + msi_free( font ); + msi_dialog_check_messages( NULL ); + } + else if( !strcmpW( attribute, szProgress ) ) + { + DWORD func, val1, val2, units; + + func = MSI_RecordGetInteger( rec, 1 ); + val1 = MSI_RecordGetInteger( rec, 2 ); + val2 = MSI_RecordGetInteger( rec, 3 ); + + TRACE("progress: func %u val1 %u val2 %u\n", func, val1, val2); + + units = val1 / 512; + switch (func) + { + case 0: /* init */ + SendMessageW( ctrl->hwnd, PBM_SETRANGE, 0, MAKELPARAM(0,100) ); + if (val2) + { + ctrl->progress_max = units ? units : 100; + ctrl->progress_current = units; + ctrl->progress_backwards = TRUE; + SendMessageW( ctrl->hwnd, PBM_SETPOS, 100, 0 ); + } + else + { + ctrl->progress_max = units ? units : 100; + ctrl->progress_current = 0; + ctrl->progress_backwards = FALSE; + SendMessageW( ctrl->hwnd, PBM_SETPOS, 0, 0 ); + } + break; + case 1: /* action data increment */ + if (val2) dialog->package->action_progress_increment = val1; + else dialog->package->action_progress_increment = 0; + break; + case 2: /* move */ + if (ctrl->progress_backwards) + { + if (units >= ctrl->progress_current) ctrl->progress_current -= units; + else ctrl->progress_current = 0; + } + else + { + if (ctrl->progress_current + units < ctrl->progress_max) ctrl->progress_current += units; + else ctrl->progress_current = ctrl->progress_max; + } + SendMessageW( ctrl->hwnd, PBM_SETPOS, MulDiv(100, ctrl->progress_current, ctrl->progress_max), 0 ); + break; + case 3: /* add */ + ctrl->progress_max += units; + break; + default: + FIXME("Unknown progress message %u\n", func); + break; + } + } + else if ( !strcmpW( attribute, szProperty ) ) + { + MSIFEATURE *feature = msi_seltree_get_selected_feature( ctrl ); + msi_dialog_set_property( dialog->package, ctrl->property, feature->Directory ); + } + else if ( !strcmpW( attribute, szSelectionPath ) ) + { + BOOL indirect = ctrl->attributes & msidbControlAttributesIndirect; + LPWSTR path = msi_dialog_dup_property( dialog, ctrl->property, indirect ); + if (!path) return; + SetWindowTextW( ctrl->hwnd, path ); + msi_free(path); + } + else + { + FIXME("Attribute %s not being set\n", debugstr_w(attribute)); + return; + } +} + +static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control) +{ + static const WCHAR Query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ', + 'A','N','D',' ', + '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0 + }; + MSIRECORD *row; + LPCWSTR event, attribute; + + row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control ); + if (!row) + return; + + event = MSI_RecordGetString( row, 3 ); + attribute = MSI_RecordGetString( row, 4 ); + ControlEvent_SubscribeToEvent( dialog->package, dialog, event, control, attribute ); + msiobj_release( &row->hdr ); +} + +/* everything except radio buttons */ +static msi_control *msi_dialog_add_control( msi_dialog *dialog, + MSIRECORD *rec, LPCWSTR szCls, DWORD style ) +{ + DWORD attributes; + LPCWSTR text, name; + DWORD exstyle = 0; + + name = MSI_RecordGetString( rec, 2 ); + attributes = MSI_RecordGetInteger( rec, 8 ); + text = MSI_RecordGetString( rec, 10 ); + + TRACE("%s, %s, %08x, %s, %08x\n", debugstr_w(szCls), debugstr_w(name), + attributes, debugstr_w(text), style); + + if( attributes & msidbControlAttributesVisible ) + style |= WS_VISIBLE; + if( ~attributes & msidbControlAttributesEnabled ) + style |= WS_DISABLED; + if( attributes & msidbControlAttributesSunken ) + exstyle |= WS_EX_CLIENTEDGE; + + msi_dialog_map_events(dialog, name); + + return msi_dialog_create_window( dialog, rec, exstyle, szCls, name, + text, style, dialog->hwnd ); +} + +struct msi_text_info +{ + msi_font *font; + WNDPROC oldproc; + DWORD attributes; +}; + +/* + * we don't erase our own background, + * so we have to make sure that the parent window redraws first + */ +static void msi_text_on_settext( HWND hWnd ) +{ + HWND hParent; + RECT rc; + + hParent = GetParent( hWnd ); + GetWindowRect( hWnd, &rc ); + MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 ); + InvalidateRect( hParent, &rc, TRUE ); +} + +static LRESULT WINAPI +MSIText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_text_info *info; + LRESULT r = 0; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + if( msg == WM_CTLCOLORSTATIC && + ( info->attributes & msidbControlAttributesTransparent ) ) + { + SetBkMode( (HDC)wParam, TRANSPARENT ); + return (LRESULT) GetStockObject(NULL_BRUSH); + } + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + if ( info->font ) + SetTextColor( (HDC)wParam, info->font->color ); + + switch( msg ) + { + case WM_SETTEXT: + msi_text_on_settext( hWnd ); + break; + case WM_NCDESTROY: + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + struct msi_text_info *info; + LPCWSTR text, ptr, prop, control_name; + LPWSTR font_name; + + TRACE("%p %p\n", dialog, rec); + + control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + info = msi_alloc( sizeof *info ); + if( !info ) + return ERROR_SUCCESS; + + control_name = MSI_RecordGetString( rec, 2 ); + control->attributes = MSI_RecordGetInteger( rec, 8 ); + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + text = MSI_RecordGetString( rec, 10 ); + font_name = msi_dialog_get_style( text, &ptr ); + info->font = ( font_name ) ? msi_dialog_find_font( dialog, font_name ) : NULL; + msi_free( font_name ); + + info->attributes = MSI_RecordGetInteger( rec, 8 ); + if( info->attributes & msidbControlAttributesTransparent ) + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT ); + + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIText_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + ControlEvent_SubscribeToEvent( dialog->package, dialog, + szSelectionPath, control_name, szSelectionPath ); + + return ERROR_SUCCESS; +} + +/* strip any leading text style label from text field */ +static WCHAR *msi_get_binary_name( MSIPACKAGE *package, MSIRECORD *rec ) +{ + WCHAR *p, *text; + + text = msi_get_deformatted_field( package, rec, 10 ); + if (!text) + return NULL; + + p = text; + while (*p && *p != '{') p++; + if (!*p++) return text; + + while (*p && *p != '}') p++; + if (!*p++) return text; + + p = strdupW( p ); + msi_free( text ); + return p; +} + +static UINT msi_dialog_set_property_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg ) +{ + static const WCHAR szNullArg[] = {'{','}',0}; + LPWSTR p, prop, arg_fmt = NULL; + UINT len; + + len = strlenW( event ); + prop = msi_alloc( len * sizeof(WCHAR) ); + strcpyW( prop, &event[1] ); + p = strchrW( prop, ']' ); + if (p && (p[1] == 0 || p[1] == ' ')) + { + *p = 0; + if (strcmpW( szNullArg, arg )) + deformat_string( dialog->package, arg, &arg_fmt ); + msi_dialog_set_property( dialog->package, prop, arg_fmt ); + msi_dialog_update_controls( dialog, prop ); + msi_free( arg_fmt ); + } + else ERR("Badly formatted property string - what happens?\n"); + msi_free( prop ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg ) +{ + LPWSTR event_fmt = NULL, arg_fmt = NULL; + + TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg)); + + deformat_string( dialog->package, event, &event_fmt ); + deformat_string( dialog->package, arg, &arg_fmt ); + + dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog ); + + msi_free( event_fmt ); + msi_free( arg_fmt ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param ) +{ + msi_dialog *dialog = param; + LPCWSTR condition, event, arg; + UINT r; + + condition = MSI_RecordGetString( rec, 5 ); + r = MSI_EvaluateConditionW( dialog->package, condition ); + if (r == MSICONDITION_TRUE || r == MSICONDITION_NONE) + { + event = MSI_RecordGetString( rec, 3 ); + arg = MSI_RecordGetString( rec, 4 ); + if (event[0] == '[') + msi_dialog_set_property_event( dialog, event, arg ); + else + msi_dialog_send_event( dialog, event, arg ); + } + return ERROR_SUCCESS; +} + +static UINT msi_dialog_button_handler( msi_dialog *dialog, msi_control *control, WPARAM param ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'C','o','n','t','r','o','l','E','v','e','n','t',' ','W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ','A','N','D',' ', + '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0}; + MSIQUERY *view; + UINT r; + + if (HIWORD(param) != BN_CLICKED) + return ERROR_SUCCESS; + + r = MSI_OpenQuery( dialog->package->db, &view, query, dialog->name, control->name ); + if (r != ERROR_SUCCESS) + { + ERR("query failed\n"); + return ERROR_SUCCESS; + } + r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + UINT attributes, style; + + TRACE("%p %p\n", dialog, rec); + + style = WS_TABSTOP; + attributes = MSI_RecordGetInteger( rec, 8 ); + if( attributes & msidbControlAttributesIcon ) + style |= BS_ICON; + + control = msi_dialog_add_control( dialog, rec, szButton, style ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + control->handler = msi_dialog_button_handler; + + if (attributes & msidbControlAttributesIcon) + { + /* set the icon */ + LPWSTR name = msi_get_binary_name( dialog->package, rec ); + control->hIcon = msi_load_icon( dialog->package->db, name, attributes ); + if (control->hIcon) + { + SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon ); + } + else + ERR("Failed to load icon %s\n", debugstr_w(name)); + msi_free( name ); + } + + return ERROR_SUCCESS; +} + +static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x','`',' ', + 'W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ', + '\'','%','s','\'',0 + }; + MSIRECORD *rec = NULL; + LPWSTR ret = NULL; + + /* find if there is a value associated with the checkbox */ + rec = MSI_QueryGetRecord( dialog->package->db, query, prop ); + if (!rec) + return ret; + + ret = msi_get_deformatted_field( dialog->package, rec, 2 ); + if( ret && !ret[0] ) + { + msi_free( ret ); + ret = NULL; + } + msiobj_release( &rec->hdr ); + if (ret) + return ret; + + ret = msi_dup_property( dialog->package->db, prop ); + if( ret && !ret[0] ) + { + msi_free( ret ); + ret = NULL; + } + + return ret; +} + +static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog, msi_control *control ) +{ + WCHAR state[2] = {0}; + DWORD sz = 2; + + msi_get_property( dialog->package->db, control->property, state, &sz ); + return state[0] ? 1 : 0; +} + +static void msi_dialog_set_checkbox_state( msi_dialog *dialog, msi_control *control, UINT state ) +{ + static const WCHAR szState[] = {'1',0}; + LPCWSTR val; + + /* if uncheck then the property is set to NULL */ + if (!state) + { + msi_dialog_set_property( dialog->package, control->property, NULL ); + return; + } + + /* check for a custom state */ + if (control->value && control->value[0]) + val = control->value; + else + val = szState; + + msi_dialog_set_property( dialog->package, control->property, val ); +} + +static void msi_dialog_checkbox_sync_state( msi_dialog *dialog, msi_control *control ) +{ + UINT state = msi_dialog_get_checkbox_state( dialog, control ); + SendMessageW( control->hwnd, BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0 ); +} + +static UINT msi_dialog_checkbox_handler( msi_dialog *dialog, msi_control *control, WPARAM param ) +{ + UINT state; + + if (HIWORD(param) != BN_CLICKED) + return ERROR_SUCCESS; + + TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property)); + + state = msi_dialog_get_checkbox_state( dialog, control ); + state = state ? 0 : 1; + msi_dialog_set_checkbox_state( dialog, control, state ); + msi_dialog_checkbox_sync_state( dialog, control ); + + return msi_dialog_button_handler( dialog, control, param ); +} + +static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + + TRACE("%p %p\n", dialog, rec); + + control = msi_dialog_add_control( dialog, rec, szButton, BS_CHECKBOX | BS_MULTILINE | WS_TABSTOP ); + control->handler = msi_dialog_checkbox_handler; + control->update = msi_dialog_checkbox_sync_state; + prop = MSI_RecordGetString( rec, 9 ); + if (prop) + { + control->property = strdupW( prop ); + control->value = msi_get_checkbox_value( dialog, prop ); + TRACE("control %s value %s\n", debugstr_w(control->property), debugstr_w(control->value)); + } + msi_dialog_checkbox_sync_state( dialog, control ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + DWORD attributes; + LPCWSTR name; + DWORD style, exstyle = 0; + DWORD x, y, width, height; + msi_control *control; + + TRACE("%p %p\n", dialog, rec); + + style = WS_CHILD | SS_ETCHEDHORZ | SS_SUNKEN; + + name = MSI_RecordGetString( rec, 2 ); + attributes = MSI_RecordGetInteger( rec, 8 ); + + if( attributes & msidbControlAttributesVisible ) + style |= WS_VISIBLE; + if( ~attributes & msidbControlAttributesEnabled ) + style |= WS_DISABLED; + if( attributes & msidbControlAttributesSunken ) + exstyle |= WS_EX_CLIENTEDGE; + + msi_dialog_map_events(dialog, name); + + control = msi_alloc( sizeof(*control) + strlenW(name) * sizeof(WCHAR) ); + if (!control) + return ERROR_OUTOFMEMORY; + + strcpyW( control->name, name ); + list_add_head( &dialog->controls, &control->entry ); + control->handler = NULL; + control->property = NULL; + control->value = NULL; + control->hBitmap = NULL; + control->hIcon = NULL; + control->hDll = NULL; + control->tabnext = strdupW( MSI_RecordGetString( rec, 11) ); + control->type = strdupW( MSI_RecordGetString( rec, 3 ) ); + control->progress_current = 0; + control->progress_max = 100; + control->progress_backwards = FALSE; + + x = MSI_RecordGetInteger( rec, 4 ); + y = MSI_RecordGetInteger( rec, 5 ); + width = MSI_RecordGetInteger( rec, 6 ); + + x = msi_dialog_scale_unit( dialog, x ); + y = msi_dialog_scale_unit( dialog, y ); + width = msi_dialog_scale_unit( dialog, width ); + height = 2; /* line is exactly 2 units in height */ + + control->hwnd = CreateWindowExW( exstyle, szStatic, NULL, style, + x, y, width, height, dialog->hwnd, NULL, NULL, NULL ); + + TRACE("Dialog %s control %s hwnd %p\n", + debugstr_w(dialog->name), debugstr_w(name), control->hwnd ); + + return ERROR_SUCCESS; +} + +/******************** Scroll Text ********************************************/ + +struct msi_scrolltext_info +{ + msi_dialog *dialog; + msi_control *control; + WNDPROC oldproc; +}; + +static LRESULT WINAPI +MSIScrollText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_scrolltext_info *info; + HRESULT r; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW( hWnd, szButtonData ); + + r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam ); + + switch( msg ) + { + case WM_GETDLGCODE: + return DLGC_WANTARROWS; + case WM_NCDESTROY: + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + case WM_PAINT: + /* native MSI sets a wait cursor here */ + msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED ); + break; + } + return r; +} + +struct msi_streamin_info +{ + LPSTR string; + DWORD offset; + DWORD length; +}; + +static DWORD CALLBACK +msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb ) +{ + struct msi_streamin_info *info = (struct msi_streamin_info*) arg; + + if( (count + info->offset) > info->length ) + count = info->length - info->offset; + memcpy( buffer, &info->string[ info->offset ], count ); + *pcb = count; + info->offset += count; + + TRACE("%d/%d\n", info->offset, info->length); + + return 0; +} + +static void msi_scrolltext_add_text( msi_control *control, LPCWSTR text ) +{ + struct msi_streamin_info info; + EDITSTREAM es; + + info.string = strdupWtoA( text ); + info.offset = 0; + info.length = lstrlenA( info.string ) + 1; + + es.dwCookie = (DWORD_PTR) &info; + es.dwError = 0; + es.pfnCallback = msi_richedit_stream_in; + + SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es ); + + msi_free( info.string ); +} + +static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + static const WCHAR szRichEdit20W[] = {'R','i','c','h','E','d','i','t','2','0','W',0}; + struct msi_scrolltext_info *info; + msi_control *control; + HMODULE hRichedit; + LPCWSTR text; + DWORD style; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + hRichedit = LoadLibraryA("riched20"); + + style = WS_BORDER | ES_MULTILINE | WS_VSCROLL | + ES_READONLY | ES_AUTOVSCROLL | WS_TABSTOP; + control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style ); + if (!control) + { + FreeLibrary( hRichedit ); + msi_free( info ); + return ERROR_FUNCTION_FAILED; + } + + control->hDll = hRichedit; + + info->dialog = dialog; + info->control = control; + + /* subclass the static control */ + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIScrollText_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + /* add the text into the richedit */ + text = MSI_RecordGetString( rec, 10 ); + if (text) + msi_scrolltext_add_text( control, text ); + + return ERROR_SUCCESS; +} + +static HBITMAP msi_load_picture( MSIDATABASE *db, LPCWSTR name, + INT cx, INT cy, DWORD flags ) +{ + HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap; + MSIRECORD *rec = NULL; + IStream *stm = NULL; + IPicture *pic = NULL; + HDC srcdc, destdc; + BITMAP bm; + UINT r; + + rec = msi_get_binary_record( db, name ); + if( !rec ) + goto end; + + r = MSI_RecordGetIStream( rec, 2, &stm ); + msiobj_release( &rec->hdr ); + if( r != ERROR_SUCCESS ) + goto end; + + r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic ); + IStream_Release( stm ); + if( FAILED( r ) ) + { + ERR("failed to load picture\n"); + goto end; + } + + r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap ); + if( FAILED( r ) ) + { + ERR("failed to get bitmap handle\n"); + goto end; + } + + /* make the bitmap the desired size */ + r = GetObjectW( hOleBitmap, sizeof bm, &bm ); + if (r != sizeof bm ) + { + ERR("failed to get bitmap size\n"); + goto end; + } + + if (flags & LR_DEFAULTSIZE) + { + cx = bm.bmWidth; + cy = bm.bmHeight; + } + + srcdc = CreateCompatibleDC( NULL ); + hOldSrcBitmap = SelectObject( srcdc, hOleBitmap ); + destdc = CreateCompatibleDC( NULL ); + hBitmap = CreateCompatibleBitmap( srcdc, cx, cy ); + hOldDestBitmap = SelectObject( destdc, hBitmap ); + StretchBlt( destdc, 0, 0, cx, cy, + srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); + SelectObject( srcdc, hOldSrcBitmap ); + SelectObject( destdc, hOldDestBitmap ); + DeleteDC( srcdc ); + DeleteDC( destdc ); + +end: + if ( pic ) + IPicture_Release( pic ); + return hBitmap; +} + +static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + UINT cx, cy, flags, style, attributes; + msi_control *control; + LPWSTR name; + + flags = LR_LOADFROMFILE; + style = SS_BITMAP | SS_LEFT | WS_GROUP; + + attributes = MSI_RecordGetInteger( rec, 8 ); + if( attributes & msidbControlAttributesFixedSize ) + { + flags |= LR_DEFAULTSIZE; + style |= SS_CENTERIMAGE; + } + + control = msi_dialog_add_control( dialog, rec, szStatic, style ); + cx = MSI_RecordGetInteger( rec, 6 ); + cy = MSI_RecordGetInteger( rec, 7 ); + cx = msi_dialog_scale_unit( dialog, cx ); + cy = msi_dialog_scale_unit( dialog, cy ); + + name = msi_get_binary_name( dialog->package, rec ); + control->hBitmap = msi_load_picture( dialog->package->db, name, cx, cy, flags ); + if( control->hBitmap ) + SendMessageW( control->hwnd, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM) control->hBitmap ); + else + ERR("Failed to load bitmap %s\n", debugstr_w(name)); + + msi_free( name ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_icon_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + DWORD attributes; + LPWSTR name; + + TRACE("\n"); + + control = msi_dialog_add_control( dialog, rec, szStatic, + SS_ICON | SS_CENTERIMAGE | WS_GROUP ); + + attributes = MSI_RecordGetInteger( rec, 8 ); + name = msi_get_binary_name( dialog->package, rec ); + control->hIcon = msi_load_icon( dialog->package->db, name, attributes ); + if( control->hIcon ) + SendMessageW( control->hwnd, STM_SETICON, (WPARAM) control->hIcon, 0 ); + else + ERR("Failed to load bitmap %s\n", debugstr_w(name)); + msi_free( name ); + return ERROR_SUCCESS; +} + +/******************** Combo Box ***************************************/ + +struct msi_combobox_info +{ + msi_dialog *dialog; + HWND hwnd; + WNDPROC oldproc; + DWORD num_items; + DWORD addpos_items; + LPWSTR *items; +}; + +static LRESULT WINAPI MSIComboBox_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_combobox_info *info; + LRESULT r; + DWORD j; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW( hWnd, szButtonData ); + if (!info) + return 0; + + r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam ); + + switch (msg) + { + case WM_NCDESTROY: + for (j = 0; j < info->num_items; j++) + msi_free( info->items[j] ); + msi_free( info->items ); + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +static UINT msi_combobox_add_item( MSIRECORD *rec, LPVOID param ) +{ + struct msi_combobox_info *info = param; + LPCWSTR value, text; + int pos; + + value = MSI_RecordGetString( rec, 3 ); + text = MSI_RecordGetString( rec, 4 ); + + info->items[info->addpos_items] = strdupW( value ); + + pos = SendMessageW( info->hwnd, CB_ADDSTRING, 0, (LPARAM)text ); + SendMessageW( info->hwnd, CB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] ); + info->addpos_items++; + + return ERROR_SUCCESS; +} + +static UINT msi_combobox_add_items( struct msi_combobox_info *info, LPCWSTR property ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','o','m','b','o','B','o','x','`',' ','W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0}; + MSIQUERY *view; + DWORD count; + UINT r; + + r = MSI_OpenQuery( info->dialog->package->db, &view, query, property ); + if (r != ERROR_SUCCESS) + return r; + + /* just get the number of records */ + count = 0; + r = MSI_IterateRecords( view, &count, NULL, NULL ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return r; + } + info->num_items = count; + info->items = msi_alloc( sizeof(*info->items) * count ); + + r = MSI_IterateRecords( view, NULL, msi_combobox_add_item, info ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR szHide[] = {'H','i','d','e',0}; + static const WCHAR szShow[] = {'S','h','o','w',0}; + static const WCHAR szDisable[] = {'D','i','s','a','b','l','e',0}; + static const WCHAR szEnable[] = {'E','n','a','b','l','e',0}; + static const WCHAR szDefault[] = {'D','e','f','a','u','l','t',0}; + msi_dialog *dialog = param; + msi_control *control; + LPCWSTR name, action, condition; + UINT r; + + name = MSI_RecordGetString( rec, 2 ); + action = MSI_RecordGetString( rec, 3 ); + condition = MSI_RecordGetString( rec, 4 ); + r = MSI_EvaluateConditionW( dialog->package, condition ); + control = msi_dialog_find_control( dialog, name ); + if (r == MSICONDITION_TRUE && control) + { + TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name)); + + /* FIXME: case sensitive? */ + if (!strcmpW( action, szHide )) + ShowWindow(control->hwnd, SW_HIDE); + else if (!strcmpW( action, szShow )) + ShowWindow(control->hwnd, SW_SHOW); + else if (!strcmpW( action, szDisable )) + EnableWindow(control->hwnd, FALSE); + else if (!strcmpW( action, szEnable )) + EnableWindow(control->hwnd, TRUE); + else if (!strcmpW( action, szDefault )) + SetFocus(control->hwnd); + else + FIXME("Unhandled action %s\n", debugstr_w(action)); + } + return ERROR_SUCCESS; +} + +static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ', + 'W','H','E','R','E',' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0}; + UINT r; + MSIQUERY *view; + MSIPACKAGE *package = dialog->package; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name)); + + /* query the Control table for all the elements of the control */ + r = MSI_OpenQuery( package->db, &view, query, dialog->name ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT msi_dialog_combobox_handler( msi_dialog *dialog, msi_control *control, WPARAM param ) +{ + struct msi_combobox_info *info; + int index; + LPWSTR value; + + if (HIWORD(param) != CBN_SELCHANGE && HIWORD(param) != CBN_EDITCHANGE) + return ERROR_SUCCESS; + + info = GetPropW( control->hwnd, szButtonData ); + index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 ); + if (index == CB_ERR) + value = msi_get_window_text( control->hwnd ); + else + value = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, index, 0 ); + + msi_dialog_set_property( info->dialog->package, control->property, value ); + msi_dialog_evaluate_control_conditions( info->dialog ); + + if (index == CB_ERR) + msi_free( value ); + + return ERROR_SUCCESS; +} + +static void msi_dialog_combobox_update( msi_dialog *dialog, msi_control *control ) +{ + struct msi_combobox_info *info; + LPWSTR value, tmp; + DWORD j; + + info = GetPropW( control->hwnd, szButtonData ); + + value = msi_dup_property( dialog->package->db, control->property ); + if (!value) + { + SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 ); + return; + } + + for (j = 0; j < info->num_items; j++) + { + tmp = (LPWSTR) SendMessageW( control->hwnd, CB_GETITEMDATA, j, 0 ); + if (!strcmpW( value, tmp )) + break; + } + + if (j < info->num_items) + { + SendMessageW( control->hwnd, CB_SETCURSEL, j, 0 ); + } + else + { + SendMessageW( control->hwnd, CB_SETCURSEL, -1, 0 ); + SetWindowTextW( control->hwnd, value ); + } + + msi_free(value); +} + +static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + struct msi_combobox_info *info; + msi_control *control; + DWORD attributes, style; + LPCWSTR prop; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + style = CBS_AUTOHSCROLL | WS_TABSTOP | WS_GROUP | WS_CHILD; + attributes = MSI_RecordGetInteger( rec, 8 ); + if ( ~attributes & msidbControlAttributesSorted) + style |= CBS_SORT; + if ( attributes & msidbControlAttributesComboList) + style |= CBS_DROPDOWNLIST; + else + style |= CBS_DROPDOWN; + + control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style ); + if (!control) + { + msi_free( info ); + return ERROR_FUNCTION_FAILED; + } + + control->handler = msi_dialog_combobox_handler; + control->update = msi_dialog_combobox_update; + + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + /* subclass */ + info->dialog = dialog; + info->hwnd = control->hwnd; + info->items = NULL; + info->addpos_items = 0; + info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIComboBox_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + if (control->property) + msi_combobox_add_items( info, control->property ); + + msi_dialog_combobox_update( dialog, control ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_edit_handler( msi_dialog *dialog, msi_control *control, WPARAM param ) +{ + LPWSTR buf; + + if (HIWORD(param) != EN_CHANGE) + return ERROR_SUCCESS; + + TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), debugstr_w(control->property)); + + buf = msi_get_window_text( control->hwnd ); + msi_dialog_set_property( dialog->package, control->property, buf ); + msi_free( buf ); + + return ERROR_SUCCESS; +} + +/* length of 2^32 + 1 */ +#define MAX_NUM_DIGITS 11 + +static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop, text; + LPWSTR val, begin, end; + WCHAR num[MAX_NUM_DIGITS]; + DWORD limit; + + control = msi_dialog_add_control( dialog, rec, szEdit, + WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL ); + control->handler = msi_dialog_edit_handler; + + text = MSI_RecordGetString( rec, 10 ); + if ( text ) + { + begin = strchrW( text, '{' ); + end = strchrW( text, '}' ); + + if ( begin && end && end > begin && + begin[0] >= '0' && begin[0] <= '9' && + end - begin < MAX_NUM_DIGITS) + { + lstrcpynW( num, begin + 1, end - begin ); + limit = atolW( num ); + + SendMessageW( control->hwnd, EM_SETLIMITTEXT, limit, 0 ); + } + } + + prop = MSI_RecordGetString( rec, 9 ); + if( prop ) + control->property = strdupW( prop ); + + val = msi_dup_property( dialog->package->db, control->property ); + SetWindowTextW( control->hwnd, val ); + msi_free( val ); + return ERROR_SUCCESS; +} + +/******************** Masked Edit ********************************************/ + +#define MASK_MAX_GROUPS 20 + +struct msi_mask_group +{ + UINT len; + UINT ofs; + WCHAR type; + HWND hwnd; +}; + +struct msi_maskedit_info +{ + msi_dialog *dialog; + WNDPROC oldproc; + HWND hwnd; + LPWSTR prop; + UINT num_chars; + UINT num_groups; + struct msi_mask_group group[MASK_MAX_GROUPS]; +}; + +static BOOL msi_mask_editable( WCHAR type ) +{ + switch (type) + { + case '%': + case '#': + case '&': + case '`': + case '?': + case '^': + return TRUE; + } + return FALSE; +} + +static void msi_mask_control_change( struct msi_maskedit_info *info ) +{ + LPWSTR val; + UINT i, n, r; + + val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) ); + for( i=0, n=0; i<info->num_groups; i++ ) + { + if( (info->group[i].len + n) > info->num_chars ) + { + ERR("can't fit control %d text into template\n",i); + break; + } + if (!msi_mask_editable(info->group[i].type)) + { + for(r=0; r<info->group[i].len; r++) + val[n+r] = info->group[i].type; + val[n+r] = 0; + } + else + { + r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 ); + if( r != info->group[i].len ) + break; + } + n += r; + } + + TRACE("%d/%d controls were good\n", i, info->num_groups); + + if( i == info->num_groups ) + { + TRACE("Set property %s to %s\n", debugstr_w(info->prop), debugstr_w(val)); + CharUpperBuffW( val, info->num_chars ); + msi_dialog_set_property( info->dialog->package, info->prop, val ); + msi_dialog_evaluate_control_conditions( info->dialog ); + } + msi_free( val ); +} + +/* now move to the next control if necessary */ +static VOID msi_mask_next_control( struct msi_maskedit_info *info, HWND hWnd ) +{ + HWND hWndNext; + UINT len, i; + + for( i=0; i<info->num_groups; i++ ) + if( info->group[i].hwnd == hWnd ) + break; + + /* don't move from the last control */ + if( i >= (info->num_groups-1) ) + return; + + len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 ); + if( len < info->group[i].len ) + return; + + hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE ); + SetFocus( hWndNext ); +} + +static LRESULT WINAPI +MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_maskedit_info *info; + HRESULT r; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + switch( msg ) + { + case WM_COMMAND: + if (HIWORD(wParam) == EN_CHANGE) + { + msi_mask_control_change( info ); + msi_mask_next_control( info, (HWND) lParam ); + } + break; + case WM_NCDESTROY: + msi_free( info->prop ); + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +/* fish the various bits of the property out and put them in the control */ +static void +msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text ) +{ + LPCWSTR p; + UINT i; + + p = text; + for( i = 0; i < info->num_groups; i++ ) + { + if( info->group[i].len < strlenW( p ) ) + { + LPWSTR chunk = strdupW( p ); + chunk[ info->group[i].len ] = 0; + SetWindowTextW( info->group[i].hwnd, chunk ); + msi_free( chunk ); + } + else + { + SetWindowTextW( info->group[i].hwnd, p ); + break; + } + p += info->group[i].len; + } +} + +static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask ) +{ + struct msi_maskedit_info * info = NULL; + int i = 0, n = 0, total = 0; + LPCWSTR p; + + TRACE("masked control, template %s\n", debugstr_w(mask)); + + if( !mask ) + return info; + + info = msi_alloc_zero( sizeof *info ); + if( !info ) + return info; + + p = strchrW(mask, '<'); + if( p ) + p++; + else + p = mask; + + for( i=0; i<MASK_MAX_GROUPS; i++ ) + { + /* stop at the end of the string */ + if( p[0] == 0 || p[0] == '>' ) + break; + + /* count the number of the same identifier */ + for( n=0; p[n] == p[0]; n++ ) + ; + info->group[i].ofs = total; + info->group[i].type = p[0]; + if( p[n] == '=' ) + { + n++; + total++; /* an extra not part of the group */ + } + info->group[i].len = n; + total += n; + p += n; + } + + TRACE("%d characters in %d groups\n", total, i ); + if( i == MASK_MAX_GROUPS ) + ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask)); + + info->num_chars = total; + info->num_groups = i; + + return info; +} + +static void +msi_maskedit_create_children( struct msi_maskedit_info *info, LPCWSTR font ) +{ + DWORD width, height, style, wx, ww; + RECT rect; + HWND hwnd; + UINT i; + + style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL; + + GetClientRect( info->hwnd, &rect ); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + for( i = 0; i < info->num_groups; i++ ) + { + if (!msi_mask_editable( info->group[i].type )) + continue; + wx = (info->group[i].ofs * width) / info->num_chars; + ww = (info->group[i].len * width) / info->num_chars; + + hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height, + info->hwnd, NULL, NULL, NULL ); + if( !hwnd ) + { + ERR("failed to create mask edit sub window\n"); + break; + } + + SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 ); + + msi_dialog_set_font( info->dialog, hwnd, + font?font:info->dialog->default_font ); + info->group[i].hwnd = hwnd; + } +} + +/* + * office 2003 uses "73931<````=````=````=````=`````>@@@@@" + * delphi 7 uses "<????-??????-??????-????>" and "<???-???>" + * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>" + */ +static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + LPWSTR font_mask, val = NULL, font; + struct msi_maskedit_info *info = NULL; + UINT ret = ERROR_SUCCESS; + msi_control *control; + LPCWSTR prop, mask; + + TRACE("\n"); + + font_mask = msi_get_deformatted_field( dialog->package, rec, 10 ); + font = msi_dialog_get_style( font_mask, &mask ); + if( !mask ) + { + WARN("mask template is empty\n"); + goto end; + } + + info = msi_dialog_parse_groups( mask ); + if( !info ) + { + ERR("template %s is invalid\n", debugstr_w(mask)); + goto end; + } + + info->dialog = dialog; + + control = msi_dialog_add_control( dialog, rec, szStatic, + SS_OWNERDRAW | WS_GROUP | WS_VISIBLE ); + if( !control ) + { + ERR("Failed to create maskedit container\n"); + ret = ERROR_FUNCTION_FAILED; + goto end; + } + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT ); + + info->hwnd = control->hwnd; + + /* subclass the static control */ + info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIMaskedEdit_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + prop = MSI_RecordGetString( rec, 9 ); + if( prop ) + info->prop = strdupW( prop ); + + msi_maskedit_create_children( info, font ); + + if( prop ) + { + val = msi_dup_property( dialog->package->db, prop ); + if( val ) + { + msi_maskedit_set_text( info, val ); + msi_free( val ); + } + } + +end: + if( ret != ERROR_SUCCESS ) + msi_free( info ); + msi_free( font_mask ); + msi_free( font ); + return ret; +} + +/******************** Progress Bar *****************************************/ + +static UINT msi_dialog_progress_bar( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + DWORD attributes, style; + + style = WS_VISIBLE; + attributes = MSI_RecordGetInteger( rec, 8 ); + if( !(attributes & msidbControlAttributesProgress95) ) + style |= PBS_SMOOTH; + + control = msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, style ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + ControlEvent_SubscribeToEvent( dialog->package, dialog, + szSetProgress, control->name, szProgress ); + return ERROR_SUCCESS; +} + +/******************** Path Edit ********************************************/ + +struct msi_pathedit_info +{ + msi_dialog *dialog; + msi_control *control; + WNDPROC oldproc; +}; + +static void msi_dialog_update_pathedit( msi_dialog *dialog, msi_control *control ) +{ + LPWSTR prop, path; + BOOL indirect; + + if (!control && !(control = msi_dialog_find_control_by_type( dialog, szPathEdit ))) + return; + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + path = msi_dialog_dup_property( dialog, prop, TRUE ); + + SetWindowTextW( control->hwnd, path ); + SendMessageW( control->hwnd, EM_SETSEL, 0, -1 ); + + msi_free( path ); + msi_free( prop ); +} + +/* FIXME: test when this should fail */ +static BOOL msi_dialog_verify_path( LPWSTR path ) +{ + if ( !lstrlenW( path ) ) + return FALSE; + + if ( PathIsRelativeW( path ) ) + return FALSE; + + return TRUE; +} + +/* returns TRUE if the path is valid, FALSE otherwise */ +static BOOL msi_dialog_onkillfocus( msi_dialog *dialog, msi_control *control ) +{ + LPWSTR buf, prop; + BOOL indirect; + BOOL valid; + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + + buf = msi_get_window_text( control->hwnd ); + + if ( !msi_dialog_verify_path( buf ) ) + { + /* FIXME: display an error message box */ + ERR("Invalid path %s\n", debugstr_w( buf )); + valid = FALSE; + SetFocus( control->hwnd ); + } + else + { + valid = TRUE; + msi_dialog_set_property( dialog->package, prop, buf ); + } + + msi_dialog_update_pathedit( dialog, control ); + + TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), + debugstr_w(prop)); + + msi_free( buf ); + msi_free( prop ); + + return valid; +} + +static LRESULT WINAPI MSIPathEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_pathedit_info *info = GetPropW(hWnd, szButtonData); + LRESULT r = 0; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + if ( msg == WM_KILLFOCUS ) + { + /* if the path is invalid, don't handle this message */ + if ( !msi_dialog_onkillfocus( info->dialog, info->control ) ) + return 0; + } + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + if ( msg == WM_NCDESTROY ) + { + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + } + + return r; +} + +static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + struct msi_pathedit_info *info; + msi_control *control; + LPCWSTR prop; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + control = msi_dialog_add_control( dialog, rec, szEdit, + WS_BORDER | WS_TABSTOP ); + control->attributes = MSI_RecordGetInteger( rec, 8 ); + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + info->dialog = dialog; + info->control = control; + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIPathEdit_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + msi_dialog_update_pathedit( dialog, control ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog, msi_control *control, WPARAM param ) +{ + if (HIWORD(param) != BN_CLICKED) + return ERROR_SUCCESS; + + TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), debugstr_w(control->property)); + + msi_dialog_set_property( dialog->package, control->property, control->name ); + + return msi_dialog_button_handler( dialog, control, param ); +} + +/* radio buttons are a bit different from normal controls */ +static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param ) +{ + radio_button_group_descr *group = param; + msi_dialog *dialog = group->dialog; + msi_control *control; + LPCWSTR prop, text, name; + DWORD style, attributes = group->attributes; + + style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE | WS_TABSTOP; + name = MSI_RecordGetString( rec, 3 ); + text = MSI_RecordGetString( rec, 8 ); + if( attributes & msidbControlAttributesVisible ) + style |= WS_VISIBLE; + if( ~attributes & msidbControlAttributesEnabled ) + style |= WS_DISABLED; + + control = msi_dialog_create_window( dialog, rec, 0, szButton, name, text, + style, group->parent->hwnd ); + if (!control) + return ERROR_FUNCTION_FAILED; + control->handler = msi_dialog_radiogroup_handler; + + if (group->propval && !strcmpW( control->name, group->propval )) + SendMessageW(control->hwnd, BM_SETCHECK, BST_CHECKED, 0); + + prop = MSI_RecordGetString( rec, 1 ); + if( prop ) + control->property = strdupW( prop ); + + return ERROR_SUCCESS; +} + +static BOOL CALLBACK msi_radioground_child_enum( HWND hWnd, LPARAM lParam ) +{ + EnableWindow( hWnd, lParam ); + return TRUE; +} + +static LRESULT WINAPI MSIRadioGroup_WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + WNDPROC oldproc = (WNDPROC)GetPropW( hWnd, szButtonData ); + LRESULT r; + + TRACE("hWnd %p msg %04x wParam 0x%08lx lParam 0x%08lx\n", hWnd, msg, wParam, lParam); + + if (msg == WM_COMMAND) /* Forward notifications to dialog */ + SendMessageW( GetParent( hWnd ), msg, wParam, lParam ); + + r = CallWindowProcW( oldproc, hWnd, msg, wParam, lParam ); + + /* make sure the radio buttons show as disabled if the parent is disabled */ + if (msg == WM_ENABLE) + EnumChildWindows( hWnd, msi_radioground_child_enum, wParam ); + + return r; +} + +static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'R','a','d','i','o','B','u','t','t','o','n',' ','W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0}; + UINT r; + LPCWSTR prop; + msi_control *control; + MSIQUERY *view; + radio_button_group_descr group; + MSIPACKAGE *package = dialog->package; + WNDPROC oldproc; + DWORD attr, style = WS_GROUP; + + prop = MSI_RecordGetString( rec, 9 ); + + TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop )); + + attr = MSI_RecordGetInteger( rec, 8 ); + if (attr & msidbControlAttributesHasBorder) + style |= BS_GROUPBOX; + else + style |= BS_OWNERDRAW; + + /* Create parent group box to hold radio buttons */ + control = msi_dialog_add_control( dialog, rec, szButton, style ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIRadioGroup_WndProc ); + SetPropW(control->hwnd, szButtonData, oldproc); + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT ); + + if( prop ) + control->property = strdupW( prop ); + + /* query the Radio Button table for all control in this group */ + r = MSI_OpenQuery( package->db, &view, query, prop ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed for dialog %s radio group %s\n", + debugstr_w(dialog->name), debugstr_w(prop)); + return ERROR_INVALID_PARAMETER; + } + + group.dialog = dialog; + group.parent = control; + group.attributes = MSI_RecordGetInteger( rec, 8 ); + group.propval = msi_dup_property( dialog->package->db, control->property ); + + r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group ); + msiobj_release( &view->hdr ); + msi_free( group.propval ); + return r; +} + +static void +msi_seltree_sync_item_state( HWND hwnd, MSIFEATURE *feature, HTREEITEM hItem ) +{ + TVITEMW tvi; + DWORD index = feature->ActionRequest; + + TRACE("Feature %s -> %d %d %d\n", debugstr_w(feature->Title), + feature->Installed, feature->Action, feature->ActionRequest); + + if (index == INSTALLSTATE_UNKNOWN) + index = INSTALLSTATE_ABSENT; + + tvi.mask = TVIF_STATE; + tvi.hItem = hItem; + tvi.state = INDEXTOSTATEIMAGEMASK( index ); + tvi.stateMask = TVIS_STATEIMAGEMASK; + + SendMessageW( hwnd, TVM_SETITEMW, 0, (LPARAM) &tvi ); +} + +static UINT +msi_seltree_popup_menu( HWND hwnd, INT x, INT y ) +{ + HMENU hMenu; + INT r; + + /* create a menu to display */ + hMenu = CreatePopupMenu(); + + /* FIXME: load strings from resources */ + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_LOCAL, "Install feature locally"); + AppendMenuA( hMenu, MF_ENABLED, USER_INSTALLSTATE_ALL, "Install entire feature"); + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ADVERTISED, "Install on demand"); + AppendMenuA( hMenu, MF_ENABLED, INSTALLSTATE_ABSENT, "Don't install"); + r = TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, + x, y, 0, hwnd, NULL ); + DestroyMenu( hMenu ); + return r; +} + +static void +msi_seltree_update_feature_installstate( HWND hwnd, HTREEITEM hItem, + MSIPACKAGE *package, MSIFEATURE *feature, INSTALLSTATE state ) +{ + feature->ActionRequest = state; + msi_seltree_sync_item_state( hwnd, feature, hItem ); + ACTION_UpdateComponentStates( package, feature ); +} + +static void +msi_seltree_update_siblings_and_children_installstate( HWND hwnd, HTREEITEM curr, + MSIPACKAGE *package, INSTALLSTATE state) +{ + /* update all siblings */ + do + { + MSIFEATURE *feature; + HTREEITEM child; + + feature = msi_seltree_feature_from_item( hwnd, curr ); + msi_seltree_update_feature_installstate( hwnd, curr, package, feature, state ); + + /* update this sibling's children */ + child = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)curr ); + if (child) + msi_seltree_update_siblings_and_children_installstate( hwnd, child, + package, state ); + } + while ((curr = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)curr ))); +} + +static LRESULT +msi_seltree_menu( HWND hwnd, HTREEITEM hItem ) +{ + struct msi_selection_tree_info *info; + MSIFEATURE *feature; + MSIPACKAGE *package; + union { + RECT rc; + POINT pt[2]; + HTREEITEM hItem; + } u; + UINT r; + + info = GetPropW(hwnd, szButtonData); + package = info->dialog->package; + + feature = msi_seltree_feature_from_item( hwnd, hItem ); + if (!feature) + { + ERR("item %p feature was NULL\n", hItem); + return 0; + } + + /* get the item's rectangle to put the menu just below it */ + u.hItem = hItem; + SendMessageW( hwnd, TVM_GETITEMRECT, 0, (LPARAM) &u.rc ); + MapWindowPoints( hwnd, NULL, u.pt, 2 ); + + r = msi_seltree_popup_menu( hwnd, u.rc.left, u.rc.top ); + + switch (r) + { + case USER_INSTALLSTATE_ALL: + r = INSTALLSTATE_LOCAL; + /* fall-through */ + case INSTALLSTATE_ADVERTISED: + case INSTALLSTATE_ABSENT: + { + HTREEITEM child; + child = (HTREEITEM)SendMessageW( hwnd, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)hItem ); + if (child) + msi_seltree_update_siblings_and_children_installstate( hwnd, child, package, r ); + } + /* fall-through */ + case INSTALLSTATE_LOCAL: + msi_seltree_update_feature_installstate( hwnd, hItem, package, feature, r ); + break; + } + + return 0; +} + +static LRESULT WINAPI +MSISelectionTree_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_selection_tree_info *info; + TVHITTESTINFO tvhti; + HRESULT r; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + switch( msg ) + { + case WM_LBUTTONDOWN: + tvhti.pt.x = (short)LOWORD( lParam ); + tvhti.pt.y = (short)HIWORD( lParam ); + tvhti.flags = 0; + tvhti.hItem = 0; + CallWindowProcW(info->oldproc, hWnd, TVM_HITTEST, 0, (LPARAM) &tvhti ); + if (tvhti.flags & TVHT_ONITEMSTATEICON) + return msi_seltree_menu( hWnd, tvhti.hItem ); + break; + } + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + switch( msg ) + { + case WM_NCDESTROY: + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + return r; +} + +static void +msi_seltree_add_child_features( MSIPACKAGE *package, HWND hwnd, + LPCWSTR parent, HTREEITEM hParent ) +{ + struct msi_selection_tree_info *info = GetPropW( hwnd, szButtonData ); + MSIFEATURE *feature; + TVINSERTSTRUCTW tvis; + HTREEITEM hitem, hfirst = NULL; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if ( parent && feature->Feature_Parent && strcmpW( parent, feature->Feature_Parent )) + continue; + else if ( parent && !feature->Feature_Parent ) + continue; + else if ( !parent && feature->Feature_Parent ) + continue; + + if ( !feature->Title ) + continue; + + if ( !feature->Display ) + continue; + + memset( &tvis, 0, sizeof tvis ); + tvis.hParent = hParent; + tvis.hInsertAfter = TVI_LAST; + tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM; + tvis.u.item.pszText = feature->Title; + tvis.u.item.lParam = (LPARAM) feature; + + hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis ); + if (!hitem) + continue; + + if (!hfirst) + hfirst = hitem; + + msi_seltree_sync_item_state( hwnd, feature, hitem ); + msi_seltree_add_child_features( package, hwnd, + feature->Feature, hitem ); + + /* the node is expanded if Display is odd */ + if ( feature->Display % 2 != 0 ) + SendMessageW( hwnd, TVM_EXPAND, TVE_EXPAND, (LPARAM) hitem ); + } + + /* select the first item */ + SendMessageW( hwnd, TVM_SELECTITEM, TVGN_CARET | TVGN_DROPHILITE, (LPARAM) hfirst ); + info->selected = hfirst; +} + +static void msi_seltree_create_imagelist( HWND hwnd ) +{ + const int bm_width = 32, bm_height = 16, bm_count = 3; + const int bm_resource = 0x1001; + HIMAGELIST himl; + int i; + HBITMAP hbmp; + + himl = ImageList_Create( bm_width, bm_height, FALSE, 4, 0 ); + if (!himl) + { + ERR("failed to create image list\n"); + return; + } + + for (i=0; i<bm_count; i++) + { + hbmp = LoadBitmapW( msi_hInstance, MAKEINTRESOURCEW(i+bm_resource) ); + if (!hbmp) + { + ERR("failed to load bitmap %d\n", i); + break; + } + + /* + * Add a dummy bitmap at offset zero because the treeview + * can't use it as a state mask (zero means no user state). + */ + if (!i) + ImageList_Add( himl, hbmp, NULL ); + + ImageList_Add( himl, hbmp, NULL ); + } + + SendMessageW( hwnd, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl ); +} + +static UINT msi_dialog_seltree_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + struct msi_selection_tree_info *info = GetPropW( control->hwnd, szButtonData ); + LPNMTREEVIEWW tv = (LPNMTREEVIEWW)param; + MSIRECORD *row, *rec; + MSIFOLDER *folder; + MSIFEATURE *feature; + LPCWSTR dir, title = NULL; + UINT r = ERROR_SUCCESS; + + static const WCHAR select[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ', + '`','T','i','t','l','e','`',' ','=',' ','\'','%','s','\'',0 + }; + + if (tv->hdr.code != TVN_SELCHANGINGW) + return ERROR_SUCCESS; + + info->selected = tv->itemNew.hItem; + + if (!(tv->itemNew.mask & TVIF_TEXT)) + { + feature = msi_seltree_feature_from_item( control->hwnd, tv->itemNew.hItem ); + if (feature) + title = feature->Title; + } + else + title = tv->itemNew.pszText; + + row = MSI_QueryGetRecord( dialog->package->db, select, title ); + if (!row) + return ERROR_FUNCTION_FAILED; + + rec = MSI_CreateRecord( 1 ); + + MSI_RecordSetStringW( rec, 1, MSI_RecordGetString( row, 4 ) ); + ControlEvent_FireSubscribedEvent( dialog->package, szSelectionDescription, rec ); + + dir = MSI_RecordGetString( row, 7 ); + if (dir) + { + folder = msi_get_loaded_folder( dialog->package, dir ); + if (!folder) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + MSI_RecordSetStringW( rec, 1, folder->ResolvedTarget ); + } + else + MSI_RecordSetStringW( rec, 1, NULL ); + + ControlEvent_FireSubscribedEvent( dialog->package, szSelectionPath, rec ); + +done: + msiobj_release(&row->hdr); + msiobj_release(&rec->hdr); + + return r; +} + +static UINT msi_dialog_selection_tree( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop, control_name; + MSIPACKAGE *package = dialog->package; + DWORD style; + struct msi_selection_tree_info *info; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + /* create the treeview control */ + style = TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT; + style |= WS_GROUP | WS_VSCROLL; + control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, style ); + if (!control) + { + msi_free(info); + return ERROR_FUNCTION_FAILED; + } + + control->handler = msi_dialog_seltree_handler; + control_name = MSI_RecordGetString( rec, 2 ); + control->attributes = MSI_RecordGetInteger( rec, 8 ); + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + /* subclass */ + info->dialog = dialog; + info->hwnd = control->hwnd; + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSISelectionTree_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + ControlEvent_SubscribeToEvent( dialog->package, dialog, + szSelectionPath, control_name, szProperty ); + + /* initialize it */ + msi_seltree_create_imagelist( control->hwnd ); + msi_seltree_add_child_features( package, control->hwnd, NULL, NULL ); + + return ERROR_SUCCESS; +} + +/******************** Group Box ***************************************/ + +static UINT msi_dialog_group_box( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + DWORD style; + + style = BS_GROUPBOX | WS_CHILD | WS_GROUP; + control = msi_dialog_add_control( dialog, rec, WC_BUTTONW, style ); + if (!control) + return ERROR_FUNCTION_FAILED; + + return ERROR_SUCCESS; +} + +/******************** List Box ***************************************/ + +struct msi_listbox_info +{ + msi_dialog *dialog; + HWND hwnd; + WNDPROC oldproc; + DWORD num_items; + DWORD addpos_items; + LPWSTR *items; +}; + +static LRESULT WINAPI MSIListBox_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_listbox_info *info; + LRESULT r; + DWORD j; + + TRACE("%p %04x %08lx %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW( hWnd, szButtonData ); + if (!info) + return 0; + + r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam ); + + switch( msg ) + { + case WM_NCDESTROY: + for (j = 0; j < info->num_items; j++) + msi_free( info->items[j] ); + msi_free( info->items ); + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +static UINT msi_listbox_add_item( MSIRECORD *rec, LPVOID param ) +{ + struct msi_listbox_info *info = param; + LPCWSTR value, text; + int pos; + + value = MSI_RecordGetString( rec, 3 ); + text = MSI_RecordGetString( rec, 4 ); + + info->items[info->addpos_items] = strdupW( value ); + + pos = SendMessageW( info->hwnd, LB_ADDSTRING, 0, (LPARAM)text ); + SendMessageW( info->hwnd, LB_SETITEMDATA, pos, (LPARAM)info->items[info->addpos_items] ); + info->addpos_items++; + return ERROR_SUCCESS; +} + +static UINT msi_listbox_add_items( struct msi_listbox_info *info, LPCWSTR property ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','L','i','s','t','B','o','x','`',' ','W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','`',0}; + MSIQUERY *view; + DWORD count; + UINT r; + + r = MSI_OpenQuery( info->dialog->package->db, &view, query, property ); + if ( r != ERROR_SUCCESS ) + return r; + + /* just get the number of records */ + count = 0; + r = MSI_IterateRecords( view, &count, NULL, NULL ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + return r; + } + info->num_items = count; + info->items = msi_alloc( sizeof(*info->items) * count ); + + r = MSI_IterateRecords( view, NULL, msi_listbox_add_item, info ); + msiobj_release( &view->hdr ); + return r; +} + +static UINT msi_dialog_listbox_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + struct msi_listbox_info *info; + int index; + LPCWSTR value; + + if( HIWORD(param) != LBN_SELCHANGE ) + return ERROR_SUCCESS; + + info = GetPropW( control->hwnd, szButtonData ); + index = SendMessageW( control->hwnd, LB_GETCURSEL, 0, 0 ); + value = (LPCWSTR) SendMessageW( control->hwnd, LB_GETITEMDATA, index, 0 ); + + msi_dialog_set_property( info->dialog->package, control->property, value ); + msi_dialog_evaluate_control_conditions( info->dialog ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_list_box( msi_dialog *dialog, MSIRECORD *rec ) +{ + struct msi_listbox_info *info; + msi_control *control; + DWORD attributes, style; + LPCWSTR prop; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + style = WS_TABSTOP | WS_GROUP | WS_CHILD | LBS_NOTIFY | WS_VSCROLL | WS_BORDER; + attributes = MSI_RecordGetInteger( rec, 8 ); + if (~attributes & msidbControlAttributesSorted) + style |= LBS_SORT; + + control = msi_dialog_add_control( dialog, rec, WC_LISTBOXW, style ); + if (!control) + { + msi_free(info); + return ERROR_FUNCTION_FAILED; + } + + control->handler = msi_dialog_listbox_handler; + + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + /* subclass */ + info->dialog = dialog; + info->hwnd = control->hwnd; + info->items = NULL; + info->addpos_items = 0; + info->oldproc = (WNDPROC)SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIListBox_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + if ( control->property ) + msi_listbox_add_items( info, control->property ); + + return ERROR_SUCCESS; +} + +/******************** Directory Combo ***************************************/ + +static void msi_dialog_update_directory_combo( msi_dialog *dialog, msi_control *control ) +{ + LPWSTR prop, path; + BOOL indirect; + + if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryCombo ))) + return; + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + path = msi_dialog_dup_property( dialog, prop, TRUE ); + + PathStripPathW( path ); + PathRemoveBackslashW( path ); + + SendMessageW( control->hwnd, CB_INSERTSTRING, 0, (LPARAM)path ); + SendMessageW( control->hwnd, CB_SETCURSEL, 0, 0 ); + + msi_free( path ); + msi_free( prop ); +} + +static UINT msi_dialog_directory_combo( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + DWORD style; + + /* FIXME: use CBS_OWNERDRAWFIXED and add owner draw code */ + style = CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | + WS_GROUP | WS_TABSTOP | WS_VSCROLL; + control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style ); + if (!control) + return ERROR_FUNCTION_FAILED; + + control->attributes = MSI_RecordGetInteger( rec, 8 ); + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + msi_dialog_update_directory_combo( dialog, control ); + + return ERROR_SUCCESS; +} + +/******************** Directory List ***************************************/ + +static void msi_dialog_update_directory_list( msi_dialog *dialog, msi_control *control ) +{ + WCHAR dir_spec[MAX_PATH]; + WIN32_FIND_DATAW wfd; + LPWSTR prop, path; + BOOL indirect; + LVITEMW item; + HANDLE file; + + static const WCHAR asterisk[] = {'*',0}; + + if (!control && !(control = msi_dialog_find_control_by_type( dialog, szDirectoryList ))) + return; + + /* clear the list-view */ + SendMessageW( control->hwnd, LVM_DELETEALLITEMS, 0, 0 ); + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + path = msi_dialog_dup_property( dialog, prop, TRUE ); + + lstrcpyW( dir_spec, path ); + lstrcatW( dir_spec, asterisk ); + + file = FindFirstFileW( dir_spec, &wfd ); + if ( file == INVALID_HANDLE_VALUE ) + return; + + do + { + if ( wfd.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY ) + continue; + + if ( !strcmpW( wfd.cFileName, szDot ) || !strcmpW( wfd.cFileName, szDotDot ) ) + continue; + + item.mask = LVIF_TEXT; + item.cchTextMax = MAX_PATH; + item.iItem = 0; + item.iSubItem = 0; + item.pszText = wfd.cFileName; + + SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item ); + } while ( FindNextFileW( file, &wfd ) ); + + msi_free( prop ); + msi_free( path ); + FindClose( file ); +} + +UINT msi_dialog_directorylist_up( msi_dialog *dialog ) +{ + msi_control *control; + LPWSTR prop, path, ptr; + BOOL indirect; + + control = msi_dialog_find_control_by_type( dialog, szDirectoryList ); + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + path = msi_dialog_dup_property( dialog, prop, TRUE ); + + /* strip off the last directory */ + ptr = PathFindFileNameW( path ); + if (ptr != path) *(ptr - 1) = '\0'; + PathAddBackslashW( path ); + + msi_dialog_set_property( dialog->package, prop, path ); + + msi_dialog_update_directory_list( dialog, NULL ); + msi_dialog_update_directory_combo( dialog, NULL ); + msi_dialog_update_pathedit( dialog, NULL ); + + msi_free( path ); + msi_free( prop ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_dirlist_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + LPNMHDR nmhdr = (LPNMHDR)param; + WCHAR new_path[MAX_PATH]; + WCHAR text[MAX_PATH]; + LPWSTR path, prop; + BOOL indirect; + LVITEMW item; + int index; + + if (nmhdr->code != LVN_ITEMACTIVATE) + return ERROR_SUCCESS; + + index = SendMessageW( control->hwnd, LVM_GETNEXTITEM, -1, LVNI_SELECTED ); + if ( index < 0 ) + { + ERR("No list-view item selected!\n"); + return ERROR_FUNCTION_FAILED; + } + + item.iSubItem = 0; + item.pszText = text; + item.cchTextMax = MAX_PATH; + SendMessageW( control->hwnd, LVM_GETITEMTEXTW, index, (LPARAM)&item ); + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + path = msi_dialog_dup_property( dialog, prop, TRUE ); + + lstrcpyW( new_path, path ); + lstrcatW( new_path, text ); + lstrcatW( new_path, szBackSlash ); + + msi_dialog_set_property( dialog->package, prop, new_path ); + + msi_dialog_update_directory_list( dialog, NULL ); + msi_dialog_update_directory_combo( dialog, NULL ); + msi_dialog_update_pathedit( dialog, NULL ); + + msi_free( prop ); + msi_free( path ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_directory_list( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + DWORD style; + + style = LVS_LIST | WS_VSCROLL | LVS_SHAREIMAGELISTS | + LVS_AUTOARRANGE | LVS_SINGLESEL | WS_BORDER | + LVS_SORTASCENDING | WS_CHILD | WS_GROUP | WS_TABSTOP; + control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style ); + if (!control) + return ERROR_FUNCTION_FAILED; + + control->attributes = MSI_RecordGetInteger( rec, 8 ); + control->handler = msi_dialog_dirlist_handler; + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + /* double click to activate an item in the list */ + SendMessageW( control->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, + 0, LVS_EX_TWOCLICKACTIVATE ); + + msi_dialog_update_directory_list( dialog, control ); + + return ERROR_SUCCESS; +} + +/******************** VolumeCost List ***************************************/ + +static BOOL str_is_number( LPCWSTR str ) +{ + int i; + + for (i = 0; i < lstrlenW( str ); i++) + if (!isdigitW(str[i])) + return FALSE; + + return TRUE; +} + +static const WCHAR column_keys[][80] = +{ + {'V','o','l','u','m','e','C','o','s','t','V','o','l','u','m','e',0}, + {'V','o','l','u','m','e','C','o','s','t','S','i','z','e',0}, + {'V','o','l','u','m','e','C','o','s','t','A','v','a','i','l','a','b','l','e',0}, + {'V','o','l','u','m','e','C','o','s','t','R','e','q','u','i','r','e','d',0}, + {'V','o','l','u','m','e','C','o','s','t','D','i','f','f','e','r','e','n','c','e',0} +}; + +static void msi_dialog_vcl_add_columns( msi_dialog *dialog, msi_control *control, MSIRECORD *rec ) +{ + LPCWSTR text = MSI_RecordGetString( rec, 10 ); + LPCWSTR begin = text, end; + WCHAR *num; + LVCOLUMNW lvc; + DWORD count = 0; + + static const WCHAR negative[] = {'-',0}; + + if (!text) return; + + while ((begin = strchrW( begin, '{' )) && count < 5) + { + if (!(end = strchrW( begin, '}' ))) + return; + + num = msi_alloc( (end-begin+1)*sizeof(WCHAR) ); + if (!num) + return; + + lstrcpynW( num, begin + 1, end - begin ); + begin += end - begin + 1; + + /* empty braces or '0' hides the column */ + if ( !num[0] || !strcmpW( num, szZero ) ) + { + count++; + msi_free( num ); + continue; + } + + /* the width must be a positive number + * if a width is invalid, all remaining columns are hidden + */ + if ( !strncmpW( num, negative, 1 ) || !str_is_number( num ) ) { + msi_free( num ); + return; + } + + ZeroMemory( &lvc, sizeof(lvc) ); + lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc.cx = atolW( num ); + lvc.pszText = msi_dialog_get_uitext( dialog, column_keys[count] ); + + SendMessageW( control->hwnd, LVM_INSERTCOLUMNW, count++, (LPARAM)&lvc ); + msi_free( lvc.pszText ); + msi_free( num ); + } +} + +static LONGLONG msi_vcl_get_cost( msi_dialog *dialog ) +{ + MSIFEATURE *feature; + INT each_cost; + LONGLONG total_cost = 0; + + LIST_FOR_EACH_ENTRY( feature, &dialog->package->features, MSIFEATURE, entry ) + { + if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature, + MSICOSTTREE_SELFONLY, INSTALLSTATE_LOCAL, &each_cost))) + { + /* each_cost is in 512-byte units */ + total_cost += each_cost * 512; + } + if (ERROR_SUCCESS == (MSI_GetFeatureCost(dialog->package, feature, + MSICOSTTREE_SELFONLY, INSTALLSTATE_ABSENT, &each_cost))) + { + /* each_cost is in 512-byte units */ + total_cost -= each_cost * 512; + } + } + return total_cost; +} + +static void msi_dialog_vcl_add_drives( msi_dialog *dialog, msi_control *control ) +{ + ULARGE_INTEGER total, free; + LONGLONG difference, cost; + WCHAR size_text[MAX_PATH]; + WCHAR cost_text[MAX_PATH]; + LPWSTR drives, ptr; + LVITEMW lvitem; + DWORD size; + int i = 0; + + cost = msi_vcl_get_cost(dialog); + StrFormatByteSizeW(cost, cost_text, MAX_PATH); + + size = GetLogicalDriveStringsW( 0, NULL ); + if ( !size ) return; + + drives = msi_alloc( (size + 1) * sizeof(WCHAR) ); + if ( !drives ) return; + + GetLogicalDriveStringsW( size, drives ); + + ptr = drives; + while (*ptr) + { + lvitem.mask = LVIF_TEXT; + lvitem.iItem = i; + lvitem.iSubItem = 0; + lvitem.pszText = ptr; + lvitem.cchTextMax = lstrlenW(ptr) + 1; + SendMessageW( control->hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvitem ); + + GetDiskFreeSpaceExW(ptr, &free, &total, NULL); + difference = free.QuadPart - cost; + + StrFormatByteSizeW(total.QuadPart, size_text, MAX_PATH); + lvitem.iSubItem = 1; + lvitem.pszText = size_text; + lvitem.cchTextMax = lstrlenW(size_text) + 1; + SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem ); + + StrFormatByteSizeW(free.QuadPart, size_text, MAX_PATH); + lvitem.iSubItem = 2; + lvitem.pszText = size_text; + lvitem.cchTextMax = lstrlenW(size_text) + 1; + SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem ); + + lvitem.iSubItem = 3; + lvitem.pszText = cost_text; + lvitem.cchTextMax = lstrlenW(cost_text) + 1; + SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem ); + + StrFormatByteSizeW(difference, size_text, MAX_PATH); + lvitem.iSubItem = 4; + lvitem.pszText = size_text; + lvitem.cchTextMax = lstrlenW(size_text) + 1; + SendMessageW( control->hwnd, LVM_SETITEMW, 0, (LPARAM)&lvitem ); + + ptr += lstrlenW(ptr) + 1; + i++; + } + + msi_free( drives ); +} + +static UINT msi_dialog_volumecost_list( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + DWORD style; + + style = LVS_REPORT | WS_VSCROLL | WS_HSCROLL | LVS_SHAREIMAGELISTS | + LVS_AUTOARRANGE | LVS_SINGLESEL | WS_BORDER | + WS_CHILD | WS_TABSTOP | WS_GROUP; + control = msi_dialog_add_control( dialog, rec, WC_LISTVIEWW, style ); + if (!control) + return ERROR_FUNCTION_FAILED; + + msi_dialog_vcl_add_columns( dialog, control, rec ); + msi_dialog_vcl_add_drives( dialog, control ); + + return ERROR_SUCCESS; +} + +/******************** VolumeSelect Combo ***************************************/ + +static UINT msi_dialog_volsel_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + WCHAR text[MAX_PATH]; + LPWSTR prop; + BOOL indirect; + int index; + + if (HIWORD(param) != CBN_SELCHANGE) + return ERROR_SUCCESS; + + index = SendMessageW( control->hwnd, CB_GETCURSEL, 0, 0 ); + if ( index == CB_ERR ) + { + ERR("No ComboBox item selected!\n"); + return ERROR_FUNCTION_FAILED; + } + + SendMessageW( control->hwnd, CB_GETLBTEXT, index, (LPARAM)text ); + + indirect = control->attributes & msidbControlAttributesIndirect; + prop = msi_dialog_dup_property( dialog, control->property, indirect ); + + msi_dialog_set_property( dialog->package, prop, text ); + + msi_free( prop ); + return ERROR_SUCCESS; +} + +static void msi_dialog_vsc_add_drives( msi_dialog *dialog, msi_control *control ) +{ + LPWSTR drives, ptr; + DWORD size; + + size = GetLogicalDriveStringsW( 0, NULL ); + if ( !size ) return; + + drives = msi_alloc( (size + 1) * sizeof(WCHAR) ); + if ( !drives ) return; + + GetLogicalDriveStringsW( size, drives ); + + ptr = drives; + while (*ptr) + { + SendMessageW( control->hwnd, CB_ADDSTRING, 0, (LPARAM)ptr ); + ptr += lstrlenW(ptr) + 1; + } + + msi_free( drives ); +} + +static UINT msi_dialog_volumeselect_combo( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + DWORD style; + + /* FIXME: CBS_OWNERDRAWFIXED */ + style = WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | + CBS_DROPDOWNLIST | CBS_SORT | CBS_HASSTRINGS | + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR; + control = msi_dialog_add_control( dialog, rec, WC_COMBOBOXW, style ); + if (!control) + return ERROR_FUNCTION_FAILED; + + control->attributes = MSI_RecordGetInteger( rec, 8 ); + control->handler = msi_dialog_volsel_handler; + prop = MSI_RecordGetString( rec, 9 ); + control->property = msi_dialog_dup_property( dialog, prop, FALSE ); + + msi_dialog_vsc_add_drives( dialog, control ); + + return ERROR_SUCCESS; +} + +static const struct control_handler msi_dialog_handler[] = +{ + { szText, msi_dialog_text_control }, + { szPushButton, msi_dialog_button_control }, + { szLine, msi_dialog_line_control }, + { szBitmap, msi_dialog_bitmap_control }, + { szCheckBox, msi_dialog_checkbox_control }, + { szScrollableText, msi_dialog_scrolltext_control }, + { szComboBox, msi_dialog_combo_control }, + { szEdit, msi_dialog_edit_control }, + { szMaskedEdit, msi_dialog_maskedit_control }, + { szPathEdit, msi_dialog_pathedit_control }, + { szProgressBar, msi_dialog_progress_bar }, + { szRadioButtonGroup, msi_dialog_radiogroup_control }, + { szIcon, msi_dialog_icon_control }, + { szSelectionTree, msi_dialog_selection_tree }, + { szGroupBox, msi_dialog_group_box }, + { szListBox, msi_dialog_list_box }, + { szDirectoryCombo, msi_dialog_directory_combo }, + { szDirectoryList, msi_dialog_directory_list }, + { szVolumeCostList, msi_dialog_volumecost_list }, + { szVolumeSelectCombo, msi_dialog_volumeselect_combo }, +}; + +#define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0]) + +static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param ) +{ + msi_dialog *dialog = param; + LPCWSTR control_type; + UINT i; + + /* find and call the function that can create this type of control */ + control_type = MSI_RecordGetString( rec, 3 ); + for( i=0; i<NUM_CONTROL_TYPES; i++ ) + if (!strcmpiW( msi_dialog_handler[i].control_type, control_type )) + break; + if( i != NUM_CONTROL_TYPES ) + msi_dialog_handler[i].func( dialog, rec ); + else + ERR("no handler for element type %s\n", debugstr_w(control_type)); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_fill_controls( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + 'C','o','n','t','r','o','l',' ','W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0}; + UINT r; + MSIQUERY *view; + MSIPACKAGE *package = dialog->package; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name) ); + + /* query the Control table for all the elements of the control */ + r = MSI_OpenQuery( package->db, &view, query, dialog->name ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed for dialog %s\n", debugstr_w(dialog->name)); + return ERROR_INVALID_PARAMETER; + } + + r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog ); + msiobj_release( &view->hdr ); + return r; +} + +UINT msi_dialog_reset( msi_dialog *dialog ) +{ + /* FIXME: should restore the original values of any properties we changed */ + return msi_dialog_evaluate_control_conditions( dialog ); +} + +/* figure out the height of 10 point MS Sans Serif */ +static INT msi_dialog_get_sans_serif_height( HWND hwnd ) +{ + static const WCHAR szSansSerif[] = { + 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 }; + LOGFONTW lf; + TEXTMETRICW tm; + BOOL r; + LONG height = 0; + HFONT hFont, hOldFont; + HDC hdc; + + hdc = GetDC( hwnd ); + if (hdc) + { + memset( &lf, 0, sizeof lf ); + lf.lfHeight = MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72); + strcpyW( lf.lfFaceName, szSansSerif ); + hFont = CreateFontIndirectW(&lf); + if (hFont) + { + hOldFont = SelectObject( hdc, hFont ); + r = GetTextMetricsW( hdc, &tm ); + if (r) + height = tm.tmHeight; + SelectObject( hdc, hOldFont ); + DeleteObject( hFont ); + } + ReleaseDC( hwnd, hdc ); + } + return height; +} + +/* fetch the associated record from the Dialog table */ +static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','D','i','a','l','o','g',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0}; + MSIPACKAGE *package = dialog->package; + MSIRECORD *rec = NULL; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name) ); + + rec = MSI_QueryGetRecord( package->db, query, dialog->name ); + if( !rec ) + WARN("query failed for dialog %s\n", debugstr_w(dialog->name)); + + return rec; +} + +static void msi_dialog_adjust_dialog_pos( msi_dialog *dialog, MSIRECORD *rec, LPRECT pos ) +{ + static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0}; + static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0}; + + UINT xres, yres; + POINT center; + SIZE sz; + LONG style; + + center.x = MSI_RecordGetInteger( rec, 2 ); + center.y = MSI_RecordGetInteger( rec, 3 ); + + sz.cx = MSI_RecordGetInteger( rec, 4 ); + sz.cy = MSI_RecordGetInteger( rec, 5 ); + + sz.cx = msi_dialog_scale_unit( dialog, sz.cx ); + sz.cy = msi_dialog_scale_unit( dialog, sz.cy ); + + xres = msi_get_property_int( dialog->package->db, szScreenX, 0 ); + yres = msi_get_property_int( dialog->package->db, szScreenY, 0 ); + + center.x = MulDiv( center.x, xres, 100 ); + center.y = MulDiv( center.y, yres, 100 ); + + /* turn the client pos into the window rectangle */ + if (dialog->package->center_x && dialog->package->center_y) + { + pos->left = dialog->package->center_x - sz.cx / 2.0; + pos->right = pos->left + sz.cx; + pos->top = dialog->package->center_y - sz.cy / 2.0; + pos->bottom = pos->top + sz.cy; + } + else + { + pos->left = center.x - sz.cx/2; + pos->right = pos->left + sz.cx; + pos->top = center.y - sz.cy/2; + pos->bottom = pos->top + sz.cy; + + /* save the center */ + dialog->package->center_x = center.x; + dialog->package->center_y = center.y; + } + + dialog->size.cx = sz.cx; + dialog->size.cy = sz.cy; + + TRACE("%u %u %u %u\n", pos->left, pos->top, pos->right, pos->bottom); + + style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE ); + AdjustWindowRect( pos, style, FALSE ); +} + +static void msi_dialog_set_tab_order( msi_dialog *dialog, LPCWSTR first ) +{ + struct list tab_chain; + msi_control *control; + HWND prev = HWND_TOP; + + list_init( &tab_chain ); + if (!(control = msi_dialog_find_control( dialog, first ))) return; + + dialog->hWndFocus = control->hwnd; + while (control) + { + list_remove( &control->entry ); + list_add_tail( &tab_chain, &control->entry ); + if (!control->tabnext) break; + control = msi_dialog_find_control( dialog, control->tabnext ); + } + + LIST_FOR_EACH_ENTRY( control, &tab_chain, msi_control, entry ) + { + SetWindowPos( control->hwnd, prev, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | + SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE ); + prev = control->hwnd; + } + + /* put them back on the main list */ + list_move_head( &dialog->controls, &tab_chain ); +} + +static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs ) +{ + static const WCHAR df[] = { + 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 }; + static const WCHAR dfv[] = { + 'M','S',' ','S','h','e','l','l',' ','D','l','g',0 }; + msi_dialog *dialog = cs->lpCreateParams; + MSIRECORD *rec = NULL; + LPWSTR title = NULL; + RECT pos; + + TRACE("%p %p\n", dialog, dialog->package); + + dialog->hwnd = hwnd; + SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog ); + + rec = msi_get_dialog_record( dialog ); + if( !rec ) + { + TRACE("No record found for dialog %s\n", debugstr_w(dialog->name)); + return -1; + } + + dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd); + + msi_dialog_adjust_dialog_pos( dialog, rec, &pos ); + + dialog->attributes = MSI_RecordGetInteger( rec, 6 ); + + dialog->default_font = msi_dup_property( dialog->package->db, df ); + if (!dialog->default_font) + { + dialog->default_font = strdupW(dfv); + if (!dialog->default_font) return -1; + } + + title = msi_get_deformatted_field( dialog->package, rec, 7 ); + SetWindowTextW( hwnd, title ); + msi_free( title ); + + SetWindowPos( hwnd, 0, pos.left, pos.top, + pos.right - pos.left, pos.bottom - pos.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW ); + + msi_dialog_build_font_list( dialog ); + msi_dialog_fill_controls( dialog ); + msi_dialog_evaluate_control_conditions( dialog ); + msi_dialog_set_tab_order( dialog, MSI_RecordGetString( rec, 8 ) ); + msiobj_release( &rec->hdr ); + + return 0; +} + +static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd ) +{ + msi_control *control = NULL; + + TRACE("%p %p %08lx\n", dialog, hwnd, param); + + switch (param) + { + case 1: /* enter */ + control = msi_dialog_find_control( dialog, dialog->control_default ); + break; + case 2: /* escape */ + control = msi_dialog_find_control( dialog, dialog->control_cancel ); + break; + default: + control = msi_dialog_find_control_by_hwnd( dialog, hwnd ); + } + + if( control ) + { + if( control->handler ) + { + control->handler( dialog, control, param ); + msi_dialog_evaluate_control_conditions( dialog ); + } + } + + return 0; +} + +static LRESULT msi_dialog_onnotify( msi_dialog *dialog, LPARAM param ) +{ + LPNMHDR nmhdr = (LPNMHDR) param; + msi_control *control = msi_dialog_find_control_by_hwnd( dialog, nmhdr->hwndFrom ); + + TRACE("%p %p\n", dialog, nmhdr->hwndFrom); + + if ( control && control->handler ) + control->handler( dialog, control, param ); + + return 0; +} + +static void msi_dialog_setfocus( msi_dialog *dialog ) +{ + HWND hwnd = dialog->hWndFocus; + + hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, TRUE); + hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, FALSE); + SetFocus( hwnd ); + dialog->hWndFocus = hwnd; +} + +static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam ) +{ + msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA ); + + TRACE("0x%04x\n", msg); + + switch (msg) + { + case WM_MOVE: + dialog->package->center_x = LOWORD(lParam) + dialog->size.cx / 2.0; + dialog->package->center_y = HIWORD(lParam) + dialog->size.cy / 2.0; + break; + + case WM_CREATE: + return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam ); + + case WM_COMMAND: + return msi_dialog_oncommand( dialog, wParam, (HWND)lParam ); + + case WM_ACTIVATE: + if( LOWORD(wParam) == WA_INACTIVE ) + dialog->hWndFocus = GetFocus(); + else + msi_dialog_setfocus( dialog ); + return 0; + + case WM_SETFOCUS: + msi_dialog_setfocus( dialog ); + return 0; + + /* bounce back to our subclassed static control */ + case WM_CTLCOLORSTATIC: + return SendMessageW( (HWND) lParam, WM_CTLCOLORSTATIC, wParam, lParam ); + + case WM_DESTROY: + dialog->hwnd = NULL; + return 0; + case WM_NOTIFY: + return msi_dialog_onnotify( dialog, lParam ); + } + return DefWindowProcW(hwnd, msg, wParam, lParam); +} + +static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam ) +{ + msi_dialog *dialog = (msi_dialog*) lParam; + + TRACE("%d %p\n", msg, dialog); + + switch (msg) + { + case WM_MSI_DIALOG_CREATE: + return msi_dialog_run_message_loop( dialog ); + case WM_MSI_DIALOG_DESTROY: + msi_dialog_destroy( dialog ); + return 0; + } + return DefWindowProcW( hwnd, msg, wParam, lParam ); +} + +static BOOL msi_dialog_register_class( void ) +{ + WNDCLASSW cls; + + ZeroMemory( &cls, sizeof cls ); + cls.lpfnWndProc = MSIDialog_WndProc; + cls.hInstance = NULL; + cls.hIcon = LoadIconW(0, (LPWSTR)IDI_APPLICATION); + cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + cls.lpszMenuName = NULL; + cls.lpszClassName = szMsiDialogClass; + + if( !RegisterClassW( &cls ) ) + return FALSE; + + cls.lpfnWndProc = MSIHiddenWindowProc; + cls.lpszClassName = szMsiHiddenWindow; + + if( !RegisterClassW( &cls ) ) + return FALSE; + + uiThreadId = GetCurrentThreadId(); + + hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED, + 0, 0, 100, 100, NULL, NULL, NULL, NULL ); + if( !hMsiHiddenWindow ) + return FALSE; + + return TRUE; +} + +/* functions that interface to other modules within MSI */ + +msi_dialog *msi_dialog_create( MSIPACKAGE* package, + LPCWSTR szDialogName, msi_dialog *parent, + msi_dialog_event_handler event_handler ) +{ + MSIRECORD *rec = NULL; + msi_dialog *dialog; + + TRACE("%p %s\n", package, debugstr_w(szDialogName)); + + if (!hMsiHiddenWindow) + msi_dialog_register_class(); + + /* allocate the structure for the dialog to use */ + dialog = msi_alloc_zero( sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) ); + if( !dialog ) + return NULL; + strcpyW( dialog->name, szDialogName ); + dialog->parent = parent; + msiobj_addref( &package->hdr ); + dialog->package = package; + dialog->event_handler = event_handler; + dialog->finished = 0; + list_init( &dialog->controls ); + list_init( &dialog->fonts ); + + /* verify that the dialog exists */ + rec = msi_get_dialog_record( dialog ); + if( !rec ) + { + msiobj_release( &package->hdr ); + msi_free( dialog ); + return NULL; + } + dialog->attributes = MSI_RecordGetInteger( rec, 6 ); + dialog->control_default = strdupW( MSI_RecordGetString( rec, 9 ) ); + dialog->control_cancel = strdupW( MSI_RecordGetString( rec, 10 ) ); + msiobj_release( &rec->hdr ); + + return dialog; +} + +static void msi_process_pending_messages( HWND hdlg ) +{ + MSG msg; + + while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) ) + { + if( hdlg && IsDialogMessageW( hdlg, &msg )) + continue; + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } +} + +void msi_dialog_end_dialog( msi_dialog *dialog ) +{ + TRACE("%p\n", dialog); + dialog->finished = 1; + PostMessageW(dialog->hwnd, WM_NULL, 0, 0); +} + +void msi_dialog_check_messages( HANDLE handle ) +{ + DWORD r; + + /* in threads other than the UI thread, block */ + if( uiThreadId != GetCurrentThreadId() ) + { + if (!handle) return; + while (MsgWaitForMultipleObjectsEx( 1, &handle, INFINITE, QS_ALLINPUT, 0 ) == WAIT_OBJECT_0 + 1) + { + MSG msg; + while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + } + return; + } + + /* there's two choices for the UI thread */ + while (1) + { + msi_process_pending_messages( NULL ); + + if( !handle ) + break; + + /* + * block here until somebody creates a new dialog or + * the handle we're waiting on becomes ready + */ + r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT ); + if( r == WAIT_OBJECT_0 ) + break; + } +} + +UINT msi_dialog_run_message_loop( msi_dialog *dialog ) +{ + DWORD style; + HWND hwnd; + + if( uiThreadId != GetCurrentThreadId() ) + return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog ); + + /* create the dialog window, don't show it yet */ + style = WS_OVERLAPPED; + if( dialog->attributes & msidbDialogAttributesVisible ) + style |= WS_VISIBLE; + + hwnd = CreateWindowW( szMsiDialogClass, dialog->name, style, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, NULL, dialog ); + if( !hwnd ) + { + ERR("Failed to create dialog %s\n", debugstr_w( dialog->name )); + return ERROR_FUNCTION_FAILED; + } + + ShowWindow( hwnd, SW_SHOW ); + /* UpdateWindow( hwnd ); - and causes the transparent static controls not to paint */ + + if( dialog->attributes & msidbDialogAttributesModal ) + { + while( !dialog->finished ) + { + MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLINPUT ); + msi_process_pending_messages( dialog->hwnd ); + } + } + else + return ERROR_IO_PENDING; + + return ERROR_SUCCESS; +} + +static void msi_dialog_do_preview( msi_dialog *dialog ) +{ + TRACE("\n"); + dialog->attributes |= msidbDialogAttributesVisible; + dialog->attributes &= ~msidbDialogAttributesModal; + msi_dialog_run_message_loop( dialog ); +} + +void msi_dialog_destroy( msi_dialog *dialog ) +{ + msi_font *font, *next; + + if( uiThreadId != GetCurrentThreadId() ) + { + SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog ); + return; + } + + if( dialog->hwnd ) + ShowWindow( dialog->hwnd, SW_HIDE ); + + if( dialog->hwnd ) + DestroyWindow( dialog->hwnd ); + + /* unsubscribe events */ + ControlEvent_CleanupDialogSubscriptions(dialog->package, dialog->name); + + /* destroy the list of controls */ + while( !list_empty( &dialog->controls ) ) + { + msi_control *t; + + t = LIST_ENTRY( list_head( &dialog->controls ), + msi_control, entry ); + msi_destroy_control( t ); + } + + /* destroy the list of fonts */ + LIST_FOR_EACH_ENTRY_SAFE( font, next, &dialog->fonts, msi_font, entry ) + { + list_remove( &font->entry ); + DeleteObject( font->hfont ); + msi_free( font ); + } + msi_free( dialog->default_font ); + + msi_free( dialog->control_default ); + msi_free( dialog->control_cancel ); + msiobj_release( &dialog->package->hdr ); + dialog->package = NULL; + msi_free( dialog ); +} + +void msi_dialog_unregister_class( void ) +{ + DestroyWindow( hMsiHiddenWindow ); + hMsiHiddenWindow = NULL; + UnregisterClassW( szMsiDialogClass, NULL ); + UnregisterClassW( szMsiHiddenWindow, NULL ); + uiThreadId = 0; +} + +static UINT error_dialog_handler(MSIPACKAGE *package, LPCWSTR event, + LPCWSTR argument, msi_dialog* dialog) +{ + static const WCHAR end_dialog[] = {'E','n','d','D','i','a','l','o','g',0}; + static const WCHAR error_abort[] = {'E','r','r','o','r','A','b','o','r','t',0}; + static const WCHAR error_cancel[] = {'E','r','r','o','r','C','a','n','c','e','l',0}; + static const WCHAR error_no[] = {'E','r','r','o','r','N','o',0}; + static const WCHAR result_prop[] = { + 'M','S','I','E','r','r','o','r','D','i','a','l','o','g','R','e','s','u','l','t',0 + }; + + if ( strcmpW( event, end_dialog ) ) + return ERROR_SUCCESS; + + if ( !strcmpW( argument, error_abort ) || !strcmpW( argument, error_cancel ) || + !strcmpW( argument, error_no ) ) + { + msi_set_property( package->db, result_prop, error_abort ); + } + + ControlEvent_CleanupSubscriptions(package); + msi_dialog_end_dialog( dialog ); + + return ERROR_SUCCESS; +} + +static UINT msi_error_dialog_set_error( MSIPACKAGE *package, LPWSTR error_dialog, LPWSTR error ) +{ + MSIRECORD * row; + + static const WCHAR update[] = + {'U','P','D','A','T','E',' ','`','C','o','n','t','r','o','l','`',' ', + 'S','E','T',' ','`','T','e','x','t','`',' ','=',' ','\'','%','s','\'',' ', + 'W','H','E','R','E', ' ','`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ', + 'A','N','D',' ','`','C','o','n','t','r','o','l','`',' ','=',' ', + '\'','E','r','r','o','r','T','e','x','t','\'',0}; + + row = MSI_QueryGetRecord( package->db, update, error, error_dialog ); + if (!row) + return ERROR_FUNCTION_FAILED; + + msiobj_release(&row->hdr); + return ERROR_SUCCESS; +} + +UINT msi_spawn_error_dialog( MSIPACKAGE *package, LPWSTR error_dialog, LPWSTR error ) +{ + msi_dialog *dialog; + WCHAR result[MAX_PATH]; + UINT r = ERROR_SUCCESS; + DWORD size = MAX_PATH; + int res; + + static const WCHAR pn_prop[] = {'P','r','o','d','u','c','t','N','a','m','e',0}; + static const WCHAR title_fmt[] = {'%','s',' ','W','a','r','n','i','n','g',0}; + static const WCHAR error_abort[] = {'E','r','r','o','r','A','b','o','r','t',0}; + static const WCHAR result_prop[] = { + 'M','S','I','E','r','r','o','r','D','i','a','l','o','g','R','e','s','u','l','t',0 + }; + + if ((package->ui_level & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE) return ERROR_SUCCESS; + + if ( !error_dialog ) + { + LPWSTR product_name = msi_dup_property( package->db, pn_prop ); + WCHAR title[MAX_PATH]; + + sprintfW( title, title_fmt, product_name ); + res = MessageBoxW( NULL, error, title, MB_OKCANCEL | MB_ICONWARNING ); + + msi_free( product_name ); + + if ( res == IDOK ) + return ERROR_SUCCESS; + else + return ERROR_FUNCTION_FAILED; + } + + r = msi_error_dialog_set_error( package, error_dialog, error ); + if ( r != ERROR_SUCCESS ) + return r; + + dialog = msi_dialog_create( package, error_dialog, package->dialog, + error_dialog_handler ); + if ( !dialog ) + return ERROR_FUNCTION_FAILED; + + dialog->finished = FALSE; + r = msi_dialog_run_message_loop( dialog ); + if ( r != ERROR_SUCCESS ) + goto done; + + r = msi_get_property( package->db, result_prop, result, &size ); + if ( r != ERROR_SUCCESS) + r = ERROR_SUCCESS; + + if ( !strcmpW( result, error_abort ) ) + r = ERROR_FUNCTION_FAILED; + +done: + msi_dialog_destroy( dialog ); + + return r; +} + +static void MSI_ClosePreview( MSIOBJECTHDR *arg ) +{ + MSIPREVIEW *preview = (MSIPREVIEW *)arg; + msiobj_release( &preview->package->hdr ); +} + +static MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE *db ) +{ + MSIPREVIEW *preview = NULL; + MSIPACKAGE *package; + + package = MSI_CreatePackage( db, NULL ); + if (package) + { + preview = alloc_msiobject( MSIHANDLETYPE_PREVIEW, sizeof(MSIPREVIEW), MSI_ClosePreview ); + if (preview) + { + preview->package = package; + msiobj_addref( &package->hdr ); + } + msiobj_release( &package->hdr ); + } + return preview; +} + +UINT WINAPI MsiEnableUIPreview( MSIHANDLE hdb, MSIHANDLE *phPreview ) +{ + MSIDATABASE *db; + MSIPREVIEW *preview; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%d %p\n", hdb, phPreview); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if (!db) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); + if (!remote_database) + return ERROR_INVALID_HANDLE; + + *phPreview = 0; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiEnableUIPreview not allowed during a custom action!\n"); + + return ERROR_FUNCTION_FAILED; + } + preview = MSI_EnableUIPreview( db ); + if (preview) + { + *phPreview = alloc_msihandle( &preview->hdr ); + msiobj_release( &preview->hdr ); + r = ERROR_SUCCESS; + if (!*phPreview) + r = ERROR_NOT_ENOUGH_MEMORY; + } + msiobj_release( &db->hdr ); + return r; +} + +static UINT preview_event_handler( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR argument, msi_dialog *dialog ) +{ + MESSAGE("Preview dialog event '%s' (arg='%s')\n", debugstr_w(event), debugstr_w(argument)); + return ERROR_SUCCESS; +} + +static UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName ) +{ + msi_dialog *dialog = NULL; + UINT r = ERROR_SUCCESS; + + if (preview->dialog) + msi_dialog_destroy( preview->dialog ); + + /* an empty name means we should just destroy the current preview dialog */ + if (szDialogName) + { + dialog = msi_dialog_create( preview->package, szDialogName, NULL, preview_event_handler ); + if (dialog) + msi_dialog_do_preview( dialog ); + else + r = ERROR_FUNCTION_FAILED; + } + preview->dialog = dialog; + return r; +} + +UINT WINAPI MsiPreviewDialogW( MSIHANDLE hPreview, LPCWSTR szDialogName ) +{ + MSIPREVIEW *preview; + UINT r; + + TRACE("%d %s\n", hPreview, debugstr_w(szDialogName)); + + preview = msihandle2msiinfo( hPreview, MSIHANDLETYPE_PREVIEW ); + if (!preview) + return ERROR_INVALID_HANDLE; + + r = MSI_PreviewDialogW( preview, szDialogName ); + msiobj_release( &preview->hdr ); + return r; +} + +UINT WINAPI MsiPreviewDialogA( MSIHANDLE hPreview, LPCSTR szDialogName ) +{ + UINT r; + LPWSTR strW = NULL; + + TRACE("%d %s\n", hPreview, debugstr_a(szDialogName)); + + if (szDialogName) + { + strW = strdupAtoW( szDialogName ); + if (!strW) + return ERROR_OUTOFMEMORY; + } + r = MsiPreviewDialogW( hPreview, strW ); + msi_free( strW ); + return r; +} + +UINT WINAPI MsiPreviewBillboardW( MSIHANDLE hPreview, LPCWSTR szControlName, LPCWSTR szBillboard ) +{ + FIXME("%d %s %s\n", hPreview, debugstr_w(szControlName), debugstr_w(szBillboard)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName, LPCSTR szBillboard ) +{ + FIXME("%d %s %s\n", hPreview, debugstr_a(szControlName), debugstr_a(szBillboard)); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/libmsi/distinct.c b/libmsi/distinct.c new file mode 100644 index 0000000..04f09fa --- /dev/null +++ b/libmsi/distinct.c @@ -0,0 +1,322 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct tagDISTINCTSET +{ + UINT val; + UINT count; + UINT row; + struct tagDISTINCTSET *nextrow; + struct tagDISTINCTSET *nextcol; +} DISTINCTSET; + +typedef struct tagMSIDISTINCTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT row_count; + UINT *translation; +} MSIDISTINCTVIEW; + +static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row ) +{ + /* horrible O(n) find */ + while( *x ) + { + if( (*x)->val == val ) + { + (*x)->count++; + return x; + } + x = &(*x)->nextrow; + } + + /* nothing found, so add one */ + *x = msi_alloc( sizeof (DISTINCTSET) ); + if( *x ) + { + (*x)->val = val; + (*x)->count = 1; + (*x)->row = row; + (*x)->nextrow = NULL; + (*x)->nextcol = NULL; + } + return x; +} + +static void distinct_free( DISTINCTSET *x ) +{ + while( x ) + { + DISTINCTSET *next = x->nextrow; + distinct_free( x->nextcol ); + msi_free( x ); + x = next; + } +} + +static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, val ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( row >= dv->row_count ) + return ERROR_INVALID_PARAMETER; + + row = dv->translation[ row ]; + + return dv->table->ops->fetch_int( dv->table, row, col, val ); +} + +static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + UINT r, i, j, r_count, c_count; + DISTINCTSET *rowset = NULL; + + TRACE("%p %p\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute( dv->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count ); + if( r != ERROR_SUCCESS ) + return r; + + dv->translation = msi_alloc( r_count*sizeof(UINT) ); + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + + /* build it */ + for( i=0; i<r_count; i++ ) + { + DISTINCTSET **x = &rowset; + + for( j=1; j<=c_count; j++ ) + { + UINT val = 0; + r = dv->table->ops->fetch_int( dv->table, i, j, &val ); + if( r != ERROR_SUCCESS ) + { + ERR("Failed to fetch int at %d %d\n", i, j ); + distinct_free( rowset ); + return r; + } + x = distinct_insert( x, val, i ); + if( !*x ) + { + ERR("Failed to insert at %d %d\n", i, j ); + distinct_free( rowset ); + return ERROR_FUNCTION_FAILED; + } + if( j != c_count ) + x = &(*x)->nextcol; + } + + /* check if it was distinct and if so, include it */ + if( (*x)->row == i ) + { + TRACE("Row %d -> %d\n", dv->row_count, i); + dv->translation[dv->row_count++] = i; + } + } + + distinct_free( rowset ); + + return ERROR_SUCCESS; +} + +static UINT DISTINCT_close( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + msi_free( dv->translation ); + dv->translation = NULL; + dv->row_count = 0; + + return dv->table->ops->close( dv->table ); +} + +static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + *rows = dv->row_count; + } + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p %p %p %p\n", dv, n, name, type, temporary, table_name ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, + type, temporary, table_name ); +} + +static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p\n", dv, eModifyMode, rec ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->modify( dv->table, eModifyMode, rec, row ); +} + +static UINT DISTINCT_delete( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + msi_free( dv->translation ); + msiobj_release( &dv->db->hdr ); + msi_free( dv ); + + return ERROR_SUCCESS; +} + +static UINT DISTINCT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + UINT r; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->find_matching_rows( dv->table, col, val, row, handle ); + + if( *row > dv->row_count ) + return ERROR_NO_MORE_ITEMS; + + *row = dv->translation[ *row ]; + + return r; +} + +static const MSIVIEWOPS distinct_ops = +{ + DISTINCT_fetch_int, + NULL, + NULL, + NULL, + NULL, + NULL, + DISTINCT_execute, + DISTINCT_close, + DISTINCT_get_dimensions, + DISTINCT_get_column_info, + DISTINCT_modify, + DISTINCT_delete, + DISTINCT_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIDISTINCTVIEW *dv = NULL; + UINT count = 0, r; + + TRACE("%p\n", dv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + dv = msi_alloc_zero( sizeof *dv ); + if( !dv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + dv->view.ops = &distinct_ops; + msiobj_addref( &db->hdr ); + dv->db = db; + dv->table = table; + dv->translation = NULL; + dv->row_count = 0; + *view = (MSIVIEW*) dv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/drop.c b/libmsi/drop.c new file mode 100644 index 0000000..f0b5803 --- /dev/null +++ b/libmsi/drop.c @@ -0,0 +1,142 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2008 James Hawkins + * + * 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct tagMSIDROPVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + column_info *colinfo; + INT hold; +} MSIDROPVIEW; + +static UINT DROP_execute(struct tagMSIVIEW *view, MSIRECORD *record) +{ + MSIDROPVIEW *dv = (MSIDROPVIEW*)view; + UINT r; + + TRACE("%p %p\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute(dv->table, record); + if (r != ERROR_SUCCESS) + return r; + + return dv->table->ops->drop(dv->table); +} + +static UINT DROP_close(struct tagMSIVIEW *view) +{ + MSIDROPVIEW *dv = (MSIDROPVIEW*)view; + + TRACE("%p\n", dv); + + return ERROR_SUCCESS; +} + +static UINT DROP_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSIDROPVIEW *dv = (MSIDROPVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DROP_delete( struct tagMSIVIEW *view ) +{ + MSIDROPVIEW *dv = (MSIDROPVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + msi_free( dv ); + + return ERROR_SUCCESS; +} + +static const MSIVIEWOPS drop_ops = +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + DROP_execute, + DROP_close, + DROP_get_dimensions, + NULL, + NULL, + DROP_delete, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +UINT DROP_CreateView(MSIDATABASE *db, MSIVIEW **view, LPCWSTR name) +{ + MSIDROPVIEW *dv; + UINT r; + + TRACE("%p %s\n", view, debugstr_w(name)); + + dv = msi_alloc_zero(sizeof *dv); + if(!dv) + return ERROR_FUNCTION_FAILED; + + r = TABLE_CreateView(db, name, &dv->table); + if (r != ERROR_SUCCESS) + { + msi_free( dv ); + return r; + } + + dv->view.ops = &drop_ops; + dv->db = db; + + *view = (MSIVIEW *)dv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/events.c b/libmsi/events.c new file mode 100644 index 0000000..d42ac5b --- /dev/null +++ b/libmsi/events.c @@ -0,0 +1,449 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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> +#include <stdio.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "msi.h" +#include "msipriv.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef UINT (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *); + +struct control_events +{ + const WCHAR *event; + EVENTHANDLER handler; +}; + +struct subscriber { + struct list entry; + msi_dialog *dialog; + LPWSTR event; + LPWSTR control; + LPWSTR attribute; +}; + +static UINT ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*); + +/* + * Create a dialog box and run it if it's modal + */ +static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name, msi_dialog *parent, BOOL destroy_modeless ) +{ + msi_dialog *dialog; + UINT r; + + /* create a new dialog */ + dialog = msi_dialog_create( package, name, parent, + ControlEvent_HandleControlEvent ); + if( dialog ) + { + /* kill the current modeless dialog */ + if( destroy_modeless && package->dialog ) + { + msi_dialog_destroy( package->dialog ); + package->dialog = NULL; + } + + /* modeless dialogs return an error message */ + r = msi_dialog_run_message_loop( dialog ); + if( r == ERROR_SUCCESS ) + msi_dialog_destroy( dialog ); + else + package->dialog = dialog; + } + else + r = ERROR_FUNCTION_FAILED; + + return r; +} + + +/* + * End a modal dialog box + */ +static UINT ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szExit[] = {'E','x','i','t',0}; + static const WCHAR szRetry[] = {'R','e','t','r','y',0}; + static const WCHAR szIgnore[] = {'I','g','n','o','r','e',0}; + static const WCHAR szReturn[] = {'R','e','t','u','r','n',0}; + + if (!strcmpW( argument, szExit )) + package->CurrentInstallState = ERROR_INSTALL_USEREXIT; + else if (!strcmpW( argument, szRetry )) + package->CurrentInstallState = ERROR_INSTALL_SUSPEND; + else if (!strcmpW( argument, szIgnore )) + package->CurrentInstallState = ERROR_SUCCESS; + else if (!strcmpW( argument, szReturn )) + { + msi_dialog *parent = msi_dialog_get_parent(dialog); + msi_free(package->next_dialog); + package->next_dialog = (parent) ? strdupW(msi_dialog_get_name(parent)) : NULL; + package->CurrentInstallState = ERROR_SUCCESS; + } + else + { + ERR("Unknown argument string %s\n",debugstr_w(argument)); + package->CurrentInstallState = ERROR_FUNCTION_FAILED; + } + + ControlEvent_CleanupDialogSubscriptions(package, msi_dialog_get_name( dialog )); + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * transition from one modal dialog to another modal dialog + */ +static UINT ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog *dialog) +{ + /* store the name of the next dialog, and signal this one to end */ + package->next_dialog = strdupW(argument); + ControlEvent_CleanupSubscriptions(package); + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * Create a new child dialog of an existing modal dialog + */ +static UINT ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog *dialog) +{ + /* don't destroy a modeless dialogs that might be our parent */ + event_do_dialog( package, argument, dialog, FALSE ); + if( package->CurrentInstallState != ERROR_SUCCESS ) + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * Creates a dialog that remains up for a period of time + * based on a condition + */ +static UINT ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + FIXME("Doing Nothing\n"); + return ERROR_SUCCESS; +} + +static UINT ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + ACTION_PerformAction(package, argument, SCRIPT_NONE); + return ERROR_SUCCESS; +} + +static UINT ControlEvent_AddLocal( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll )) + { + if (feature->ActionRequest != INSTALLSTATE_LOCAL) + msi_set_property( package->db, szPreselected, szOne ); + MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_LOCAL ); + } + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_Remove( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll )) + { + if (feature->ActionRequest != INSTALLSTATE_ABSENT) + msi_set_property( package->db, szPreselected, szOne ); + MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_ABSENT ); + } + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_AddSource( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll )) + { + if (feature->ActionRequest != INSTALLSTATE_SOURCE) + msi_set_property( package->db, szPreselected, szOne ); + MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_SOURCE ); + } + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_SetTargetPath(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0}; + LPWSTR path = msi_dup_property( package->db, argument ); + MSIRECORD *rec = MSI_CreateRecord( 1 ); + UINT r = ERROR_SUCCESS; + + MSI_RecordSetStringW( rec, 1, path ); + ControlEvent_FireSubscribedEvent( package, szSelectionPath, rec ); + if (path) + { + /* failure to set the path halts the executing of control events */ + r = MSI_SetTargetPathW(package, argument, path); + msi_free(path); + } + msi_free(&rec->hdr); + return r; +} + +static UINT ControlEvent_Reset(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + msi_dialog_reset(dialog); + return ERROR_SUCCESS; +} + +/* + * Subscribed events + */ +static void free_subscriber( struct subscriber *sub ) +{ + msi_free(sub->event); + msi_free(sub->control); + msi_free(sub->attribute); + msi_free(sub); +} + +VOID ControlEvent_SubscribeToEvent( MSIPACKAGE *package, msi_dialog *dialog, + LPCWSTR event, LPCWSTR control, LPCWSTR attribute ) +{ + struct subscriber *sub; + + TRACE("event %s control %s attribute %s\n", debugstr_w(event), debugstr_w(control), debugstr_w(attribute)); + + LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry ) + { + if (!strcmpiW( sub->event, event ) && + !strcmpiW( sub->control, control ) && + !strcmpiW( sub->attribute, attribute )) + { + TRACE("already subscribed\n"); + return; + }; + } + if (!(sub = msi_alloc( sizeof(*sub) ))) return; + sub->dialog = dialog; + sub->event = strdupW(event); + sub->control = strdupW(control); + sub->attribute = strdupW(attribute); + list_add_tail( &package->subscriptions, &sub->entry ); +} + +VOID ControlEvent_FireSubscribedEvent( MSIPACKAGE *package, LPCWSTR event, MSIRECORD *rec ) +{ + struct subscriber *sub; + + TRACE("Firing event %s\n", debugstr_w(event)); + + LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry ) + { + if (strcmpiW( sub->event, event )) continue; + msi_dialog_handle_event( sub->dialog, sub->control, sub->attribute, rec ); + } +} + +VOID ControlEvent_CleanupDialogSubscriptions(MSIPACKAGE *package, LPWSTR dialog) +{ + struct list *i, *t; + struct subscriber *sub; + + LIST_FOR_EACH_SAFE( i, t, &package->subscriptions ) + { + sub = LIST_ENTRY( i, struct subscriber, entry ); + + if (strcmpW( msi_dialog_get_name( sub->dialog ), dialog )) + continue; + + list_remove( &sub->entry ); + free_subscriber( sub ); + } +} + +VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package) +{ + struct list *i, *t; + struct subscriber *sub; + + LIST_FOR_EACH_SAFE( i, t, &package->subscriptions ) + { + sub = LIST_ENTRY( i, struct subscriber, entry ); + + list_remove( &sub->entry ); + free_subscriber( sub ); + } +} + +/* + * ACTION_DialogBox() + * + * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED + * if the given parameter is not a dialog box + */ +UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName ) +{ + UINT r = ERROR_SUCCESS; + + if( package->next_dialog ) + ERR("Already a next dialog... ignoring it\n"); + package->next_dialog = NULL; + + /* + * Dialogs are chained by filling in the next_dialog member + * of the package structure, then terminating the current dialog. + * The code below sees the next_dialog member set, and runs the + * next dialog. + * We fall out of the loop below if we come across a modeless + * dialog, as it returns ERROR_IO_PENDING when we try to run + * its message loop. + */ + r = event_do_dialog( package, szDialogName, NULL, TRUE ); + while( r == ERROR_SUCCESS && package->next_dialog ) + { + LPWSTR name = package->next_dialog; + + package->next_dialog = NULL; + r = event_do_dialog( package, name, NULL, TRUE ); + msi_free( name ); + } + + if( r == ERROR_IO_PENDING ) + r = ERROR_SUCCESS; + + return r; +} + +static UINT ControlEvent_SetInstallLevel(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + int iInstallLevel = atolW(argument); + + TRACE("Setting install level: %i\n", iInstallLevel); + + return MSI_SetInstallLevel( package, iInstallLevel ); +} + +static UINT ControlEvent_DirectoryListUp(MSIPACKAGE *package, LPCWSTR argument, + msi_dialog *dialog) +{ + return msi_dialog_directorylist_up( dialog ); +} + +static UINT ControlEvent_ReinstallMode(MSIPACKAGE *package, LPCWSTR argument, + msi_dialog *dialog) +{ + return msi_set_property( package->db, szReinstallMode, argument ); +} + +static UINT ControlEvent_Reinstall( MSIPACKAGE *package, LPCWSTR argument, + msi_dialog *dialog ) +{ + return msi_set_property( package->db, szReinstall, argument ); +} + +static UINT ControlEvent_ValidateProductID(MSIPACKAGE *package, LPCWSTR argument, + msi_dialog *dialog) +{ + return msi_validate_product_id( package ); +} + +static const WCHAR end_dialogW[] = {'E','n','d','D','i','a','l','o','g',0}; +static const WCHAR new_dialogW[] = {'N','e','w','D','i','a','l','o','g',0}; +static const WCHAR spawn_dialogW[] = {'S','p','a','w','n','D','i','a','l','o','g',0}; +static const WCHAR spawn_wait_dialogW[] = {'S','p','a','w','n','W','a','i','t','D','i','a','l','o','g',0}; +static const WCHAR do_actionW[] = {'D','o','A','c','t','i','o','n',0}; +static const WCHAR add_localW[] = {'A','d','d','L','o','c','a','l',0}; +static const WCHAR removeW[] = {'R','e','m','o','v','e',0}; +static const WCHAR add_sourceW[] = {'A','d','d','S','o','u','r','c','e',0}; +static const WCHAR set_target_pathW[] = {'S','e','t','T','a','r','g','e','t','P','a','t','h',0}; +static const WCHAR resetW[] = {'R','e','s','e','t',0}; +static const WCHAR set_install_levelW[] = {'S','e','t','I','n','s','t','a','l','l','L','e','v','e','l',0}; +static const WCHAR directory_list_upW[] = {'D','i','r','e','c','t','o','r','y','L','i','s','t','U','p',0}; +static const WCHAR selection_browseW[] = {'S','e','l','e','c','t','i','o','n','B','r','o','w','s','e',0}; +static const WCHAR reinstall_modeW[] = {'R','e','i','n','s','t','a','l','l','M','o','d','e',0}; +static const WCHAR reinstallW[] = {'R','e','i','n','s','t','a','l','l',0}; +static const WCHAR validate_product_idW[] = {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0}; + +static const struct control_events control_events[] = +{ + { end_dialogW, ControlEvent_EndDialog }, + { new_dialogW, ControlEvent_NewDialog }, + { spawn_dialogW, ControlEvent_SpawnDialog }, + { spawn_wait_dialogW, ControlEvent_SpawnWaitDialog }, + { do_actionW, ControlEvent_DoAction }, + { add_localW, ControlEvent_AddLocal }, + { removeW, ControlEvent_Remove }, + { add_sourceW, ControlEvent_AddSource }, + { set_target_pathW, ControlEvent_SetTargetPath }, + { resetW, ControlEvent_Reset }, + { set_install_levelW, ControlEvent_SetInstallLevel }, + { directory_list_upW, ControlEvent_DirectoryListUp }, + { selection_browseW, ControlEvent_SpawnDialog }, + { reinstall_modeW, ControlEvent_ReinstallMode }, + { reinstallW, ControlEvent_Reinstall }, + { validate_product_idW, ControlEvent_ValidateProductID }, + { NULL, NULL } +}; + +UINT ControlEvent_HandleControlEvent( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR argument, msi_dialog *dialog ) +{ + unsigned int i; + + TRACE("handling control event %s\n", debugstr_w(event)); + + if (!event) return ERROR_SUCCESS; + + for (i = 0; control_events[i].event; i++) + { + if (!strcmpW( control_events[i].event, event )) + return control_events[i].handler( package, argument, dialog ); + } + FIXME("unhandled control event %s arg(%s)\n", debugstr_w(event), debugstr_w(argument)); + return ERROR_SUCCESS; +} diff --git a/libmsi/files.c b/libmsi/files.c new file mode 100644 index 0000000..11913ef --- /dev/null +++ b/libmsi/files.c @@ -0,0 +1,1329 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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 + */ + + +/* + * Actions dealing with files: + * + * InstallFiles + * DuplicateFiles + * MoveFiles + * PatchFiles + * RemoveDuplicateFiles + * RemoveFiles + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "fdi.h" +#include "msi.h" +#include "msidefs.h" +#include "msipriv.h" +#include "winuser.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static HMODULE hmspatcha; +static BOOL (WINAPI *ApplyPatchToFileW)(LPCWSTR, LPCWSTR, LPCWSTR, ULONG); + +static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action ) +{ + MSIRECORD *uirow; + + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, f->FileName ); + MSI_RecordSetStringW( uirow, 9, f->Component->Directory ); + MSI_RecordSetInteger( uirow, 6, f->FileSize ); + msi_ui_actiondata( package, action, uirow ); + msiobj_release( &uirow->hdr ); + msi_ui_progress( package, 2, f->FileSize, 0, 0 ); +} + +static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file ) +{ + MSICOMPONENT *comp = file->Component; + VS_FIXEDFILEINFO *file_version; + WCHAR *font_version; + msi_file_state state; + DWORD file_size; + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_LOCAL || (comp->assembly && comp->assembly->installed)) + { + TRACE("file %s is not scheduled for install\n", debugstr_w(file->File)); + return msifs_skipped; + } + if ((comp->assembly && !comp->assembly->application && !comp->assembly->installed) || + GetFileAttributesW( file->TargetPath ) == INVALID_FILE_ATTRIBUTES) + { + TRACE("file %s is missing\n", debugstr_w(file->File)); + return msifs_missing; + } + if (file->Version) + { + if ((file_version = msi_get_disk_file_version( file->TargetPath ))) + { + TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version), + HIWORD(file_version->dwFileVersionMS), + LOWORD(file_version->dwFileVersionMS), + HIWORD(file_version->dwFileVersionLS), + LOWORD(file_version->dwFileVersionLS)); + + if (msi_compare_file_versions( file_version, file->Version ) < 0) + state = msifs_overwrite; + else + { + TRACE("destination file version equal or greater, not overwriting\n"); + state = msifs_present; + } + msi_free( file_version ); + return state; + } + else if ((font_version = msi_font_version_from_file( file->TargetPath ))) + { + TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(font_version)); + + if (msi_compare_font_versions( font_version, file->Version ) < 0) + state = msifs_overwrite; + else + { + TRACE("destination file version equal or greater, not overwriting\n"); + state = msifs_present; + } + msi_free( font_version ); + return state; + } + } + if ((file_size = msi_get_disk_file_size( file->TargetPath )) != file->FileSize) + { + return msifs_overwrite; + } + if (file->hash.dwFileHashInfoSize) + { + if (msi_file_hash_matches( file )) + { + TRACE("file hashes match, not overwriting\n"); + return msifs_hashmatch; + } + else + { + TRACE("file hashes do not match, overwriting\n"); + return msifs_overwrite; + } + } + /* assume present */ + return msifs_present; +} + +static void schedule_install_files(MSIPACKAGE *package) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry) + { + MSICOMPONENT *comp = file->Component; + + file->state = calculate_install_state( package, file ); + if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite)) + { + TRACE("not overwriting %s\n", debugstr_w(file->TargetPath)); + file->state = msifs_skipped; + } + } +} + +static UINT copy_file(MSIFILE *file, LPWSTR source) +{ + BOOL ret; + + ret = CopyFileW(source, file->TargetPath, FALSE); + if (!ret) + return GetLastError(); + + SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL); + + file->state = msifs_installed; + return ERROR_SUCCESS; +} + +static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source) +{ + UINT gle; + + TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); + + gle = copy_file(file, source); + if (gle == ERROR_SUCCESS) + return gle; + + if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite) + { + TRACE("overwriting existing file\n"); + return ERROR_SUCCESS; + } + else if (gle == ERROR_ACCESS_DENIED) + { + SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL); + + gle = copy_file(file, source); + TRACE("Overwriting existing file: %d\n", gle); + } + if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE) + { + WCHAR *tmpfileW, *pathW, *p; + DWORD len; + + TRACE("file in use, scheduling rename operation\n"); + + if (!(pathW = strdupW( file->TargetPath ))) return ERROR_OUTOFMEMORY; + if ((p = strrchrW(pathW, '\\'))) *p = 0; + len = strlenW( pathW ) + 16; + if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR)))) + { + msi_free( pathW ); + return ERROR_OUTOFMEMORY; + } + if (!GetTempFileNameW( pathW, szMsi, 0, tmpfileW )) tmpfileW[0] = 0; + msi_free( pathW ); + + if (CopyFileW(source, tmpfileW, FALSE) && + MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) && + MoveFileExW(tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT)) + { + file->state = msifs_installed; + package->need_reboot_at_end = 1; + gle = ERROR_SUCCESS; + } + else + { + gle = GetLastError(); + WARN("failed to schedule rename operation: %d)\n", gle); + DeleteFileW( tmpfileW ); + } + msi_free(tmpfileW); + } + + return gle; +} + +static UINT msi_create_directory( MSIPACKAGE *package, const WCHAR *dir ) +{ + MSIFOLDER *folder; + const WCHAR *install_path; + + install_path = msi_get_target_folder( package, dir ); + if (!install_path) return ERROR_FUNCTION_FAILED; + + folder = msi_get_loaded_folder( package, dir ); + if (folder->State == FOLDER_STATE_UNINITIALIZED) + { + msi_create_full_path( install_path ); + folder->State = FOLDER_STATE_CREATED; + } + return ERROR_SUCCESS; +} + +static MSIFILE *find_file( MSIPACKAGE *package, const WCHAR *filename ) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (file->state != msifs_installed && !strcmpiW( filename, file->File )) return file; + } + return NULL; +} + +static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, + LPWSTR *path, DWORD *attrs, PVOID user) +{ + static MSIFILE *f = NULL; + UINT_PTR disk_id = (UINT_PTR)user; + + if (action == MSICABEXTRACT_BEGINEXTRACT) + { + if (!(f = find_file( package, file ))) + { + TRACE("unknown file in cabinet (%s)\n", debugstr_w(file)); + return FALSE; + } + if (f->disk_id != disk_id || (f->state != msifs_missing && f->state != msifs_overwrite)) + return FALSE; + + if (!f->Component->assembly || f->Component->assembly->application) + { + msi_create_directory(package, f->Component->Directory); + } + *path = strdupW(f->TargetPath); + *attrs = f->Attributes; + } + else if (action == MSICABEXTRACT_FILEEXTRACTED) + { + f->state = msifs_installed; + f = NULL; + } + + return TRUE; +} + +WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file ) +{ + WCHAR *p, *path; + + TRACE("Working to resolve source of file %s\n", debugstr_w(file->File)); + + if (file->IsCompressed) return NULL; + + p = msi_resolve_source_folder( package, file->Component->Directory, NULL ); + path = msi_build_directory_name( 2, p, file->ShortName ); + + if (file->LongName && GetFileAttributesW( path ) == INVALID_FILE_ATTRIBUTES) + { + msi_free( path ); + path = msi_build_directory_name( 2, p, file->LongName ); + } + msi_free( p ); + TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path)); + return path; +} + +/* + * ACTION_InstallFiles() + * + * For efficiency, this is done in two passes: + * 1) Correct all the TargetPaths and determine what files are to be installed. + * 2) Extract Cabinets and copy files. + */ +UINT ACTION_InstallFiles(MSIPACKAGE *package) +{ + MSIMEDIAINFO *mi; + MSICOMPONENT *comp; + UINT rc = ERROR_SUCCESS; + MSIFILE *file; + + schedule_install_files(package); + mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) ); + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + msi_file_update_ui( package, file, szInstallFiles ); + + rc = msi_load_media_info( package, file->Sequence, mi ); + if (rc != ERROR_SUCCESS) + { + ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); + return ERROR_FUNCTION_FAILED; + } + if (!file->Component->Enabled) continue; + + if (file->state != msifs_hashmatch && + file->state != msifs_skipped && + (file->state != msifs_present || !msi_get_property_int( package->db, szInstalled, 0 )) && + (rc = ready_media( package, file->IsCompressed, mi ))) + { + ERR("Failed to ready media for %s\n", debugstr_w(file->File)); + goto done; + } + + if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite) + continue; + + if (file->Sequence > mi->last_sequence || mi->is_continuous || + (file->IsCompressed && !mi->is_extracted)) + { + MSICABDATA data; + + data.mi = mi; + data.package = package; + data.cb = installfiles_cb; + data.user = (PVOID)(UINT_PTR)mi->disk_id; + + if (file->IsCompressed && + !msi_cabextract(package, mi, &data)) + { + ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); + rc = ERROR_INSTALL_FAILURE; + goto done; + } + } + + if (!file->IsCompressed) + { + WCHAR *source = msi_resolve_file_source(package, file); + + TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); + + if (!file->Component->assembly || file->Component->assembly->application) + { + msi_create_directory(package, file->Component->Directory); + } + rc = copy_install_file(package, file, source); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to copy %s to %s (%d)\n", debugstr_w(source), + debugstr_w(file->TargetPath), rc); + rc = ERROR_INSTALL_FAILURE; + msi_free(source); + goto done; + } + msi_free(source); + } + else if (file->state != msifs_installed && !(file->Attributes & msidbFileAttributesPatchAdded)) + { + ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath)); + rc = ERROR_INSTALL_FAILURE; + goto done; + } + } + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action == INSTALLSTATE_LOCAL && comp->assembly && !comp->assembly->installed) + { + rc = msi_install_assembly( package, comp ); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to install assembly\n"); + rc = ERROR_INSTALL_FAILURE; + break; + } + } + } + +done: + msi_free_media_info(mi); + return rc; +} + +static BOOL load_mspatcha(void) +{ + hmspatcha = LoadLibraryA("mspatcha.dll"); + if (!hmspatcha) + { + ERR("Failed to load mspatcha.dll: %d\n", GetLastError()); + return FALSE; + } + + ApplyPatchToFileW = (void*)GetProcAddress(hmspatcha, "ApplyPatchToFileW"); + if(!ApplyPatchToFileW) + { + ERR("GetProcAddress(ApplyPatchToFileW) failed: %d.\n", GetLastError()); + return FALSE; + } + + return TRUE; +} + +static void unload_mspatch(void) +{ + FreeLibrary(hmspatcha); +} + +static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, + LPWSTR *path, DWORD *attrs, PVOID user) +{ + static MSIFILEPATCH *p = NULL; + static WCHAR patch_path[MAX_PATH] = {0}; + static WCHAR temp_folder[MAX_PATH] = {0}; + + if (action == MSICABEXTRACT_BEGINEXTRACT) + { + if (temp_folder[0] == '\0') + GetTempPathW(MAX_PATH, temp_folder); + + p = msi_get_loaded_filepatch(package, file); + if (!p) + { + TRACE("unknown file in cabinet (%s)\n", debugstr_w(file)); + return FALSE; + } + GetTempFileNameW(temp_folder, NULL, 0, patch_path); + + *path = strdupW(patch_path); + *attrs = p->File->Attributes; + } + else if (action == MSICABEXTRACT_FILEEXTRACTED) + { + WCHAR patched_file[MAX_PATH]; + BOOL br; + + GetTempFileNameW(temp_folder, NULL, 0, patched_file); + + br = ApplyPatchToFileW(patch_path, p->File->TargetPath, patched_file, 0); + if (br) + { + /* FIXME: baseline cache */ + + DeleteFileW( p->File->TargetPath ); + MoveFileW( patched_file, p->File->TargetPath ); + + p->IsApplied = TRUE; + } + else + ERR("Failed patch %s: %d.\n", debugstr_w(p->File->TargetPath), GetLastError()); + + DeleteFileW(patch_path); + p = NULL; + } + + return TRUE; +} + +UINT ACTION_PatchFiles( MSIPACKAGE *package ) +{ + MSIFILEPATCH *patch; + MSIMEDIAINFO *mi; + UINT rc = ERROR_SUCCESS; + BOOL mspatcha_loaded = FALSE; + + TRACE("%p\n", package); + + mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) ); + + LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) + { + MSIFILE *file = patch->File; + MSICOMPONENT *comp = file->Component; + + rc = msi_load_media_info( package, patch->Sequence, mi ); + if (rc != ERROR_SUCCESS) + { + ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); + return ERROR_FUNCTION_FAILED; + } + comp->Action = msi_get_component_action( package, comp ); + if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue; + + if (!patch->IsApplied) + { + MSICABDATA data; + + rc = ready_media( package, TRUE, mi ); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to ready media for %s\n", debugstr_w(file->File)); + goto done; + } + + if (!mspatcha_loaded && !load_mspatcha()) + { + rc = ERROR_FUNCTION_FAILED; + goto done; + } + mspatcha_loaded = TRUE; + + data.mi = mi; + data.package = package; + data.cb = patchfiles_cb; + data.user = (PVOID)(UINT_PTR)mi->disk_id; + + if (!msi_cabextract(package, mi, &data)) + { + ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); + rc = ERROR_INSTALL_FAILURE; + goto done; + } + } + + if (!patch->IsApplied && !(patch->Attributes & msidbPatchAttributesNonVital)) + { + ERR("Failed to apply patch to file: %s\n", debugstr_w(file->File)); + rc = ERROR_INSTALL_FAILURE; + goto done; + } + } + +done: + msi_free_media_info(mi); + if (mspatcha_loaded) + unload_mspatch(); + return rc; +} + +#define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) + +typedef struct +{ + struct list entry; + LPWSTR sourcename; + LPWSTR destname; + LPWSTR source; + LPWSTR dest; +} FILE_LIST; + +static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options) +{ + BOOL ret; + + if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY || + GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY) + { + WARN("Source or dest is directory, not moving\n"); + return FALSE; + } + + if (options == msidbMoveFileOptionsMove) + { + TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest)); + ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING); + if (!ret) + { + WARN("MoveFile failed: %d\n", GetLastError()); + return FALSE; + } + } + else + { + TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest)); + ret = CopyFileW(source, dest, FALSE); + if (!ret) + { + WARN("CopyFile failed: %d\n", GetLastError()); + return FALSE; + } + } + + return TRUE; +} + +static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename) +{ + LPWSTR path, ptr; + DWORD dirlen, pathlen; + + ptr = strrchrW(wildcard, '\\'); + dirlen = ptr - wildcard + 1; + + pathlen = dirlen + lstrlenW(filename) + 1; + path = msi_alloc(pathlen * sizeof(WCHAR)); + + lstrcpynW(path, wildcard, dirlen + 1); + lstrcatW(path, filename); + + return path; +} + +static void free_file_entry(FILE_LIST *file) +{ + msi_free(file->source); + msi_free(file->dest); + msi_free(file); +} + +static void free_list(FILE_LIST *list) +{ + while (!list_empty(&list->entry)) + { + FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry); + + list_remove(&file->entry); + free_file_entry(file); + } +} + +static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest) +{ + FILE_LIST *new, *file; + LPWSTR ptr, filename; + DWORD size; + + new = msi_alloc_zero(sizeof(FILE_LIST)); + if (!new) + return FALSE; + + new->source = strdupW(source); + ptr = strrchrW(dest, '\\') + 1; + filename = strrchrW(new->source, '\\') + 1; + + new->sourcename = filename; + + if (*ptr) + new->destname = ptr; + else + new->destname = new->sourcename; + + size = (ptr - dest) + lstrlenW(filename) + 1; + new->dest = msi_alloc(size * sizeof(WCHAR)); + if (!new->dest) + { + free_file_entry(new); + return FALSE; + } + + lstrcpynW(new->dest, dest, ptr - dest + 1); + lstrcatW(new->dest, filename); + + if (list_empty(&files->entry)) + { + list_add_head(&files->entry, &new->entry); + return TRUE; + } + + LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry) + { + if (strcmpW( source, file->source ) < 0) + { + list_add_before(&file->entry, &new->entry); + return TRUE; + } + } + + list_add_after(&file->entry, &new->entry); + return TRUE; +} + +static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options) +{ + WIN32_FIND_DATAW wfd; + HANDLE hfile; + LPWSTR path; + BOOL res; + FILE_LIST files, *file; + DWORD size; + + hfile = FindFirstFileW(source, &wfd); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + list_init(&files.entry); + + for (res = TRUE; res; res = FindNextFileW(hfile, &wfd)) + { + if (is_dot_dir(wfd.cFileName)) continue; + + path = wildcard_to_file(source, wfd.cFileName); + if (!path) + { + res = FALSE; + goto done; + } + + add_wildcard(&files, path, dest); + msi_free(path); + } + + /* no files match the wildcard */ + if (list_empty(&files.entry)) + goto done; + + /* only the first wildcard match gets renamed to dest */ + file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); + size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2; + file->dest = msi_realloc(file->dest, size * sizeof(WCHAR)); + if (!file->dest) + { + res = FALSE; + goto done; + } + + /* file->dest may be shorter after the reallocation, so add a NULL + * terminator. This is needed for the call to strrchrW, as there will no + * longer be a NULL terminator within the bounds of the allocation in this case. + */ + file->dest[size - 1] = '\0'; + lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname); + + while (!list_empty(&files.entry)) + { + file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); + + msi_move_file(file->source, file->dest, options); + + list_remove(&file->entry); + free_file_entry(file); + } + + res = TRUE; + +done: + free_list(&files); + FindClose(hfile); + return res; +} + +void msi_reduce_to_long_filename( WCHAR *filename ) +{ + WCHAR *p = strchrW( filename, '|' ); + if (p) memmove( filename, p + 1, (strlenW( p + 1 ) + 1) * sizeof(WCHAR) ); +} + +static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSIRECORD *uirow; + MSICOMPONENT *comp; + LPCWSTR sourcename, component; + LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL; + int options; + DWORD size; + BOOL ret, wildcards; + + 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; + } + + sourcename = MSI_RecordGetString(rec, 3); + options = MSI_RecordGetInteger(rec, 7); + + sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5)); + if (!sourcedir) + goto done; + + destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6)); + if (!destdir) + goto done; + + if (!sourcename) + { + if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES) + goto done; + + source = strdupW(sourcedir); + if (!source) + goto done; + } + else + { + size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2; + source = msi_alloc(size * sizeof(WCHAR)); + if (!source) + goto done; + + lstrcpyW(source, sourcedir); + if (source[lstrlenW(source) - 1] != '\\') + lstrcatW(source, szBackSlash); + lstrcatW(source, sourcename); + } + + wildcards = strchrW(source, '*') || strchrW(source, '?'); + + if (MSI_RecordIsNull(rec, 4)) + { + if (!wildcards) + { + destname = strdupW(sourcename); + if (!destname) + goto done; + } + } + else + { + destname = strdupW(MSI_RecordGetString(rec, 4)); + if (destname) msi_reduce_to_long_filename(destname); + } + + size = 0; + if (destname) + size = lstrlenW(destname); + + size += lstrlenW(destdir) + 2; + dest = msi_alloc(size * sizeof(WCHAR)); + if (!dest) + goto done; + + lstrcpyW(dest, destdir); + if (dest[lstrlenW(dest) - 1] != '\\') + lstrcatW(dest, szBackSlash); + + if (destname) + lstrcatW(dest, destname); + + if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES) + { + if (!(ret = msi_create_full_path(destdir))) + { + WARN("failed to create directory %u\n", GetLastError()); + goto done; + } + } + + if (!wildcards) + msi_move_file(source, dest, options); + else + move_files_wildcard(source, dest, options); + +done: + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) ); + MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */ + MSI_RecordSetStringW( uirow, 9, destdir ); + msi_ui_actiondata( package, szMoveFiles, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(sourcedir); + msi_free(destdir); + msi_free(destname); + msi_free(source); + msi_free(dest); + + return ERROR_SUCCESS; +} + +UINT ACTION_MoveFiles( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','o','v','e','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_MoveFiles, package); + msiobj_release(&view->hdr); + return rc; +} + +static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src ) +{ + DWORD len; + WCHAR *dst_name, *dst_path, *dst; + + if (MSI_RecordIsNull( row, 4 )) + { + len = strlenW( src ) + 1; + if (!(dst_name = msi_alloc( len * sizeof(WCHAR)))) return NULL; + strcpyW( dst_name, strrchrW( src, '\\' ) + 1 ); + } + else + { + MSI_RecordGetStringW( row, 4, NULL, &len ); + if (!(dst_name = msi_alloc( ++len * sizeof(WCHAR) ))) return NULL; + MSI_RecordGetStringW( row, 4, dst_name, &len ); + msi_reduce_to_long_filename( dst_name ); + } + + if (MSI_RecordIsNull( row, 5 )) + { + WCHAR *p; + dst_path = strdupW( src ); + p = strrchrW( dst_path, '\\' ); + if (p) *p = 0; + } + else + { + const WCHAR *dst_key = MSI_RecordGetString( row, 5 ); + + dst_path = strdupW( msi_get_target_folder( package, dst_key ) ); + if (!dst_path) + { + /* try a property */ + dst_path = msi_dup_property( package->db, dst_key ); + if (!dst_path) + { + FIXME("Unable to get destination folder, try AppSearch properties\n"); + msi_free( dst_name ); + return NULL; + } + } + } + + dst = msi_build_directory_name( 2, dst_path, dst_name ); + msi_create_full_path( dst_path ); + + msi_free( dst_name ); + msi_free( dst_path ); + return dst; +} + +static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPWSTR dest; + LPCWSTR file_key, component; + MSICOMPONENT *comp; + MSIRECORD *uirow; + MSIFILE *file; + + component = MSI_RecordGetString(row,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; + } + + file_key = MSI_RecordGetString(row,3); + if (!file_key) + { + ERR("Unable to get file key\n"); + return ERROR_FUNCTION_FAILED; + } + + file = msi_get_loaded_file( package, file_key ); + if (!file) + { + ERR("Original file unknown %s\n", debugstr_w(file_key)); + return ERROR_SUCCESS; + } + + dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); + if (!dest) + { + WARN("Unable to get duplicate filename\n"); + return ERROR_SUCCESS; + } + + TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest)); + + if (!CopyFileW( file->TargetPath, dest, TRUE )) + { + WARN("Failed to copy file %s -> %s (%u)\n", + debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError()); + } + + FIXME("We should track these duplicate files as well\n"); + + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); + MSI_RecordSetInteger( uirow, 6, file->FileSize ); + MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); + msi_ui_actiondata( package, szDuplicateFiles, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(dest); + return ERROR_SUCCESS; +} + +UINT ACTION_DuplicateFiles(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','u','p','l','i','c','a','t','e','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_DuplicateFiles, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPWSTR dest; + LPCWSTR file_key, component; + MSICOMPONENT *comp; + MSIRECORD *uirow; + MSIFILE *file; + + component = MSI_RecordGetString( row, 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; + } + + file_key = MSI_RecordGetString( row, 3 ); + if (!file_key) + { + ERR("Unable to get file key\n"); + return ERROR_FUNCTION_FAILED; + } + + file = msi_get_loaded_file( package, file_key ); + if (!file) + { + ERR("Original file unknown %s\n", debugstr_w(file_key)); + return ERROR_SUCCESS; + } + + dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); + if (!dest) + { + WARN("Unable to get duplicate filename\n"); + return ERROR_SUCCESS; + } + + TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath)); + + if (!DeleteFileW( dest )) + { + WARN("Failed to delete duplicate file %s (%u)\n", debugstr_w(dest), GetLastError()); + } + + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); + MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); + msi_ui_actiondata( package, szRemoveDuplicateFiles, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(dest); + return ERROR_SUCCESS; +} + +UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','u','p','l','i','c','a','t','e','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_RemoveDuplicateFiles, package ); + msiobj_release( &view->hdr ); + return rc; +} + +static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode) +{ + /* special case */ + if (comp->Action != INSTALLSTATE_SOURCE && + comp->Attributes & msidbComponentAttributesSourceOnly && + (install_mode == msidbRemoveFileInstallModeOnRemove || + install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE; + + switch (comp->Action) + { + case INSTALLSTATE_LOCAL: + case INSTALLSTATE_SOURCE: + if (install_mode == msidbRemoveFileInstallModeOnInstall || + install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; + break; + case INSTALLSTATE_ABSENT: + if (install_mode == msidbRemoveFileInstallModeOnRemove || + install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; + break; + default: break; + } + return FALSE; +} + +static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + MSIRECORD *uirow; + LPCWSTR component, dirprop; + UINT install_mode; + LPWSTR dir = NULL, path = NULL, filename = NULL; + DWORD size; + UINT ret = ERROR_SUCCESS; + + component = MSI_RecordGetString(row, 2); + dirprop = MSI_RecordGetString(row, 4); + install_mode = MSI_RecordGetInteger(row, 5); + + comp = msi_get_loaded_component(package, component); + if (!comp) + return ERROR_SUCCESS; + + comp->Action = msi_get_component_action( package, comp ); + if (!verify_comp_for_removal(comp, install_mode)) + { + TRACE("Skipping removal due to install mode\n"); + return ERROR_SUCCESS; + } + if (comp->assembly && !comp->assembly->application) + { + return ERROR_SUCCESS; + } + if (comp->Attributes & msidbComponentAttributesPermanent) + { + TRACE("permanent component, not removing file\n"); + return ERROR_SUCCESS; + } + + dir = msi_dup_property(package->db, dirprop); + if (!dir) + { + WARN("directory property has no value\n"); + return ERROR_SUCCESS; + } + size = 0; + if ((filename = strdupW( MSI_RecordGetString(row, 3) ))) + { + msi_reduce_to_long_filename( filename ); + size = lstrlenW( filename ); + } + size += lstrlenW(dir) + 2; + path = msi_alloc(size * sizeof(WCHAR)); + if (!path) + { + ret = ERROR_OUTOFMEMORY; + goto done; + } + + if (filename) + { + lstrcpyW(path, dir); + PathAddBackslashW(path); + lstrcatW(path, filename); + + TRACE("Deleting misc file: %s\n", debugstr_w(path)); + DeleteFileW(path); + } + else + { + TRACE("Removing misc directory: %s\n", debugstr_w(dir)); + RemoveDirectoryW(dir); + } + +done: + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) ); + MSI_RecordSetStringW( uirow, 9, dir ); + msi_ui_actiondata( package, szRemoveFiles, uirow ); + msiobj_release( &uirow->hdr ); + + msi_free(filename); + msi_free(path); + msi_free(dir); + return ret; +} + +static void remove_folder( MSIFOLDER *folder ) +{ + FolderList *fl; + + LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) + { + remove_folder( fl->folder ); + } + if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED) + { + if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED; + } +} + +UINT ACTION_RemoveFiles( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','m','o','v','e','F','i','l','e','`',0}; + MSIQUERY *view; + MSICOMPONENT *comp; + MSIFILE *file; + UINT r; + + r = MSI_DatabaseOpenViewW(package->db, query, &view); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package); + msiobj_release(&view->hdr); + if (r != ERROR_SUCCESS) + return r; + } + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + MSIRECORD *uirow; + VS_FIXEDFILEINFO *ver; + + comp = file->Component; + msi_file_update_ui( package, file, szRemoveFiles ); + + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE) + continue; + + if (comp->assembly && !comp->assembly->application) + continue; + + if (comp->Attributes & msidbComponentAttributesPermanent) + { + TRACE("permanent component, not removing file\n"); + continue; + } + + if (file->Version) + { + ver = msi_get_disk_file_version( file->TargetPath ); + if (ver && msi_compare_file_versions( ver, file->Version ) > 0) + { + TRACE("newer version detected, not removing file\n"); + msi_free( ver ); + continue; + } + msi_free( ver ); + } + + if (file->state == msifs_installed) + WARN("removing installed file %s\n", debugstr_w(file->TargetPath)); + + TRACE("removing %s\n", debugstr_w(file->File) ); + + SetFileAttributesW( file->TargetPath, FILE_ATTRIBUTE_NORMAL ); + if (!DeleteFileW( file->TargetPath )) + { + WARN("failed to delete %s (%u)\n", debugstr_w(file->TargetPath), GetLastError()); + } + file->state = msifs_missing; + + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, file->FileName ); + MSI_RecordSetStringW( uirow, 9, comp->Directory ); + msi_ui_actiondata( package, szRemoveFiles, uirow ); + msiobj_release( &uirow->hdr ); + } + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + comp->Action = msi_get_component_action( package, comp ); + if (comp->Action != INSTALLSTATE_ABSENT) continue; + + if (comp->Attributes & msidbComponentAttributesPermanent) + { + TRACE("permanent component, not removing directory\n"); + continue; + } + if (comp->assembly && !comp->assembly->application) + msi_uninstall_assembly( package, comp ); + else + { + MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory ); + remove_folder( folder ); + } + } + return ERROR_SUCCESS; +} diff --git a/libmsi/font.c b/libmsi/font.c new file mode 100644 index 0000000..0b728a3 --- /dev/null +++ b/libmsi/font.c @@ -0,0 +1,385 @@ +/* + * 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> +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msipriv.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct _tagTT_OFFSET_TABLE { + USHORT uMajorVersion; + USHORT uMinorVersion; + USHORT uNumOfTables; + USHORT uSearchRange; + USHORT uEntrySelector; + USHORT uRangeShift; +} TT_OFFSET_TABLE; + +typedef struct _tagTT_TABLE_DIRECTORY { + char szTag[4]; /* table name */ + ULONG uCheckSum; /* Check sum */ + ULONG uOffset; /* Offset from beginning of file */ + ULONG uLength; /* length of the table in bytes */ +} TT_TABLE_DIRECTORY; + +typedef struct _tagTT_NAME_TABLE_HEADER { + USHORT uFSelector; /* format selector. Always 0 */ + USHORT uNRCount; /* Name Records count */ + USHORT uStorageOffset; /* Offset for strings storage, + * from start of the table */ +} TT_NAME_TABLE_HEADER; + +#define NAME_ID_FULL_FONT_NAME 4 +#define NAME_ID_VERSION 5 + +typedef struct _tagTT_NAME_RECORD { + USHORT uPlatformID; + USHORT uEncodingID; + USHORT uLanguageID; + USHORT uNameID; + USHORT uStringLength; + USHORT uStringOffset; /* from start of storage area */ +} TT_NAME_RECORD; + +#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x)) +#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x))) + +static const WCHAR regfont1[] = + {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s',' ','N','T','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'F','o','n','t','s',0}; +static const WCHAR regfont2[] = + {'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','\\', + 'F','o','n','t','s',0}; + +/* + * Code based off of code located here + * http://www.codeproject.com/gdi/fontnamefromfile.asp + */ +static WCHAR *load_ttf_name_id( const WCHAR *filename, DWORD id ) +{ + TT_TABLE_DIRECTORY tblDir; + BOOL bFound = FALSE; + TT_OFFSET_TABLE ttOffsetTable; + TT_NAME_TABLE_HEADER ttNTHeader; + TT_NAME_RECORD ttRecord; + DWORD dwRead; + HANDLE handle; + LPWSTR ret = NULL; + int i; + + handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle == INVALID_HANDLE_VALUE) + { + ERR("Unable to open font file %s\n", debugstr_w(filename)); + return NULL; + } + + if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL)) + goto end; + + ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables); + ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion); + ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion); + + if ((ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) && + (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f)) + goto end; + + for (i=0; i< ttOffsetTable.uNumOfTables; i++) + { + if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL)) + break; + if (memcmp(tblDir.szTag,"name",4)==0) + { + bFound = TRUE; + tblDir.uLength = SWAPLONG(tblDir.uLength); + tblDir.uOffset = SWAPLONG(tblDir.uOffset); + break; + } + } + + if (!bFound) + goto end; + + SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN); + if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL)) + goto end; + + ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount); + ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset); + for(i=0; i<ttNTHeader.uNRCount; i++) + { + if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL)) + break; + + ttRecord.uNameID = SWAPWORD(ttRecord.uNameID); + ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID); + ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID); + if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 && + (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1)) + { + WCHAR *buf; + unsigned int i; + + ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength); + ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset); + SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, + NULL, FILE_BEGIN); + if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end; + dwRead = 0; + ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL); + if (dwRead % sizeof(WCHAR)) + { + msi_free(buf); + goto end; + } + for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]); + ret = strdupW(buf); + msi_free(buf); + break; + } + } + +end: + CloseHandle(handle); + TRACE("Returning %s\n", debugstr_w(ret)); + return ret; +} + +static WCHAR *font_name_from_file( const WCHAR *filename ) +{ + static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0}; + WCHAR *name, *ret = NULL; + + if ((name = load_ttf_name_id( filename, NAME_ID_FULL_FONT_NAME ))) + { + if (!name[0]) + { + WARN("empty font name\n"); + msi_free( name ); + return NULL; + } + ret = msi_alloc( (strlenW( name ) + strlenW( truetypeW ) + 1 ) * sizeof(WCHAR) ); + strcpyW( ret, name ); + strcatW( ret, truetypeW ); + msi_free( name ); + } + return ret; +} + +WCHAR *msi_font_version_from_file( const WCHAR *filename ) +{ + static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0}; + WCHAR *version, *p, *q, *ret = NULL; + + if ((version = load_ttf_name_id( filename, NAME_ID_VERSION ))) + { + int len, major = 0, minor = 0; + if ((p = strchrW( version, ';' ))) *p = 0; + p = version; + while (*p && !isdigitW( *p )) p++; + if ((q = strchrW( p, '.' ))) + { + major = atoiW( p ); + p = ++q; + while (*q && isdigitW( *q )) q++; + if (!*q || *q == ' ') minor = atoiW( p ); + else major = 0; + } + len = strlenW( fmtW ) + 20; + ret = msi_alloc( len * sizeof(WCHAR) ); + sprintfW( ret, fmtW, major, minor ); + msi_free( version ); + } + return ret; +} + +static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = param; + LPWSTR name; + LPCWSTR filename; + MSIFILE *file; + MSICOMPONENT *comp; + HKEY hkey1, hkey2; + MSIRECORD *uirow; + LPWSTR uipath, p; + + 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; + } + comp = msi_get_loaded_component( package, file->Component->Component ); + if (!comp) + { + WARN("unable to find component %s\n", debugstr_w(file->Component->Component)); + 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(comp->Component)); + return ERROR_SUCCESS; + } + + RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1); + RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2); + + if (MSI_RecordIsNull(row,2)) + name = font_name_from_file( file->TargetPath ); + else + name = msi_dup_record_field(row,2); + + if (name) + { + msi_reg_set_val_str( hkey1, name, file->TargetPath); + msi_reg_set_val_str( hkey2, name, file->TargetPath); + } + + msi_free(name); + RegCloseKey(hkey1); + RegCloseKey(hkey2); + + /* the UI chunk */ + uirow = MSI_CreateRecord( 1 ); + uipath = strdupW( file->TargetPath ); + p = strrchrW(uipath,'\\'); + if (p) p++; + else p = uipath; + MSI_RecordSetStringW( uirow, 1, p ); + msi_ui_actiondata( package, szRegisterFonts, uirow ); + msiobj_release( &uirow->hdr ); + msi_free( uipath ); + /* FIXME: call msi_ui_progress? */ + + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterFonts(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','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_RegisterFonts, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + LPWSTR name; + LPCWSTR filename; + MSIFILE *file; + MSICOMPONENT *comp; + HKEY hkey1, hkey2; + MSIRECORD *uirow; + LPWSTR uipath, p; + + 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; + } + comp = msi_get_loaded_component( package, file->Component->Component ); + if (!comp) + { + WARN("unable to find component %s\n", debugstr_w(file->Component->Component)); + 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(comp->Component)); + return ERROR_SUCCESS; + } + + RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 ); + RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 ); + + if (MSI_RecordIsNull( row, 2 )) + name = font_name_from_file( file->TargetPath ); + else + name = msi_dup_record_field( row, 2 ); + + if (name) + { + RegDeleteValueW( hkey1, name ); + RegDeleteValueW( hkey2, name ); + } + + msi_free( name ); + RegCloseKey( hkey1 ); + RegCloseKey( hkey2 ); + + /* the UI chunk */ + uirow = MSI_CreateRecord( 1 ); + uipath = strdupW( file->TargetPath ); + p = strrchrW( uipath,'\\' ); + if (p) p++; + else p = uipath; + MSI_RecordSetStringW( uirow, 1, p ); + msi_ui_actiondata( package, szUnregisterFonts, uirow ); + msiobj_release( &uirow->hdr ); + msi_free( uipath ); + /* FIXME: call msi_ui_progress? */ + + return ERROR_SUCCESS; +} + +UINT ACTION_UnregisterFonts( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package ); + msiobj_release( &view->hdr ); + return r; +} diff --git a/libmsi/format.c b/libmsi/format.c new file mode 100644 index 0000000..8188f13 --- /dev/null +++ b/libmsi/format.c @@ -0,0 +1,1045 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack for CodeWeavers + * Copyright 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> +#include <stdio.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "winnls.h" +#include "objbase.h" +#include "oleauto.h" + +#include "msipriv.h" +#include "msiserver.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* types arranged by precedence */ +#define FORMAT_NULL 0x0001 +#define FORMAT_LITERAL 0x0002 +#define FORMAT_NUMBER 0x0004 +#define FORMAT_LBRACK 0x0010 +#define FORMAT_LBRACE 0x0020 +#define FORMAT_RBRACK 0x0011 +#define FORMAT_RBRACE 0x0021 +#define FORMAT_ESCAPE 0x0040 +#define FORMAT_PROPNULL 0x0080 +#define FORMAT_ERROR 0x1000 +#define FORMAT_FAIL 0x2000 + +#define left_type(x) (x & 0xF0) + +typedef struct _tagFORMAT +{ + MSIPACKAGE *package; + MSIRECORD *record; + LPWSTR deformatted; + int len; + int n; + BOOL propfailed; + BOOL groupfailed; + int groups; +} FORMAT; + +typedef struct _tagFORMSTR +{ + struct list entry; + int n; + int len; + int type; + BOOL propfound; + BOOL nonprop; +} FORMSTR; + +typedef struct _tagSTACK +{ + struct list items; +} STACK; + +static STACK *create_stack(void) +{ + STACK *stack = msi_alloc(sizeof(STACK)); + list_init(&stack->items); + return stack; +} + +static void free_stack(STACK *stack) +{ + while (!list_empty(&stack->items)) + { + FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); + list_remove(&str->entry); + msi_free(str); + } + + msi_free(stack); +} + +static void stack_push(STACK *stack, FORMSTR *str) +{ + list_add_head(&stack->items, &str->entry); +} + +static FORMSTR *stack_pop(STACK *stack) +{ + FORMSTR *ret; + + if (list_empty(&stack->items)) + return NULL; + + ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); + list_remove(&ret->entry); + return ret; +} + +static FORMSTR *stack_find(STACK *stack, int type) +{ + FORMSTR *str; + + LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry) + { + if (str->type == type) + return str; + } + + return NULL; +} + +static FORMSTR *stack_peek(STACK *stack) +{ + return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); +} + +static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str) +{ + return &format->deformatted[str->n]; +} + +static LPWSTR dup_formstr(FORMAT *format, FORMSTR *str) +{ + LPWSTR val; + LPCWSTR data; + + if (str->len == 0) + return NULL; + + val = msi_alloc((str->len + 1) * sizeof(WCHAR)); + data = get_formstr_data(format, str); + lstrcpynW(val, data, str->len + 1); + + return val; +} + +static LPWSTR deformat_index(FORMAT *format, FORMSTR *str) +{ + LPWSTR val, ret; + + val = msi_alloc((str->len + 1) * sizeof(WCHAR)); + lstrcpynW(val, get_formstr_data(format, str), str->len + 1); + + ret = msi_dup_record_field(format->record, atoiW(val)); + + msi_free(val); + return ret; +} + +static LPWSTR deformat_property(FORMAT *format, FORMSTR *str) +{ + LPWSTR val, ret; + + val = msi_alloc((str->len + 1) * sizeof(WCHAR)); + lstrcpynW(val, get_formstr_data(format, str), str->len + 1); + + ret = msi_dup_property(format->package->db, val); + + msi_free(val); + return ret; +} + +static LPWSTR deformat_component(FORMAT *format, FORMSTR *str) +{ + LPWSTR key, ret = NULL; + MSICOMPONENT *comp; + + key = msi_alloc((str->len + 1) * sizeof(WCHAR)); + lstrcpynW(key, get_formstr_data(format, str), str->len + 1); + + comp = msi_get_loaded_component(format->package, key); + if (!comp) + goto done; + + if (comp->Action == INSTALLSTATE_SOURCE) + ret = msi_resolve_source_folder( format->package, comp->Directory, NULL ); + else + ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) ); + +done: + msi_free(key); + return ret; +} + +static LPWSTR deformat_file(FORMAT *format, FORMSTR *str, BOOL shortname) +{ + LPWSTR key, ret = NULL; + MSIFILE *file; + DWORD size; + + key = msi_alloc((str->len + 1) * sizeof(WCHAR)); + lstrcpynW(key, get_formstr_data(format, str), str->len + 1); + + file = msi_get_loaded_file(format->package, key); + if (!file) + goto done; + + if (!shortname) + { + ret = strdupW(file->TargetPath); + goto done; + } + + size = GetShortPathNameW(file->TargetPath, NULL, 0); + if (size <= 0) + { + ret = strdupW(file->TargetPath); + goto done; + } + + size++; + ret = msi_alloc(size * sizeof(WCHAR)); + GetShortPathNameW(file->TargetPath, ret, size); + +done: + msi_free(key); + return ret; +} + +static LPWSTR deformat_environment(FORMAT *format, FORMSTR *str) +{ + LPWSTR key, ret = NULL; + DWORD sz; + + key = msi_alloc((str->len + 1) * sizeof(WCHAR)); + lstrcpynW(key, get_formstr_data(format, str), str->len + 1); + + sz = GetEnvironmentVariableW(key, NULL ,0); + if (sz <= 0) + goto done; + + sz++; + ret = msi_alloc(sz * sizeof(WCHAR)); + GetEnvironmentVariableW(key, ret, sz); + +done: + msi_free(key); + return ret; +} + +static LPWSTR deformat_literal(FORMAT *format, FORMSTR *str, BOOL *propfound, + BOOL *nonprop, int *type) +{ + LPCWSTR data = get_formstr_data(format, str); + LPWSTR replaced = NULL; + char ch = data[0]; + + if (ch == '\\') + { + str->n++; + if (str->len == 1) + { + str->len = 0; + replaced = NULL; + } + else + { + str->len = 1; + replaced = dup_formstr(format, str); + } + } + else if (ch == '~') + { + if (str->len != 1) + replaced = NULL; + else + { + replaced = msi_alloc(sizeof(WCHAR)); + *replaced = '\0'; + } + } + else if (ch == '%' || ch == '#' || ch == '!' || ch == '$') + { + str->n++; + str->len--; + + switch (ch) + { + case '%': + replaced = deformat_environment(format, str); break; + case '#': + replaced = deformat_file(format, str, FALSE); break; + case '!': + replaced = deformat_file(format, str, TRUE); break; + case '$': + replaced = deformat_component(format, str); break; + } + + *type = FORMAT_LITERAL; + } + else + { + replaced = deformat_property(format, str); + *type = FORMAT_LITERAL; + + if (replaced) + *propfound = TRUE; + else + format->propfailed = TRUE; + } + + return replaced; +} + +static LPWSTR build_default_format(const MSIRECORD* record) +{ + int i; + int count; + LPWSTR rc, buf; + static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0}; + static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0}; + static const WCHAR fmt_index[] = {'%','i',0}; + LPCWSTR str; + WCHAR index[10]; + DWORD size, max_len, len; + + count = MSI_RecordGetFieldCount(record); + + max_len = MAX_PATH; + buf = msi_alloc((max_len + 1) * sizeof(WCHAR)); + + rc = NULL; + size = 1; + for (i = 1; i <= count; i++) + { + sprintfW(index, fmt_index, i); + str = MSI_RecordGetString(record, i); + len = (str) ? lstrlenW(str) : 0; + len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index); + size += len; + + if (len > max_len) + { + max_len = len; + buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR)); + if (!buf) return NULL; + } + + if (str) + sprintfW(buf, fmt, i, str); + else + sprintfW(buf, fmt_null, i); + + if (!rc) + { + rc = msi_alloc(size * sizeof(WCHAR)); + lstrcpyW(rc, buf); + } + else + { + rc = msi_realloc(rc, size * sizeof(WCHAR)); + lstrcatW(rc, buf); + } + } + + msi_free(buf); + return rc; +} + +static BOOL format_is_number(WCHAR x) +{ + return ((x >= '0') && (x <= '9')); +} + +static BOOL format_str_is_number(LPWSTR str) +{ + LPWSTR ptr; + + for (ptr = str; *ptr; ptr++) + if (!format_is_number(*ptr)) + return FALSE; + + return TRUE; +} + +static BOOL format_is_alpha(WCHAR x) +{ + return (!format_is_number(x) && x != '\0' && + x != '[' && x != ']' && x != '{' && x != '}'); +} + +static BOOL format_is_literal(WCHAR x) +{ + return (format_is_alpha(x) || format_is_number(x)); +} + +static int format_lex(FORMAT *format, FORMSTR **out) +{ + int type, len = 1; + FORMSTR *str; + LPCWSTR data; + WCHAR ch; + + *out = NULL; + + if (!format->deformatted) + return FORMAT_NULL; + + *out = msi_alloc_zero(sizeof(FORMSTR)); + if (!*out) + return FORMAT_FAIL; + + str = *out; + str->n = format->n; + str->len = 1; + data = get_formstr_data(format, str); + + ch = data[0]; + switch (ch) + { + case '{': type = FORMAT_LBRACE; break; + case '}': type = FORMAT_RBRACE; break; + case '[': type = FORMAT_LBRACK; break; + case ']': type = FORMAT_RBRACK; break; + case '~': type = FORMAT_PROPNULL; break; + case '\0': type = FORMAT_NULL; break; + + default: + type = 0; + } + + if (type) + { + str->type = type; + format->n++; + return type; + } + + if (ch == '\\') + { + while (data[len] && data[len] != ']') + len++; + + type = FORMAT_ESCAPE; + } + else if (format_is_alpha(ch)) + { + while (format_is_literal(data[len])) + len++; + + type = FORMAT_LITERAL; + } + else if (format_is_number(ch)) + { + while (format_is_number(data[len])) + len++; + + type = FORMAT_NUMBER; + + if (data[len] != ']') + { + while (format_is_literal(data[len])) + len++; + + type = FORMAT_LITERAL; + } + } + else + { + ERR("Got unknown character %c(%x)\n", ch, ch); + return FORMAT_ERROR; + } + + format->n += len; + str->len = len; + str->type = type; + + return type; +} + +static FORMSTR *format_replace(FORMAT *format, BOOL propfound, BOOL nonprop, + int oldsize, int type, LPWSTR replace) +{ + FORMSTR *ret; + LPWSTR str, ptr; + DWORD size = 0; + int n; + + if (replace) + { + if (!*replace) + size = 1; + else + size = lstrlenW(replace); + } + + size -= oldsize; + size = format->len + size + 1; + + if (size <= 1) + { + msi_free(format->deformatted); + format->deformatted = NULL; + format->len = 0; + return NULL; + } + + str = msi_alloc(size * sizeof(WCHAR)); + if (!str) + return NULL; + + str[0] = '\0'; + memcpy(str, format->deformatted, format->n * sizeof(WCHAR)); + n = format->n; + + if (replace) + { + if (!*replace) + { + str[n] = '\0'; + n++; + } + else + { + lstrcpyW(&str[n], replace); + n += lstrlenW(replace); + } + } + + ptr = &format->deformatted[format->n + oldsize]; + memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR)); + + msi_free(format->deformatted); + format->deformatted = str; + format->len = size - 1; + + /* don't reformat the NULL */ + if (replace && !*replace) + format->n++; + + if (!replace) + return NULL; + + ret = msi_alloc_zero(sizeof(FORMSTR)); + if (!ret) + return NULL; + + ret->len = lstrlenW(replace); + ret->type = type; + ret->n = format->n; + ret->propfound = propfound; + ret->nonprop = nonprop; + + return ret; +} + +static LPWSTR replace_stack_group(FORMAT *format, STACK *values, + BOOL *propfound, BOOL *nonprop, + int *oldsize, int *type) +{ + LPWSTR replaced = NULL; + FORMSTR *content; + FORMSTR *node; + int n; + + *nonprop = FALSE; + *propfound = FALSE; + + node = stack_pop(values); + n = node->n; + *oldsize = node->len; + msi_free(node); + + while ((node = stack_pop(values))) + { + *oldsize += node->len; + + if (node->nonprop) + *nonprop = TRUE; + + if (node->propfound) + *propfound = TRUE; + + msi_free(node); + } + + content = msi_alloc_zero(sizeof(FORMSTR)); + content->n = n; + content->len = *oldsize; + content->type = FORMAT_LITERAL; + + if (!format->groupfailed && (*oldsize == 2 || + (format->propfailed && !*nonprop))) + { + msi_free(content); + return NULL; + } + else if (format->deformatted[content->n + 1] == '{' && + format->deformatted[content->n + content->len - 2] == '}') + { + format->groupfailed = FALSE; + content->len = 0; + } + else if (*propfound && !*nonprop && + !format->groupfailed && format->groups == 0) + { + content->n++; + content->len -= 2; + } + else + { + if (format->groups != 0) + format->groupfailed = TRUE; + + *nonprop = TRUE; + } + + replaced = dup_formstr(format, content); + *type = content->type; + msi_free(content); + + if (format->groups == 0) + format->propfailed = FALSE; + + return replaced; +} + +static LPWSTR replace_stack_prop(FORMAT *format, STACK *values, + BOOL *propfound, BOOL *nonprop, + int *oldsize, int *type) +{ + LPWSTR replaced = NULL; + FORMSTR *content; + FORMSTR *node; + int n; + + *propfound = FALSE; + *nonprop = FALSE; + + node = stack_pop(values); + n = node->n; + *oldsize = node->len; + *type = stack_peek(values)->type; + msi_free(node); + + while ((node = stack_pop(values))) + { + *oldsize += node->len; + + if (*type != FORMAT_ESCAPE && + stack_peek(values) && node->type != *type) + *type = FORMAT_LITERAL; + + msi_free(node); + } + + content = msi_alloc_zero(sizeof(FORMSTR)); + content->n = n + 1; + content->len = *oldsize - 2; + content->type = *type; + + if (*type == FORMAT_NUMBER) + { + replaced = deformat_index(format, content); + if (replaced) + *propfound = TRUE; + else + format->propfailed = TRUE; + + if (replaced) + *type = format_str_is_number(replaced) ? + FORMAT_NUMBER : FORMAT_LITERAL; + } + else if (format->package) + { + replaced = deformat_literal(format, content, propfound, nonprop, type); + } + else + { + *nonprop = TRUE; + content->n--; + content->len += 2; + replaced = dup_formstr(format, content); + } + + msi_free(content); + return replaced; +} + +static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values) +{ + LPWSTR replaced = NULL; + FORMSTR *beg; + FORMSTR *top; + FORMSTR *node; + BOOL propfound = FALSE; + BOOL nonprop = FALSE; + BOOL group = FALSE; + int oldsize = 0; + int type, n; + + node = stack_peek(values); + type = node->type; + n = node->n; + + if (type == FORMAT_LBRACK) + replaced = replace_stack_prop(format, values, &propfound, + &nonprop, &oldsize, &type); + else if (type == FORMAT_LBRACE) + { + replaced = replace_stack_group(format, values, &propfound, + &nonprop, &oldsize, &type); + group = TRUE; + } + + format->n = n; + beg = format_replace(format, propfound, nonprop, oldsize, type, replaced); + if (!beg) + return ERROR_SUCCESS; + + msi_free(replaced); + format->n = beg->n + beg->len; + + top = stack_peek(stack); + if (top) + { + type = top->type; + + if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) && + type == beg->type) + { + top->len += beg->len; + + if (group) + top->nonprop = FALSE; + + if (type == FORMAT_LITERAL) + top->nonprop = beg->nonprop; + + if (beg->propfound) + top->propfound = TRUE; + + msi_free(beg); + return ERROR_SUCCESS; + } + } + + stack_push(stack, beg); + return ERROR_SUCCESS; +} + +static BOOL verify_format(LPWSTR data) +{ + int count = 0; + + while (*data) + { + if (*data == '[' && *(data - 1) != '\\') + count++; + else if (*data == ']') + count--; + + data++; + } + + if (count > 0) + return FALSE; + + return TRUE; +} + +static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, + WCHAR** data, DWORD *len, + MSIRECORD* record, INT* failcount) +{ + FORMAT format; + FORMSTR *str = NULL; + STACK *stack, *temp; + FORMSTR *node; + int type; + + if (!ptr) + { + *data = NULL; + *len = 0; + return ERROR_SUCCESS; + } + + *data = strdupW(ptr); + *len = lstrlenW(ptr); + + ZeroMemory(&format, sizeof(FORMAT)); + format.package = package; + format.record = record; + format.deformatted = *data; + format.len = *len; + + if (!verify_format(*data)) + return ERROR_SUCCESS; + + stack = create_stack(); + temp = create_stack(); + + while ((type = format_lex(&format, &str)) != FORMAT_NULL) + { + if (type == FORMAT_LBRACK || type == FORMAT_LBRACE || + type == FORMAT_LITERAL || type == FORMAT_NUMBER || + type == FORMAT_ESCAPE || type == FORMAT_PROPNULL) + { + if (type == FORMAT_LBRACE) + { + format.propfailed = FALSE; + format.groups++; + } + else if (type == FORMAT_ESCAPE && + !stack_find(stack, FORMAT_LBRACK)) + { + format.n -= str->len - 1; + str->len = 1; + } + + stack_push(stack, str); + } + else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE) + { + if (type == FORMAT_RBRACE) + format.groups--; + + stack_push(stack, str); + + if (stack_find(stack, left_type(type))) + { + do + { + node = stack_pop(stack); + stack_push(temp, node); + } while (node->type != left_type(type)); + + replace_stack(&format, stack, temp); + } + } + } + + *data = format.deformatted; + *len = format.len; + + msi_free(str); + free_stack(stack); + free_stack(temp); + + return ERROR_SUCCESS; +} + +UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer, + LPDWORD size ) +{ + LPWSTR deformated; + LPWSTR rec; + DWORD len; + UINT rc = ERROR_INVALID_PARAMETER; + + TRACE("%p %p %p %p\n", package, record, buffer, size); + + rec = msi_dup_record_field(record,0); + if (!rec) + rec = build_default_format(record); + + TRACE("(%s)\n",debugstr_w(rec)); + + deformat_string_internal(package, rec, &deformated, &len, record, NULL); + if (buffer) + { + if (*size>len) + { + memcpy(buffer,deformated,len*sizeof(WCHAR)); + rc = ERROR_SUCCESS; + buffer[len] = 0; + } + else + { + if (*size > 0) + { + memcpy(buffer,deformated,(*size)*sizeof(WCHAR)); + buffer[(*size)-1] = 0; + } + rc = ERROR_MORE_DATA; + } + } + else + rc = ERROR_SUCCESS; + + *size = len; + + msi_free(rec); + msi_free(deformated); + return rc; +} + +UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, + LPWSTR szResult, LPDWORD sz ) +{ + UINT r = ERROR_INVALID_HANDLE; + MSIPACKAGE *package; + MSIRECORD *record; + + TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + BSTR value = NULL; + awstring wstr; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (remote_package) + { + hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord, + &value ); + if (FAILED(hr)) + goto done; + + wstr.unicode = TRUE; + wstr.str.w = szResult; + r = msi_strcpy_to_awstring( value, &wstr, sz ); + +done: + IWineMsiRemotePackage_Release( remote_package ); + SysFreeString( value ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return r; + } + } + + record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + + if (!record) + return ERROR_INVALID_HANDLE; + if (!sz) + { + msiobj_release( &record->hdr ); + if (szResult) + return ERROR_INVALID_PARAMETER; + else + return ERROR_SUCCESS; + } + + r = MSI_FormatRecordW( package, record, szResult, sz ); + msiobj_release( &record->hdr ); + if (package) + msiobj_release( &package->hdr ); + return r; +} + +UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord, + LPSTR szResult, LPDWORD sz ) +{ + UINT r; + DWORD len, save; + LPWSTR value; + + TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); + + if (!hRecord) + return ERROR_INVALID_HANDLE; + + if (!sz) + { + if (szResult) + return ERROR_INVALID_PARAMETER; + else + return ERROR_SUCCESS; + } + + r = MsiFormatRecordW( hInstall, hRecord, NULL, &len ); + if (r != ERROR_SUCCESS) + return r; + + value = msi_alloc(++len * sizeof(WCHAR)); + if (!value) + return ERROR_OUTOFMEMORY; + + r = MsiFormatRecordW( hInstall, hRecord, value, &len ); + if (r != ERROR_SUCCESS) + goto done; + + save = len + 1; + len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL); + + if (szResult && len > *sz) + { + if (*sz) szResult[*sz - 1] = '\0'; + r = ERROR_MORE_DATA; + } + + *sz = save - 1; + +done: + msi_free(value); + return r; +} + +/* wrapper to resist a need for a full rewrite right now */ +DWORD deformat_string( MSIPACKAGE *package, const WCHAR *ptr, WCHAR **data ) +{ + if (ptr) + { + DWORD size = 0; + MSIRECORD *rec = MSI_CreateRecord( 1 ); + + MSI_RecordSetStringW( rec, 0, ptr ); + MSI_FormatRecordW( package, rec, NULL, &size ); + + size++; + *data = msi_alloc( size * sizeof(WCHAR) ); + if (size > 1) MSI_FormatRecordW( package, rec, *data, &size ); + else *data[0] = 0; + + msiobj_release( &rec->hdr ); + return size * sizeof(WCHAR); + } + *data = NULL; + return 0; +} diff --git a/libmsi/handle.c b/libmsi/handle.c new file mode 100644 index 0000000..80c6873 --- /dev/null +++ b/libmsi/handle.c @@ -0,0 +1,346 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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 + */ + +#define COBJMACROS + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static CRITICAL_SECTION MSI_handle_cs; +static CRITICAL_SECTION_DEBUG MSI_handle_cs_debug = +{ + 0, 0, &MSI_handle_cs, + { &MSI_handle_cs_debug.ProcessLocksList, + &MSI_handle_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSI_handle_cs") } +}; +static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 }; + +static CRITICAL_SECTION MSI_object_cs; +static CRITICAL_SECTION_DEBUG MSI_object_cs_debug = +{ + 0, 0, &MSI_object_cs, + { &MSI_object_cs_debug.ProcessLocksList, + &MSI_object_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSI_object_cs") } +}; +static CRITICAL_SECTION MSI_object_cs = { &MSI_object_cs_debug, -1, 0, 0, 0, 0 }; + +typedef struct msi_handle_info_t +{ + BOOL remote; + union { + MSIOBJECTHDR *obj; + IUnknown *unk; + } u; + DWORD dwThreadId; +} msi_handle_info; + +static msi_handle_info *msihandletable = NULL; +static unsigned int msihandletable_size = 0; + +void msi_free_handle_table(void) +{ + msi_free( msihandletable ); + msihandletable = NULL; + msihandletable_size = 0; + DeleteCriticalSection(&MSI_handle_cs); + DeleteCriticalSection(&MSI_object_cs); +} + +static MSIHANDLE alloc_handle_table_entry(void) +{ + UINT i; + + /* find a slot */ + for(i=0; i<msihandletable_size; i++) + if( !msihandletable[i].u.obj && !msihandletable[i].u.unk ) + break; + if( i==msihandletable_size ) + { + msi_handle_info *p; + int newsize; + if (msihandletable_size == 0) + { + newsize = 256; + p = msi_alloc_zero(newsize*sizeof(msi_handle_info)); + } + else + { + newsize = msihandletable_size * 2; + p = msi_realloc_zero(msihandletable, + newsize*sizeof(msi_handle_info)); + } + if (!p) + return 0; + msihandletable = p; + msihandletable_size = newsize; + } + return i + 1; +} + +MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj ) +{ + msi_handle_info *entry; + MSIHANDLE ret; + + EnterCriticalSection( &MSI_handle_cs ); + + ret = alloc_handle_table_entry(); + if (ret) + { + entry = &msihandletable[ ret - 1 ]; + msiobj_addref( obj ); + entry->u.obj = obj; + entry->dwThreadId = GetCurrentThreadId(); + entry->remote = FALSE; + } + + LeaveCriticalSection( &MSI_handle_cs ); + + TRACE("%p -> %d\n", obj, ret ); + + return ret; +} + +MSIHANDLE alloc_msi_remote_handle( IUnknown *unk ) +{ + msi_handle_info *entry; + MSIHANDLE ret; + + EnterCriticalSection( &MSI_handle_cs ); + + ret = alloc_handle_table_entry(); + if (ret) + { + entry = &msihandletable[ ret - 1 ]; + IUnknown_AddRef( unk ); + entry->u.unk = unk; + entry->dwThreadId = GetCurrentThreadId(); + entry->remote = TRUE; + } + + LeaveCriticalSection( &MSI_handle_cs ); + + TRACE("%p -> %d\n", unk, ret); + + return ret; +} + +void *msihandle2msiinfo(MSIHANDLE handle, UINT type) +{ + MSIOBJECTHDR *ret = NULL; + + EnterCriticalSection( &MSI_handle_cs ); + handle--; + if( handle >= msihandletable_size ) + goto out; + if( msihandletable[handle].remote) + goto out; + if( !msihandletable[handle].u.obj ) + goto out; + if( msihandletable[handle].u.obj->magic != MSIHANDLE_MAGIC ) + goto out; + if( type && (msihandletable[handle].u.obj->type != type) ) + goto out; + ret = msihandletable[handle].u.obj; + msiobj_addref( ret ); + +out: + LeaveCriticalSection( &MSI_handle_cs ); + + return ret; +} + +IUnknown *msi_get_remote( MSIHANDLE handle ) +{ + IUnknown *unk = NULL; + + EnterCriticalSection( &MSI_handle_cs ); + handle--; + if( handle>=msihandletable_size ) + goto out; + if( !msihandletable[handle].remote) + goto out; + unk = msihandletable[handle].u.unk; + if( unk ) + IUnknown_AddRef( unk ); + +out: + LeaveCriticalSection( &MSI_handle_cs ); + + return unk; +} + +void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy ) +{ + MSIOBJECTHDR *info; + + info = msi_alloc_zero( size ); + if( info ) + { + info->magic = MSIHANDLE_MAGIC; + info->type = type; + info->refcount = 1; + info->destructor = destroy; + } + + return info; +} + +void msiobj_addref( MSIOBJECTHDR *info ) +{ + if( !info ) + return; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + return; + } + + InterlockedIncrement(&info->refcount); +} + +void msiobj_lock( MSIOBJECTHDR *info ) +{ + EnterCriticalSection( &MSI_object_cs ); +} + +void msiobj_unlock( MSIOBJECTHDR *info ) +{ + LeaveCriticalSection( &MSI_object_cs ); +} + +int msiobj_release( MSIOBJECTHDR *info ) +{ + int ret; + + if( !info ) + return -1; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + return -1; + } + + ret = InterlockedDecrement( &info->refcount ); + if( ret==0 ) + { + if( info->destructor ) + info->destructor( info ); + msi_free( info ); + TRACE("object %p destroyed\n", info); + } + + return ret; +} + +/*********************************************************** + * MsiCloseHandle [MSI.@] + */ +UINT WINAPI MsiCloseHandle(MSIHANDLE handle) +{ + MSIOBJECTHDR *info = NULL; + UINT ret = ERROR_INVALID_HANDLE; + + TRACE("%x\n",handle); + + if (!handle) + return ERROR_SUCCESS; + + EnterCriticalSection( &MSI_handle_cs ); + + handle--; + if (handle >= msihandletable_size) + goto out; + + if (msihandletable[handle].remote) + { + IUnknown_Release( msihandletable[handle].u.unk ); + } + else + { + info = msihandletable[handle].u.obj; + if( !info ) + goto out; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + goto out; + } + } + + msihandletable[handle].u.obj = NULL; + msihandletable[handle].remote = 0; + msihandletable[handle].dwThreadId = 0; + + ret = ERROR_SUCCESS; + + TRACE("handle %x destroyed\n", handle+1); +out: + LeaveCriticalSection( &MSI_handle_cs ); + if( info ) + msiobj_release( info ); + + return ret; +} + +/*********************************************************** + * MsiCloseAllHandles [MSI.@] + * + * Closes all handles owned by the current thread + * + * RETURNS: + * The number of handles closed + */ +UINT WINAPI MsiCloseAllHandles(void) +{ + UINT i, n=0; + + TRACE("\n"); + + EnterCriticalSection( &MSI_handle_cs ); + for(i=0; i<msihandletable_size; i++) + { + if(msihandletable[i].dwThreadId == GetCurrentThreadId()) + { + LeaveCriticalSection( &MSI_handle_cs ); + MsiCloseHandle( i+1 ); + EnterCriticalSection( &MSI_handle_cs ); + n++; + } + } + LeaveCriticalSection( &MSI_handle_cs ); + + return n; +} diff --git a/libmsi/insert.c b/libmsi/insert.c new file mode 100644 index 0000000..c404929 --- /dev/null +++ b/libmsi/insert.c @@ -0,0 +1,401 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIINSERTVIEW +{ + MSIVIEW view; + MSIVIEW *table; + MSIDATABASE *db; + BOOL bIsTemp; + MSIVIEW *sv; + column_info *vals; +} MSIINSERTVIEW; + +static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + + TRACE("%p %d %d %p\n", iv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +/* + * msi_query_merge_record + * + * Merge a value_list and a record to create a second record. + * Replace wildcard entries in the valuelist with values from the record + */ +MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec ) +{ + MSIRECORD *merged; + DWORD wildcard_count = 1, i; + + merged = MSI_CreateRecord( fields ); + for( i=1; i <= fields; i++ ) + { + if( !vl ) + { + TRACE("Not enough elements in the list to insert\n"); + goto err; + } + switch( vl->val->type ) + { + case EXPR_SVAL: + TRACE("field %d -> %s\n", i, debugstr_w(vl->val->u.sval)); + MSI_RecordSetStringW( merged, i, vl->val->u.sval ); + break; + case EXPR_IVAL: + MSI_RecordSetInteger( merged, i, vl->val->u.ival ); + break; + case EXPR_WILDCARD: + if( !rec ) + goto err; + MSI_RecordCopyField( rec, wildcard_count, merged, i ); + wildcard_count++; + break; + default: + ERR("Unknown expression type %d\n", vl->val->type); + } + vl = vl->next; + } + + return merged; +err: + msiobj_release( &merged->hdr ); + return NULL; +} + +/* checks to see if the column order specified in the INSERT query + * matches the column order of the table + */ +static BOOL msi_columns_in_order(MSIINSERTVIEW *iv, UINT col_count) +{ + LPCWSTR a, b; + UINT i; + + for (i = 1; i <= col_count; i++) + { + iv->sv->ops->get_column_info(iv->sv, i, &a, NULL, NULL, NULL); + iv->table->ops->get_column_info(iv->table, i, &b, NULL, NULL, NULL); + + if (strcmpW( a, b )) return FALSE; + } + return TRUE; +} + +/* rearranges the data in the record to be inserted based on column order, + * and pads the record for any missing columns in the INSERT query + */ +static UINT msi_arrange_record(MSIINSERTVIEW *iv, MSIRECORD **values) +{ + MSIRECORD *padded; + UINT col_count, val_count; + UINT r, i, colidx; + LPCWSTR a, b; + + r = iv->table->ops->get_dimensions(iv->table, NULL, &col_count); + if (r != ERROR_SUCCESS) + return r; + + val_count = MSI_RecordGetFieldCount(*values); + + /* check to see if the columns are arranged already + * to avoid unnecessary copying + */ + if (col_count == val_count && msi_columns_in_order(iv, col_count)) + return ERROR_SUCCESS; + + padded = MSI_CreateRecord(col_count); + if (!padded) + return ERROR_OUTOFMEMORY; + + for (colidx = 1; colidx <= val_count; colidx++) + { + r = iv->sv->ops->get_column_info(iv->sv, colidx, &a, NULL, NULL, NULL); + if (r != ERROR_SUCCESS) + goto err; + + for (i = 1; i <= col_count; i++) + { + r = iv->table->ops->get_column_info(iv->table, i, &b, NULL, + NULL, NULL); + if (r != ERROR_SUCCESS) + goto err; + + if (!strcmpW( a, b )) + { + MSI_RecordCopyField(*values, colidx, padded, i); + break; + } + } + } + msiobj_release(&(*values)->hdr); + *values = padded; + return ERROR_SUCCESS; + +err: + msiobj_release(&padded->hdr); + return r; +} + +static BOOL row_has_null_primary_keys(MSIINSERTVIEW *iv, MSIRECORD *row) +{ + UINT r, i, col_count, type; + + r = iv->table->ops->get_dimensions( iv->table, NULL, &col_count ); + if (r != ERROR_SUCCESS) + return FALSE; + + for (i = 1; i <= col_count; i++) + { + r = iv->table->ops->get_column_info(iv->table, i, NULL, &type, + NULL, NULL); + if (r != ERROR_SUCCESS) + return FALSE; + + if (!(type & MSITYPE_KEY)) + continue; + + if (MSI_RecordIsNull(row, i)) + return TRUE; + } + + return FALSE; +} + +static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + UINT r, row = -1, col_count = 0; + MSIVIEW *sv; + MSIRECORD *values = NULL; + + TRACE("%p %p\n", iv, record ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + r = sv->ops->execute( sv, 0 ); + TRACE("sv execute returned %x\n", r); + if( r ) + return r; + + r = sv->ops->get_dimensions( sv, NULL, &col_count ); + if( r ) + goto err; + + /* + * Merge the wildcard values into the list of values provided + * in the query, and create a record containing both. + */ + values = msi_query_merge_record( col_count, iv->vals, record ); + if( !values ) + goto err; + + r = msi_arrange_record( iv, &values ); + if( r != ERROR_SUCCESS ) + goto err; + + /* rows with NULL primary keys are inserted at the beginning of the table */ + if( row_has_null_primary_keys( iv, values ) ) + row = 0; + + r = iv->table->ops->insert_row( iv->table, values, row, iv->bIsTemp ); + +err: + if( values ) + msiobj_release( &values->hdr ); + + return r; +} + + +static UINT INSERT_close( struct tagMSIVIEW *view ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p\n", iv); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->close( sv ); +} + +static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p %p %p\n", iv, rows, cols ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->get_dimensions( sv, rows, cols ); +} + +static UINT INSERT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p %d %p %p %p %p\n", iv, n, name, type, temporary, table_name ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->get_column_info( sv, n, name, type, temporary, table_name ); +} + +static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + + TRACE("%p %d %p\n", iv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT INSERT_delete( struct tagMSIVIEW *view ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p\n", iv ); + + sv = iv->sv; + if( sv ) + sv->ops->delete( sv ); + msiobj_release( &iv->db->hdr ); + msi_free( iv ); + + return ERROR_SUCCESS; +} + +static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + return ERROR_FUNCTION_FAILED; +} + + +static const MSIVIEWOPS insert_ops = +{ + INSERT_fetch_int, + NULL, + NULL, + NULL, + NULL, + NULL, + INSERT_execute, + INSERT_close, + INSERT_get_dimensions, + INSERT_get_column_info, + INSERT_modify, + INSERT_delete, + INSERT_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static UINT count_column_info( const column_info *ci ) +{ + UINT n = 0; + for ( ; ci; ci = ci->next ) + n++; + return n; +} + +UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, + column_info *columns, column_info *values, BOOL temp ) +{ + MSIINSERTVIEW *iv = NULL; + UINT r; + MSIVIEW *tv = NULL, *sv = NULL; + + TRACE("%p\n", iv ); + + /* there should be one value for each column */ + if ( count_column_info( columns ) != count_column_info(values) ) + return ERROR_BAD_QUERY_SYNTAX; + + r = TABLE_CreateView( db, table, &tv ); + if( r != ERROR_SUCCESS ) + return r; + + r = SELECT_CreateView( db, &sv, tv, columns ); + if( r != ERROR_SUCCESS ) + { + if( tv ) + tv->ops->delete( tv ); + return r; + } + + iv = msi_alloc_zero( sizeof *iv ); + if( !iv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + iv->view.ops = &insert_ops; + msiobj_addref( &db->hdr ); + iv->table = tv; + iv->db = db; + iv->vals = values; + iv->bIsTemp = temp; + iv->sv = sv; + *view = (MSIVIEW*) iv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/instabsent.bmp b/libmsi/instabsent.bmp Binary files differnew file mode 100644 index 0000000..9347961 --- /dev/null +++ b/libmsi/instabsent.bmp diff --git a/libmsi/instadvert.bmp b/libmsi/instadvert.bmp Binary files differnew file mode 100644 index 0000000..77b5309 --- /dev/null +++ b/libmsi/instadvert.bmp diff --git a/libmsi/install.c b/libmsi/install.c new file mode 100644 index 0000000..a6c832a --- /dev/null +++ b/libmsi/install.c @@ -0,0 +1,1740 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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 + */ + +/* Msi top level apis directly related to installs */ + +#define COBJMACROS + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msidefs.h" +#include "objbase.h" +#include "oleauto.h" + +#include "msipriv.h" +#include "msiserver.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/*********************************************************************** + * MsiDoActionA (MSI.@) + */ +UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction ) +{ + LPWSTR szwAction; + UINT ret; + + TRACE("%s\n", debugstr_a(szAction)); + + szwAction = strdupAtoW(szAction); + if (szAction && !szwAction) + return ERROR_FUNCTION_FAILED; + + ret = MsiDoActionW( hInstall, szwAction ); + msi_free( szwAction ); + return ret; +} + +/*********************************************************************** + * MsiDoActionW (MSI.@) + */ +UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction ) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("%s\n",debugstr_w(szAction)); + + if (!szAction) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + BSTR action; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + action = SysAllocString( szAction ); + if (!action) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_DoAction( remote_package, action ); + + SysFreeString( action ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = ACTION_PerformUIAction( package, szAction, SCRIPT_NONE ); + msiobj_release( &package->hdr ); + + return ret; +} + +/*********************************************************************** + * MsiSequenceA (MSI.@) + */ +UINT WINAPI MsiSequenceA( MSIHANDLE hInstall, LPCSTR szTable, INT iSequenceMode ) +{ + LPWSTR szwTable; + UINT ret; + + TRACE("%s, %d\n", debugstr_a(szTable), iSequenceMode); + + szwTable = strdupAtoW(szTable); + if (szTable && !szwTable) + return ERROR_FUNCTION_FAILED; + + ret = MsiSequenceW( hInstall, szwTable, iSequenceMode ); + msi_free( szwTable ); + return ret; +} + +/*********************************************************************** + * MsiSequenceW (MSI.@) + */ +UINT WINAPI MsiSequenceW( MSIHANDLE hInstall, LPCWSTR szTable, INT iSequenceMode ) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("%s, %d\n", debugstr_w(szTable), iSequenceMode); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + BSTR table; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + table = SysAllocString( szTable ); + if (!table) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_Sequence( remote_package, table, iSequenceMode ); + + SysFreeString( table ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + ret = MSI_Sequence( package, szTable ); + msiobj_release( &package->hdr ); + return ret; +} + +UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz ) +{ + UINT len, r = ERROR_SUCCESS; + + if (awbuf->str.w && !sz ) + return ERROR_INVALID_PARAMETER; + + if (!sz) + return r; + + if (awbuf->unicode) + { + len = lstrlenW( str ); + if (awbuf->str.w) + lstrcpynW( awbuf->str.w, str, *sz ); + } + else + { + len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); + if (len) + len--; + WideCharToMultiByte( CP_ACP, 0, str, -1, awbuf->str.a, *sz, NULL, NULL ); + if ( awbuf->str.a && *sz && (len >= *sz) ) + awbuf->str.a[*sz - 1] = 0; + } + + if (awbuf->str.w && len >= *sz) + r = ERROR_MORE_DATA; + *sz = len; + return r; +} + +const WCHAR *msi_get_target_folder( MSIPACKAGE *package, const WCHAR *name ) +{ + MSIFOLDER *folder = msi_get_loaded_folder( package, name ); + + if (!folder) return NULL; + if (!folder->ResolvedTarget) + { + MSIFOLDER *parent = folder; + while (parent->Parent && strcmpW( parent->Parent, parent->Directory )) + { + parent = msi_get_loaded_folder( package, parent->Parent ); + } + msi_resolve_target_folder( package, parent->Directory, TRUE ); + } + return folder->ResolvedTarget; +} + +/*********************************************************************** + * MsiGetTargetPath (internal) + */ +static UINT MSI_GetTargetPath( MSIHANDLE hInstall, LPCWSTR szFolder, + awstring *szPathBuf, LPDWORD pcchPathBuf ) +{ + MSIPACKAGE *package; + const WCHAR *path; + UINT r = ERROR_FUNCTION_FAILED; + + if (!szFolder) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + LPWSTR value = NULL; + BSTR folder; + DWORD len; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + folder = SysAllocString( szFolder ); + if (!folder) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + len = 0; + hr = IWineMsiRemotePackage_GetTargetPath( remote_package, folder, NULL, &len ); + if (FAILED(hr)) + goto done; + + len++; + value = msi_alloc(len * sizeof(WCHAR)); + if (!value) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + hr = IWineMsiRemotePackage_GetTargetPath( remote_package, folder, value, &len ); + if (FAILED(hr)) + goto done; + + r = msi_strcpy_to_awstring( value, szPathBuf, pcchPathBuf ); + +done: + IWineMsiRemotePackage_Release( remote_package ); + SysFreeString( folder ); + msi_free( value ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return r; + } + + path = msi_get_target_folder( package, szFolder ); + msiobj_release( &package->hdr ); + + if (!path) + return ERROR_DIRECTORY; + + r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf ); + return r; +} + +/*********************************************************************** + * MsiGetTargetPathA (MSI.@) + */ +UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPSTR szPathBuf, LPDWORD pcchPathBuf ) +{ + LPWSTR szwFolder; + awstring path; + UINT r; + + TRACE("%s %p %p\n", debugstr_a(szFolder), szPathBuf, pcchPathBuf); + + szwFolder = strdupAtoW(szFolder); + if (szFolder && !szwFolder) + return ERROR_FUNCTION_FAILED; + + path.unicode = FALSE; + path.str.a = szPathBuf; + + r = MSI_GetTargetPath( hInstall, szwFolder, &path, pcchPathBuf ); + + msi_free( szwFolder ); + + return r; +} + +/*********************************************************************** + * MsiGetTargetPathW (MSI.@) + */ +UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, + LPWSTR szPathBuf, LPDWORD pcchPathBuf ) +{ + awstring path; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf); + + path.unicode = TRUE; + path.str.w = szPathBuf; + + return MSI_GetTargetPath( hInstall, szFolder, &path, pcchPathBuf ); +} + +static WCHAR *get_source_root( MSIPACKAGE *package ) +{ + msi_set_sourcedir_props( package, FALSE ); + return msi_dup_property( package->db, szSourceDir ); +} + +WCHAR *msi_resolve_source_folder( MSIPACKAGE *package, const WCHAR *name, MSIFOLDER **folder ) +{ + MSIFOLDER *f; + LPWSTR p, path = NULL, parent; + + TRACE("working to resolve %s\n", debugstr_w(name)); + + if (!strcmpW( name, szSourceDir )) name = szTargetDir; + if (!(f = msi_get_loaded_folder( package, name ))) return NULL; + + /* special resolving for root dir */ + if (!strcmpW( name, szTargetDir ) && !f->ResolvedSource) + { + f->ResolvedSource = get_source_root( package ); + } + if (folder) *folder = f; + if (f->ResolvedSource) + { + path = strdupW( f->ResolvedSource ); + TRACE(" already resolved to %s\n", debugstr_w(path)); + return path; + } + if (!f->Parent) return path; + parent = f->Parent; + TRACE(" ! parent is %s\n", debugstr_w(parent)); + + p = msi_resolve_source_folder( package, parent, NULL ); + + if (package->WordCount & msidbSumInfoSourceTypeCompressed) + path = get_source_root( package ); + else if (package->WordCount & msidbSumInfoSourceTypeSFN) + path = msi_build_directory_name( 3, p, f->SourceShortPath, NULL ); + else + path = msi_build_directory_name( 3, p, f->SourceLongPath, NULL ); + + TRACE("-> %s\n", debugstr_w(path)); + f->ResolvedSource = strdupW( path ); + msi_free( p ); + + return path; +} + +/*********************************************************************** + * MSI_GetSourcePath (internal) + */ +static UINT MSI_GetSourcePath( MSIHANDLE hInstall, LPCWSTR szFolder, + awstring *szPathBuf, LPDWORD pcchPathBuf ) +{ + MSIPACKAGE *package; + LPWSTR path; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf ); + + if (!szFolder) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + LPWSTR value = NULL; + BSTR folder; + DWORD len; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + folder = SysAllocString( szFolder ); + if (!folder) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + len = 0; + hr = IWineMsiRemotePackage_GetSourcePath( remote_package, folder, NULL, &len ); + if (FAILED(hr)) + goto done; + + len++; + value = msi_alloc(len * sizeof(WCHAR)); + if (!value) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + hr = IWineMsiRemotePackage_GetSourcePath( remote_package, folder, value, &len ); + if (FAILED(hr)) + goto done; + + r = msi_strcpy_to_awstring( value, szPathBuf, pcchPathBuf ); + +done: + IWineMsiRemotePackage_Release( remote_package ); + SysFreeString( folder ); + msi_free( value ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return r; + } + + if (szPathBuf->str.w && !pcchPathBuf ) + { + msiobj_release( &package->hdr ); + return ERROR_INVALID_PARAMETER; + } + + path = msi_resolve_source_folder( package, szFolder, NULL ); + msiobj_release( &package->hdr ); + + TRACE("path = %s\n", debugstr_w(path)); + if (!path) + return ERROR_DIRECTORY; + + r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf ); + msi_free( path ); + return r; +} + +/*********************************************************************** + * MsiGetSourcePathA (MSI.@) + */ +UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPSTR szPathBuf, LPDWORD pcchPathBuf ) +{ + LPWSTR folder; + awstring str; + UINT r; + + TRACE("%s %p %p\n", debugstr_a(szFolder), szPathBuf, pcchPathBuf); + + str.unicode = FALSE; + str.str.a = szPathBuf; + + folder = strdupAtoW( szFolder ); + r = MSI_GetSourcePath( hInstall, folder, &str, pcchPathBuf ); + msi_free( folder ); + + return r; +} + +/*********************************************************************** + * MsiGetSourcePathW (MSI.@) + */ +UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, + LPWSTR szPathBuf, LPDWORD pcchPathBuf ) +{ + awstring str; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf ); + + str.unicode = TRUE; + str.str.w = szPathBuf; + + return MSI_GetSourcePath( hInstall, szFolder, &str, pcchPathBuf ); +} + +/*********************************************************************** + * MsiSetTargetPathA (MSI.@) + */ +UINT WINAPI MsiSetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPCSTR szFolderPath ) +{ + LPWSTR szwFolder = NULL, szwFolderPath = NULL; + UINT rc = ERROR_OUTOFMEMORY; + + if ( !szFolder || !szFolderPath ) + return ERROR_INVALID_PARAMETER; + + szwFolder = strdupAtoW(szFolder); + szwFolderPath = strdupAtoW(szFolderPath); + if (!szwFolder || !szwFolderPath) + goto end; + + rc = MsiSetTargetPathW( hInstall, szwFolder, szwFolderPath ); + +end: + msi_free(szwFolder); + msi_free(szwFolderPath); + + return rc; +} + +static void set_target_path( MSIPACKAGE *package, MSIFOLDER *folder, const WCHAR *path ) +{ + FolderList *fl; + MSIFOLDER *child; + WCHAR *target_path; + + if (!(target_path = msi_normalize_path( path ))) return; + if (strcmpW( target_path, folder->ResolvedTarget )) + { + msi_free( folder->ResolvedTarget ); + folder->ResolvedTarget = target_path; + msi_set_property( package->db, folder->Directory, folder->ResolvedTarget ); + + LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) + { + child = fl->folder; + msi_resolve_target_folder( package, child->Directory, FALSE ); + } + } + else msi_free( target_path ); +} + +UINT MSI_SetTargetPathW( MSIPACKAGE *package, LPCWSTR szFolder, LPCWSTR szFolderPath ) +{ + DWORD attrib; + MSIFOLDER *folder; + MSIFILE *file; + + TRACE("%p %s %s\n", package, debugstr_w(szFolder), debugstr_w(szFolderPath)); + + attrib = GetFileAttributesW(szFolderPath); + /* native MSI tests writeability by making temporary files at each drive */ + if (attrib != INVALID_FILE_ATTRIBUTES && + (attrib & FILE_ATTRIBUTE_OFFLINE || attrib & FILE_ATTRIBUTE_READONLY)) + { + return ERROR_FUNCTION_FAILED; + } + if (!(folder = msi_get_loaded_folder( package, szFolder ))) return ERROR_DIRECTORY; + + set_target_path( package, folder, szFolderPath ); + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + const WCHAR *dir; + MSICOMPONENT *comp = file->Component; + + if (!comp->Enabled || (comp->assembly && !comp->assembly->application)) continue; + + dir = msi_get_target_folder( package, comp->Directory ); + msi_free( file->TargetPath ); + file->TargetPath = msi_build_directory_name( 2, dir, file->FileName ); + } + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiSetTargetPathW (MSI.@) + */ +UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, + LPCWSTR szFolderPath) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("%s %s\n",debugstr_w(szFolder),debugstr_w(szFolderPath)); + + if ( !szFolder || !szFolderPath ) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR folder, path; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + folder = SysAllocString( szFolder ); + path = SysAllocString( szFolderPath ); + if (!folder || !path) + { + SysFreeString(folder); + SysFreeString(path); + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_SetTargetPath( remote_package, folder, path ); + + SysFreeString(folder); + SysFreeString(path); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_SetTargetPathW( package, szFolder, szFolderPath ); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** + * MsiGetMode (MSI.@) + * + * Returns an internal installer state (if it is running in a mode iRunMode) + * + * PARAMS + * hInstall [I] Handle to the installation + * hRunMode [I] Checking run mode + * MSIRUNMODE_ADMIN Administrative mode + * MSIRUNMODE_ADVERTISE Advertisement mode + * MSIRUNMODE_MAINTENANCE Maintenance mode + * MSIRUNMODE_ROLLBACKENABLED Rollback is enabled + * MSIRUNMODE_LOGENABLED Log file is writing + * MSIRUNMODE_OPERATIONS Operations in progress?? + * MSIRUNMODE_REBOOTATEND We need to reboot after installation completed + * MSIRUNMODE_REBOOTNOW We need to reboot to continue the installation + * MSIRUNMODE_CABINET Files from cabinet are installed + * MSIRUNMODE_SOURCESHORTNAMES Long names in source files is suppressed + * MSIRUNMODE_TARGETSHORTNAMES Long names in destination files is suppressed + * MSIRUNMODE_RESERVED11 Reserved + * MSIRUNMODE_WINDOWS9X Running under Windows95/98 + * MSIRUNMODE_ZAWENABLED Demand installation is supported + * MSIRUNMODE_RESERVED14 Reserved + * MSIRUNMODE_RESERVED15 Reserved + * MSIRUNMODE_SCHEDULED called from install script + * MSIRUNMODE_ROLLBACK called from rollback script + * MSIRUNMODE_COMMIT called from commit script + * + * RETURNS + * In the state: TRUE + * Not in the state: FALSE + * + */ +BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode) +{ + MSIPACKAGE *package; + BOOL r = FALSE; + + TRACE("%d %d\n", hInstall, iRunMode); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + BOOL ret; + HRESULT hr; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return FALSE; + + hr = IWineMsiRemotePackage_GetMode(remote_package, iRunMode, &ret); + IWineMsiRemotePackage_Release(remote_package); + + if (hr == S_OK) + return ret; + + return FALSE; + } + + switch (iRunMode) + { + case MSIRUNMODE_ADMIN: + FIXME("no support for administrative installs\n"); + break; + + case MSIRUNMODE_ADVERTISE: + FIXME("no support for advertised installs\n"); + break; + + case MSIRUNMODE_WINDOWS9X: + if (GetVersion() & 0x80000000) + r = TRUE; + break; + + case MSIRUNMODE_OPERATIONS: + case MSIRUNMODE_RESERVED11: + case MSIRUNMODE_RESERVED14: + case MSIRUNMODE_RESERVED15: + break; + + case MSIRUNMODE_SCHEDULED: + r = package->scheduled_action_running; + break; + + case MSIRUNMODE_ROLLBACK: + r = package->rollback_action_running; + break; + + case MSIRUNMODE_COMMIT: + r = package->commit_action_running; + break; + + case MSIRUNMODE_MAINTENANCE: + r = msi_get_property_int( package->db, szInstalled, 0 ) != 0; + break; + + case MSIRUNMODE_ROLLBACKENABLED: + r = msi_get_property_int( package->db, szRollbackDisabled, 0 ) == 0; + break; + + case MSIRUNMODE_REBOOTATEND: + r = package->need_reboot_at_end; + break; + + case MSIRUNMODE_REBOOTNOW: + r = package->need_reboot_now; + break; + + case MSIRUNMODE_LOGENABLED: + r = (package->log_file != INVALID_HANDLE_VALUE); + break; + + default: + FIXME("unimplemented run mode: %d\n", iRunMode); + r = TRUE; + } + + msiobj_release( &package->hdr ); + return r; +} + +/*********************************************************************** + * MsiSetMode (MSI.@) + */ +UINT WINAPI MsiSetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode, BOOL fState) +{ + MSIPACKAGE *package; + UINT r; + + TRACE("%d %d %d\n", hInstall, iRunMode, fState); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return FALSE; + + hr = IWineMsiRemotePackage_SetMode( remote_package, iRunMode, fState ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + switch (iRunMode) + { + case MSIRUNMODE_REBOOTATEND: + package->need_reboot_at_end = (fState != 0); + r = ERROR_SUCCESS; + break; + + case MSIRUNMODE_REBOOTNOW: + package->need_reboot_now = (fState != 0); + r = ERROR_SUCCESS; + break; + + default: + r = ERROR_ACCESS_DENIED; + } + + msiobj_release( &package->hdr ); + return r; +} + +/*********************************************************************** + * MsiSetFeatureStateA (MSI.@) + * + * According to the docs, when this is called it immediately recalculates + * all the component states as well + */ +UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature, + INSTALLSTATE iState) +{ + LPWSTR szwFeature = NULL; + UINT rc; + + szwFeature = strdupAtoW(szFeature); + + if (!szwFeature) + return ERROR_FUNCTION_FAILED; + + rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); + + msi_free(szwFeature); + + return rc; +} + +/* update component state based on a feature change */ +void ACTION_UpdateComponentStates( MSIPACKAGE *package, MSIFEATURE *feature ) +{ + INSTALLSTATE newstate; + ComponentList *cl; + + newstate = feature->ActionRequest; + if (newstate == INSTALLSTATE_ABSENT) newstate = INSTALLSTATE_UNKNOWN; + + LIST_FOR_EACH_ENTRY(cl, &feature->Components, ComponentList, entry) + { + MSICOMPONENT *component = cl->component; + + if (!component->Enabled) continue; + + TRACE("Modifying (%d): Component %s (Installed %d, Action %d, Request %d)\n", + newstate, debugstr_w(component->Component), component->Installed, + component->Action, component->ActionRequest); + + if (newstate == INSTALLSTATE_LOCAL) + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + else + { + ComponentList *clist; + MSIFEATURE *f; + + component->hasLocalFeature = FALSE; + + component->Action = newstate; + component->ActionRequest = newstate; + /* if any other feature wants it local we need to set it local */ + LIST_FOR_EACH_ENTRY(f, &package->features, MSIFEATURE, entry) + { + if ( f->ActionRequest != INSTALLSTATE_LOCAL && + f->ActionRequest != INSTALLSTATE_SOURCE ) + { + continue; + } + LIST_FOR_EACH_ENTRY(clist, &f->Components, ComponentList, entry) + { + if (clist->component == component && + (f->ActionRequest == INSTALLSTATE_LOCAL || + f->ActionRequest == INSTALLSTATE_SOURCE)) + { + TRACE("Saved by %s\n", debugstr_w(f->Feature)); + component->hasLocalFeature = TRUE; + + if (component->Attributes & msidbComponentAttributesOptional) + { + if (f->Attributes & msidbFeatureAttributesFavorSource) + { + component->Action = INSTALLSTATE_SOURCE; + component->ActionRequest = INSTALLSTATE_SOURCE; + } + else + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + } + else if (component->Attributes & msidbComponentAttributesSourceOnly) + { + component->Action = INSTALLSTATE_SOURCE; + component->ActionRequest = INSTALLSTATE_SOURCE; + } + else + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + } + } + } + } + TRACE("Result (%d): Component %s (Installed %d, Action %d, Request %d)\n", + newstate, debugstr_w(component->Component), component->Installed, + component->Action, component->ActionRequest); + } +} + +UINT MSI_SetFeatureStateW( MSIPACKAGE *package, LPCWSTR szFeature, INSTALLSTATE iState ) +{ + UINT rc = ERROR_SUCCESS; + MSIFEATURE *feature, *child; + + TRACE("%s %i\n", debugstr_w(szFeature), iState); + + feature = msi_get_loaded_feature( package, szFeature ); + if (!feature) + return ERROR_UNKNOWN_FEATURE; + + if (iState == INSTALLSTATE_ADVERTISED && + feature->Attributes & msidbFeatureAttributesDisallowAdvertise) + return ERROR_FUNCTION_FAILED; + + feature->ActionRequest = iState; + + ACTION_UpdateComponentStates( package, feature ); + + /* update all the features that are children of this feature */ + LIST_FOR_EACH_ENTRY( child, &package->features, MSIFEATURE, entry ) + { + if (child->Feature_Parent && !strcmpW( szFeature, child->Feature_Parent )) + MSI_SetFeatureStateW(package, child->Feature, iState); + } + + return rc; +} + +/*********************************************************************** + * MsiSetFeatureStateW (MSI.@) + */ +UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature, + INSTALLSTATE iState) +{ + MSIPACKAGE* package; + UINT rc = ERROR_SUCCESS; + + TRACE("%s %i\n",debugstr_w(szFeature), iState); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR feature; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + feature = SysAllocString(szFeature); + if (!feature) + { + IWineMsiRemotePackage_Release(remote_package); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_SetFeatureState(remote_package, feature, iState); + + SysFreeString(feature); + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + rc = MSI_SetFeatureStateW(package,szFeature,iState); + + msiobj_release( &package->hdr ); + return rc; +} + +/*********************************************************************** +* MsiSetFeatureAttributesA (MSI.@) +*/ +UINT WINAPI MsiSetFeatureAttributesA( MSIHANDLE handle, LPCSTR feature, DWORD attrs ) +{ + UINT r; + WCHAR *featureW = NULL; + + TRACE("%u, %s, 0x%08x\n", handle, debugstr_a(feature), attrs); + + if (feature && !(featureW = strdupAtoW( feature ))) return ERROR_OUTOFMEMORY; + + r = MsiSetFeatureAttributesW( handle, featureW, attrs ); + msi_free( featureW ); + return r; +} + +static DWORD unmap_feature_attributes( DWORD attrs ) +{ + DWORD ret = 0; + + if (attrs & INSTALLFEATUREATTRIBUTE_FAVORLOCAL) ret = msidbFeatureAttributesFavorLocal; + if (attrs & INSTALLFEATUREATTRIBUTE_FAVORSOURCE) ret |= msidbFeatureAttributesFavorSource; + if (attrs & INSTALLFEATUREATTRIBUTE_FOLLOWPARENT) ret |= msidbFeatureAttributesFollowParent; + if (attrs & INSTALLFEATUREATTRIBUTE_FAVORADVERTISE) ret |= msidbFeatureAttributesFavorAdvertise; + if (attrs & INSTALLFEATUREATTRIBUTE_DISALLOWADVERTISE) ret |= msidbFeatureAttributesDisallowAdvertise; + if (attrs & INSTALLFEATUREATTRIBUTE_NOUNSUPPORTEDADVERTISE) ret |= msidbFeatureAttributesNoUnsupportedAdvertise; + return ret; +} + +/*********************************************************************** +* MsiSetFeatureAttributesW (MSI.@) +*/ +UINT WINAPI MsiSetFeatureAttributesW( MSIHANDLE handle, LPCWSTR name, DWORD attrs ) +{ + MSIPACKAGE *package; + MSIFEATURE *feature; + WCHAR *costing; + + TRACE("%u, %s, 0x%08x\n", handle, debugstr_w(name), attrs); + + if (!name || !name[0]) return ERROR_UNKNOWN_FEATURE; + + if (!(package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE ))) + return ERROR_INVALID_HANDLE; + + costing = msi_dup_property( package->db, szCostingComplete ); + if (!costing || !strcmpW( costing, szOne )) + { + msi_free( costing ); + msiobj_release( &package->hdr ); + return ERROR_FUNCTION_FAILED; + } + msi_free( costing ); + if (!(feature = msi_get_loaded_feature( package, name ))) + { + msiobj_release( &package->hdr ); + return ERROR_UNKNOWN_FEATURE; + } + feature->Attributes = unmap_feature_attributes( attrs ); + msiobj_release( &package->hdr ); + return ERROR_SUCCESS; +} + +/*********************************************************************** +* MsiGetFeatureStateA (MSI.@) +*/ +UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + LPWSTR szwFeature = NULL; + UINT rc; + + if (szFeature && !(szwFeature = strdupAtoW(szFeature))) return ERROR_OUTOFMEMORY; + + rc = MsiGetFeatureStateW(hInstall, szwFeature, piInstalled, piAction); + msi_free( szwFeature); + return rc; +} + +UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPCWSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIFEATURE *feature; + + feature = msi_get_loaded_feature(package,szFeature); + if (!feature) + return ERROR_UNKNOWN_FEATURE; + + if (piInstalled) + *piInstalled = feature->Installed; + + if (piAction) + *piAction = feature->ActionRequest; + + TRACE("returning %i %i\n", feature->Installed, feature->ActionRequest); + + return ERROR_SUCCESS; +} + +/*********************************************************************** +* MsiGetFeatureStateW (MSI.@) +*/ +UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIPACKAGE* package; + UINT ret; + + TRACE("%d %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled, piAction); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR feature; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + feature = SysAllocString(szFeature); + if (!feature) + { + IWineMsiRemotePackage_Release(remote_package); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_GetFeatureState(remote_package, feature, + piInstalled, piAction); + + SysFreeString(feature); + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** +* MsiGetFeatureCostA (MSI.@) +*/ +UINT WINAPI MsiGetFeatureCostA(MSIHANDLE hInstall, LPCSTR szFeature, + MSICOSTTREE iCostTree, INSTALLSTATE iState, LPINT piCost) +{ + LPWSTR szwFeature = NULL; + UINT rc; + + szwFeature = strdupAtoW(szFeature); + + rc = MsiGetFeatureCostW(hInstall, szwFeature, iCostTree, iState, piCost); + + msi_free(szwFeature); + + return rc; +} + +static INT feature_cost( MSIFEATURE *feature ) +{ + INT cost = 0; + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY( comp, &feature->Components, MSICOMPONENT, entry ) + { + cost += comp->Cost; + } + return cost; +} + +UINT MSI_GetFeatureCost( MSIPACKAGE *package, MSIFEATURE *feature, MSICOSTTREE tree, + INSTALLSTATE state, LPINT cost ) +{ + TRACE("%s, %u, %d, %p\n", debugstr_w(feature->Feature), tree, state, cost); + + *cost = 0; + switch (tree) + { + case MSICOSTTREE_CHILDREN: + { + MSIFEATURE *child; + + LIST_FOR_EACH_ENTRY( child, &feature->Children, MSIFEATURE, entry ) + { + if (child->ActionRequest == state) + *cost += feature_cost( child ); + } + break; + } + case MSICOSTTREE_PARENTS: + { + const WCHAR *feature_parent = feature->Feature_Parent; + for (;;) + { + MSIFEATURE *parent = msi_get_loaded_feature( package, feature_parent ); + if (!parent) + break; + + if (parent->ActionRequest == state) + *cost += feature_cost( parent ); + + feature_parent = parent->Feature_Parent; + } + break; + } + case MSICOSTTREE_SELFONLY: + if (feature->ActionRequest == state) + *cost = feature_cost( feature ); + break; + + default: + WARN("unhandled cost tree %u\n", tree); + break; + } + + *cost /= 512; + return ERROR_SUCCESS; +} + +/*********************************************************************** +* MsiGetFeatureCostW (MSI.@) +*/ +UINT WINAPI MsiGetFeatureCostW(MSIHANDLE hInstall, LPCWSTR szFeature, + MSICOSTTREE iCostTree, INSTALLSTATE iState, LPINT piCost) +{ + MSIPACKAGE *package; + MSIFEATURE *feature; + UINT ret; + + TRACE("(%d %s %i %i %p)\n", hInstall, debugstr_w(szFeature), + iCostTree, iState, piCost); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR feature; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + feature = SysAllocString(szFeature); + if (!feature) + { + IWineMsiRemotePackage_Release(remote_package); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_GetFeatureCost(remote_package, feature, + iCostTree, iState, piCost); + + SysFreeString(feature); + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + feature = msi_get_loaded_feature(package, szFeature); + + if (feature) + ret = MSI_GetFeatureCost(package, feature, iCostTree, iState, piCost); + else + ret = ERROR_UNKNOWN_FEATURE; + + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** +* MsiGetFeatureInfoA (MSI.@) +*/ +UINT WINAPI MsiGetFeatureInfoA( MSIHANDLE handle, LPCSTR feature, LPDWORD attrs, + LPSTR title, LPDWORD title_len, LPSTR help, LPDWORD help_len ) +{ + UINT r; + WCHAR *titleW = NULL, *helpW = NULL, *featureW = NULL; + + TRACE("%u, %s, %p, %p, %p, %p, %p\n", handle, debugstr_a(feature), attrs, title, + title_len, help, help_len); + + if (feature && !(featureW = strdupAtoW( feature ))) return ERROR_OUTOFMEMORY; + + if (title && title_len && !(titleW = msi_alloc( *title_len * sizeof(WCHAR) ))) + { + msi_free( featureW ); + return ERROR_OUTOFMEMORY; + } + if (help && help_len && !(helpW = msi_alloc( *help_len * sizeof(WCHAR) ))) + { + msi_free( featureW ); + msi_free( titleW ); + return ERROR_OUTOFMEMORY; + } + r = MsiGetFeatureInfoW( handle, featureW, attrs, titleW, title_len, helpW, help_len ); + if (r == ERROR_SUCCESS) + { + if (titleW) WideCharToMultiByte( CP_ACP, 0, titleW, -1, title, *title_len + 1, NULL, NULL ); + if (helpW) WideCharToMultiByte( CP_ACP, 0, helpW, -1, help, *help_len + 1, NULL, NULL ); + } + msi_free( titleW ); + msi_free( helpW ); + msi_free( featureW ); + return r; +} + +static DWORD map_feature_attributes( DWORD attrs ) +{ + DWORD ret = 0; + + if (attrs == msidbFeatureAttributesFavorLocal) ret |= INSTALLFEATUREATTRIBUTE_FAVORLOCAL; + if (attrs & msidbFeatureAttributesFavorSource) ret |= INSTALLFEATUREATTRIBUTE_FAVORSOURCE; + if (attrs & msidbFeatureAttributesFollowParent) ret |= INSTALLFEATUREATTRIBUTE_FOLLOWPARENT; + if (attrs & msidbFeatureAttributesFavorAdvertise) ret |= INSTALLFEATUREATTRIBUTE_FAVORADVERTISE; + if (attrs & msidbFeatureAttributesDisallowAdvertise) ret |= INSTALLFEATUREATTRIBUTE_DISALLOWADVERTISE; + if (attrs & msidbFeatureAttributesNoUnsupportedAdvertise) ret |= INSTALLFEATUREATTRIBUTE_NOUNSUPPORTEDADVERTISE; + return ret; +} + +static UINT MSI_GetFeatureInfo( MSIPACKAGE *package, LPCWSTR name, LPDWORD attrs, + LPWSTR title, LPDWORD title_len, LPWSTR help, LPDWORD help_len ) +{ + UINT r = ERROR_SUCCESS; + MSIFEATURE *feature = msi_get_loaded_feature( package, name ); + int len; + + if (!feature) return ERROR_UNKNOWN_FEATURE; + if (attrs) *attrs = map_feature_attributes( feature->Attributes ); + if (title_len) + { + if (feature->Title) len = strlenW( feature->Title ); + else len = 0; + if (*title_len <= len) + { + *title_len = len; + if (title) r = ERROR_MORE_DATA; + } + else if (title) + { + if (feature->Title) strcpyW( title, feature->Title ); + else *title = 0; + *title_len = len; + } + } + if (help_len) + { + if (feature->Description) len = strlenW( feature->Description ); + else len = 0; + if (*help_len <= len) + { + *help_len = len; + if (help) r = ERROR_MORE_DATA; + } + else if (help) + { + if (feature->Description) strcpyW( help, feature->Description ); + else *help = 0; + *help_len = len; + } + } + return r; +} + +/*********************************************************************** +* MsiGetFeatureInfoW (MSI.@) +*/ +UINT WINAPI MsiGetFeatureInfoW( MSIHANDLE handle, LPCWSTR feature, LPDWORD attrs, + LPWSTR title, LPDWORD title_len, LPWSTR help, LPDWORD help_len ) +{ + UINT r; + MSIPACKAGE *package; + + TRACE("%u, %s, %p, %p, %p, %p, %p\n", handle, debugstr_w(feature), attrs, title, + title_len, help, help_len); + + if (!feature) return ERROR_INVALID_PARAMETER; + + if (!(package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE ))) + return ERROR_INVALID_HANDLE; + + /* features may not have been loaded yet */ + msi_load_all_components( package ); + msi_load_all_features( package ); + + r = MSI_GetFeatureInfo( package, feature, attrs, title, title_len, help, help_len ); + msiobj_release( &package->hdr ); + return r; +} + +/*********************************************************************** + * MsiSetComponentStateA (MSI.@) + */ +UINT WINAPI MsiSetComponentStateA(MSIHANDLE hInstall, LPCSTR szComponent, + INSTALLSTATE iState) +{ + UINT rc; + LPWSTR szwComponent = strdupAtoW(szComponent); + + rc = MsiSetComponentStateW(hInstall, szwComponent, iState); + + msi_free(szwComponent); + + return rc; +} + +/*********************************************************************** + * MsiGetComponentStateA (MSI.@) + */ +UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPCSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + LPWSTR szwComponent= NULL; + UINT rc; + + szwComponent= strdupAtoW(szComponent); + + rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction); + + msi_free( szwComponent); + + return rc; +} + +static UINT MSI_SetComponentStateW(MSIPACKAGE *package, LPCWSTR szComponent, + INSTALLSTATE iState) +{ + MSICOMPONENT *comp; + + TRACE("%p %s %d\n", package, debugstr_w(szComponent), iState); + + comp = msi_get_loaded_component(package, szComponent); + if (!comp) + return ERROR_UNKNOWN_COMPONENT; + + if (comp->Enabled) + comp->Action = iState; + + return ERROR_SUCCESS; +} + +UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPCWSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSICOMPONENT *comp; + + TRACE("%p %s %p %p\n", package, debugstr_w(szComponent), + piInstalled, piAction); + + comp = msi_get_loaded_component(package,szComponent); + if (!comp) + return ERROR_UNKNOWN_COMPONENT; + + if (piInstalled) + { + if (comp->Enabled) + *piInstalled = comp->Installed; + else + *piInstalled = INSTALLSTATE_UNKNOWN; + } + + if (piAction) + { + if (comp->Enabled) + *piAction = comp->Action; + else + *piAction = INSTALLSTATE_UNKNOWN; + } + + TRACE("states (%i, %i)\n", comp->Installed, comp->Action ); + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiSetComponentStateW (MSI.@) + */ +UINT WINAPI MsiSetComponentStateW(MSIHANDLE hInstall, LPCWSTR szComponent, + INSTALLSTATE iState) +{ + MSIPACKAGE* package; + UINT ret; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR component; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + component = SysAllocString(szComponent); + if (!component) + { + IWineMsiRemotePackage_Release(remote_package); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_SetComponentState(remote_package, component, iState); + + SysFreeString(component); + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_SetComponentStateW(package, szComponent, iState); + msiobj_release(&package->hdr); + return ret; +} + +/*********************************************************************** + * MsiGetComponentStateW (MSI.@) + */ +UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPCWSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIPACKAGE* package; + UINT ret; + + TRACE("%d %s %p %p\n", hInstall, debugstr_w(szComponent), + piInstalled, piAction); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + BSTR component; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + component = SysAllocString(szComponent); + if (!component) + { + IWineMsiRemotePackage_Release(remote_package); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_GetComponentState(remote_package, component, + piInstalled, piAction); + + SysFreeString(component); + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** + * MsiGetLanguage (MSI.@) + */ +LANGID WINAPI MsiGetLanguage(MSIHANDLE hInstall) +{ + MSIPACKAGE* package; + LANGID langid; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + LANGID lang; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemotePackage_GetLanguage(remote_package, &lang); + + if (SUCCEEDED(hr)) + return lang; + + return 0; + } + + langid = msi_get_property_int( package->db, szProductLanguage, 0 ); + msiobj_release( &package->hdr ); + return langid; +} + +UINT MSI_SetInstallLevel( MSIPACKAGE *package, int iInstallLevel ) +{ + static const WCHAR fmt[] = { '%','d',0 }; + WCHAR level[6]; + UINT r; + + TRACE("%p %i\n", package, iInstallLevel); + + if (iInstallLevel > 32767) + return ERROR_INVALID_PARAMETER; + + if (iInstallLevel < 1) + return MSI_SetFeatureStates( package ); + + sprintfW( level, fmt, iInstallLevel ); + r = msi_set_property( package->db, szInstallLevel, level ); + if ( r == ERROR_SUCCESS ) + r = MSI_SetFeatureStates( package ); + + return r; +} + +/*********************************************************************** + * MsiSetInstallLevel (MSI.@) + */ +UINT WINAPI MsiSetInstallLevel(MSIHANDLE hInstall, int iInstallLevel) +{ + MSIPACKAGE* package; + UINT r; + + TRACE("%d %i\n", hInstall, iInstallLevel); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote(hInstall); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemotePackage_SetInstallLevel(remote_package, iInstallLevel); + + IWineMsiRemotePackage_Release(remote_package); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + r = MSI_SetInstallLevel( package, iInstallLevel ); + + msiobj_release( &package->hdr ); + + return r; +} + +/*********************************************************************** + * MsiGetFeatureValidStatesW (MSI.@) + */ +UINT WINAPI MsiGetFeatureValidStatesW(MSIHANDLE hInstall, LPCWSTR szFeature, + LPDWORD pInstallState) +{ + if(pInstallState) *pInstallState = 1<<INSTALLSTATE_LOCAL; + FIXME("%d %s %p stub returning %d\n", + hInstall, debugstr_w(szFeature), pInstallState, pInstallState ? *pInstallState : 0); + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiGetFeatureValidStatesA (MSI.@) + */ +UINT WINAPI MsiGetFeatureValidStatesA(MSIHANDLE hInstall, LPCSTR szFeature, + LPDWORD pInstallState) +{ + UINT ret; + LPWSTR szwFeature = strdupAtoW(szFeature); + + ret = MsiGetFeatureValidStatesW(hInstall, szwFeature, pInstallState); + + msi_free(szwFeature); + + return ret; +} diff --git a/libmsi/instlocal.bmp b/libmsi/instlocal.bmp Binary files differnew file mode 100644 index 0000000..c9591ad --- /dev/null +++ b/libmsi/instlocal.bmp diff --git a/libmsi/media.c b/libmsi/media.c new file mode 100644 index 0000000..612624d --- /dev/null +++ b/libmsi/media.c @@ -0,0 +1,936 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2008 James Hawkins + * + * 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 "winerror.h" +#include "wine/debug.h" +#include "fdi.h" +#include "msipriv.h" +#include "winuser.h" +#include "winreg.h" +#include "shlwapi.h" +#include "objidl.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* from msvcrt/fcntl.h */ +#define _O_RDONLY 0 +#define _O_WRONLY 1 +#define _O_RDWR 2 +#define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR) +#define _O_APPEND 0x0008 +#define _O_RANDOM 0x0010 +#define _O_SEQUENTIAL 0x0020 +#define _O_TEMPORARY 0x0040 +#define _O_NOINHERIT 0x0080 +#define _O_CREAT 0x0100 +#define _O_TRUNC 0x0200 +#define _O_EXCL 0x0400 +#define _O_SHORT_LIVED 0x1000 +#define _O_TEXT 0x4000 +#define _O_BINARY 0x8000 + +static BOOL source_matches_volume(MSIMEDIAINFO *mi, LPCWSTR source_root) +{ + WCHAR volume_name[MAX_PATH + 1]; + WCHAR root[MAX_PATH + 1]; + + strcpyW(root, source_root); + PathStripToRootW(root); + PathAddBackslashW(root); + + if (!GetVolumeInformationW(root, volume_name, MAX_PATH + 1, NULL, NULL, NULL, NULL, 0)) + { + WARN("failed to get volume information for %s (%u)\n", debugstr_w(root), GetLastError()); + return FALSE; + } + return !strcmpiW( mi->volume_label, volume_name ); +} + +static UINT msi_change_media(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + LPWSTR error, error_dialog; + LPWSTR source_dir; + UINT r = ERROR_SUCCESS; + + static const WCHAR error_prop[] = {'E','r','r','o','r','D','i','a','l','o','g',0}; + + if ((package->ui_level & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE && + !gUIHandlerA && !gUIHandlerW && !gUIHandlerRecord) return ERROR_SUCCESS; + + error = msi_build_error_string(package, 1302, 1, mi->disk_prompt); + error_dialog = msi_dup_property(package->db, error_prop); + source_dir = msi_dup_property(package->db, szSourceDir); + + while (r == ERROR_SUCCESS && !source_matches_volume(mi, source_dir)) + { + r = msi_spawn_error_dialog(package, error_dialog, error); + + if (gUIHandlerW) + { + gUIHandlerW(gUIContext, MB_RETRYCANCEL | INSTALLMESSAGE_ERROR, error); + } + else if (gUIHandlerA) + { + char *msg = strdupWtoA(error); + gUIHandlerA(gUIContext, MB_RETRYCANCEL | INSTALLMESSAGE_ERROR, msg); + msi_free(msg); + } + else if (gUIHandlerRecord) + { + MSIHANDLE rec = MsiCreateRecord(1); + MsiRecordSetStringW(rec, 0, error); + gUIHandlerRecord(gUIContext, MB_RETRYCANCEL | INSTALLMESSAGE_ERROR, rec); + MsiCloseHandle(rec); + } + } + + msi_free(error); + msi_free(error_dialog); + msi_free(source_dir); + + return r; +} + +static MSICABINETSTREAM *msi_get_cabinet_stream( MSIPACKAGE *package, UINT disk_id ) +{ + MSICABINETSTREAM *cab; + + LIST_FOR_EACH_ENTRY( cab, &package->cabinet_streams, MSICABINETSTREAM, entry ) + { + if (cab->disk_id == disk_id) return cab; + } + return NULL; +} + +static void * CDECL cabinet_alloc(ULONG cb) +{ + return msi_alloc(cb); +} + +static void CDECL cabinet_free(void *pv) +{ + msi_free(pv); +} + +static INT_PTR CDECL cabinet_open(char *pszFile, int oflag, int pmode) +{ + HANDLE handle; + DWORD dwAccess = 0; + DWORD dwShareMode = 0; + DWORD dwCreateDisposition = OPEN_EXISTING; + + switch (oflag & _O_ACCMODE) + { + case _O_RDONLY: + dwAccess = GENERIC_READ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case _O_WRONLY: + dwAccess = GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _O_RDWR: + dwAccess = GENERIC_READ | GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + } + + if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) + dwCreateDisposition = CREATE_NEW; + else if (oflag & _O_CREAT) + dwCreateDisposition = CREATE_ALWAYS; + + handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL, + dwCreateDisposition, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) + return 0; + + return (INT_PTR)handle; +} + +static UINT CDECL cabinet_read(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE)hf; + DWORD read; + + if (ReadFile(handle, pv, cb, &read, NULL)) + return read; + + return 0; +} + +static UINT CDECL cabinet_write(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE)hf; + DWORD written; + + if (WriteFile(handle, pv, cb, &written, NULL)) + return written; + + return 0; +} + +static int CDECL cabinet_close(INT_PTR hf) +{ + HANDLE handle = (HANDLE)hf; + return CloseHandle(handle) ? 0 : -1; +} + +static LONG CDECL cabinet_seek(INT_PTR hf, LONG dist, int seektype) +{ + HANDLE handle = (HANDLE)hf; + /* flags are compatible and so are passed straight through */ + return SetFilePointer(handle, dist, NULL, seektype); +} + +struct package_disk +{ + MSIPACKAGE *package; + UINT id; +}; + +static struct package_disk package_disk; + +static INT_PTR CDECL cabinet_open_stream( char *pszFile, int oflag, int pmode ) +{ + MSICABINETSTREAM *cab; + IStream *stream; + WCHAR *encoded; + HRESULT hr; + + cab = msi_get_cabinet_stream( package_disk.package, package_disk.id ); + if (!cab) + { + WARN("failed to get cabinet stream\n"); + return 0; + } + if (!cab->stream[0] || !(encoded = encode_streamname( FALSE, cab->stream + 1 ))) + { + WARN("failed to encode stream name\n"); + return 0; + } + if (msi_clone_open_stream( package_disk.package->db, cab->storage, encoded, &stream ) != ERROR_SUCCESS) + { + hr = IStorage_OpenStream( cab->storage, encoded, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &stream ); + if (FAILED(hr)) + { + WARN("failed to open stream 0x%08x\n", hr); + msi_free( encoded ); + return 0; + } + } + msi_free( encoded ); + return (INT_PTR)stream; +} + +static UINT CDECL cabinet_read_stream( INT_PTR hf, void *pv, UINT cb ) +{ + IStream *stm = (IStream *)hf; + DWORD read; + HRESULT hr; + + hr = IStream_Read( stm, pv, cb, &read ); + if (hr == S_OK || hr == S_FALSE) + return read; + + return 0; +} + +static int CDECL cabinet_close_stream( INT_PTR hf ) +{ + IStream *stm = (IStream *)hf; + IStream_Release( stm ); + return 0; +} + +static LONG CDECL cabinet_seek_stream( INT_PTR hf, LONG dist, int seektype ) +{ + IStream *stm = (IStream *)hf; + LARGE_INTEGER move; + ULARGE_INTEGER newpos; + HRESULT hr; + + move.QuadPart = dist; + hr = IStream_Seek( stm, move, seektype, &newpos ); + if (SUCCEEDED(hr)) + { + if (newpos.QuadPart <= MAXLONG) return newpos.QuadPart; + ERR("Too big!\n"); + } + return -1; +} + +static UINT CDECL msi_media_get_disk_info(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + MSIRECORD *row; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','D','i','s','k','I','d','`',' ','=',' ','%','i',0}; + + row = MSI_QueryGetRecord(package->db, query, mi->disk_id); + if (!row) + { + TRACE("Unable to query row\n"); + return ERROR_FUNCTION_FAILED; + } + + mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); + mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); + mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); + + if (!mi->first_volume) + mi->first_volume = strdupW(mi->volume_label); + + msiobj_release(&row->hdr); + return ERROR_SUCCESS; +} + +static INT_PTR cabinet_partial_file(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = pfdin->pv; + data->mi->is_continuous = FALSE; + return 0; +} + +static WCHAR *get_cabinet_filename(MSIMEDIAINFO *mi) +{ + int len; + WCHAR *ret; + + len = strlenW(mi->sourcedir) + strlenW(mi->cabinet) + 1; + if (!(ret = msi_alloc(len * sizeof(WCHAR)))) return NULL; + strcpyW(ret, mi->sourcedir); + strcatW(ret, mi->cabinet); + return ret; +} + +static INT_PTR cabinet_next_cabinet(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = pfdin->pv; + MSIMEDIAINFO *mi = data->mi; + LPWSTR cabinet_file = NULL, cab = strdupAtoW(pfdin->psz1); + INT_PTR res = -1; + UINT rc; + + msi_free(mi->disk_prompt); + msi_free(mi->cabinet); + msi_free(mi->volume_label); + mi->disk_prompt = NULL; + mi->cabinet = NULL; + mi->volume_label = NULL; + + mi->disk_id++; + mi->is_continuous = TRUE; + + rc = msi_media_get_disk_info(data->package, mi); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get next cabinet information: %d\n", rc); + goto done; + } + + if (strcmpiW( mi->cabinet, cab )) + { + ERR("Continuous cabinet does not match the next cabinet in the Media table\n"); + goto done; + } + + if (!(cabinet_file = get_cabinet_filename(mi))) + goto done; + + TRACE("Searching for %s\n", debugstr_w(cabinet_file)); + + res = 0; + if (GetFileAttributesW(cabinet_file) == INVALID_FILE_ATTRIBUTES) + { + if (msi_change_media(data->package, mi) != ERROR_SUCCESS) + res = -1; + } + +done: + msi_free(cab); + msi_free(cabinet_file); + return res; +} + +static INT_PTR cabinet_next_cabinet_stream( FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin ) +{ + MSICABDATA *data = pfdin->pv; + MSIMEDIAINFO *mi = data->mi; + UINT rc; + + msi_free( mi->disk_prompt ); + msi_free( mi->cabinet ); + msi_free( mi->volume_label ); + mi->disk_prompt = NULL; + mi->cabinet = NULL; + mi->volume_label = NULL; + + mi->disk_id++; + mi->is_continuous = TRUE; + + rc = msi_media_get_disk_info( data->package, mi ); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get next cabinet information: %u\n", rc); + return -1; + } + package_disk.id = mi->disk_id; + + TRACE("next cabinet is %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); + return 0; +} + +static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = pfdin->pv; + HANDLE handle = 0; + LPWSTR path = NULL; + DWORD attrs; + + data->curfile = strdupAtoW(pfdin->psz1); + if (!data->cb(data->package, data->curfile, MSICABEXTRACT_BEGINEXTRACT, &path, + &attrs, data->user)) + { + /* We're not extracting this file, so free the filename. */ + msi_free(data->curfile); + data->curfile = NULL; + goto done; + } + + TRACE("extracting %s -> %s\n", debugstr_w(data->curfile), debugstr_w(path)); + + attrs = attrs & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM); + if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL; + + handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, attrs, NULL); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD err = GetLastError(); + DWORD attrs2 = GetFileAttributesW(path); + + if (attrs2 == INVALID_FILE_ATTRIBUTES) + { + ERR("failed to create %s (error %d)\n", debugstr_w(path), err); + goto done; + } + else if (err == ERROR_ACCESS_DENIED && (attrs2 & FILE_ATTRIBUTE_READONLY)) + { + TRACE("removing read-only attribute on %s\n", debugstr_w(path)); + SetFileAttributesW( path, attrs2 & ~FILE_ATTRIBUTE_READONLY ); + handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs2, NULL); + + if (handle != INVALID_HANDLE_VALUE) goto done; + err = GetLastError(); + } + if (err == ERROR_SHARING_VIOLATION || err == ERROR_USER_MAPPED_FILE) + { + WCHAR *tmpfileW, *tmppathW, *p; + DWORD len; + + TRACE("file in use, scheduling rename operation\n"); + + if (!(tmppathW = strdupW( path ))) return ERROR_OUTOFMEMORY; + if ((p = strrchrW(tmppathW, '\\'))) *p = 0; + len = strlenW( tmppathW ) + 16; + if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR)))) + { + msi_free( tmppathW ); + return ERROR_OUTOFMEMORY; + } + if (!GetTempFileNameW(tmppathW, szMsi, 0, tmpfileW)) tmpfileW[0] = 0; + msi_free( tmppathW ); + + handle = CreateFileW(tmpfileW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs, NULL); + + if (handle != INVALID_HANDLE_VALUE && + MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) && + MoveFileExW(tmpfileW, path, MOVEFILE_DELAY_UNTIL_REBOOT)) + { + data->package->need_reboot_at_end = 1; + } + else + { + WARN("failed to schedule rename operation %s (error %d)\n", debugstr_w(path), GetLastError()); + DeleteFileW( tmpfileW ); + } + msi_free(tmpfileW); + } + else + WARN("failed to create %s (error %d)\n", debugstr_w(path), err); + } + +done: + msi_free(path); + + return (INT_PTR)handle; +} + +static INT_PTR cabinet_close_file_info(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = pfdin->pv; + FILETIME ft; + FILETIME ftLocal; + HANDLE handle = (HANDLE)pfdin->hf; + + data->mi->is_continuous = FALSE; + + if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) + return -1; + if (!LocalFileTimeToFileTime(&ft, &ftLocal)) + return -1; + if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) + return -1; + + CloseHandle(handle); + + data->cb(data->package, data->curfile, MSICABEXTRACT_FILEEXTRACTED, NULL, NULL, + data->user); + + msi_free(data->curfile); + data->curfile = NULL; + + return 1; +} + +static INT_PTR CDECL cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + switch (fdint) + { + case fdintPARTIAL_FILE: + return cabinet_partial_file(fdint, pfdin); + + case fdintNEXT_CABINET: + return cabinet_next_cabinet(fdint, pfdin); + + case fdintCOPY_FILE: + return cabinet_copy_file(fdint, pfdin); + + case fdintCLOSE_FILE_INFO: + return cabinet_close_file_info(fdint, pfdin); + + default: + return 0; + } +} + +static INT_PTR CDECL cabinet_notify_stream( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) +{ + switch (fdint) + { + case fdintPARTIAL_FILE: + return cabinet_partial_file( fdint, pfdin ); + + case fdintNEXT_CABINET: + return cabinet_next_cabinet_stream( fdint, pfdin ); + + case fdintCOPY_FILE: + return cabinet_copy_file( fdint, pfdin ); + + case fdintCLOSE_FILE_INFO: + return cabinet_close_file_info( fdint, pfdin ); + + case fdintCABINET_INFO: + return 0; + + default: + ERR("Unexpected notification %d\n", fdint); + return 0; + } +} + +static BOOL extract_cabinet( MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data ) +{ + LPSTR cabinet, cab_path = NULL; + HFDI hfdi; + ERF erf; + BOOL ret = FALSE; + + TRACE("extracting %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); + + hfdi = FDICreate( cabinet_alloc, cabinet_free, cabinet_open, cabinet_read, + cabinet_write, cabinet_close, cabinet_seek, 0, &erf ); + if (!hfdi) + { + ERR("FDICreate failed\n"); + return FALSE; + } + + cabinet = strdupWtoA( mi->cabinet ); + if (!cabinet) + goto done; + + cab_path = strdupWtoA( mi->sourcedir ); + if (!cab_path) + goto done; + + ret = FDICopy( hfdi, cabinet, cab_path, 0, cabinet_notify, NULL, data ); + if (!ret) + ERR("FDICopy failed\n"); + +done: + FDIDestroy( hfdi ); + msi_free(cabinet ); + msi_free( cab_path ); + + if (ret) + mi->is_extracted = TRUE; + + return ret; +} + +static BOOL extract_cabinet_stream( MSIPACKAGE *package, MSIMEDIAINFO *mi, LPVOID data ) +{ + static char filename[] = {'<','S','T','R','E','A','M','>',0}; + HFDI hfdi; + ERF erf; + BOOL ret = FALSE; + + TRACE("extracting %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); + + hfdi = FDICreate( cabinet_alloc, cabinet_free, cabinet_open_stream, cabinet_read_stream, + cabinet_write, cabinet_close_stream, cabinet_seek_stream, 0, &erf ); + if (!hfdi) + { + ERR("FDICreate failed\n"); + return FALSE; + } + + package_disk.package = package; + package_disk.id = mi->disk_id; + + ret = FDICopy( hfdi, filename, NULL, 0, cabinet_notify_stream, NULL, data ); + if (!ret) ERR("FDICopy failed\n"); + + FDIDestroy( hfdi ); + if (ret) mi->is_extracted = TRUE; + return ret; +} + +/*********************************************************************** + * msi_cabextract + * + * Extract files from a cabinet file or stream. + */ +BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data) +{ + if (mi->cabinet[0] == '#') + { + return extract_cabinet_stream( package, mi, data ); + } + return extract_cabinet( package, mi, data ); +} + +void msi_free_media_info(MSIMEDIAINFO *mi) +{ + msi_free(mi->disk_prompt); + msi_free(mi->cabinet); + msi_free(mi->volume_label); + msi_free(mi->first_volume); + msi_free(mi); +} + +static UINT get_drive_type(const WCHAR *path) +{ + WCHAR root[MAX_PATH + 1]; + + strcpyW(root, path); + PathStripToRootW(root); + PathAddBackslashW(root); + + return GetDriveTypeW(root); +} + +UINT msi_load_media_info(MSIPACKAGE *package, UINT Sequence, MSIMEDIAINFO *mi) +{ + 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}; + MSIRECORD *row; + LPWSTR source_dir, source; + DWORD options; + + if (Sequence <= mi->last_sequence) /* already loaded */ + return ERROR_SUCCESS; + + row = MSI_QueryGetRecord(package->db, query, Sequence); + if (!row) + { + TRACE("Unable to query row\n"); + return ERROR_FUNCTION_FAILED; + } + + mi->is_extracted = FALSE; + mi->disk_id = MSI_RecordGetInteger(row, 1); + mi->last_sequence = MSI_RecordGetInteger(row, 2); + msi_free(mi->disk_prompt); + mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); + msi_free(mi->cabinet); + mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); + msi_free(mi->volume_label); + mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); + msiobj_release(&row->hdr); + + if (!mi->first_volume) + mi->first_volume = strdupW(mi->volume_label); + + msi_set_sourcedir_props(package, FALSE); + source_dir = msi_dup_property(package->db, szSourceDir); + lstrcpyW(mi->sourcedir, source_dir); + PathAddBackslashW(mi->sourcedir); + mi->type = get_drive_type(source_dir); + + options = MSICODE_PRODUCT; + if (mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE) + { + source = source_dir; + options |= MSISOURCETYPE_MEDIA; + } + else if (package->BaseURL && UrlIsW(package->BaseURL, URLIS_URL)) + { + source = package->BaseURL; + options |= MSISOURCETYPE_URL; + } + else + { + source = mi->sourcedir; + options |= MSISOURCETYPE_NETWORK; + } + + msi_package_add_media_disk(package, package->Context, + MSICODE_PRODUCT, mi->disk_id, + mi->volume_label, mi->disk_prompt); + + msi_package_add_info(package, package->Context, + options, INSTALLPROPERTY_LASTUSEDSOURCEW, source); + + msi_free(source_dir); + TRACE("sequence %u -> cabinet %s disk id %u\n", Sequence, debugstr_w(mi->cabinet), mi->disk_id); + return ERROR_SUCCESS; +} + +/* FIXME: search URL sources as well */ +static UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + WCHAR source[MAX_PATH]; + WCHAR volume[MAX_PATH]; + WCHAR prompt[MAX_PATH]; + DWORD volumesz, promptsz; + DWORD index, size, id; + WCHAR last_type[2]; + UINT r; + + size = 2; + r = MsiSourceListGetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDTYPEW, last_type, &size); + if (r != ERROR_SUCCESS) + return r; + + size = MAX_PATH; + r = MsiSourceListGetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDSOURCEW, source, &size); + if (r != ERROR_SUCCESS) + return r; + + if (last_type[0] == 'n') + { + WCHAR cabinet_file[MAX_PATH]; + BOOL check_all = FALSE; + + while(TRUE) + { + index = 0; + volumesz = MAX_PATH; + while (MsiSourceListEnumSourcesW(package->ProductCode, NULL, + package->Context, + MSISOURCETYPE_NETWORK, index++, + volume, &volumesz) == ERROR_SUCCESS) + { + if (check_all || !strncmpiW(source, volume, strlenW(source))) + { + lstrcpyW(cabinet_file, volume); + PathAddBackslashW(cabinet_file); + lstrcatW(cabinet_file, mi->cabinet); + + if (GetFileAttributesW(cabinet_file) == INVALID_FILE_ATTRIBUTES) + { + volumesz = MAX_PATH; + if(!check_all) + break; + continue; + } + + lstrcpyW(mi->sourcedir, volume); + PathAddBackslashW(mi->sourcedir); + TRACE("Found network source %s\n", debugstr_w(mi->sourcedir)); + return ERROR_SUCCESS; + } + } + + if (!check_all) + check_all = TRUE; + else + break; + } + } + + index = 0; + volumesz = MAX_PATH; + promptsz = MAX_PATH; + while (MsiSourceListEnumMediaDisksW(package->ProductCode, NULL, + package->Context, + MSICODE_PRODUCT, index++, &id, + volume, &volumesz, prompt, &promptsz) == ERROR_SUCCESS) + { + mi->disk_id = id; + msi_free( mi->volume_label ); + if (!(mi->volume_label = msi_alloc( ++volumesz * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY; + strcpyW( mi->volume_label, volume ); + + msi_free( mi->disk_prompt ); + if (!(mi->disk_prompt = msi_alloc( ++promptsz * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY; + strcpyW( mi->disk_prompt, prompt ); + + if (source_matches_volume(mi, source)) + { + /* FIXME: what about SourceDir */ + lstrcpyW(mi->sourcedir, source); + PathAddBackslashW(mi->sourcedir); + TRACE("Found disk source %s\n", debugstr_w(mi->sourcedir)); + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +UINT ready_media( MSIPACKAGE *package, BOOL compressed, MSIMEDIAINFO *mi ) +{ + UINT rc; + WCHAR *cabinet_file = NULL; + + /* media info for continuous cabinet is already loaded */ + if (mi->is_continuous) return ERROR_SUCCESS; + + if (mi->cabinet) + { + /* cabinet is internal, no checks needed */ + if (mi->cabinet[0] == '#') return ERROR_SUCCESS; + + if (!(cabinet_file = get_cabinet_filename( mi ))) return ERROR_OUTOFMEMORY; + + /* package should be downloaded */ + if (compressed && GetFileAttributesW( cabinet_file ) == INVALID_FILE_ATTRIBUTES && + package->BaseURL && UrlIsW( package->BaseURL, URLIS_URL )) + { + WCHAR temppath[MAX_PATH], *p; + + if ((rc = msi_download_file( cabinet_file, temppath )) != ERROR_SUCCESS) + { + ERR("failed to download %s (%u)\n", debugstr_w(cabinet_file), rc); + msi_free( cabinet_file ); + return rc; + } + if ((p = strrchrW( temppath, '\\' ))) *p = 0; + strcpyW( mi->sourcedir, temppath ); + PathAddBackslashW( mi->sourcedir ); + msi_free( mi->cabinet ); + mi->cabinet = strdupW( p + 1 ); + msi_free( cabinet_file ); + return ERROR_SUCCESS; + } + } + /* check volume matches, change media if not */ + if (mi->volume_label && mi->disk_id > 1 && strcmpW( mi->first_volume, mi->volume_label )) + { + WCHAR *source = msi_dup_property( package->db, szSourceDir ); + BOOL match = source_matches_volume( mi, source ); + msi_free( source ); + + if (!match && (mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE)) + { + if ((rc = msi_change_media( package, mi )) != ERROR_SUCCESS) + { + msi_free( cabinet_file ); + return rc; + } + } + } + if (mi->cabinet) + { + if (compressed && GetFileAttributesW( cabinet_file ) == INVALID_FILE_ATTRIBUTES) + { + if ((rc = find_published_source( package, mi )) != ERROR_SUCCESS) + { + ERR("cabinet not found: %s\n", debugstr_w(cabinet_file)); + msi_free( cabinet_file ); + return ERROR_INSTALL_FAILURE; + } + } + } + msi_free( cabinet_file ); + return ERROR_SUCCESS; +} + +UINT msi_add_cabinet_stream( MSIPACKAGE *package, UINT disk_id, IStorage *storage, const WCHAR *name ) +{ + MSICABINETSTREAM *cab, *item; + + TRACE("%p, %u, %p, %s\n", package, disk_id, storage, debugstr_w(name)); + + LIST_FOR_EACH_ENTRY( item, &package->cabinet_streams, MSICABINETSTREAM, entry ) + { + if (item->disk_id == disk_id) + { + TRACE("duplicate disk id %u\n", disk_id); + return ERROR_FUNCTION_FAILED; + } + } + if (!(cab = msi_alloc( sizeof(*cab) ))) return ERROR_OUTOFMEMORY; + if (!(cab->stream = msi_alloc( (strlenW( name ) + 1) * sizeof(WCHAR ) ))) + { + msi_free( cab ); + return ERROR_OUTOFMEMORY; + } + strcpyW( cab->stream, name ); + cab->disk_id = disk_id; + cab->storage = storage; + IStorage_AddRef( storage ); + list_add_tail( &package->cabinet_streams, &cab->entry ); + + return ERROR_SUCCESS; +} diff --git a/libmsi/msi.c b/libmsi/msi.c new file mode 100644 index 0000000..68e9f81 --- /dev/null +++ b/libmsi/msi.c @@ -0,0 +1,4215 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002,2003,2004,2005 Mike McCormack 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 +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "msi.h" +#include "msidefs.h" +#include "msiquery.h" +#include "msipriv.h" +#include "msiserver.h" +#include "wincrypt.h" +#include "winver.h" +#include "winuser.h" +#include "shlobj.h" +#include "shobjidl.h" +#include "objidl.h" +#include "wintrust.h" +#include "softpub.h" + +#include "initguid.h" +#include "msxml2.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static const WCHAR installerW[] = {'\\','I','n','s','t','a','l','l','e','r',0}; + +UINT msi_locate_product(LPCWSTR szProduct, MSIINSTALLCONTEXT *context) +{ + HKEY hkey = NULL; + + *context = MSIINSTALLCONTEXT_NONE; + if (!szProduct) return ERROR_UNKNOWN_PRODUCT; + + if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + &hkey, FALSE) == ERROR_SUCCESS) + *context = MSIINSTALLCONTEXT_USERMANAGED; + else if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, + &hkey, FALSE) == ERROR_SUCCESS) + *context = MSIINSTALLCONTEXT_MACHINE; + else if (MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE) == ERROR_SUCCESS) + *context = MSIINSTALLCONTEXT_USERUNMANAGED; + + RegCloseKey(hkey); + + if (*context == MSIINSTALLCONTEXT_NONE) + return ERROR_UNKNOWN_PRODUCT; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct) +{ + UINT r; + LPWSTR szwProd = NULL; + + TRACE("%s %p\n",debugstr_a(szProduct), phProduct); + + if( szProduct ) + { + szwProd = strdupAtoW( szProduct ); + if( !szwProd ) + return ERROR_OUTOFMEMORY; + } + + r = MsiOpenProductW( szwProd, phProduct ); + + msi_free( szwProd ); + + return r; +} + +static UINT MSI_OpenProductW(LPCWSTR szProduct, MSIPACKAGE **package) +{ + UINT r; + HKEY props; + LPWSTR path; + MSIINSTALLCONTEXT context; + + static const WCHAR managed[] = { + 'M','a','n','a','g','e','d','L','o','c','a','l','P','a','c','k','a','g','e',0}; + static const WCHAR local[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; + + TRACE("%s %p\n", debugstr_w(szProduct), package); + + r = msi_locate_product(szProduct, &context); + if (r != ERROR_SUCCESS) + return r; + + r = MSIREG_OpenInstallProps(szProduct, context, NULL, &props, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + if (context == MSIINSTALLCONTEXT_USERMANAGED) + path = msi_reg_get_val_str(props, managed); + else + path = msi_reg_get_val_str(props, local); + + r = ERROR_UNKNOWN_PRODUCT; + + if (!path || GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) + goto done; + + if (PathIsRelativeW(path)) + { + r = ERROR_INSTALL_PACKAGE_OPEN_FAILED; + goto done; + } + + r = MSI_OpenPackageW(path, package); + +done: + RegCloseKey(props); + msi_free(path); + return r; +} + +UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct) +{ + MSIPACKAGE *package = NULL; + WCHAR squished_pc[GUID_SIZE]; + UINT r; + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!phProduct) + return ERROR_INVALID_PARAMETER; + + r = MSI_OpenProductW(szProduct, &package); + if (r != ERROR_SUCCESS) + return r; + + *phProduct = alloc_msihandle(&package->hdr); + if (!*phProduct) + r = ERROR_NOT_ENOUGH_MEMORY; + + msiobj_release(&package->hdr); + return r; +} + +UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, + LPCSTR szTransforms, LANGID lgidLanguage) +{ + FIXME("%s %s %s %08x\n",debugstr_a(szPackagePath), + debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductW(LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, + LPCWSTR szTransforms, LANGID lgidLanguage) +{ + FIXME("%s %s %s %08x\n",debugstr_w(szPackagePath), + debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductExA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, + LPCSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) +{ + FIXME("%s %s %s %08x %08x %08x\n", debugstr_a(szPackagePath), + debugstr_a(szScriptfilePath), debugstr_a(szTransforms), + lgidLanguage, dwPlatform, dwOptions); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductExW( LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, + LPCWSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) +{ + FIXME("%s %s %s %08x %08x %08x\n", debugstr_w(szPackagePath), + debugstr_w(szScriptfilePath), debugstr_w(szTransforms), + lgidLanguage, dwPlatform, dwOptions); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine) +{ + LPWSTR szwPath = NULL, szwCommand = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine)); + + if( szPackagePath ) + { + szwPath = strdupAtoW( szPackagePath ); + if( !szwPath ) + goto end; + } + + if( szCommandLine ) + { + szwCommand = strdupAtoW( szCommandLine ); + if( !szwCommand ) + goto end; + } + + r = MsiInstallProductW( szwPath, szwCommand ); + +end: + msi_free( szwPath ); + msi_free( szwCommand ); + + return r; +} + +UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine) +{ + MSIPACKAGE *package = NULL; + UINT r; + + TRACE("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine)); + + if (!szPackagePath) + return ERROR_INVALID_PARAMETER; + + if (!*szPackagePath) + return ERROR_PATH_NOT_FOUND; + + r = MSI_OpenPackageW( szPackagePath, &package ); + if (r == ERROR_SUCCESS) + { + r = MSI_InstallPackage( package, szPackagePath, szCommandLine ); + msiobj_release( &package->hdr ); + } + + return r; +} + +UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode) +{ + LPWSTR wszProduct; + UINT rc; + + TRACE("%s %08x\n", debugstr_a(szProduct), dwReinstallMode); + + wszProduct = strdupAtoW(szProduct); + + rc = MsiReinstallProductW(wszProduct, dwReinstallMode); + + msi_free(wszProduct); + return rc; +} + +UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode) +{ + TRACE("%s %08x\n", debugstr_w(szProduct), dwReinstallMode); + + return MsiReinstallFeatureW(szProduct, szAll, dwReinstallMode); +} + +UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage, + INSTALLTYPE eInstallType, LPCSTR szCommandLine) +{ + LPWSTR patch_package = NULL; + LPWSTR install_package = NULL; + LPWSTR command_line = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage), + eInstallType, debugstr_a(szCommandLine)); + + if (szPatchPackage && !(patch_package = strdupAtoW(szPatchPackage))) + goto done; + + if (szInstallPackage && !(install_package = strdupAtoW(szInstallPackage))) + goto done; + + if (szCommandLine && !(command_line = strdupAtoW(szCommandLine))) + goto done; + + r = MsiApplyPatchW(patch_package, install_package, eInstallType, command_line); + +done: + msi_free(patch_package); + msi_free(install_package); + msi_free(command_line); + + return r; +} + +static UINT get_patch_product_codes( LPCWSTR szPatchPackage, WCHAR ***product_codes ) +{ + MSIHANDLE patch, info = 0; + UINT r, type; + DWORD size; + static WCHAR empty[] = {0}; + WCHAR *codes = NULL; + + r = MsiOpenDatabaseW( szPatchPackage, MSIDBOPEN_READONLY, &patch ); + if (r != ERROR_SUCCESS) + return r; + + r = MsiGetSummaryInformationW( patch, NULL, 0, &info ); + if (r != ERROR_SUCCESS) + goto done; + + size = 0; + r = MsiSummaryInfoGetPropertyW( info, PID_TEMPLATE, &type, NULL, NULL, empty, &size ); + if (r != ERROR_MORE_DATA || !size || type != VT_LPSTR) + { + ERR("Failed to read product codes from patch\n"); + r = ERROR_FUNCTION_FAILED; + goto done; + } + + codes = msi_alloc( ++size * sizeof(WCHAR) ); + if (!codes) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiSummaryInfoGetPropertyW( info, PID_TEMPLATE, &type, NULL, NULL, codes, &size ); + if (r == ERROR_SUCCESS) + *product_codes = msi_split_string( codes, ';' ); + +done: + MsiCloseHandle( info ); + MsiCloseHandle( patch ); + msi_free( codes ); + return r; +} + +static UINT MSI_ApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szProductCode, LPCWSTR szCommandLine) +{ + UINT i, r = ERROR_FUNCTION_FAILED; + DWORD size; + LPCWSTR cmd_ptr = szCommandLine; + LPWSTR cmd, *codes = NULL; + BOOL succeeded = FALSE; + + static const WCHAR fmt[] = {'%','s',' ','P','A','T','C','H','=','"','%','s','"',0}; + static WCHAR empty[] = {0}; + + if (!szPatchPackage || !szPatchPackage[0]) + return ERROR_INVALID_PARAMETER; + + if (!szProductCode && (r = get_patch_product_codes( szPatchPackage, &codes ))) + return r; + + if (!szCommandLine) + cmd_ptr = empty; + + size = strlenW(cmd_ptr) + strlenW(fmt) + strlenW(szPatchPackage) + 1; + cmd = msi_alloc(size * sizeof(WCHAR)); + if (!cmd) + { + msi_free(codes); + return ERROR_OUTOFMEMORY; + } + sprintfW(cmd, fmt, cmd_ptr, szPatchPackage); + + if (szProductCode) + r = MsiConfigureProductExW(szProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, cmd); + else + { + for (i = 0; codes[i]; i++) + { + r = MsiConfigureProductExW(codes[i], INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, cmd); + if (r == ERROR_SUCCESS) + { + TRACE("patch applied\n"); + succeeded = TRUE; + } + } + + if (succeeded) + r = ERROR_SUCCESS; + } + + msi_free(cmd); + msi_free(codes); + return r; +} + +UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage, + INSTALLTYPE eInstallType, LPCWSTR szCommandLine) +{ + TRACE("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage), + eInstallType, debugstr_w(szCommandLine)); + + if (szInstallPackage || eInstallType == INSTALLTYPE_NETWORK_IMAGE || + eInstallType == INSTALLTYPE_SINGLE_INSTANCE) + { + FIXME("Only reading target products from patch\n"); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + return MSI_ApplyPatchW(szPatchPackage, NULL, szCommandLine); +} + +UINT WINAPI MsiApplyMultiplePatchesA(LPCSTR szPatchPackages, + LPCSTR szProductCode, LPCSTR szPropertiesList) +{ + LPWSTR patch_packages = NULL; + LPWSTR product_code = NULL; + LPWSTR properties_list = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%s %s %s\n", debugstr_a(szPatchPackages), debugstr_a(szProductCode), + debugstr_a(szPropertiesList)); + + if (!szPatchPackages || !szPatchPackages[0]) + return ERROR_INVALID_PARAMETER; + + if (!(patch_packages = strdupAtoW(szPatchPackages))) + return ERROR_OUTOFMEMORY; + + if (szProductCode && !(product_code = strdupAtoW(szProductCode))) + goto done; + + if (szPropertiesList && !(properties_list = strdupAtoW(szPropertiesList))) + goto done; + + r = MsiApplyMultiplePatchesW(patch_packages, product_code, properties_list); + +done: + msi_free(patch_packages); + msi_free(product_code); + msi_free(properties_list); + + return r; +} + +UINT WINAPI MsiApplyMultiplePatchesW(LPCWSTR szPatchPackages, + LPCWSTR szProductCode, LPCWSTR szPropertiesList) +{ + UINT r = ERROR_SUCCESS; + LPCWSTR beg, end; + + TRACE("%s %s %s\n", debugstr_w(szPatchPackages), debugstr_w(szProductCode), + debugstr_w(szPropertiesList)); + + if (!szPatchPackages || !szPatchPackages[0]) + return ERROR_INVALID_PARAMETER; + + beg = end = szPatchPackages; + while (*beg) + { + DWORD len; + LPWSTR patch; + + while (*beg == ' ') beg++; + while (*end && *end != ';') end++; + + len = end - beg; + while (len && beg[len - 1] == ' ') len--; + + if (!len) return ERROR_INVALID_NAME; + + patch = msi_alloc((len + 1) * sizeof(WCHAR)); + if (!patch) + return ERROR_OUTOFMEMORY; + + memcpy(patch, beg, len * sizeof(WCHAR)); + patch[len] = '\0'; + + r = MSI_ApplyPatchW(patch, szProductCode, szPropertiesList); + msi_free(patch); + + if (r != ERROR_SUCCESS) + break; + + beg = ++end; + } + return r; +} + +static void free_patchinfo( DWORD count, MSIPATCHSEQUENCEINFOW *info ) +{ + DWORD i; + for (i = 0; i < count; i++) msi_free( (WCHAR *)info[i].szPatchData ); + msi_free( info ); +} + +static MSIPATCHSEQUENCEINFOW *patchinfoAtoW( DWORD count, const MSIPATCHSEQUENCEINFOA *info ) +{ + DWORD i; + MSIPATCHSEQUENCEINFOW *ret; + + if (!(ret = msi_alloc( count * sizeof(MSIPATCHSEQUENCEINFOW) ))) return NULL; + for (i = 0; i < count; i++) + { + if (info[i].szPatchData && !(ret[i].szPatchData = strdupAtoW( info[i].szPatchData ))) + { + free_patchinfo( i, ret ); + return NULL; + } + ret[i].ePatchDataType = info[i].ePatchDataType; + ret[i].dwOrder = info[i].dwOrder; + ret[i].uStatus = info[i].uStatus; + } + return ret; +} + +UINT WINAPI MsiDetermineApplicablePatchesA(LPCSTR szProductPackagePath, + DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOA pPatchInfo) +{ + UINT i, r; + WCHAR *package_path = NULL; + MSIPATCHSEQUENCEINFOW *psi; + + TRACE("%s, %u, %p\n", debugstr_a(szProductPackagePath), cPatchInfo, pPatchInfo); + + if (szProductPackagePath && !(package_path = strdupAtoW( szProductPackagePath ))) + return ERROR_OUTOFMEMORY; + + if (!(psi = patchinfoAtoW( cPatchInfo, pPatchInfo ))) + { + msi_free( package_path ); + return ERROR_OUTOFMEMORY; + } + r = MsiDetermineApplicablePatchesW( package_path, cPatchInfo, psi ); + if (r == ERROR_SUCCESS) + { + for (i = 0; i < cPatchInfo; i++) + { + pPatchInfo[i].dwOrder = psi[i].dwOrder; + pPatchInfo[i].uStatus = psi[i].uStatus; + } + } + msi_free( package_path ); + free_patchinfo( cPatchInfo, psi ); + return r; +} + +static UINT MSI_ApplicablePatchW( MSIPACKAGE *package, LPCWSTR patch ) +{ + MSISUMMARYINFO *si; + MSIDATABASE *patch_db; + UINT r = ERROR_SUCCESS; + + r = MSI_OpenDatabaseW( patch, MSIDBOPEN_READONLY, &patch_db ); + if (r != ERROR_SUCCESS) + { + WARN("failed to open patch file %s\n", debugstr_w(patch)); + return r; + } + + si = MSI_GetSummaryInformationW( patch_db->storage, 0 ); + if (!si) + { + msiobj_release( &patch_db->hdr ); + return ERROR_FUNCTION_FAILED; + } + + r = msi_check_patch_applicable( package, si ); + if (r != ERROR_SUCCESS) + TRACE("patch not applicable\n"); + + msiobj_release( &patch_db->hdr ); + msiobj_release( &si->hdr ); + return r; +} + +/* IXMLDOMDocument should be set to XPath mode already */ +static UINT MSI_ApplicablePatchXML( MSIPACKAGE *package, IXMLDOMDocument *desc ) +{ + static const WCHAR queryW[] = {'M','s','i','P','a','t','c','h','/', + 'T','a','r','g','e','t','P','r','o','d','u','c','t','/', + 'T','a','r','g','e','t','P','r','o','d','u','c','t','C','o','d','e',0}; + UINT r = ERROR_FUNCTION_FAILED; + IXMLDOMNodeList *list; + LPWSTR product_code; + IXMLDOMNode *node; + HRESULT hr; + BSTR s; + + product_code = msi_dup_property( package->db, szProductCode ); + if (!product_code) + { + /* FIXME: the property ProductCode should be written into the DB somewhere */ + ERR("no product code to check\n"); + return ERROR_SUCCESS; + } + + s = SysAllocString(queryW); + hr = IXMLDOMDocument_selectNodes( desc, s, &list ); + SysFreeString(s); + if (hr != S_OK) + return ERROR_INVALID_PATCH_XML; + + while (IXMLDOMNodeList_nextNode( list, &node ) == S_OK && r != ERROR_SUCCESS) + { + hr = IXMLDOMNode_get_text( node, &s ); + IXMLDOMNode_Release( node ); + if (hr == S_OK) + { + if (!strcmpW( s, product_code )) r = ERROR_SUCCESS; + SysFreeString( s ); + } + } + IXMLDOMNodeList_Release( list ); + + if (r != ERROR_SUCCESS) + TRACE("patch not applicable\n"); + + msi_free( product_code ); + return r; +} + +static UINT determine_patch_sequence( MSIPACKAGE *package, DWORD count, MSIPATCHSEQUENCEINFOW *info ) +{ + IXMLDOMDocument *desc = NULL; + DWORD i; + + if (count > 1) + FIXME("patch ordering not supported\n"); + + for (i = 0; i < count; i++) + { + switch (info[i].ePatchDataType) + { + case MSIPATCH_DATATYPE_PATCHFILE: + { + if (MSI_ApplicablePatchW( package, info[i].szPatchData ) != ERROR_SUCCESS) + { + info[i].dwOrder = ~0u; + info[i].uStatus = ERROR_PATCH_TARGET_NOT_FOUND; + } + else + { + info[i].dwOrder = i; + info[i].uStatus = ERROR_SUCCESS; + } + break; + } + case MSIPATCH_DATATYPE_XMLPATH: + case MSIPATCH_DATATYPE_XMLBLOB: + { + VARIANT_BOOL b; + HRESULT hr; + BSTR s; + + if (!desc) + { + hr = CoCreateInstance( &CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, + &IID_IXMLDOMDocument, (void**)&desc ); + if (hr != S_OK) + { + ERR("failed to create DOMDocument30 instance, 0x%08x\n", hr); + return ERROR_FUNCTION_FAILED; + } + } + + s = SysAllocString( info[i].szPatchData ); + if (info[i].ePatchDataType == MSIPATCH_DATATYPE_XMLPATH) + { + VARIANT src; + + V_VT(&src) = VT_BSTR; + V_BSTR(&src) = s; + hr = IXMLDOMDocument_load( desc, src, &b ); + } + else + hr = IXMLDOMDocument_loadXML( desc, s, &b ); + SysFreeString( s ); + if ( hr != S_OK ) + { + ERR("failed to parse patch description\n"); + IXMLDOMDocument_Release( desc ); + break; + } + + if (MSI_ApplicablePatchXML( package, desc ) != ERROR_SUCCESS) + { + info[i].dwOrder = ~0u; + info[i].uStatus = ERROR_PATCH_TARGET_NOT_FOUND; + } + else + { + info[i].dwOrder = i; + info[i].uStatus = ERROR_SUCCESS; + } + break; + } + default: + { + FIXME("unknown patch data type %u\n", info[i].ePatchDataType); + info[i].dwOrder = i; + info[i].uStatus = ERROR_SUCCESS; + break; + } + } + + TRACE("szPatchData: %s\n", debugstr_w(info[i].szPatchData)); + TRACE("ePatchDataType: %u\n", info[i].ePatchDataType); + TRACE("dwOrder: %u\n", info[i].dwOrder); + TRACE("uStatus: %u\n", info[i].uStatus); + } + + if (desc) IXMLDOMDocument_Release( desc ); + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiDetermineApplicablePatchesW(LPCWSTR szProductPackagePath, + DWORD cPatchInfo, PMSIPATCHSEQUENCEINFOW pPatchInfo) +{ + UINT r; + MSIPACKAGE *package; + + TRACE("%s, %u, %p\n", debugstr_w(szProductPackagePath), cPatchInfo, pPatchInfo); + + r = MSI_OpenPackageW( szProductPackagePath, &package ); + if (r != ERROR_SUCCESS) + { + ERR("failed to open package %u\n", r); + return r; + } + r = determine_patch_sequence( package, cPatchInfo, pPatchInfo ); + msiobj_release( &package->hdr ); + return r; +} + +UINT WINAPI MsiDeterminePatchSequenceA( LPCSTR product, LPCSTR usersid, + MSIINSTALLCONTEXT context, DWORD count, PMSIPATCHSEQUENCEINFOA patchinfo ) +{ + UINT i, r; + WCHAR *productW, *usersidW = NULL; + MSIPATCHSEQUENCEINFOW *patchinfoW; + + TRACE("%s, %s, %d, %d, %p\n", debugstr_a(product), debugstr_a(usersid), + context, count, patchinfo); + + if (!product) return ERROR_INVALID_PARAMETER; + if (!(productW = strdupAtoW( product ))) return ERROR_OUTOFMEMORY; + if (usersid && !(usersidW = strdupAtoW( usersid ))) + { + msi_free( productW ); + return ERROR_OUTOFMEMORY; + } + if (!(patchinfoW = patchinfoAtoW( count, patchinfo ))) + { + msi_free( productW ); + msi_free( usersidW ); + return ERROR_OUTOFMEMORY; + } + r = MsiDeterminePatchSequenceW( productW, usersidW, context, count, patchinfoW ); + if (r == ERROR_SUCCESS) + { + for (i = 0; i < count; i++) + { + patchinfo[i].dwOrder = patchinfoW[i].dwOrder; + patchinfo[i].uStatus = patchinfoW[i].uStatus; + } + } + msi_free( productW ); + msi_free( usersidW ); + free_patchinfo( count, patchinfoW ); + return r; +} + +static UINT open_package( const WCHAR *product, const WCHAR *usersid, + MSIINSTALLCONTEXT context, MSIPACKAGE **package ) +{ + UINT r; + HKEY props; + WCHAR *localpath, sourcepath[MAX_PATH], filename[MAX_PATH]; + + r = MSIREG_OpenInstallProps( product, context, usersid, &props, FALSE ); + if (r != ERROR_SUCCESS) return ERROR_BAD_CONFIGURATION; + + if ((localpath = msi_reg_get_val_str( props, szLocalPackage ))) + { + strcpyW( sourcepath, localpath ); + msi_free( localpath ); + } + RegCloseKey( props ); + if (!localpath || GetFileAttributesW( sourcepath ) == INVALID_FILE_ATTRIBUTES) + { + DWORD sz = sizeof(sourcepath); + MsiSourceListGetInfoW( product, usersid, context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz ); + sz = sizeof(filename); + MsiSourceListGetInfoW( product, usersid, context, MSICODE_PRODUCT, + INSTALLPROPERTY_PACKAGENAMEW, filename, &sz ); + strcatW( sourcepath, filename ); + } + if (GetFileAttributesW( sourcepath ) == INVALID_FILE_ATTRIBUTES) + return ERROR_INSTALL_SOURCE_ABSENT; + + return MSI_OpenPackageW( sourcepath, package ); +} + +UINT WINAPI MsiDeterminePatchSequenceW( LPCWSTR product, LPCWSTR usersid, + MSIINSTALLCONTEXT context, DWORD count, PMSIPATCHSEQUENCEINFOW patchinfo ) +{ + UINT r; + MSIPACKAGE *package; + + TRACE("%s, %s, %d, %d, %p\n", debugstr_w(product), debugstr_w(usersid), + context, count, patchinfo); + + if (!product) return ERROR_INVALID_PARAMETER; + r = open_package( product, usersid, context, &package ); + if (r != ERROR_SUCCESS) return r; + + r = determine_patch_sequence( package, count, patchinfo ); + msiobj_release( &package->hdr ); + return r; +} + +UINT WINAPI MsiConfigureProductExW(LPCWSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState, LPCWSTR szCommandLine) +{ + MSIPACKAGE* package = NULL; + MSIINSTALLCONTEXT context; + UINT r; + DWORD sz; + WCHAR sourcepath[MAX_PATH], filename[MAX_PATH]; + LPWSTR commandline; + + static const WCHAR szInstalled[] = { + ' ','I','n','s','t','a','l','l','e','d','=','1',0}; + static const WCHAR szMaxInstallLevel[] = { + ' ','I','N','S','T','A','L','L','L','E','V','E','L','=','3','2','7','6','7',0}; + static const WCHAR szRemoveAll[] = { + ' ','R','E','M','O','V','E','=','A','L','L',0}; + static const WCHAR szMachine[] = { + ' ','A','L','L','U','S','E','R','S','=','1',0}; + + TRACE("%s %d %d %s\n",debugstr_w(szProduct), iInstallLevel, eInstallState, + debugstr_w(szCommandLine)); + + if (!szProduct || lstrlenW(szProduct) != GUID_SIZE - 1) + return ERROR_INVALID_PARAMETER; + + if (eInstallState == INSTALLSTATE_ADVERTISED || + eInstallState == INSTALLSTATE_SOURCE) + { + FIXME("State %d not implemented\n", eInstallState); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + r = msi_locate_product(szProduct, &context); + if (r != ERROR_SUCCESS) + return r; + + r = open_package(szProduct, NULL, context, &package); + if (r != ERROR_SUCCESS) + return r; + + sz = lstrlenW(szInstalled) + 1; + + if (szCommandLine) + sz += lstrlenW(szCommandLine); + + if (eInstallState != INSTALLSTATE_DEFAULT) + sz += lstrlenW(szMaxInstallLevel); + + if (eInstallState == INSTALLSTATE_ABSENT) + sz += lstrlenW(szRemoveAll); + + if (context == MSIINSTALLCONTEXT_MACHINE) + sz += lstrlenW(szMachine); + + commandline = msi_alloc(sz * sizeof(WCHAR)); + if (!commandline) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + commandline[0] = 0; + if (szCommandLine) + lstrcpyW(commandline,szCommandLine); + + if (eInstallState != INSTALLSTATE_DEFAULT) + lstrcatW(commandline, szMaxInstallLevel); + + if (eInstallState == INSTALLSTATE_ABSENT) + lstrcatW(commandline, szRemoveAll); + + if (context == MSIINSTALLCONTEXT_MACHINE) + lstrcatW(commandline, szMachine); + + sz = sizeof(sourcepath); + MsiSourceListGetInfoW(szProduct, NULL, context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); + + sz = sizeof(filename); + MsiSourceListGetInfoW(szProduct, NULL, context, MSICODE_PRODUCT, + INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); + + strcatW(sourcepath, filename); + + r = MSI_InstallPackage( package, sourcepath, commandline ); + + msi_free(commandline); + +end: + msiobj_release( &package->hdr ); + + return r; +} + +UINT WINAPI MsiConfigureProductExA(LPCSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState, LPCSTR szCommandLine) +{ + LPWSTR szwProduct = NULL; + LPWSTR szwCommandLine = NULL; + UINT r = ERROR_OUTOFMEMORY; + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + goto end; + } + + if( szCommandLine) + { + szwCommandLine = strdupAtoW( szCommandLine ); + if( !szwCommandLine) + goto end; + } + + r = MsiConfigureProductExW( szwProduct, iInstallLevel, eInstallState, + szwCommandLine ); +end: + msi_free( szwProduct ); + msi_free( szwCommandLine); + + return r; +} + +UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState) +{ + LPWSTR szwProduct = NULL; + UINT r; + + TRACE("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState); + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + + r = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState ); + msi_free( szwProduct ); + + return r; +} + +UINT WINAPI MsiConfigureProductW(LPCWSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState) +{ + return MsiConfigureProductExW(szProduct, iInstallLevel, eInstallState, NULL); +} + +UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer) +{ + LPWSTR szwComponent = NULL; + UINT r; + WCHAR szwBuffer[GUID_SIZE]; + + TRACE("%s %p\n", debugstr_a(szComponent), szBuffer); + + if( szComponent ) + { + szwComponent = strdupAtoW( szComponent ); + if( !szwComponent ) + return ERROR_OUTOFMEMORY; + } + + *szwBuffer = '\0'; + r = MsiGetProductCodeW( szwComponent, szwBuffer ); + + if(*szwBuffer) + WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL); + + msi_free( szwComponent ); + + return r; +} + +UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer) +{ + UINT rc, index; + HKEY compkey, prodkey; + WCHAR squished_comp[GUID_SIZE]; + WCHAR squished_prod[GUID_SIZE]; + DWORD sz = GUID_SIZE; + + TRACE("%s %p\n", debugstr_w(szComponent), szBuffer); + + if (!szComponent || !*szComponent) + return ERROR_INVALID_PARAMETER; + + if (!squash_guid(szComponent, squished_comp)) + return ERROR_INVALID_PARAMETER; + + if (MSIREG_OpenUserDataComponentKey(szComponent, NULL, &compkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &compkey, FALSE) != ERROR_SUCCESS) + { + return ERROR_UNKNOWN_COMPONENT; + } + + rc = RegEnumValueW(compkey, 0, squished_prod, &sz, NULL, NULL, NULL, NULL); + if (rc != ERROR_SUCCESS) + { + RegCloseKey(compkey); + return ERROR_UNKNOWN_COMPONENT; + } + + /* check simple case, only one product */ + rc = RegEnumValueW(compkey, 1, squished_prod, &sz, NULL, NULL, NULL, NULL); + if (rc == ERROR_NO_MORE_ITEMS) + { + rc = ERROR_SUCCESS; + goto done; + } + + index = 0; + while ((rc = RegEnumValueW(compkey, index, squished_prod, &sz, + NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS) + { + index++; + sz = GUID_SIZE; + unsquash_guid(squished_prod, szBuffer); + + if (MSIREG_OpenProductKey(szBuffer, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + &prodkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenProductKey(szBuffer, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + &prodkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenProductKey(szBuffer, NULL, + MSIINSTALLCONTEXT_MACHINE, + &prodkey, FALSE) == ERROR_SUCCESS) + { + RegCloseKey(prodkey); + rc = ERROR_SUCCESS; + goto done; + } + } + + rc = ERROR_INSTALL_FAILURE; + +done: + RegCloseKey(compkey); + unsquash_guid(squished_prod, szBuffer); + return rc; +} + +static LPWSTR msi_reg_get_value(HKEY hkey, LPCWSTR name, DWORD *type) +{ + DWORD dval; + LONG res; + WCHAR temp[20]; + + static const WCHAR format[] = {'%','d',0}; + + res = RegQueryValueExW(hkey, name, NULL, type, NULL, NULL); + if (res != ERROR_SUCCESS) + return NULL; + + if (*type == REG_SZ) + return msi_reg_get_val_str(hkey, name); + + if (!msi_reg_get_val_dword(hkey, name, &dval)) + return NULL; + + sprintfW(temp, format, dval); + return strdupW(temp); +} + +static UINT MSI_GetProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, + awstring *szValue, LPDWORD pcchValueBuf) +{ + MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_USERUNMANAGED; + UINT r = ERROR_UNKNOWN_PROPERTY; + HKEY prodkey, userdata, source; + LPWSTR val = NULL; + WCHAR squished_pc[GUID_SIZE]; + WCHAR packagecode[GUID_SIZE]; + BOOL badconfig = FALSE; + LONG res; + DWORD type = REG_NONE; + + static WCHAR empty[] = {0}; + static const WCHAR sourcelist[] = { + 'S','o','u','r','c','e','L','i','s','t',0}; + static const WCHAR display_name[] = { + 'D','i','s','p','l','a','y','N','a','m','e',0}; + static const WCHAR display_version[] = { + 'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; + static const WCHAR assignment[] = { + 'A','s','s','i','g','n','m','e','n','t',0}; + + TRACE("%s %s %p %p\n", debugstr_w(szProduct), + debugstr_w(szAttribute), szValue, pcchValueBuf); + + if ((szValue->str.w && !pcchValueBuf) || !szProduct || !szAttribute) + return ERROR_INVALID_PARAMETER; + + if (!squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if ((r = MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + &prodkey, FALSE)) != ERROR_SUCCESS && + (r = MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + &prodkey, FALSE)) != ERROR_SUCCESS && + (r = MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_MACHINE, + &prodkey, FALSE)) == ERROR_SUCCESS) + { + context = MSIINSTALLCONTEXT_MACHINE; + } + + MSIREG_OpenInstallProps(szProduct, context, NULL, &userdata, FALSE); + + if (!strcmpW( szAttribute, INSTALLPROPERTY_HELPLINKW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_HELPTELEPHONEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_INSTALLDATEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_INSTALLLOCATIONW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_INSTALLSOURCEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_LOCALPACKAGEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PUBLISHERW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_URLINFOABOUTW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_URLUPDATEINFOW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_VERSIONMINORW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_VERSIONMAJORW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_VERSIONSTRINGW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PRODUCTIDW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_REGCOMPANYW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_REGOWNERW )) + { + if (!prodkey) + { + r = ERROR_UNKNOWN_PRODUCT; + goto done; + } + + if (!userdata) + return ERROR_UNKNOWN_PROPERTY; + + if (!strcmpW( szAttribute, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW )) + szAttribute = display_name; + else if (!strcmpW( szAttribute, INSTALLPROPERTY_VERSIONSTRINGW )) + szAttribute = display_version; + + val = msi_reg_get_value(userdata, szAttribute, &type); + if (!val) + val = empty; + } + else if (!strcmpW( szAttribute, INSTALLPROPERTY_INSTANCETYPEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_TRANSFORMSW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_LANGUAGEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PRODUCTNAMEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PACKAGECODEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_VERSIONW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PRODUCTICONW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_PACKAGENAMEW ) || + !strcmpW( szAttribute, INSTALLPROPERTY_AUTHORIZED_LUA_APPW )) + { + if (!prodkey) + { + r = ERROR_UNKNOWN_PRODUCT; + goto done; + } + + if (!strcmpW( szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW )) + szAttribute = assignment; + + if (!strcmpW( szAttribute, INSTALLPROPERTY_PACKAGENAMEW )) + { + res = RegOpenKeyW(prodkey, sourcelist, &source); + if (res != ERROR_SUCCESS) + { + r = ERROR_UNKNOWN_PRODUCT; + goto done; + } + + val = msi_reg_get_value(source, szAttribute, &type); + if (!val) + val = empty; + + RegCloseKey(source); + } + else + { + val = msi_reg_get_value(prodkey, szAttribute, &type); + if (!val) + val = empty; + } + + if (val != empty && type != REG_DWORD && + !strcmpW( szAttribute, INSTALLPROPERTY_PACKAGECODEW )) + { + if (lstrlenW(val) != SQUISH_GUID_SIZE - 1) + badconfig = TRUE; + else + { + unsquash_guid(val, packagecode); + msi_free(val); + val = strdupW(packagecode); + } + } + } + + if (!val) + { + r = ERROR_UNKNOWN_PROPERTY; + goto done; + } + + if (pcchValueBuf) + { + /* If szBuffer (szValue->str) is NULL, there's no need to copy the value + * out. Also, *pcchValueBuf may be uninitialized in this case, so we + * can't rely on its value. + */ + if (szValue->str.a || szValue->str.w) + { + DWORD size = *pcchValueBuf; + if (strlenW(val) < size) + r = msi_strcpy_to_awstring(val, szValue, &size); + else + { + r = ERROR_MORE_DATA; + } + } + + if (!badconfig) + *pcchValueBuf = lstrlenW(val); + } + + if (badconfig) + r = ERROR_BAD_CONFIGURATION; + + if (val != empty) + msi_free(val); + +done: + RegCloseKey(prodkey); + RegCloseKey(userdata); + return r; +} + +UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, + LPSTR szBuffer, LPDWORD pcchValueBuf) +{ + LPWSTR szwProduct, szwAttribute = NULL; + UINT r = ERROR_OUTOFMEMORY; + awstring buffer; + + TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szAttribute), + szBuffer, pcchValueBuf); + + szwProduct = strdupAtoW( szProduct ); + if( szProduct && !szwProduct ) + goto end; + + szwAttribute = strdupAtoW( szAttribute ); + if( szAttribute && !szwAttribute ) + goto end; + + buffer.unicode = FALSE; + buffer.str.a = szBuffer; + + r = MSI_GetProductInfo( szwProduct, szwAttribute, + &buffer, pcchValueBuf ); + +end: + msi_free( szwProduct ); + msi_free( szwAttribute ); + + return r; +} + +UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, + LPWSTR szBuffer, LPDWORD pcchValueBuf) +{ + awstring buffer; + + TRACE("%s %s %p %p\n", debugstr_w(szProduct), debugstr_w(szAttribute), + szBuffer, pcchValueBuf); + + buffer.unicode = TRUE; + buffer.str.w = szBuffer; + + return MSI_GetProductInfo( szProduct, szAttribute, + &buffer, pcchValueBuf ); +} + +UINT WINAPI MsiGetProductInfoExA(LPCSTR szProductCode, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, LPCSTR szProperty, + LPSTR szValue, LPDWORD pcchValue) +{ + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR property = NULL; + LPWSTR value = NULL; + DWORD len = 0; + UINT r; + + TRACE("(%s, %s, %d, %s, %p, %p)\n", debugstr_a(szProductCode), + debugstr_a(szUserSid), dwContext, debugstr_a(szProperty), + szValue, pcchValue); + + if (szValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (szProductCode) product = strdupAtoW(szProductCode); + if (szUserSid) usersid = strdupAtoW(szUserSid); + if (szProperty) property = strdupAtoW(szProperty); + + r = MsiGetProductInfoExW(product, usersid, dwContext, property, + NULL, &len); + if (r != ERROR_SUCCESS) + goto done; + + value = msi_alloc(++len * sizeof(WCHAR)); + if (!value) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiGetProductInfoExW(product, usersid, dwContext, property, + value, &len); + if (r != ERROR_SUCCESS) + goto done; + + if (!pcchValue) + goto done; + + len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL); + if (*pcchValue >= len) + WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL); + else if (szValue) + { + r = ERROR_MORE_DATA; + if (*pcchValue > 0) + *szValue = '\0'; + } + + if (*pcchValue <= len || !szValue) + len = len * sizeof(WCHAR) - 1; + + *pcchValue = len - 1; + +done: + msi_free(product); + msi_free(usersid); + msi_free(property); + msi_free(value); + + return r; +} + +static UINT msi_copy_outval(LPWSTR val, LPWSTR out, LPDWORD size) +{ + UINT r = ERROR_SUCCESS; + + if (!val) + return ERROR_UNKNOWN_PROPERTY; + + if (out) + { + if (strlenW(val) >= *size) + { + r = ERROR_MORE_DATA; + if (*size > 0) + *out = '\0'; + } + else + lstrcpyW(out, val); + } + + if (size) + *size = lstrlenW(val); + + return r; +} + +UINT WINAPI MsiGetProductInfoExW(LPCWSTR szProductCode, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, LPCWSTR szProperty, + LPWSTR szValue, LPDWORD pcchValue) +{ + WCHAR squished_pc[GUID_SIZE]; + LPWSTR val = NULL; + LPCWSTR package = NULL; + HKEY props = NULL, prod; + HKEY classes = NULL, managed; + HKEY hkey = NULL; + DWORD type; + UINT r = ERROR_UNKNOWN_PRODUCT; + + static const WCHAR five[] = {'5',0}; + static const WCHAR displayname[] = { + 'D','i','s','p','l','a','y','N','a','m','e',0}; + static const WCHAR displayversion[] = { + 'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0}; + static const WCHAR managed_local_package[] = { + 'M','a','n','a','g','e','d','L','o','c','a','l', + 'P','a','c','k','a','g','e',0}; + + TRACE("(%s, %s, %d, %s, %p, %p)\n", debugstr_w(szProductCode), + debugstr_w(szUserSid), dwContext, debugstr_w(szProperty), + szValue, pcchValue); + + if (!szProductCode || !squash_guid(szProductCode, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (szValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (dwContext != MSIINSTALLCONTEXT_USERUNMANAGED && + dwContext != MSIINSTALLCONTEXT_USERMANAGED && + dwContext != MSIINSTALLCONTEXT_MACHINE) + return ERROR_INVALID_PARAMETER; + + if (!szProperty || !*szProperty) + return ERROR_INVALID_PARAMETER; + + if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) + return ERROR_INVALID_PARAMETER; + + /* FIXME: dwContext is provided, no need to search for it */ + MSIREG_OpenProductKey(szProductCode, NULL,MSIINSTALLCONTEXT_USERMANAGED, + &managed, FALSE); + MSIREG_OpenProductKey(szProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + &prod, FALSE); + + MSIREG_OpenInstallProps(szProductCode, dwContext, NULL, &props, FALSE); + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + { + package = INSTALLPROPERTY_LOCALPACKAGEW; + + if (!props && !prod) + goto done; + } + else if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) + { + package = managed_local_package; + + if (!props && !managed) + goto done; + } + else if (dwContext == MSIINSTALLCONTEXT_MACHINE) + { + package = INSTALLPROPERTY_LOCALPACKAGEW; + MSIREG_OpenProductKey(szProductCode, NULL, dwContext, &classes, FALSE); + + if (!props && !classes) + goto done; + } + + if (!strcmpW( szProperty, INSTALLPROPERTY_HELPLINKW ) || + !strcmpW( szProperty, INSTALLPROPERTY_HELPTELEPHONEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_INSTALLDATEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_INSTALLLOCATIONW ) || + !strcmpW( szProperty, INSTALLPROPERTY_INSTALLSOURCEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PUBLISHERW ) || + !strcmpW( szProperty, INSTALLPROPERTY_URLINFOABOUTW ) || + !strcmpW( szProperty, INSTALLPROPERTY_URLUPDATEINFOW ) || + !strcmpW( szProperty, INSTALLPROPERTY_VERSIONMINORW ) || + !strcmpW( szProperty, INSTALLPROPERTY_VERSIONMAJORW ) || + !strcmpW( szProperty, INSTALLPROPERTY_VERSIONSTRINGW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PRODUCTIDW ) || + !strcmpW( szProperty, INSTALLPROPERTY_REGCOMPANYW ) || + !strcmpW( szProperty, INSTALLPROPERTY_REGOWNERW ) || + !strcmpW( szProperty, INSTALLPROPERTY_INSTANCETYPEW )) + { + val = msi_reg_get_value(props, package, &type); + if (!val) + { + if (prod || classes) + r = ERROR_UNKNOWN_PROPERTY; + + goto done; + } + + msi_free(val); + + if (!strcmpW( szProperty, INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW )) + szProperty = displayname; + else if (!strcmpW( szProperty, INSTALLPROPERTY_VERSIONSTRINGW )) + szProperty = displayversion; + + val = msi_reg_get_value(props, szProperty, &type); + if (!val) + val = strdupW(szEmpty); + + r = msi_copy_outval(val, szValue, pcchValue); + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_TRANSFORMSW ) || + !strcmpW( szProperty, INSTALLPROPERTY_LANGUAGEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PRODUCTNAMEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PACKAGECODEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_VERSIONW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PRODUCTICONW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_AUTHORIZED_LUA_APPW )) + { + if (!prod && !classes) + goto done; + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + hkey = prod; + else if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) + hkey = managed; + else if (dwContext == MSIINSTALLCONTEXT_MACHINE) + hkey = classes; + + val = msi_reg_get_value(hkey, szProperty, &type); + if (!val) + val = strdupW(szEmpty); + + r = msi_copy_outval(val, szValue, pcchValue); + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_PRODUCTSTATEW )) + { + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + { + if (props) + { + val = msi_reg_get_value(props, package, &type); + if (!val) + goto done; + + msi_free(val); + val = strdupW(five); + } + else + val = strdupW(szOne); + + r = msi_copy_outval(val, szValue, pcchValue); + goto done; + } + else if (props && (val = msi_reg_get_value(props, package, &type))) + { + msi_free(val); + val = strdupW(five); + r = msi_copy_outval(val, szValue, pcchValue); + goto done; + } + + if (prod || managed) + val = strdupW(szOne); + else + goto done; + + r = msi_copy_outval(val, szValue, pcchValue); + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_ASSIGNMENTTYPEW )) + { + if (!prod && !classes) + goto done; + + /* FIXME */ + val = strdupW(szEmpty); + r = msi_copy_outval(val, szValue, pcchValue); + } + else + r = ERROR_UNKNOWN_PROPERTY; + +done: + RegCloseKey(props); + RegCloseKey(prod); + RegCloseKey(managed); + RegCloseKey(classes); + msi_free(val); + + return r; +} + +UINT WINAPI MsiGetPatchInfoExA(LPCSTR szPatchCode, LPCSTR szProductCode, + LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, + LPCSTR szProperty, LPSTR lpValue, DWORD *pcchValue) +{ + LPWSTR patch = NULL, product = NULL, usersid = NULL; + LPWSTR property = NULL, val = NULL; + DWORD len; + UINT r; + + TRACE("(%s, %s, %s, %d, %s, %p, %p)\n", debugstr_a(szPatchCode), + debugstr_a(szProductCode), debugstr_a(szUserSid), dwContext, + debugstr_a(szProperty), lpValue, pcchValue); + + if (lpValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (szPatchCode) patch = strdupAtoW(szPatchCode); + if (szProductCode) product = strdupAtoW(szProductCode); + if (szUserSid) usersid = strdupAtoW(szUserSid); + if (szProperty) property = strdupAtoW(szProperty); + + len = 0; + r = MsiGetPatchInfoExW(patch, product, usersid, dwContext, property, + NULL, &len); + if (r != ERROR_SUCCESS) + goto done; + + val = msi_alloc(++len * sizeof(WCHAR)); + if (!val) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiGetPatchInfoExW(patch, product, usersid, dwContext, property, + val, &len); + if (r != ERROR_SUCCESS || !pcchValue) + goto done; + + if (lpValue) + WideCharToMultiByte(CP_ACP, 0, val, -1, lpValue, + *pcchValue - 1, NULL, NULL); + + len = lstrlenW(val); + if ((*val && *pcchValue < len + 1) || !lpValue) + { + if (lpValue) + { + r = ERROR_MORE_DATA; + lpValue[*pcchValue - 1] = '\0'; + } + + *pcchValue = len * sizeof(WCHAR); + } + else + *pcchValue = len; + +done: + msi_free(val); + msi_free(patch); + msi_free(product); + msi_free(usersid); + msi_free(property); + + return r; +} + +UINT WINAPI MsiGetPatchInfoExW(LPCWSTR szPatchCode, LPCWSTR szProductCode, + LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, + LPCWSTR szProperty, LPWSTR lpValue, DWORD *pcchValue) +{ + WCHAR squished_pc[GUID_SIZE]; + WCHAR squished_patch[GUID_SIZE]; + HKEY udprod = 0, prod = 0, props = 0; + HKEY patch = 0, patches = 0; + HKEY udpatch = 0, datakey = 0; + HKEY prodpatches = 0; + LPWSTR val = NULL; + UINT r = ERROR_UNKNOWN_PRODUCT; + DWORD len; + LONG res; + + static const WCHAR szManagedPackage[] = {'M','a','n','a','g','e','d', + 'L','o','c','a','l','P','a','c','k','a','g','e',0}; + + TRACE("(%s, %s, %s, %d, %s, %p, %p)\n", debugstr_w(szPatchCode), + debugstr_w(szProductCode), debugstr_w(szUserSid), dwContext, + debugstr_w(szProperty), lpValue, pcchValue); + + if (!szProductCode || !squash_guid(szProductCode, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!szPatchCode || !squash_guid(szPatchCode, squished_patch)) + return ERROR_INVALID_PARAMETER; + + if (!szProperty) + return ERROR_INVALID_PARAMETER; + + if (lpValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (dwContext != MSIINSTALLCONTEXT_USERMANAGED && + dwContext != MSIINSTALLCONTEXT_USERUNMANAGED && + dwContext != MSIINSTALLCONTEXT_MACHINE) + return ERROR_INVALID_PARAMETER; + + if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) + return ERROR_INVALID_PARAMETER; + + if (szUserSid && !strcmpW( szUserSid, szLocalSid )) + return ERROR_INVALID_PARAMETER; + + if (MSIREG_OpenUserDataProductKey(szProductCode, dwContext, NULL, + &udprod, FALSE) != ERROR_SUCCESS) + goto done; + + if (MSIREG_OpenInstallProps(szProductCode, dwContext, NULL, + &props, FALSE) != ERROR_SUCCESS) + goto done; + + r = ERROR_UNKNOWN_PATCH; + + res = RegOpenKeyExW(udprod, szPatches, 0, KEY_WOW64_64KEY|KEY_READ, &patches); + if (res != ERROR_SUCCESS) + goto done; + + res = RegOpenKeyExW(patches, squished_patch, 0, KEY_WOW64_64KEY|KEY_READ, &patch); + if (res != ERROR_SUCCESS) + goto done; + + if (!strcmpW( szProperty, INSTALLPROPERTY_TRANSFORMSW )) + { + if (MSIREG_OpenProductKey(szProductCode, NULL, dwContext, + &prod, FALSE) != ERROR_SUCCESS) + goto done; + + res = RegOpenKeyExW(prod, szPatches, 0, KEY_WOW64_64KEY|KEY_ALL_ACCESS, &prodpatches); + if (res != ERROR_SUCCESS) + goto done; + + datakey = prodpatches; + szProperty = squished_patch; + } + else + { + if (MSIREG_OpenUserDataPatchKey(szPatchCode, dwContext, + &udpatch, FALSE) != ERROR_SUCCESS) + goto done; + + if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) + { + if (dwContext == MSIINSTALLCONTEXT_USERMANAGED) + szProperty = szManagedPackage; + datakey = udpatch; + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_INSTALLDATEW )) + { + datakey = patch; + szProperty = szInstalled; + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) + { + datakey = udpatch; + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_UNINSTALLABLEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_PATCHSTATEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_DISPLAYNAMEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_MOREINFOURLW )) + { + datakey = patch; + } + else + { + r = ERROR_UNKNOWN_PROPERTY; + goto done; + } + } + + val = msi_reg_get_val_str(datakey, szProperty); + if (!val) + val = strdupW(szEmpty); + + r = ERROR_SUCCESS; + + if (!pcchValue) + goto done; + + if (lpValue) + lstrcpynW(lpValue, val, *pcchValue); + + len = lstrlenW(val); + if ((*val && *pcchValue < len + 1) || !lpValue) + { + if (lpValue) + r = ERROR_MORE_DATA; + + *pcchValue = len * sizeof(WCHAR); + } + + *pcchValue = len; + +done: + msi_free(val); + RegCloseKey(prodpatches); + RegCloseKey(prod); + RegCloseKey(patch); + RegCloseKey(patches); + RegCloseKey(udpatch); + RegCloseKey(props); + RegCloseKey(udprod); + + return r; +} + +UINT WINAPI MsiGetPatchInfoA( LPCSTR patch, LPCSTR attr, LPSTR buffer, LPDWORD buflen ) +{ + UINT r = ERROR_OUTOFMEMORY; + DWORD size; + LPWSTR patchW = NULL, attrW = NULL, bufferW = NULL; + + TRACE("%s %s %p %p\n", debugstr_a(patch), debugstr_a(attr), buffer, buflen); + + if (!patch || !attr) + return ERROR_INVALID_PARAMETER; + + if (!(patchW = strdupAtoW( patch ))) + goto done; + + if (!(attrW = strdupAtoW( attr ))) + goto done; + + size = 0; + r = MsiGetPatchInfoW( patchW, attrW, NULL, &size ); + if (r != ERROR_SUCCESS) + goto done; + + size++; + if (!(bufferW = msi_alloc( size * sizeof(WCHAR) ))) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiGetPatchInfoW( patchW, attrW, bufferW, &size ); + if (r == ERROR_SUCCESS) + { + int len = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL ); + if (len > *buflen) + r = ERROR_MORE_DATA; + else if (buffer) + WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, *buflen, NULL, NULL ); + + *buflen = len - 1; + } + +done: + msi_free( patchW ); + msi_free( attrW ); + msi_free( bufferW ); + return r; +} + +UINT WINAPI MsiGetPatchInfoW( LPCWSTR patch, LPCWSTR attr, LPWSTR buffer, LPDWORD buflen ) +{ + UINT r; + WCHAR product[GUID_SIZE]; + DWORD index; + + TRACE("%s %s %p %p\n", debugstr_w(patch), debugstr_w(attr), buffer, buflen); + + if (!patch || !attr) + return ERROR_INVALID_PARAMETER; + + if (strcmpW( INSTALLPROPERTY_LOCALPACKAGEW, attr )) + return ERROR_UNKNOWN_PROPERTY; + + index = 0; + while (1) + { + r = MsiEnumProductsW( index, product ); + if (r != ERROR_SUCCESS) + break; + + r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_USERMANAGED, attr, buffer, buflen ); + if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) + return r; + + r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, attr, buffer, buflen ); + if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) + return r; + + r = MsiGetPatchInfoExW( patch, product, NULL, MSIINSTALLCONTEXT_MACHINE, attr, buffer, buflen ); + if (r == ERROR_SUCCESS || r == ERROR_MORE_DATA) + return r; + + index++; + } + + return ERROR_UNKNOWN_PRODUCT; +} + +UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, DWORD attributes) +{ + LPWSTR szwLogFile = NULL; + UINT r; + + TRACE("%08x %s %08x\n", dwLogMode, debugstr_a(szLogFile), attributes); + + if( szLogFile ) + { + szwLogFile = strdupAtoW( szLogFile ); + if( !szwLogFile ) + return ERROR_OUTOFMEMORY; + } + r = MsiEnableLogW( dwLogMode, szwLogFile, attributes ); + msi_free( szwLogFile ); + return r; +} + +UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes) +{ + TRACE("%08x %s %08x\n", dwLogMode, debugstr_w(szLogFile), attributes); + + msi_free(gszLogFile); + gszLogFile = NULL; + if (szLogFile) + { + HANDLE file; + + if (!(attributes & INSTALLLOGATTRIBUTES_APPEND)) + DeleteFileW(szLogFile); + file = CreateFileW(szLogFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) + { + gszLogFile = strdupW(szLogFile); + CloseHandle(file); + } + else + ERR("Unable to enable log %s (%u)\n", debugstr_w(szLogFile), GetLastError()); + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiEnumComponentCostsA( MSIHANDLE handle, LPCSTR component, DWORD index, + INSTALLSTATE state, LPSTR drive, DWORD *buflen, + int *cost, int *temp ) +{ + UINT r; + DWORD len; + WCHAR *driveW, *componentW = NULL; + + TRACE("%d, %s, %u, %d, %p, %p, %p %p\n", handle, debugstr_a(component), index, + state, drive, buflen, cost, temp); + + if (!drive || !buflen) return ERROR_INVALID_PARAMETER; + if (component && !(componentW = strdupAtoW( component ))) return ERROR_OUTOFMEMORY; + + len = *buflen; + if (!(driveW = msi_alloc( len * sizeof(WCHAR) ))) + { + msi_free( componentW ); + return ERROR_OUTOFMEMORY; + } + r = MsiEnumComponentCostsW( handle, componentW, index, state, driveW, buflen, cost, temp ); + if (!r) + { + WideCharToMultiByte( CP_ACP, 0, driveW, -1, drive, len, NULL, NULL ); + } + msi_free( componentW ); + msi_free( driveW ); + return r; +} + +static UINT set_drive( WCHAR *buffer, WCHAR letter ) +{ + buffer[0] = letter; + buffer[1] = ':'; + buffer[2] = 0; + return 2; +} + +UINT WINAPI MsiEnumComponentCostsW( MSIHANDLE handle, LPCWSTR component, DWORD index, + INSTALLSTATE state, LPWSTR drive, DWORD *buflen, + int *cost, int *temp ) +{ + UINT r = ERROR_NO_MORE_ITEMS; + MSICOMPONENT *comp = NULL; + MSIPACKAGE *package; + MSIFILE *file; + STATSTG stat = {0}; + WCHAR path[MAX_PATH]; + + TRACE("%d, %s, %u, %d, %p, %p, %p %p\n", handle, debugstr_w(component), index, + state, drive, buflen, cost, temp); + + if (!drive || !buflen || !cost || !temp) return ERROR_INVALID_PARAMETER; + if (!(package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE ))) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + BSTR bname = NULL; + + if (!(remote_package = (IWineMsiRemotePackage *)msi_get_remote( handle ))) + return ERROR_INVALID_HANDLE; + + if (component && !(bname = SysAllocString( component ))) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + hr = IWineMsiRemotePackage_EnumComponentCosts( remote_package, bname, index, state, drive, buflen, cost, temp ); + IWineMsiRemotePackage_Release( remote_package ); + SysFreeString( bname ); + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) return HRESULT_CODE(hr); + return ERROR_FUNCTION_FAILED; + } + return ERROR_SUCCESS; + } + + if (!msi_get_property_int( package->db, szCostingComplete, 0 )) + { + msiobj_release( &package->hdr ); + return ERROR_FUNCTION_NOT_CALLED; + } + if (component && component[0] && !(comp = msi_get_loaded_component( package, component ))) + { + msiobj_release( &package->hdr ); + return ERROR_UNKNOWN_COMPONENT; + } + if (*buflen < 3) + { + *buflen = 2; + msiobj_release( &package->hdr ); + return ERROR_MORE_DATA; + } + if (index) + { + msiobj_release( &package->hdr ); + return ERROR_NO_MORE_ITEMS; + } + + drive[0] = 0; + *cost = *temp = 0; + GetWindowsDirectoryW( path, MAX_PATH ); + if (component && component[0]) + { + if (comp->assembly && !comp->assembly->application) *temp = comp->Cost; + if (!comp->Enabled || !comp->KeyPath) + { + *cost = 0; + *buflen = set_drive( drive, path[0] ); + r = ERROR_SUCCESS; + } + else if ((file = msi_get_loaded_file( package, comp->KeyPath ))) + { + *cost = max( 8, comp->Cost / 512 ); + *buflen = set_drive( drive, file->TargetPath[0] ); + r = ERROR_SUCCESS; + } + } + else if (IStorage_Stat( package->db->storage, &stat, STATFLAG_NONAME ) == S_OK) + { + *temp = max( 8, stat.cbSize.QuadPart / 512 ); + *buflen = set_drive( drive, path[0] ); + r = ERROR_SUCCESS; + } + msiobj_release( &package->hdr ); + return r; +} + +UINT WINAPI MsiQueryComponentStateA(LPCSTR szProductCode, + LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, + LPCSTR szComponent, INSTALLSTATE *pdwState) +{ + LPWSTR prodcode = NULL, usersid = NULL, comp = NULL; + UINT r; + + TRACE("(%s, %s, %d, %s, %p)\n", debugstr_a(szProductCode), + debugstr_a(szUserSid), dwContext, debugstr_a(szComponent), pdwState); + + if (szProductCode && !(prodcode = strdupAtoW(szProductCode))) + return ERROR_OUTOFMEMORY; + + if (szUserSid && !(usersid = strdupAtoW(szUserSid))) + return ERROR_OUTOFMEMORY; + + if (szComponent && !(comp = strdupAtoW(szComponent))) + return ERROR_OUTOFMEMORY; + + r = MsiQueryComponentStateW(prodcode, usersid, dwContext, comp, pdwState); + + msi_free(prodcode); + msi_free(usersid); + msi_free(comp); + + return r; +} + +static BOOL msi_comp_find_prod_key(LPCWSTR prodcode, MSIINSTALLCONTEXT context) +{ + UINT r; + HKEY hkey = NULL; + + r = MSIREG_OpenProductKey(prodcode, NULL, context, &hkey, FALSE); + RegCloseKey(hkey); + return (r == ERROR_SUCCESS); +} + +static BOOL msi_comp_find_package(LPCWSTR prodcode, MSIINSTALLCONTEXT context) +{ + LPCWSTR package; + HKEY hkey; + DWORD sz; + LONG res; + UINT r; + + static const WCHAR local_package[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; + static const WCHAR managed_local_package[] = { + 'M','a','n','a','g','e','d','L','o','c','a','l','P','a','c','k','a','g','e',0 + }; + + r = MSIREG_OpenInstallProps(prodcode, context, NULL, &hkey, FALSE); + if (r != ERROR_SUCCESS) + return FALSE; + + if (context == MSIINSTALLCONTEXT_USERMANAGED) + package = managed_local_package; + else + package = local_package; + + sz = 0; + res = RegQueryValueExW(hkey, package, NULL, NULL, NULL, &sz); + RegCloseKey(hkey); + + return (res == ERROR_SUCCESS); +} + +static UINT msi_comp_find_prodcode(LPWSTR squished_pc, + MSIINSTALLCONTEXT context, + LPCWSTR comp, LPWSTR val, DWORD *sz) +{ + HKEY hkey; + LONG res; + UINT r; + + if (context == MSIINSTALLCONTEXT_MACHINE) + r = MSIREG_OpenUserDataComponentKey(comp, szLocalSid, &hkey, FALSE); + else + r = MSIREG_OpenUserDataComponentKey(comp, NULL, &hkey, FALSE); + + if (r != ERROR_SUCCESS) + return r; + + res = RegQueryValueExW(hkey, squished_pc, NULL, NULL, (BYTE *)val, sz); + if (res != ERROR_SUCCESS) + return res; + + RegCloseKey(hkey); + return res; +} + +UINT WINAPI MsiQueryComponentStateW(LPCWSTR szProductCode, + LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, + LPCWSTR szComponent, INSTALLSTATE *pdwState) +{ + WCHAR squished_pc[GUID_SIZE]; + BOOL found; + DWORD sz; + + TRACE("(%s, %s, %d, %s, %p)\n", debugstr_w(szProductCode), + debugstr_w(szUserSid), dwContext, debugstr_w(szComponent), pdwState); + + if (!pdwState || !szComponent) + return ERROR_INVALID_PARAMETER; + + if (!szProductCode || !*szProductCode || lstrlenW(szProductCode) != GUID_SIZE - 1) + return ERROR_INVALID_PARAMETER; + + if (!squash_guid(szProductCode, squished_pc)) + return ERROR_INVALID_PARAMETER; + + found = msi_comp_find_prod_key(szProductCode, dwContext); + + if (!msi_comp_find_package(szProductCode, dwContext)) + { + if (found) + { + *pdwState = INSTALLSTATE_UNKNOWN; + return ERROR_UNKNOWN_COMPONENT; + } + + return ERROR_UNKNOWN_PRODUCT; + } + + *pdwState = INSTALLSTATE_UNKNOWN; + + sz = 0; + if (msi_comp_find_prodcode(squished_pc, dwContext, szComponent, NULL, &sz)) + return ERROR_UNKNOWN_COMPONENT; + + if (sz == 0) + *pdwState = INSTALLSTATE_NOTUSED; + else + { + WCHAR *val; + UINT r; + + if (!(val = msi_alloc( sz ))) return ERROR_OUTOFMEMORY; + if ((r = msi_comp_find_prodcode(squished_pc, dwContext, szComponent, val, &sz))) + return r; + + if (lstrlenW(val) > 2 && + val[0] >= '0' && val[0] <= '9' && val[1] >= '0' && val[1] <= '9' && val[2] != ':') + { + *pdwState = INSTALLSTATE_SOURCE; + } + else + *pdwState = INSTALLSTATE_LOCAL; + msi_free( val ); + } + + TRACE("-> %d\n", *pdwState); + return ERROR_SUCCESS; +} + +INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct) +{ + LPWSTR szwProduct = NULL; + INSTALLSTATE r; + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + r = MsiQueryProductStateW( szwProduct ); + msi_free( szwProduct ); + return r; +} + +INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct) +{ + MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_USERUNMANAGED; + INSTALLSTATE state = INSTALLSTATE_ADVERTISED; + HKEY prodkey = 0, userdata = 0; + DWORD val; + UINT r; + + TRACE("%s\n", debugstr_w(szProduct)); + + if (!szProduct || !*szProduct) + return INSTALLSTATE_INVALIDARG; + + if (lstrlenW(szProduct) != GUID_SIZE - 1) + return INSTALLSTATE_INVALIDARG; + + if (szProduct[0] != '{' || szProduct[37] != '}') + return INSTALLSTATE_UNKNOWN; + + SetLastError( ERROR_SUCCESS ); + + if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + &prodkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + &prodkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, + &prodkey, FALSE) == ERROR_SUCCESS) + { + context = MSIINSTALLCONTEXT_MACHINE; + } + + r = MSIREG_OpenInstallProps(szProduct, context, NULL, &userdata, FALSE); + if (r != ERROR_SUCCESS) + goto done; + + if (!msi_reg_get_val_dword(userdata, szWindowsInstaller, &val)) + goto done; + + if (val) + state = INSTALLSTATE_DEFAULT; + else + state = INSTALLSTATE_UNKNOWN; + +done: + if (!prodkey) + { + state = INSTALLSTATE_UNKNOWN; + + if (userdata) + state = INSTALLSTATE_ABSENT; + } + + RegCloseKey(prodkey); + RegCloseKey(userdata); + TRACE("-> %d\n", state); + return state; +} + +INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd) +{ + INSTALLUILEVEL old = gUILevel; + HWND oldwnd = gUIhwnd; + + TRACE("%08x %p\n", dwUILevel, phWnd); + + gUILevel = dwUILevel; + if (phWnd) + { + gUIhwnd = *phWnd; + *phWnd = oldwnd; + } + return old; +} + +INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler, + DWORD dwMessageFilter, LPVOID pvContext) +{ + INSTALLUI_HANDLERA prev = gUIHandlerA; + + TRACE("%p %08x %p\n", puiHandler, dwMessageFilter, pvContext); + + gUIHandlerA = puiHandler; + gUIHandlerW = NULL; + gUIFilter = dwMessageFilter; + gUIContext = pvContext; + + return prev; +} + +INSTALLUI_HANDLERW WINAPI MsiSetExternalUIW(INSTALLUI_HANDLERW puiHandler, + DWORD dwMessageFilter, LPVOID pvContext) +{ + INSTALLUI_HANDLERW prev = gUIHandlerW; + + TRACE("%p %08x %p\n", puiHandler, dwMessageFilter, pvContext); + + gUIHandlerA = NULL; + gUIHandlerW = puiHandler; + gUIFilter = dwMessageFilter; + gUIContext = pvContext; + + return prev; +} + +/****************************************************************** + * MsiLoadStringW [MSI.@] + * + * Loads a string from MSI's string resources. + * + * PARAMS + * + * handle [I] only -1 is handled currently + * id [I] id of the string to be loaded + * lpBuffer [O] buffer for the string to be written to + * nBufferMax [I] maximum size of the buffer in characters + * lang [I] the preferred language for the string + * + * RETURNS + * + * If successful, this function returns the language id of the string loaded + * If the function fails, the function returns zero. + * + * NOTES + * + * The type of the first parameter is unknown. LoadString's prototype + * suggests that it might be a module handle. I have made it an MSI handle + * for starters, as -1 is an invalid MSI handle, but not an invalid module + * handle. Maybe strings can be stored in an MSI database somehow. + */ +LANGID WINAPI MsiLoadStringW( MSIHANDLE handle, UINT id, LPWSTR lpBuffer, + int nBufferMax, LANGID lang ) +{ + HRSRC hres; + HGLOBAL hResData; + LPWSTR p; + DWORD i, len; + + TRACE("%d %u %p %d %d\n", handle, id, lpBuffer, nBufferMax, lang); + + if( handle != -1 ) + FIXME("don't know how to deal with handle = %08x\n", handle); + + if( !lang ) + lang = GetUserDefaultLangID(); + + hres = FindResourceExW( msi_hInstance, (LPCWSTR) RT_STRING, + (LPWSTR)1, lang ); + if( !hres ) + return 0; + hResData = LoadResource( msi_hInstance, hres ); + if( !hResData ) + return 0; + p = LockResource( hResData ); + if( !p ) + return 0; + + for (i = 0; i < (id & 0xf); i++) p += *p + 1; + len = *p; + + if( nBufferMax <= len ) + return 0; + + memcpy( lpBuffer, p+1, len * sizeof(WCHAR)); + lpBuffer[ len ] = 0; + + TRACE("found -> %s\n", debugstr_w(lpBuffer)); + return lang; +} + +LANGID WINAPI MsiLoadStringA( MSIHANDLE handle, UINT id, LPSTR lpBuffer, + int nBufferMax, LANGID lang ) +{ + LPWSTR bufW; + LANGID r; + INT len; + + bufW = msi_alloc(nBufferMax*sizeof(WCHAR)); + r = MsiLoadStringW(handle, id, bufW, nBufferMax, lang); + if( r ) + { + len = WideCharToMultiByte(CP_ACP, 0, bufW, -1, NULL, 0, NULL, NULL ); + if( len <= nBufferMax ) + WideCharToMultiByte( CP_ACP, 0, bufW, -1, + lpBuffer, nBufferMax, NULL, NULL ); + else + r = 0; + } + msi_free(bufW); + return r; +} + +INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf, + LPDWORD pcchBuf) +{ + char szProduct[GUID_SIZE]; + + TRACE("%s %p %p\n", debugstr_a(szComponent), lpPathBuf, pcchBuf); + + if (!szComponent || !pcchBuf) + return INSTALLSTATE_INVALIDARG; + + if (MsiGetProductCodeA( szComponent, szProduct ) != ERROR_SUCCESS) + return INSTALLSTATE_UNKNOWN; + + return MsiGetComponentPathA( szProduct, szComponent, lpPathBuf, pcchBuf ); +} + +INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPWSTR lpPathBuf, + LPDWORD pcchBuf) +{ + WCHAR szProduct[GUID_SIZE]; + + TRACE("%s %p %p\n", debugstr_w(szComponent), lpPathBuf, pcchBuf); + + if (!szComponent || !pcchBuf) + return INSTALLSTATE_INVALIDARG; + + if (MsiGetProductCodeW( szComponent, szProduct ) != ERROR_SUCCESS) + return INSTALLSTATE_UNKNOWN; + + return MsiGetComponentPathW( szProduct, szComponent, lpPathBuf, pcchBuf ); +} + +UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, + WORD wLanguageId, DWORD f) +{ + FIXME("%p %s %s %u %08x %08x\n", hWnd, debugstr_a(lpText), debugstr_a(lpCaption), + uType, wLanguageId, f); + return MessageBoxExA(hWnd,lpText,lpCaption,uType,wLanguageId); +} + +UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, + WORD wLanguageId, DWORD f) +{ + FIXME("%p %s %s %u %08x %08x\n", hWnd, debugstr_w(lpText), debugstr_w(lpCaption), + uType, wLanguageId, f); + return MessageBoxExW(hWnd,lpText,lpCaption,uType,wLanguageId); +} + +UINT WINAPI MsiMessageBoxExA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, + DWORD unknown, WORD wLanguageId, DWORD f) +{ + FIXME("(%p, %s, %s, %u, 0x%08x, 0x%08x, 0x%08x): semi-stub\n", hWnd, debugstr_a(lpText), + debugstr_a(lpCaption), uType, unknown, wLanguageId, f); + return MessageBoxExA(hWnd, lpText, lpCaption, uType, wLanguageId); +} + +UINT WINAPI MsiMessageBoxExW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, + DWORD unknown, WORD wLanguageId, DWORD f) +{ + FIXME("(%p, %s, %s, %u, 0x%08x, 0x%08x, 0x%08x): semi-stub\n", hWnd, debugstr_w(lpText), + debugstr_w(lpCaption), uType, unknown, wLanguageId, f); + return MessageBoxExW(hWnd, lpText, lpCaption, uType, wLanguageId); +} + +UINT WINAPI MsiProvideAssemblyA( LPCSTR szAssemblyName, LPCSTR szAppContext, + DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf, + LPDWORD pcchPathBuf ) +{ + FIXME("%s %s %08x %08x %p %p\n", debugstr_a(szAssemblyName), + debugstr_a(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, + pcchPathBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideAssemblyW( LPCWSTR szAssemblyName, LPCWSTR szAppContext, + DWORD dwInstallMode, DWORD dwAssemblyInfo, LPWSTR lpPathBuf, + LPDWORD pcchPathBuf ) +{ + FIXME("%s %s %08x %08x %p %p\n", debugstr_w(szAssemblyName), + debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, + pcchPathBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, + LPSTR szPath, LPDWORD pcchPath, LPDWORD pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor, + LPWSTR szPath, LPDWORD pcchPath, LPDWORD pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +HRESULT WINAPI MsiGetFileSignatureInformationA( LPCSTR path, DWORD flags, PCCERT_CONTEXT *cert, + LPBYTE hash, LPDWORD hashlen ) +{ + UINT r; + WCHAR *pathW = NULL; + + TRACE("%s %08x %p %p %p\n", debugstr_a(path), flags, cert, hash, hashlen); + + if (path && !(pathW = strdupAtoW( path ))) return ERROR_OUTOFMEMORY; + r = MsiGetFileSignatureInformationW( pathW, flags, cert, hash, hashlen ); + msi_free( pathW ); + return r; +} + +HRESULT WINAPI MsiGetFileSignatureInformationW( LPCWSTR path, DWORD flags, PCCERT_CONTEXT *cert, + LPBYTE hash, LPDWORD hashlen ) +{ + static GUID generic_verify_v2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; + HRESULT hr; + WINTRUST_DATA data; + WINTRUST_FILE_INFO info; + CRYPT_PROVIDER_SGNR *signer; + CRYPT_PROVIDER_CERT *provider; + + TRACE("%s %08x %p %p %p\n", debugstr_w(path), flags, cert, hash, hashlen); + + if (!path || !cert) return E_INVALIDARG; + + info.cbStruct = sizeof(info); + info.pcwszFilePath = path; + info.hFile = NULL; + info.pgKnownSubject = NULL; + + data.cbStruct = sizeof(data); + data.pPolicyCallbackData = NULL; + data.pSIPClientData = NULL; + data.dwUIChoice = WTD_UI_NONE; + data.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; + data.dwUnionChoice = WTD_CHOICE_FILE; + data.u.pFile = &info; + data.dwStateAction = WTD_STATEACTION_VERIFY; + data.hWVTStateData = NULL; + data.pwszURLReference = NULL; + data.dwProvFlags = 0; + data.dwUIContext = WTD_UICONTEXT_INSTALL; + hr = WinVerifyTrustEx( INVALID_HANDLE_VALUE, &generic_verify_v2, &data ); + *cert = NULL; + if (FAILED(hr)) goto done; + + if (!(signer = WTHelperGetProvSignerFromChain( data.hWVTStateData, 0, FALSE, 0 ))) + { + hr = TRUST_E_NOSIGNATURE; + goto done; + } + if (hash) + { + DWORD len = signer->psSigner->EncryptedHash.cbData; + if (*hashlen < len) + { + *hashlen = len; + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + goto done; + } + memcpy( hash, signer->psSigner->EncryptedHash.pbData, len ); + *hashlen = len; + } + if (!(provider = WTHelperGetProvCertFromChain( signer, 0 ))) + { + hr = TRUST_E_PROVIDER_UNKNOWN; + goto done; + } + *cert = CertDuplicateCertificateContext( provider->pCert ); + +done: + data.dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrustEx( INVALID_HANDLE_VALUE, &generic_verify_v2, &data ); + return hr; +} + +/****************************************************************** + * MsiGetProductPropertyA [MSI.@] + */ +UINT WINAPI MsiGetProductPropertyA(MSIHANDLE hProduct, LPCSTR szProperty, + LPSTR szValue, LPDWORD pccbValue) +{ + LPWSTR prop = NULL, val = NULL; + DWORD len; + UINT r; + + TRACE("(%d, %s, %p, %p)\n", hProduct, debugstr_a(szProperty), + szValue, pccbValue); + + if (szValue && !pccbValue) + return ERROR_INVALID_PARAMETER; + + if (szProperty) prop = strdupAtoW(szProperty); + + len = 0; + r = MsiGetProductPropertyW(hProduct, prop, NULL, &len); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + goto done; + + if (r == ERROR_SUCCESS) + { + if (szValue) *szValue = '\0'; + if (pccbValue) *pccbValue = 0; + goto done; + } + + val = msi_alloc(++len * sizeof(WCHAR)); + if (!val) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiGetProductPropertyW(hProduct, prop, val, &len); + if (r != ERROR_SUCCESS) + goto done; + + len = WideCharToMultiByte(CP_ACP, 0, val, -1, NULL, 0, NULL, NULL); + + if (szValue) + WideCharToMultiByte(CP_ACP, 0, val, -1, szValue, + *pccbValue, NULL, NULL); + + if (pccbValue) + { + if (len > *pccbValue) + r = ERROR_MORE_DATA; + + *pccbValue = len - 1; + } + +done: + msi_free(prop); + msi_free(val); + + return r; +} + +/****************************************************************** + * MsiGetProductPropertyW [MSI.@] + */ +UINT WINAPI MsiGetProductPropertyW(MSIHANDLE hProduct, LPCWSTR szProperty, + LPWSTR szValue, LPDWORD pccbValue) +{ + MSIPACKAGE *package; + MSIQUERY *view = NULL; + MSIRECORD *rec = NULL; + LPCWSTR val; + UINT r; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','r','o','p','e','r','t','y','`',' ','W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`','=','\'','%','s','\'',0}; + + TRACE("(%d, %s, %p, %p)\n", hProduct, debugstr_w(szProperty), + szValue, pccbValue); + + if (!szProperty) + return ERROR_INVALID_PARAMETER; + + if (szValue && !pccbValue) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(hProduct, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + + r = MSI_OpenQuery(package->db, &view, query, szProperty); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewExecute(view, 0); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(view, &rec); + if (r != ERROR_SUCCESS) + goto done; + + val = MSI_RecordGetString(rec, 2); + if (!val) + goto done; + + if (lstrlenW(val) >= *pccbValue) + { + lstrcpynW(szValue, val, *pccbValue); + *pccbValue = lstrlenW(val); + r = ERROR_MORE_DATA; + } + else + { + lstrcpyW(szValue, val); + *pccbValue = lstrlenW(val); + r = ERROR_SUCCESS; + } + +done: + if (view) + { + MSI_ViewClose(view); + msiobj_release(&view->hdr); + if (rec) msiobj_release(&rec->hdr); + } + + if (!rec) + { + if (szValue) *szValue = '\0'; + if (pccbValue) *pccbValue = 0; + r = ERROR_SUCCESS; + } + + msiobj_release(&package->hdr); + return r; +} + +UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage ) +{ + UINT r; + LPWSTR szPack = NULL; + + TRACE("%s\n", debugstr_a(szPackage) ); + + if( szPackage ) + { + szPack = strdupAtoW( szPackage ); + if( !szPack ) + return ERROR_OUTOFMEMORY; + } + + r = MsiVerifyPackageW( szPack ); + + msi_free( szPack ); + + return r; +} + +UINT WINAPI MsiVerifyPackageW( LPCWSTR szPackage ) +{ + MSIHANDLE handle; + UINT r; + + TRACE("%s\n", debugstr_w(szPackage) ); + + r = MsiOpenDatabaseW( szPackage, MSIDBOPEN_READONLY, &handle ); + MsiCloseHandle( handle ); + + return r; +} + +static INSTALLSTATE MSI_GetComponentPath(LPCWSTR szProduct, LPCWSTR szComponent, + awstring* lpPathBuf, LPDWORD pcchBuf) +{ + static const WCHAR wininstaller[] = + {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0}; + WCHAR squished_pc[GUID_SIZE]; + WCHAR squished_comp[GUID_SIZE]; + HKEY hkey; + LPWSTR path = NULL; + INSTALLSTATE state; + DWORD version; + + if (!szProduct || !szComponent) + return INSTALLSTATE_INVALIDARG; + + if (lpPathBuf->str.w && !pcchBuf) + return INSTALLSTATE_INVALIDARG; + + if (!squash_guid(szProduct, squished_pc) || + !squash_guid(szComponent, squished_comp)) + return INSTALLSTATE_INVALIDARG; + + state = INSTALLSTATE_UNKNOWN; + + if (MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkey, FALSE) == ERROR_SUCCESS) + { + path = msi_reg_get_val_str(hkey, squished_pc); + RegCloseKey(hkey); + + state = INSTALLSTATE_ABSENT; + + if ((MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_MACHINE, NULL, + &hkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenUserDataProductKey(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, + NULL, &hkey, FALSE) == ERROR_SUCCESS) && + msi_reg_get_val_dword(hkey, wininstaller, &version) && + GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) + { + RegCloseKey(hkey); + state = INSTALLSTATE_LOCAL; + } + } + + if (state != INSTALLSTATE_LOCAL && + (MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, + &hkey, FALSE) == ERROR_SUCCESS)) + { + RegCloseKey(hkey); + + if (MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkey, FALSE) == ERROR_SUCCESS || + MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkey, FALSE) == ERROR_SUCCESS) + { + msi_free(path); + path = msi_reg_get_val_str(hkey, squished_pc); + RegCloseKey(hkey); + + state = INSTALLSTATE_ABSENT; + + if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) + state = INSTALLSTATE_LOCAL; + } + } + + if (!path) + return INSTALLSTATE_UNKNOWN; + + if (state == INSTALLSTATE_LOCAL && !*path) + state = INSTALLSTATE_NOTUSED; + + msi_strcpy_to_awstring(path, lpPathBuf, pcchBuf); + msi_free(path); + return state; +} + +/****************************************************************** + * MsiGetComponentPathW [MSI.@] + */ +INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent, + LPWSTR lpPathBuf, LPDWORD pcchBuf) +{ + awstring path; + + TRACE("%s %s %p %p\n", debugstr_w(szProduct), debugstr_w(szComponent), lpPathBuf, pcchBuf); + + path.unicode = TRUE; + path.str.w = lpPathBuf; + + return MSI_GetComponentPath( szProduct, szComponent, &path, pcchBuf ); +} + +/****************************************************************** + * MsiGetComponentPathA [MSI.@] + */ +INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent, + LPSTR lpPathBuf, LPDWORD pcchBuf) +{ + LPWSTR szwProduct, szwComponent = NULL; + INSTALLSTATE r = INSTALLSTATE_UNKNOWN; + awstring path; + + TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szComponent), lpPathBuf, pcchBuf); + + szwProduct = strdupAtoW( szProduct ); + if( szProduct && !szwProduct) + goto end; + + szwComponent = strdupAtoW( szComponent ); + if( szComponent && !szwComponent ) + goto end; + + path.unicode = FALSE; + path.str.a = lpPathBuf; + + r = MSI_GetComponentPath( szwProduct, szwComponent, &path, pcchBuf ); + +end: + msi_free( szwProduct ); + msi_free( szwComponent ); + + return r; +} + +/****************************************************************** + * MsiQueryFeatureStateA [MSI.@] + */ +INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature) +{ + LPWSTR szwProduct = NULL, szwFeature= NULL; + INSTALLSTATE rc = INSTALLSTATE_UNKNOWN; + + szwProduct = strdupAtoW( szProduct ); + if ( szProduct && !szwProduct ) + goto end; + + szwFeature = strdupAtoW( szFeature ); + if ( szFeature && !szwFeature ) + goto end; + + rc = MsiQueryFeatureStateW(szwProduct, szwFeature); + +end: + msi_free( szwProduct); + msi_free( szwFeature); + + return rc; +} + +/****************************************************************** + * MsiQueryFeatureStateW [MSI.@] + * + * Checks the state of a feature + * + * PARAMS + * szProduct [I] Product's GUID string + * szFeature [I] Feature's GUID string + * + * RETURNS + * INSTALLSTATE_LOCAL Feature is installed and usable + * INSTALLSTATE_ABSENT Feature is absent + * INSTALLSTATE_ADVERTISED Feature should be installed on demand + * INSTALLSTATE_UNKNOWN An error occurred + * INSTALLSTATE_INVALIDARG One of the GUIDs was invalid + * + */ +INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature) +{ + WCHAR squishProduct[33], comp[GUID_SIZE]; + GUID guid; + LPWSTR components, p, parent_feature, path; + UINT rc; + HKEY hkey; + INSTALLSTATE r; + BOOL missing = FALSE; + BOOL machine = FALSE; + BOOL source = FALSE; + + TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature)); + + if (!szProduct || !szFeature) + return INSTALLSTATE_INVALIDARG; + + if (!squash_guid( szProduct, squishProduct )) + return INSTALLSTATE_INVALIDARG; + + SetLastError( ERROR_SUCCESS ); + + if (MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_USERMANAGED, + &hkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE) != ERROR_SUCCESS) + { + rc = MSIREG_OpenFeaturesKey(szProduct, MSIINSTALLCONTEXT_MACHINE, + &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return INSTALLSTATE_UNKNOWN; + + machine = TRUE; + } + + parent_feature = msi_reg_get_val_str( hkey, szFeature ); + RegCloseKey(hkey); + + if (!parent_feature) + return INSTALLSTATE_UNKNOWN; + + r = (parent_feature[0] == 6) ? INSTALLSTATE_ABSENT : INSTALLSTATE_LOCAL; + msi_free(parent_feature); + if (r == INSTALLSTATE_ABSENT) + return r; + + if (machine) + rc = MSIREG_OpenUserDataFeaturesKey(szProduct, + MSIINSTALLCONTEXT_MACHINE, + &hkey, FALSE); + else + rc = MSIREG_OpenUserDataFeaturesKey(szProduct, + MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE); + + if (rc != ERROR_SUCCESS) + return INSTALLSTATE_ADVERTISED; + + components = msi_reg_get_val_str( hkey, szFeature ); + RegCloseKey(hkey); + + TRACE("rc = %d buffer = %s\n", rc, debugstr_w(components)); + + if (!components) + return INSTALLSTATE_ADVERTISED; + + for( p = components; *p && *p != 2 ; p += 20) + { + if (!decode_base85_guid( p, &guid )) + { + if (p != components) + break; + + msi_free(components); + return INSTALLSTATE_BADCONFIG; + } + + StringFromGUID2(&guid, comp, GUID_SIZE); + + if (machine) + rc = MSIREG_OpenUserDataComponentKey(comp, szLocalSid, &hkey, FALSE); + else + rc = MSIREG_OpenUserDataComponentKey(comp, NULL, &hkey, FALSE); + + if (rc != ERROR_SUCCESS) + { + msi_free(components); + return INSTALLSTATE_ADVERTISED; + } + + path = msi_reg_get_val_str(hkey, squishProduct); + if (!path) + missing = TRUE; + else if (lstrlenW(path) > 2 && + path[0] >= '0' && path[0] <= '9' && + path[1] >= '0' && path[1] <= '9') + { + source = TRUE; + } + + msi_free(path); + } + msi_free(components); + + if (missing) + r = INSTALLSTATE_ADVERTISED; + else if (source) + r = INSTALLSTATE_SOURCE; + else + r = INSTALLSTATE_LOCAL; + + TRACE("-> %d\n", r); + return r; +} + +/****************************************************************** + * MsiGetFileVersionA [MSI.@] + */ +UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf, + LPDWORD pcchVersionBuf, LPSTR lpLangBuf, LPDWORD pcchLangBuf) +{ + LPWSTR szwFilePath = NULL, lpwVersionBuff = NULL, lpwLangBuff = NULL; + UINT ret = ERROR_OUTOFMEMORY; + + if ((lpVersionBuf && !pcchVersionBuf) || + (lpLangBuf && !pcchLangBuf)) + return ERROR_INVALID_PARAMETER; + + if( szFilePath ) + { + szwFilePath = strdupAtoW( szFilePath ); + if( !szwFilePath ) + goto end; + } + + if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf ) + { + lpwVersionBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR)); + if( !lpwVersionBuff ) + goto end; + } + + if( lpLangBuf && pcchLangBuf && *pcchLangBuf ) + { + lpwLangBuff = msi_alloc(*pcchLangBuf*sizeof(WCHAR)); + if( !lpwLangBuff ) + goto end; + } + + ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf, + lpwLangBuff, pcchLangBuf); + + if( (ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && lpwVersionBuff ) + WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1, + lpVersionBuf, *pcchVersionBuf + 1, NULL, NULL); + if( (ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && lpwLangBuff ) + WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1, + lpLangBuf, *pcchLangBuf + 1, NULL, NULL); + +end: + msi_free(szwFilePath); + msi_free(lpwVersionBuff); + msi_free(lpwLangBuff); + + return ret; +} + +static UINT get_file_version( const WCHAR *path, WCHAR *verbuf, DWORD *verlen, + WCHAR *langbuf, DWORD *langlen ) +{ + static const WCHAR szVersionResource[] = {'\\',0}; + static const WCHAR szVersionFormat[] = {'%','d','.','%','d','.','%','d','.','%','d',0}; + static const WCHAR szLangFormat[] = {'%','d',0}; + UINT ret = ERROR_SUCCESS; + DWORD len, error; + LPVOID version; + VS_FIXEDFILEINFO *ffi; + USHORT *lang; + WCHAR tmp[32]; + + if (!(len = GetFileVersionInfoSizeW( path, NULL ))) + { + error = GetLastError(); + if (error == ERROR_BAD_PATHNAME) return ERROR_FILE_NOT_FOUND; + return error; + } + if (!(version = msi_alloc( len ))) return ERROR_OUTOFMEMORY; + if (!GetFileVersionInfoW( path, 0, len, version )) + { + msi_free( version ); + return GetLastError(); + } + if (verlen) + { + if (VerQueryValueW( version, szVersionResource, (LPVOID *)&ffi, &len ) && len > 0) + { + sprintfW( tmp, szVersionFormat, + HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS), + HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS) ); + if (verbuf) lstrcpynW( verbuf, tmp, *verlen ); + len = strlenW( tmp ); + if (len >= *verlen) ret = ERROR_MORE_DATA; + *verlen = len; + } + else + { + if (verbuf) *verbuf = 0; + *verlen = 0; + } + } + if (langlen) + { + if (VerQueryValueW( version, szLangResource, (LPVOID *)&lang, &len ) && len > 0) + { + sprintfW( tmp, szLangFormat, *lang ); + if (langbuf) lstrcpynW( langbuf, tmp, *langlen ); + len = strlenW( tmp ); + if (len >= *langlen) ret = ERROR_MORE_DATA; + *langlen = len; + } + else + { + if (langbuf) *langbuf = 0; + *langlen = 0; + } + } + msi_free( version ); + return ret; +} + + +/****************************************************************** + * MsiGetFileVersionW [MSI.@] + */ +UINT WINAPI MsiGetFileVersionW( LPCWSTR path, LPWSTR verbuf, LPDWORD verlen, + LPWSTR langbuf, LPDWORD langlen ) +{ + UINT ret; + + TRACE("%s %p %u %p %u\n", debugstr_w(path), verbuf, verlen ? *verlen : 0, + langbuf, langlen ? *langlen : 0); + + if ((verbuf && !verlen) || (langbuf && !langlen)) + return ERROR_INVALID_PARAMETER; + + ret = get_file_version( path, verbuf, verlen, langbuf, langlen ); + if (ret == ERROR_RESOURCE_DATA_NOT_FOUND && verlen) + { + int len; + WCHAR *version = msi_font_version_from_file( path ); + if (!version) return ERROR_FILE_INVALID; + len = strlenW( version ); + if (len >= *verlen) ret = ERROR_MORE_DATA; + else if (verbuf) + { + strcpyW( verbuf, version ); + ret = ERROR_SUCCESS; + } + *verlen = len; + msi_free( version ); + } + return ret; +} + +/*********************************************************************** + * MsiGetFeatureUsageW [MSI.@] + */ +UINT WINAPI MsiGetFeatureUsageW( LPCWSTR szProduct, LPCWSTR szFeature, + LPDWORD pdwUseCount, LPWORD pwDateUsed ) +{ + FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szFeature), + pdwUseCount, pwDateUsed); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiGetFeatureUsageA [MSI.@] + */ +UINT WINAPI MsiGetFeatureUsageA( LPCSTR szProduct, LPCSTR szFeature, + LPDWORD pdwUseCount, LPWORD pwDateUsed ) +{ + LPWSTR prod = NULL, feat = NULL; + UINT ret = ERROR_OUTOFMEMORY; + + TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szFeature), + pdwUseCount, pwDateUsed); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + ret = MsiGetFeatureUsageW( prod, feat, pdwUseCount, pwDateUsed ); + +end: + msi_free( prod ); + msi_free( feat ); + + return ret; +} + +/*********************************************************************** + * MsiUseFeatureExW [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureExW( LPCWSTR szProduct, LPCWSTR szFeature, + DWORD dwInstallMode, DWORD dwReserved ) +{ + INSTALLSTATE state; + + TRACE("%s %s %i %i\n", debugstr_w(szProduct), debugstr_w(szFeature), + dwInstallMode, dwReserved); + + state = MsiQueryFeatureStateW( szProduct, szFeature ); + + if (dwReserved) + return INSTALLSTATE_INVALIDARG; + + if (state == INSTALLSTATE_LOCAL && dwInstallMode != INSTALLMODE_NODETECTION) + { + FIXME("mark product %s feature %s as used\n", + debugstr_w(szProduct), debugstr_w(szFeature) ); + } + + return state; +} + +/*********************************************************************** + * MsiUseFeatureExA [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureExA( LPCSTR szProduct, LPCSTR szFeature, + DWORD dwInstallMode, DWORD dwReserved ) +{ + INSTALLSTATE ret = INSTALLSTATE_UNKNOWN; + LPWSTR prod = NULL, feat = NULL; + + TRACE("%s %s %i %i\n", debugstr_a(szProduct), debugstr_a(szFeature), + dwInstallMode, dwReserved); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + ret = MsiUseFeatureExW( prod, feat, dwInstallMode, dwReserved ); + +end: + msi_free( prod ); + msi_free( feat ); + + return ret; +} + +/*********************************************************************** + * MsiUseFeatureW [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureW( LPCWSTR szProduct, LPCWSTR szFeature ) +{ + return MsiUseFeatureExW(szProduct, szFeature, 0, 0); +} + +/*********************************************************************** + * MsiUseFeatureA [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureA( LPCSTR szProduct, LPCSTR szFeature ) +{ + return MsiUseFeatureExA(szProduct, szFeature, 0, 0); +} + +/*********************************************************************** + * MSI_ProvideQualifiedComponentEx [internal] + */ +static UINT MSI_ProvideQualifiedComponentEx(LPCWSTR szComponent, + LPCWSTR szQualifier, DWORD dwInstallMode, LPCWSTR szProduct, + DWORD Unused1, DWORD Unused2, awstring *lpPathBuf, + LPDWORD pcchPathBuf) +{ + WCHAR product[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1], + feature[MAX_FEATURE_CHARS+1]; + LPWSTR info; + HKEY hkey; + DWORD sz; + UINT rc; + + rc = MSIREG_OpenUserComponentsKey(szComponent, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return ERROR_INDEX_ABSENT; + + info = msi_reg_get_val_str( hkey, szQualifier ); + RegCloseKey(hkey); + + if (!info) + return ERROR_INDEX_ABSENT; + + MsiDecomposeDescriptorW(info, product, feature, component, &sz); + + if (!szProduct) + rc = MSI_GetComponentPath(product, component, lpPathBuf, pcchPathBuf); + else + rc = MSI_GetComponentPath(szProduct, component, lpPathBuf, pcchPathBuf); + + msi_free( info ); + + if (rc != INSTALLSTATE_LOCAL) + return ERROR_FILE_NOT_FOUND; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiProvideQualifiedComponentExW [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentExW(LPCWSTR szComponent, + LPCWSTR szQualifier, DWORD dwInstallMode, LPCWSTR szProduct, + DWORD Unused1, DWORD Unused2, LPWSTR lpPathBuf, + LPDWORD pcchPathBuf) +{ + awstring path; + + TRACE("%s %s %u %s %u %u %p %p\n", debugstr_w(szComponent), + debugstr_w(szQualifier), dwInstallMode, debugstr_w(szProduct), + Unused1, Unused2, lpPathBuf, pcchPathBuf); + + path.unicode = TRUE; + path.str.w = lpPathBuf; + + return MSI_ProvideQualifiedComponentEx(szComponent, szQualifier, + dwInstallMode, szProduct, Unused1, Unused2, &path, pcchPathBuf); +} + +/*********************************************************************** + * MsiProvideQualifiedComponentExA [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentExA(LPCSTR szComponent, + LPCSTR szQualifier, DWORD dwInstallMode, LPCSTR szProduct, + DWORD Unused1, DWORD Unused2, LPSTR lpPathBuf, + LPDWORD pcchPathBuf) +{ + LPWSTR szwComponent, szwQualifier = NULL, szwProduct = NULL; + UINT r = ERROR_OUTOFMEMORY; + awstring path; + + TRACE("%s %s %u %s %u %u %p %p\n", debugstr_a(szComponent), + debugstr_a(szQualifier), dwInstallMode, debugstr_a(szProduct), + Unused1, Unused2, lpPathBuf, pcchPathBuf); + + szwComponent = strdupAtoW( szComponent ); + if (szComponent && !szwComponent) + goto end; + + szwQualifier = strdupAtoW( szQualifier ); + if (szQualifier && !szwQualifier) + goto end; + + szwProduct = strdupAtoW( szProduct ); + if (szProduct && !szwProduct) + goto end; + + path.unicode = FALSE; + path.str.a = lpPathBuf; + + r = MSI_ProvideQualifiedComponentEx(szwComponent, szwQualifier, + dwInstallMode, szwProduct, Unused1, + Unused2, &path, pcchPathBuf); +end: + msi_free(szwProduct); + msi_free(szwComponent); + msi_free(szwQualifier); + + return r; +} + +/*********************************************************************** + * MsiProvideQualifiedComponentW [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentW( LPCWSTR szComponent, + LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR lpPathBuf, + LPDWORD pcchPathBuf) +{ + return MsiProvideQualifiedComponentExW(szComponent, szQualifier, + dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf); +} + +/*********************************************************************** + * MsiProvideQualifiedComponentA [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentA( LPCSTR szComponent, + LPCSTR szQualifier, DWORD dwInstallMode, LPSTR lpPathBuf, + LPDWORD pcchPathBuf) +{ + return MsiProvideQualifiedComponentExA(szComponent, szQualifier, + dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf); +} + +/*********************************************************************** + * MSI_GetUserInfo [internal] + */ +static USERINFOSTATE MSI_GetUserInfo(LPCWSTR szProduct, + awstring *lpUserNameBuf, LPDWORD pcchUserNameBuf, + awstring *lpOrgNameBuf, LPDWORD pcchOrgNameBuf, + awstring *lpSerialBuf, LPDWORD pcchSerialBuf) +{ + WCHAR squished_pc[SQUISH_GUID_SIZE]; + LPWSTR user, org, serial; + USERINFOSTATE state; + HKEY hkey, props; + LPCWSTR orgptr; + UINT r; + + TRACE("%s %p %p %p %p %p %p\n", debugstr_w(szProduct), lpUserNameBuf, + pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf, + pcchSerialBuf); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return USERINFOSTATE_INVALIDARG; + + if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + &hkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, + &hkey, FALSE) != ERROR_SUCCESS) + { + return USERINFOSTATE_UNKNOWN; + } + + if (MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_USERUNMANAGED, + NULL, &props, FALSE) != ERROR_SUCCESS && + MSIREG_OpenInstallProps(szProduct, MSIINSTALLCONTEXT_MACHINE, + NULL, &props, FALSE) != ERROR_SUCCESS) + { + RegCloseKey(hkey); + return USERINFOSTATE_ABSENT; + } + + user = msi_reg_get_val_str(props, INSTALLPROPERTY_REGOWNERW); + org = msi_reg_get_val_str(props, INSTALLPROPERTY_REGCOMPANYW); + serial = msi_reg_get_val_str(props, INSTALLPROPERTY_PRODUCTIDW); + state = USERINFOSTATE_ABSENT; + + RegCloseKey(hkey); + RegCloseKey(props); + + if (user && serial) + state = USERINFOSTATE_PRESENT; + + if (pcchUserNameBuf) + { + if (lpUserNameBuf && !user) + { + (*pcchUserNameBuf)--; + goto done; + } + + r = msi_strcpy_to_awstring(user, lpUserNameBuf, pcchUserNameBuf); + if (r == ERROR_MORE_DATA) + { + state = USERINFOSTATE_MOREDATA; + goto done; + } + } + + if (pcchOrgNameBuf) + { + orgptr = org; + if (!orgptr) orgptr = szEmpty; + + r = msi_strcpy_to_awstring(orgptr, lpOrgNameBuf, pcchOrgNameBuf); + if (r == ERROR_MORE_DATA) + { + state = USERINFOSTATE_MOREDATA; + goto done; + } + } + + if (pcchSerialBuf) + { + if (!serial) + { + (*pcchSerialBuf)--; + goto done; + } + + r = msi_strcpy_to_awstring(serial, lpSerialBuf, pcchSerialBuf); + if (r == ERROR_MORE_DATA) + state = USERINFOSTATE_MOREDATA; + } + +done: + msi_free(user); + msi_free(org); + msi_free(serial); + + return state; +} + +/*********************************************************************** + * MsiGetUserInfoW [MSI.@] + */ +USERINFOSTATE WINAPI MsiGetUserInfoW(LPCWSTR szProduct, + LPWSTR lpUserNameBuf, LPDWORD pcchUserNameBuf, + LPWSTR lpOrgNameBuf, LPDWORD pcchOrgNameBuf, + LPWSTR lpSerialBuf, LPDWORD pcchSerialBuf) +{ + awstring user, org, serial; + + if ((lpUserNameBuf && !pcchUserNameBuf) || + (lpOrgNameBuf && !pcchOrgNameBuf) || + (lpSerialBuf && !pcchSerialBuf)) + return USERINFOSTATE_INVALIDARG; + + user.unicode = TRUE; + user.str.w = lpUserNameBuf; + org.unicode = TRUE; + org.str.w = lpOrgNameBuf; + serial.unicode = TRUE; + serial.str.w = lpSerialBuf; + + return MSI_GetUserInfo( szProduct, &user, pcchUserNameBuf, + &org, pcchOrgNameBuf, + &serial, pcchSerialBuf ); +} + +USERINFOSTATE WINAPI MsiGetUserInfoA(LPCSTR szProduct, + LPSTR lpUserNameBuf, LPDWORD pcchUserNameBuf, + LPSTR lpOrgNameBuf, LPDWORD pcchOrgNameBuf, + LPSTR lpSerialBuf, LPDWORD pcchSerialBuf) +{ + awstring user, org, serial; + LPWSTR prod; + UINT r; + + if ((lpUserNameBuf && !pcchUserNameBuf) || + (lpOrgNameBuf && !pcchOrgNameBuf) || + (lpSerialBuf && !pcchSerialBuf)) + return USERINFOSTATE_INVALIDARG; + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + return ERROR_OUTOFMEMORY; + + user.unicode = FALSE; + user.str.a = lpUserNameBuf; + org.unicode = FALSE; + org.str.a = lpOrgNameBuf; + serial.unicode = FALSE; + serial.str.a = lpSerialBuf; + + r = MSI_GetUserInfo( prod, &user, pcchUserNameBuf, + &org, pcchOrgNameBuf, + &serial, pcchSerialBuf ); + + msi_free( prod ); + + return r; +} + +UINT WINAPI MsiCollectUserInfoW(LPCWSTR szProduct) +{ + MSIHANDLE handle; + UINT rc; + MSIPACKAGE *package; + static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; + + TRACE("(%s)\n",debugstr_w(szProduct)); + + rc = MsiOpenProductW(szProduct,&handle); + if (rc != ERROR_SUCCESS) + return ERROR_INVALID_PARAMETER; + + /* MsiCollectUserInfo cannot be called from a custom action. */ + package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_CALL_NOT_IMPLEMENTED; + + rc = ACTION_PerformUIAction(package, szFirstRun, SCRIPT_NONE); + msiobj_release( &package->hdr ); + + MsiCloseHandle(handle); + + return rc; +} + +UINT WINAPI MsiCollectUserInfoA(LPCSTR szProduct) +{ + MSIHANDLE handle; + UINT rc; + MSIPACKAGE *package; + static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; + + TRACE("(%s)\n",debugstr_a(szProduct)); + + rc = MsiOpenProductA(szProduct,&handle); + if (rc != ERROR_SUCCESS) + return ERROR_INVALID_PARAMETER; + + /* MsiCollectUserInfo cannot be called from a custom action. */ + package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_CALL_NOT_IMPLEMENTED; + + rc = ACTION_PerformUIAction(package, szFirstRun, SCRIPT_NONE); + msiobj_release( &package->hdr ); + + MsiCloseHandle(handle); + + return rc; +} + +/*********************************************************************** + * MsiConfigureFeatureA [MSI.@] + */ +UINT WINAPI MsiConfigureFeatureA(LPCSTR szProduct, LPCSTR szFeature, INSTALLSTATE eInstallState) +{ + LPWSTR prod, feat = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), eInstallState); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + r = MsiConfigureFeatureW(prod, feat, eInstallState); + +end: + msi_free(feat); + msi_free(prod); + + return r; +} + +/*********************************************************************** + * MsiConfigureFeatureW [MSI.@] + */ +UINT WINAPI MsiConfigureFeatureW(LPCWSTR szProduct, LPCWSTR szFeature, INSTALLSTATE eInstallState) +{ + MSIPACKAGE *package = NULL; + UINT r; + WCHAR sourcepath[MAX_PATH], filename[MAX_PATH]; + DWORD sz; + + TRACE("%s %s %i\n", debugstr_w(szProduct), debugstr_w(szFeature), eInstallState); + + if (!szProduct || !szFeature) + return ERROR_INVALID_PARAMETER; + + switch (eInstallState) + { + case INSTALLSTATE_DEFAULT: + /* FIXME: how do we figure out the default location? */ + eInstallState = INSTALLSTATE_LOCAL; + break; + case INSTALLSTATE_LOCAL: + case INSTALLSTATE_SOURCE: + case INSTALLSTATE_ABSENT: + case INSTALLSTATE_ADVERTISED: + break; + default: + return ERROR_INVALID_PARAMETER; + } + + r = MSI_OpenProductW( szProduct, &package ); + if (r != ERROR_SUCCESS) + return r; + + sz = sizeof(sourcepath); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz); + + sz = sizeof(filename); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); + + lstrcatW( sourcepath, filename ); + + MsiSetInternalUI( INSTALLUILEVEL_BASIC, NULL ); + + r = ACTION_PerformUIAction( package, szCostInitialize, SCRIPT_NONE ); + if (r != ERROR_SUCCESS) + goto end; + + r = MSI_SetFeatureStateW( package, szFeature, eInstallState); + if (r != ERROR_SUCCESS) + goto end; + + r = MSI_InstallPackage( package, sourcepath, NULL ); + +end: + msiobj_release( &package->hdr ); + + return r; +} + +/*********************************************************************** + * MsiCreateAndVerifyInstallerDirectory [MSI.@] + * + * Notes: undocumented + */ +UINT WINAPI MsiCreateAndVerifyInstallerDirectory(DWORD dwReserved) +{ + WCHAR path[MAX_PATH]; + + TRACE("%d\n", dwReserved); + + if (dwReserved) + { + FIXME("dwReserved=%d\n", dwReserved); + return ERROR_INVALID_PARAMETER; + } + + if (!GetWindowsDirectoryW(path, MAX_PATH)) + return ERROR_FUNCTION_FAILED; + + lstrcatW(path, installerW); + + if (!CreateDirectoryW(path, NULL)) + return ERROR_FUNCTION_FAILED; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiGetShortcutTargetA [MSI.@] + */ +UINT WINAPI MsiGetShortcutTargetA( LPCSTR szShortcutTarget, + LPSTR szProductCode, LPSTR szFeatureId, + LPSTR szComponentCode ) +{ + LPWSTR target; + const int len = MAX_FEATURE_CHARS+1; + WCHAR product[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1]; + UINT r; + + target = strdupAtoW( szShortcutTarget ); + if (szShortcutTarget && !target ) + return ERROR_OUTOFMEMORY; + product[0] = 0; + feature[0] = 0; + component[0] = 0; + r = MsiGetShortcutTargetW( target, product, feature, component ); + msi_free( target ); + if (r == ERROR_SUCCESS) + { + WideCharToMultiByte( CP_ACP, 0, product, -1, szProductCode, len, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, feature, -1, szFeatureId, len, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, component, -1, szComponentCode, len, NULL, NULL ); + } + return r; +} + +/*********************************************************************** + * MsiGetShortcutTargetW [MSI.@] + */ +UINT WINAPI MsiGetShortcutTargetW( LPCWSTR szShortcutTarget, + LPWSTR szProductCode, LPWSTR szFeatureId, + LPWSTR szComponentCode ) +{ + IShellLinkDataList *dl = NULL; + IPersistFile *pf = NULL; + LPEXP_DARWIN_LINK darwin = NULL; + HRESULT r, init; + + TRACE("%s %p %p %p\n", debugstr_w(szShortcutTarget), + szProductCode, szFeatureId, szComponentCode ); + + init = CoInitialize(NULL); + + r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IPersistFile, (LPVOID*) &pf ); + if( SUCCEEDED( r ) ) + { + r = IPersistFile_Load( pf, szShortcutTarget, + STGM_READ | STGM_SHARE_DENY_WRITE ); + if( SUCCEEDED( r ) ) + { + r = IPersistFile_QueryInterface( pf, &IID_IShellLinkDataList, + (LPVOID*) &dl ); + if( SUCCEEDED( r ) ) + { + IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, + (LPVOID) &darwin ); + IShellLinkDataList_Release( dl ); + } + } + IPersistFile_Release( pf ); + } + + if (SUCCEEDED(init)) + CoUninitialize(); + + TRACE("darwin = %p\n", darwin); + + if (darwin) + { + DWORD sz; + UINT ret; + + ret = MsiDecomposeDescriptorW( darwin->szwDarwinID, + szProductCode, szFeatureId, szComponentCode, &sz ); + LocalFree( darwin ); + return ret; + } + + return ERROR_FUNCTION_FAILED; +} + +UINT WINAPI MsiReinstallFeatureW( LPCWSTR szProduct, LPCWSTR szFeature, DWORD dwReinstallMode ) +{ + static const WCHAR fmtW[] = {'%','s','=','%','s',' ','%','s','=','%','s',0}; + MSIPACKAGE *package; + MSIINSTALLCONTEXT context; + UINT r; + WCHAR sourcepath[MAX_PATH], filename[MAX_PATH], reinstallmode[11]; + WCHAR *ptr, *cmdline; + DWORD sz; + + TRACE("%s, %s, 0x%08x\n", debugstr_w(szProduct), debugstr_w(szFeature), dwReinstallMode); + + r = msi_locate_product( szProduct, &context ); + if (r != ERROR_SUCCESS) + return r; + + ptr = reinstallmode; + + if (dwReinstallMode & REINSTALLMODE_FILEMISSING) + *ptr++ = 'p'; + if (dwReinstallMode & REINSTALLMODE_FILEOLDERVERSION) + *ptr++ = 'o'; + if (dwReinstallMode & REINSTALLMODE_FILEEQUALVERSION) + *ptr++ = 'w'; + if (dwReinstallMode & REINSTALLMODE_FILEEXACT) + *ptr++ = 'd'; + if (dwReinstallMode & REINSTALLMODE_FILEVERIFY) + *ptr++ = 'c'; + if (dwReinstallMode & REINSTALLMODE_FILEREPLACE) + *ptr++ = 'a'; + if (dwReinstallMode & REINSTALLMODE_USERDATA) + *ptr++ = 'u'; + if (dwReinstallMode & REINSTALLMODE_MACHINEDATA) + *ptr++ = 'm'; + if (dwReinstallMode & REINSTALLMODE_SHORTCUT) + *ptr++ = 's'; + if (dwReinstallMode & REINSTALLMODE_PACKAGE) + *ptr++ = 'v'; + *ptr = 0; + + sz = sizeof(sourcepath); + MsiSourceListGetInfoW( szProduct, NULL, context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, &sz ); + sz = sizeof(filename); + MsiSourceListGetInfoW( szProduct, NULL, context, MSICODE_PRODUCT, + INSTALLPROPERTY_PACKAGENAMEW, filename, &sz ); + strcatW( sourcepath, filename ); + + if (dwReinstallMode & REINSTALLMODE_PACKAGE) + r = MSI_OpenPackageW( sourcepath, &package ); + else + r = MSI_OpenProductW( szProduct, &package ); + + if (r != ERROR_SUCCESS) + return r; + + sz = (strlenW( fmtW ) + strlenW( szReinstallMode ) + strlenW( reinstallmode )) * sizeof(WCHAR); + sz += (strlenW( szReinstall ) + strlenW( szFeature )) * sizeof(WCHAR); + if (!(cmdline = msi_alloc( sz ))) + { + msiobj_release( &package->hdr ); + return ERROR_OUTOFMEMORY; + } + sprintfW( cmdline, fmtW, szReinstallMode, reinstallmode, szReinstall, szFeature ); + + r = MSI_InstallPackage( package, sourcepath, cmdline ); + msiobj_release( &package->hdr ); + msi_free( cmdline ); + + return r; +} + +UINT WINAPI MsiReinstallFeatureA( LPCSTR szProduct, LPCSTR szFeature, + DWORD dwReinstallMode ) +{ + LPWSTR wszProduct; + LPWSTR wszFeature; + UINT rc; + + TRACE("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), + dwReinstallMode); + + wszProduct = strdupAtoW(szProduct); + wszFeature = strdupAtoW(szFeature); + + rc = MsiReinstallFeatureW(wszProduct, wszFeature, dwReinstallMode); + + msi_free(wszProduct); + msi_free(wszFeature); + return rc; +} + +typedef struct +{ + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; + unsigned char digest[16]; +} MD5_CTX; + +extern VOID WINAPI MD5Init( MD5_CTX *); +extern VOID WINAPI MD5Update( MD5_CTX *, const unsigned char *, unsigned int ); +extern VOID WINAPI MD5Final( MD5_CTX *); + +/*********************************************************************** + * MsiGetFileHashW [MSI.@] + */ +UINT WINAPI MsiGetFileHashW( LPCWSTR szFilePath, DWORD dwOptions, + PMSIFILEHASHINFO pHash ) +{ + HANDLE handle, mapping; + void *p; + DWORD length; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%s %08x %p\n", debugstr_w(szFilePath), dwOptions, pHash ); + + if (!szFilePath) + return ERROR_INVALID_PARAMETER; + + if (!*szFilePath) + return ERROR_PATH_NOT_FOUND; + + if (dwOptions) + return ERROR_INVALID_PARAMETER; + if (!pHash) + return ERROR_INVALID_PARAMETER; + if (pHash->dwFileHashInfoSize < sizeof *pHash) + return ERROR_INVALID_PARAMETER; + + handle = CreateFileW( szFilePath, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL ); + if (handle == INVALID_HANDLE_VALUE) + { + WARN("can't open file %u\n", GetLastError()); + return ERROR_FILE_NOT_FOUND; + } + length = GetFileSize( handle, NULL ); + + if (length) + { + mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, 0, NULL ); + if (mapping) + { + p = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, length ); + if (p) + { + MD5_CTX ctx; + + MD5Init( &ctx ); + MD5Update( &ctx, p, length ); + MD5Final( &ctx ); + UnmapViewOfFile( p ); + + memcpy( pHash->dwData, ctx.digest, sizeof pHash->dwData ); + r = ERROR_SUCCESS; + } + CloseHandle( mapping ); + } + } + else + { + /* Empty file -> set hash to 0 */ + memset( pHash->dwData, 0, sizeof pHash->dwData ); + r = ERROR_SUCCESS; + } + + CloseHandle( handle ); + + return r; +} + +/*********************************************************************** + * MsiGetFileHashA [MSI.@] + */ +UINT WINAPI MsiGetFileHashA( LPCSTR szFilePath, DWORD dwOptions, + PMSIFILEHASHINFO pHash ) +{ + LPWSTR file; + UINT r; + + TRACE("%s %08x %p\n", debugstr_a(szFilePath), dwOptions, pHash ); + + file = strdupAtoW( szFilePath ); + if (szFilePath && !file) + return ERROR_OUTOFMEMORY; + + r = MsiGetFileHashW( file, dwOptions, pHash ); + msi_free( file ); + return r; +} + +/*********************************************************************** + * MsiAdvertiseScriptW [MSI.@] + */ +UINT WINAPI MsiAdvertiseScriptW( LPCWSTR szScriptFile, DWORD dwFlags, + PHKEY phRegData, BOOL fRemoveItems ) +{ + FIXME("%s %08x %p %d\n", + debugstr_w( szScriptFile ), dwFlags, phRegData, fRemoveItems ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiAdvertiseScriptA [MSI.@] + */ +UINT WINAPI MsiAdvertiseScriptA( LPCSTR szScriptFile, DWORD dwFlags, + PHKEY phRegData, BOOL fRemoveItems ) +{ + FIXME("%s %08x %p %d\n", + debugstr_a( szScriptFile ), dwFlags, phRegData, fRemoveItems ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiIsProductElevatedW [MSI.@] + */ +UINT WINAPI MsiIsProductElevatedW( LPCWSTR szProduct, BOOL *pfElevated ) +{ + FIXME("%s %p - stub\n", + debugstr_w( szProduct ), pfElevated ); + *pfElevated = TRUE; + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiIsProductElevatedA [MSI.@] + */ +UINT WINAPI MsiIsProductElevatedA( LPCSTR szProduct, BOOL *pfElevated ) +{ + FIXME("%s %p - stub\n", + debugstr_a( szProduct ), pfElevated ); + *pfElevated = TRUE; + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiSetExternalUIRecord [MSI.@] + */ +UINT WINAPI MsiSetExternalUIRecord( INSTALLUI_HANDLER_RECORD handler, + DWORD filter, LPVOID context, + PINSTALLUI_HANDLER_RECORD prev ) +{ + TRACE("%p %08x %p %p\n", handler, filter, context, prev); + + if (prev) + *prev = gUIHandlerRecord; + + gUIHandlerRecord = handler; + gUIFilter = filter; + gUIContext = context; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiInstallMissingComponentA [MSI.@] + */ +UINT WINAPI MsiInstallMissingComponentA( LPCSTR product, LPCSTR component, INSTALLSTATE state ) +{ + UINT r; + WCHAR *productW = NULL, *componentW = NULL; + + TRACE("%s, %s, %d\n", debugstr_a(product), debugstr_a(component), state); + + if (product && !(productW = strdupAtoW( product ))) + return ERROR_OUTOFMEMORY; + + if (component && !(componentW = strdupAtoW( component ))) + { + msi_free( productW ); + return ERROR_OUTOFMEMORY; + } + + r = MsiInstallMissingComponentW( productW, componentW, state ); + msi_free( productW ); + msi_free( componentW ); + return r; +} + +/*********************************************************************** + * MsiInstallMissingComponentW [MSI.@] + */ +UINT WINAPI MsiInstallMissingComponentW(LPCWSTR szProduct, LPCWSTR szComponent, INSTALLSTATE eInstallState) +{ + FIXME("(%s %s %d\n", debugstr_w(szProduct), debugstr_w(szComponent), eInstallState); + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiBeginTransactionA [MSI.@] + */ +UINT WINAPI MsiBeginTransactionA( LPCSTR name, DWORD attrs, MSIHANDLE *id, HANDLE *event ) +{ + WCHAR *nameW; + UINT r; + + FIXME("%s %u %p %p\n", debugstr_a(name), attrs, id, event); + + nameW = strdupAtoW( name ); + if (name && !nameW) + return ERROR_OUTOFMEMORY; + + r = MsiBeginTransactionW( nameW, attrs, id, event ); + msi_free( nameW ); + return r; +} + +/*********************************************************************** + * MsiBeginTransactionW [MSI.@] + */ +UINT WINAPI MsiBeginTransactionW( LPCWSTR name, DWORD attrs, MSIHANDLE *id, HANDLE *event ) +{ + FIXME("%s %u %p %p\n", debugstr_w(name), attrs, id, event); + + *id = (MSIHANDLE)0xdeadbeef; + *event = (HANDLE)0xdeadbeef; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiEndTransaction [MSI.@] + */ +UINT WINAPI MsiEndTransaction( DWORD state ) +{ + FIXME("%u\n", state); + return ERROR_SUCCESS; +} + +UINT WINAPI Migrate10CachedPackagesW(void* a, void* b, void* c, DWORD d) +{ + FIXME("%p,%p,%p,%08x\n", a, b, c, d); + return ERROR_SUCCESS; +} diff --git a/libmsi/msi.h b/libmsi/msi.h new file mode 100644 index 0000000..d8c67c3 --- /dev/null +++ b/libmsi/msi.h @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2002,2003 Mike McCormack + * + * 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 + */ + +#ifndef __MSI_H +#define __MSI_H + +#ifndef _MSI_NO_CRYPTO +#include <wincrypt.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ULONG MSIHANDLE; + +typedef enum tagINSTALLSTATE +{ + INSTALLSTATE_NOTUSED = -7, + INSTALLSTATE_BADCONFIG = -6, + INSTALLSTATE_INCOMPLETE = -5, + INSTALLSTATE_SOURCEABSENT = -4, + INSTALLSTATE_MOREDATA = -3, + INSTALLSTATE_INVALIDARG = -2, + INSTALLSTATE_UNKNOWN = -1, + INSTALLSTATE_BROKEN = 0, + INSTALLSTATE_ADVERTISED = 1, + INSTALLSTATE_ABSENT = 2, + INSTALLSTATE_LOCAL = 3, + INSTALLSTATE_SOURCE = 4, + INSTALLSTATE_DEFAULT = 5 +} INSTALLSTATE; + +typedef enum tagMSIPATCHSTATE +{ + MSIPATCHSTATE_INVALID = 0, + MSIPATCHSTATE_APPLIED = 1, + MSIPATCHSTATE_SUPERSEDED = 2, + MSIPATCHSTATE_OBSOLETED = 4, + MSIPATCHSTATE_REGISTERED = 8, + MSIPATCHSTATE_ALL = (MSIPATCHSTATE_APPLIED | MSIPATCHSTATE_SUPERSEDED | + MSIPATCHSTATE_OBSOLETED | MSIPATCHSTATE_REGISTERED) +} MSIPATCHSTATE; + +typedef enum tagINSTALLUILEVEL +{ + INSTALLUILEVEL_NOCHANGE = 0, + INSTALLUILEVEL_DEFAULT = 1, + INSTALLUILEVEL_NONE = 2, + INSTALLUILEVEL_BASIC = 3, + INSTALLUILEVEL_REDUCED = 4, + INSTALLUILEVEL_FULL = 5, + INSTALLUILEVEL_HIDECANCEL = 0x20, + INSTALLUILEVEL_PROGRESSONLY = 0x40, + INSTALLUILEVEL_ENDDIALOG = 0x80, + INSTALLUILEVEL_SOURCERESONLY = 0x100 +} INSTALLUILEVEL; + +typedef enum tagUSERINFOSTATE +{ + USERINFOSTATE_MOREDATA = -3, + USERINFOSTATE_INVALIDARG = -2, + USERINFOSTATE_UNKNOWN = -1, + USERINFOSTATE_ABSENT = 0, + USERINFOSTATE_PRESENT = 1, +} USERINFOSTATE; + +typedef enum tagINSTALLLEVEL +{ + INSTALLLEVEL_DEFAULT = 0, + INSTALLLEVEL_MINIMUM = 1, + INSTALLLEVEL_MAXIMUM = 0xFFFF +} INSTALLLEVEL; + +typedef enum tagINSTALLMESSAGE +{ + INSTALLMESSAGE_FATALEXIT = 0, + INSTALLMESSAGE_ERROR = 0x01000000, + INSTALLMESSAGE_WARNING = 0x02000000, + INSTALLMESSAGE_USER = 0x03000000, + INSTALLMESSAGE_INFO = 0x04000000, + INSTALLMESSAGE_FILESINUSE = 0x05000000, + INSTALLMESSAGE_RESOLVESOURCE = 0x06000000, + INSTALLMESSAGE_OUTOFDISKSPACE = 0x07000000, + INSTALLMESSAGE_ACTIONSTART = 0x08000000, + INSTALLMESSAGE_ACTIONDATA = 0x09000000, + INSTALLMESSAGE_PROGRESS = 0x0a000000, + INSTALLMESSAGE_COMMONDATA = 0x0b000000, + INSTALLMESSAGE_INITIALIZE = 0x0c000000, + INSTALLMESSAGE_TERMINATE = 0x0d000000, + INSTALLMESSAGE_SHOWDIALOG = 0x0e000000 +} INSTALLMESSAGE; + +typedef enum tagREINSTALLMODE +{ + REINSTALLMODE_REPAIR = 0x00000001, + REINSTALLMODE_FILEMISSING = 0x00000002, + REINSTALLMODE_FILEOLDERVERSION = 0x00000004, + REINSTALLMODE_FILEEQUALVERSION = 0x00000008, + REINSTALLMODE_FILEEXACT = 0x00000010, + REINSTALLMODE_FILEVERIFY = 0x00000020, + REINSTALLMODE_FILEREPLACE = 0x00000040, + REINSTALLMODE_MACHINEDATA = 0x00000080, + REINSTALLMODE_USERDATA = 0x00000100, + REINSTALLMODE_SHORTCUT = 0x00000200, + REINSTALLMODE_PACKAGE = 0x00000400 +} REINSTALLMODE; + +typedef enum tagINSTALLLOGMODE +{ + INSTALLLOGMODE_FATALEXIT = (1 << (INSTALLMESSAGE_FATALEXIT >> 24)), + INSTALLLOGMODE_ERROR = (1 << (INSTALLMESSAGE_ERROR >> 24)), + INSTALLLOGMODE_WARNING = (1 << (INSTALLMESSAGE_WARNING >> 24)), + INSTALLLOGMODE_USER = (1 << (INSTALLMESSAGE_USER >> 24)), + INSTALLLOGMODE_INFO = (1 << (INSTALLMESSAGE_INFO >> 24)), + INSTALLLOGMODE_RESOLVESOURCE = (1 << (INSTALLMESSAGE_RESOLVESOURCE >> 24)), + INSTALLLOGMODE_OUTOFDISKSPACE = (1 << (INSTALLMESSAGE_OUTOFDISKSPACE >> 24)), + INSTALLLOGMODE_ACTIONSTART = (1 << (INSTALLMESSAGE_ACTIONSTART >> 24)), + INSTALLLOGMODE_ACTIONDATA = (1 << (INSTALLMESSAGE_ACTIONDATA >> 24)), + INSTALLLOGMODE_COMMONDATA = (1 << (INSTALLMESSAGE_COMMONDATA >> 24)), + INSTALLLOGMODE_PROPERTYDUMP = (1 << (INSTALLMESSAGE_PROGRESS >> 24)), + INSTALLLOGMODE_VERBOSE = (1 << (INSTALLMESSAGE_INITIALIZE >> 24)), + INSTALLLOGMODE_EXTRADEBUG = (1 << (INSTALLMESSAGE_TERMINATE >> 24)), + INSTALLLOGMODE_PROGRESS = (1 << (INSTALLMESSAGE_PROGRESS >> 24)), + INSTALLLOGMODE_INITIALIZE = (1 << (INSTALLMESSAGE_INITIALIZE >> 24)), + INSTALLLOGMODE_TERMINATE = (1 << (INSTALLMESSAGE_TERMINATE >> 24)), + INSTALLLOGMODE_SHOWDIALOG = (1 << (INSTALLMESSAGE_SHOWDIALOG >> 24)) +} INSTALLLOGMODE; + +typedef enum tagINSTALLLOGATTRIBUTES +{ + INSTALLLOGATTRIBUTES_APPEND = 0x00000001, + INSTALLLOGATTRIBUTES_FLUSHEACHLINE = 0x00000002 +} INSTALLLOGATTRIBUTES; + +typedef enum tagINSTALLMODE +{ + INSTALLMODE_NODETECTION_ANY = -4, + INSTALLMODE_NOSOURCERESOLUTION = -3, + INSTALLMODE_NODETECTION = -2, + INSTALLMODE_EXISTING = -1, + INSTALLMODE_DEFAULT = 0 +} INSTALLMODE; + +typedef enum tagADVERTISEFLAGS +{ + ADVERTISEFLAGS_MACHINEASSIGN = 0, + ADVERTISEFLAGS_USERASSIGN = 1 +} ADVERTISEFLAGS; + +typedef enum tagSCRIPTFLAGS +{ + SCRIPTFLAGS_CACHEINFO = 1, + SCRIPTFLAGS_SHORTCUTS = 4, + SCRIPTFLAGS_MACHINEASSIGN = 8, + SCRIPTFLAGS_REGDATA_APPINFO = 0x10, + SCRIPTFLAGS_REGDATA_CNFGINFO = 0x20, + SCRIPTFLAGS_VALIDATE_TRANSFORMS_LIST = 0x40, + SCRIPTFLAGS_REGDATA_CLASSINFO = 0x80, + SCRIPTFLAGS_REGDATA_EXTENSIONINFO = 0x100, +} SCRIPTFLAGS; + +typedef enum tagINSTALLTYPE +{ + INSTALLTYPE_DEFAULT = 0, + INSTALLTYPE_NETWORK_IMAGE = 1, + INSTALLTYPE_SINGLE_INSTANCE = 2, +} INSTALLTYPE; + +typedef enum tagMSIINSTALLCONTEXT +{ + MSIINSTALLCONTEXT_FIRSTVISIBLE = 0, + MSIINSTALLCONTEXT_NONE = 0, + MSIINSTALLCONTEXT_USERMANAGED = 1, + MSIINSTALLCONTEXT_USERUNMANAGED = 2, + MSIINSTALLCONTEXT_MACHINE = 4, + MSIINSTALLCONTEXT_ALL = (MSIINSTALLCONTEXT_USERMANAGED | MSIINSTALLCONTEXT_USERUNMANAGED | MSIINSTALLCONTEXT_MACHINE), + MSIINSTALLCONTEXT_ALLUSERMANAGED= 8, +} MSIINSTALLCONTEXT; + +typedef enum tagMSISOURCETYPE +{ + MSISOURCETYPE_UNKNOWN = __MSABI_LONG(0x00000000), + MSISOURCETYPE_NETWORK = __MSABI_LONG(0x00000001), + MSISOURCETYPE_URL = __MSABI_LONG(0x00000002), + MSISOURCETYPE_MEDIA = __MSABI_LONG(0x00000004) +} MSISOURCETYPE; + +typedef enum tagMSICODE +{ + MSICODE_PRODUCT = __MSABI_LONG(0x00000000), + MSICODE_PATCH = __MSABI_LONG(0x40000000) +} MSICODE; + +typedef enum tagINSTALLFEATUREATTRIBUTE +{ + INSTALLFEATUREATTRIBUTE_FAVORLOCAL = 1 << 0, + INSTALLFEATUREATTRIBUTE_FAVORSOURCE = 1 << 1, + INSTALLFEATUREATTRIBUTE_FOLLOWPARENT = 1 << 2, + INSTALLFEATUREATTRIBUTE_FAVORADVERTISE = 1 << 3, + INSTALLFEATUREATTRIBUTE_DISALLOWADVERTISE = 1 << 4, + INSTALLFEATUREATTRIBUTE_NOUNSUPPORTEDADVERTISE = 1 << 5 +} INSTALLFEATUREATTRIBUTE; + +typedef struct _MSIFILEHASHINFO { + ULONG dwFileHashInfoSize; + ULONG dwData[4]; +} MSIFILEHASHINFO, *PMSIFILEHASHINFO; + +typedef enum tagMSIPATCHDATATYPE +{ + MSIPATCH_DATATYPE_PATCHFILE = 0, + MSIPATCH_DATATYPE_XMLPATH = 1, + MSIPATCH_DATATYPE_XMLBLOB = 2, +} MSIPATCHDATATYPE, *PMSIPATCHDATATYPE; + +typedef struct tagMSIPATCHSEQUENCEINFOA +{ + LPCSTR szPatchData; + MSIPATCHDATATYPE ePatchDataType; + DWORD dwOrder; + UINT uStatus; +} MSIPATCHSEQUENCEINFOA, *PMSIPATCHSEQUENCEINFOA; + +typedef struct tagMSIPATCHSEQUENCEINFOW +{ + LPCWSTR szPatchData; + MSIPATCHDATATYPE ePatchDataType; + DWORD dwOrder; + UINT uStatus; +} MSIPATCHSEQUENCEINFOW, *PMSIPATCHSEQUENCEINFOW; + +#define MAX_FEATURE_CHARS 38 + +/* Strings defined in msi.h */ +/* Advertised Information */ + +#define INSTALLPROPERTY_PACKAGENAMEA "PackageName" +static const WCHAR INSTALLPROPERTY_PACKAGENAMEW[] = {'P','a','c','k','a','g','e','N','a','m','e',0}; +#define INSTALLPROPERTY_PACKAGENAME WINELIB_NAME_AW(INSTALLPROPERTY_PACKAGENAME) + +#define INSTALLPROPERTY_TRANSFORMSA "Transforms" +static const WCHAR INSTALLPROPERTY_TRANSFORMSW[] = {'T','r','a','n','s','f','o','r','m','s',0}; +#define INSTALLPROPERTY_TRANSFORMS WINELIB_NAME_AW(INSTALLPROPERTY_TRANSFORMS) + +#define INSTALLPROPERTY_LANGUAGEA "Language" +static const WCHAR INSTALLPROPERTY_LANGUAGEW[] = {'L','a','n','g','u','a','g','e',0}; +#define INSTALLPROPERTY_LANGUAGE WINELIB_NAME_AW(INSTALLPROPERTY_LANGUAGE) + +#define INSTALLPROPERTY_PRODUCTNAMEA "ProductName" +static const WCHAR INSTALLPROPERTY_PRODUCTNAMEW[] = {'P','r','o','d','u','c','t','N','a','m','e',0}; +#define INSTALLPROPERTY_PRODUCTNAME WINELIB_NAME_AW(INSTALLPROPERTY_PRODUCTNAME) + +#define INSTALLPROPERTY_ASSIGNMENTTYPEA "AssignmentType" +static const WCHAR INSTALLPROPERTY_ASSIGNMENTTYPEW[] = {'A','s','s','i','g','n','m','e','n','t','T','y','p','e',0}; +#define INSTALLPROPERTY_ASSIGNMENTTYPE WINELIB_NAME_AW(INSTALLPROPERTY_ASSIGNMENTTYPE) + +#define INSTALLPROPERTY_PACKAGECODEA "PackageCode" +static const WCHAR INSTALLPROPERTY_PACKAGECODEW[] = {'P','a','c','k','a','g','e','C','o','d','e',0}; +#define INSTALLPROPERTY_PACKAGECODE WINELIB_NAME_AW(INSTALLPROPERTY_PACKAGECODE) + +#define INSTALLPROPERTY_VERSIONA "Version" +static const WCHAR INSTALLPROPERTY_VERSIONW[]= {'V','e','r','s','i','o','n',0}; +#define INSTALLPROPERTY_VERSION WINELIB_NAME_AW(INSTALLPROPERTY_VERSION) + +/* MSI version 1.1 and above */ + +#define INSTALLPROPERTY_PRODUCTICONA "ProductIcon" +static const WCHAR INSTALLPROPERTY_PRODUCTICONW[] = {'P','r','o','d','u','c','t','I','c','o','n',0}; +#define INSTALLPROPERTY_PRODUCTICON WINELIB_NAME_AW(INSTALLPROPERTY_PRODUCTICON) + +/* MSI version 1.5 and above */ +#define INSTALLPROPERTY_INSTANCETYPEA "InstanceType" +static const WCHAR INSTALLPROPERTY_INSTANCETYPEW[] = {'I','n','s','t','a','n','c','e','T','y','p','e',0}; +#define INSTALLPROPERTY_INSTANCETYPE WINELIB_NAME_AW(INSTALLPROPERTY_INSTANCETYPE) + +/* MSI version 3 and above */ +#define INSTALLPROPERTY_AUTHORIZED_LUA_APPA "AuthorizedLUAApp" +static const WCHAR INSTALLPROPERTY_AUTHORIZED_LUA_APPW[] = {'A','u','t','h','o','r','i','z','e','d','L','U','A','A','p','p',0}; +#define INSTALLPROPERTY_AUTHORIZED_LUA_APP WINELIB_NAME_AW(INSTALLPROPERTY_AUTHORIZED_LUA_APP) + + +/* Installed Information */ +#define INSTALLPROPERTY_INSTALLEDPRODUCTNAMEA "InstalledProductName" +static const WCHAR INSTALLPROPERTY_INSTALLEDPRODUCTNAMEW[] = {'I','n','s','t','a','l','l','e','d','P','r','o','d','u','c','t','N','a','m','e',0}; +#define INSTALLPROPERTY_INSTALLEDPRODUCTNAME WINELIB_NAME_AW(INSTALLPROPERTY_INSTALLEDPRODUCTNAME) + +#define INSTALLPROPERTY_VERSIONSTRINGA "VersionString" +static const WCHAR INSTALLPROPERTY_VERSIONSTRINGW[] = {'V','e','r','s','i','o','n','S','t','r','i','n','g',0}; +#define INSTALLPROPERTY_VERSIONSTRING WINELIB_NAME_AW(INSTALLPROPERTY_VERSIONSTRING) + +#define INSTALLPROPERTY_HELPLINKA "HelpLink" +static const WCHAR INSTALLPROPERTY_HELPLINKW[] = {'H','e','l','p','L','i','n','k',0}; +#define INSTALLPROPERTY_HELPLINK WINELIB_NAME_AW(INSTALLPROPERTY_HELPLINK) + +#define INSTALLPROPERTY_HELPTELEPHONEA "HelpTelephone" +static const WCHAR INSTALLPROPERTY_HELPTELEPHONEW[] = {'H','e','l','p','T','e','l','e','p','h','o','n','e',0}; +#define INSTALLPROPERTY_HELPTELEPHONE WINELIB_NAME_AW(INSTALLPROPERTY_HELPTELEPHONE) + +#define INSTALLPROPERTY_INSTALLLOCATIONA "InstallLocation" +static const WCHAR INSTALLPROPERTY_INSTALLLOCATIONW[] = {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0}; +#define INSTALLPROPERTY_INSTALLLOCATION WINELIB_NAME_AW(INSTALLPROPERTY_INSTALLLOCATION) + +#define INSTALLPROPERTY_INSTALLSOURCEA "InstallSource" +static const WCHAR INSTALLPROPERTY_INSTALLSOURCEW[] = {'I','n','s','t','a','l','l','S','o','u','r','c','e',0}; +#define INSTALLPROPERTY_INSTALLSOURCE WINELIB_NAME_AW(INSTALLPROPERTY_INSTALLSOURCE) + +#define INSTALLPROPERTY_INSTALLDATEA "InstallDate" +static const WCHAR INSTALLPROPERTY_INSTALLDATEW[] = {'I','n','s','t','a','l','l','D','a','t','e',0}; +#define INSTALLPROPERTY_INSTALLDATE WINELIB_NAME_AW(INSTALLPROPERTY_INSTALLDATE) + +#define INSTALLPROPERTY_PUBLISHERA "Publisher" +static const WCHAR INSTALLPROPERTY_PUBLISHERW[] ={'P','u','b','l','i','s','h','e','r',0}; +#define INSTALLPROPERTY_PUBLISHER WINELIB_NAME_AW(INSTALLPROPERTY_PUBLISHER) + +#define INSTALLPROPERTY_LOCALPACKAGEA "LocalPackage" +static const WCHAR INSTALLPROPERTY_LOCALPACKAGEW[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; +#define INSTALLPROPERTY_LOCALPACKAGE WINELIB_NAME_AW(INSTALLPROPERTY_LOCALPACKAGE) + +#define INSTALLPROPERTY_URLINFOABOUTA "URLInfoAbout" +static const WCHAR INSTALLPROPERTY_URLINFOABOUTW[] = {'U','R','L','I','n','f','o','A','b','o','u','t',0}; +#define INSTALLPROPERTY_URLINFOABOUT WINELIB_NAME_AW(INSTALLPROPERTY_URLINFOABOUT) + +#define INSTALLPROPERTY_URLUPDATEINFOA "URLUpdateInfo" +static const WCHAR INSTALLPROPERTY_URLUPDATEINFOW[] = {'U','R','L','U','p','d','a','t','e','I','n','f','o',0}; +#define INSTALLPROPERTY_URLUPDATEINFO WINELIB_NAME_AW(INSTALLPROPERTY_URLUPDATEINFO) + +#define INSTALLPROPERTY_VERSIONMINORA "VersionMinor" +static const WCHAR INSTALLPROPERTY_VERSIONMINORW[] = {'V','e','r','s','i','o','n','M','i','n','o','r',0}; +#define INSTALLPROPERTY_VERSIONMINOR WINELIB_NAME_AW(INSTALLPROPERTY_VERSIONMINOR) + +#define INSTALLPROPERTY_VERSIONMAJORA "VersionMajor" +static const WCHAR INSTALLPROPERTY_VERSIONMAJORW[] = {'V','e','r','s','i','o','n','M','a','j','o','r',0}; +#define INSTALLPROPERTY_VERSIONMAJOR WINELIB_NAME_AW(INSTALLPROPERTY_VERSIONMAJOR) + +#define INSTALLPROPERTY_PRODUCTIDA "ProductID" +static const WCHAR INSTALLPROPERTY_PRODUCTIDW[] = {'P','r','o','d','u','c','t','I','D',0}; +#define INSTALLPROPERTY_PRODUCTID WINELIB_NAME_AW(INSTALLPROPERTY_PRODUCTID) + +#define INSTALLPROPERTY_REGCOMPANYA "RegCompany" +static const WCHAR INSTALLPROPERTY_REGCOMPANYW[] = {'R','e','g','C','o','m','p','a','n','y',0}; +#define INSTALLPROPERTY_REGCOMPANY WINELIB_NAME_AW(INSTALLPROPERTY_REGCOMPANY) + +#define INSTALLPROPERTY_REGOWNERA "RegOwner" +static const WCHAR INSTALLPROPERTY_REGOWNERW[] = {'R','e','g','O','w','n','e','r',0}; +#define INSTALLPROPERTY_REGOWNER WINELIB_NAME_AW(INSTALLPROPERTY_REGOWNER) + +/* MSI Version 3.0 and greater */ +#define INSTALLPROPERTY_UNINSTALLABLEA "Uninstallable" +static const WCHAR INSTALLPROPERTY_UNINSTALLABLEW[] = {'U','n','i','n','s','t','a','l','l','a','b','l','e',0}; +#define INSTALLPROPERTY_UNINSTALLABLE WINELIB_NAME_AW(INSTALLPROPERTY_UNINSTALLABLE) + +#define INSTALLPROPERTY_PRODUCTSTATEA "State" +static const WCHAR INSTALLPROPERTY_PRODUCTSTATEW[] = {'S','t','a','t','e',0}; +#define INSTALLPROPERTY_PRODUCTSTATE WINELIB_NAME_AW(INSTALLPROPERTY_PRODUCTSTATE) + +#define INSTALLPROPERTY_PATCHSTATEA "State" +static const WCHAR INSTALLPROPERTY_PATCHSTATEW[] ={'S','t','a','t','e',0}; +#define INSTALLPROPERTY_PATCHSTATE WINELIB_NAME_AW(INSTALLPROPERTY_PATCHSTATE) + +#define INSTALLPROPERTY_PATCHTYPEA "PatchType" +static const WCHAR INSTALLPROPERTY_PATCHTYPEW[] = {'P','a','t','c','h','T','y','p','e',0}; +#define INSTALLPROPERTY_PATCHTYPE WINELIB_NAME_AW(INSTALLPROPERTY_PATCHTYPE) + +#define INSTALLPROPERTY_LUAENABLEDA "LUAEnabled" +static const WCHAR INSTALLPROPERTY_LUAENABLEDW[] = {'L','U','A','E','n','a','b','l','e','d',0}; +#define INSTALLPROPERTY_LUAENABLED WINELIB_NAME_AW(INSTALLPROPERTY_LUAENABLED) + +#define INSTALLPROPERTY_DISPLAYNAMEA "DisplayName" +static const WCHAR INSTALLPROPERTY_DISPLAYNAMEW[] = {'D','i','s','p','l','a','y','N','a','m','e',0}; +#define INSTALLPROPERTY_DISPLAYNAME WINELIB_NAME_AW(INSTALLPROPERTY_DISPLAYNAME) + +#define INSTALLPROPERTY_MOREINFOURLA "MoreInfoURL" +static const WCHAR INSTALLPROPERTY_MOREINFOURLW[] = {'M','o','r','e','I','n','f','o','U','R','L',0}; +#define INSTALLPROPERTY_MOREINFOURL WINELIB_NAME_AW(INSTALLPROPERTY_MOREINFOURL) + +/* Source List Info */ +#define INSTALLPROPERTY_LASTUSEDSOURCEA "LastUsedSource" +static const WCHAR INSTALLPROPERTY_LASTUSEDSOURCEW[] = {'L','a','s','t','U','s','e','d','S','o','u','r','c','e',0}; +#define INSTALLPROPERTY_LASTUSEDSOURCE WINELIB_NAME_AW(INSTALLPROPERTY_LASTUSEDSOURCE) + +#define INSTALLPROPERTY_LASTUSEDTYPEA "LastUsedType" +static const WCHAR INSTALLPROPERTY_LASTUSEDTYPEW[] = {'L','a','s','t','U','s','e','d','T','y','p','e',0}; +#define INSTALLPROPERTY_LASTUSEDTYPE WINELIB_NAME_AW(INSTALLPROPERTY_LASTUSEDTYPE) + +#define INSTALLPROPERTY_MEDIAPACKAGEPATHA "MediaPackagePath" +static const WCHAR INSTALLPROPERTY_MEDIAPACKAGEPATHW[] = {'M','e','d','i','a','P','a','c','k','a','g','e','P','a','t','h',0}; +#define INSTALLPROPERTY_MEDIAPACKAGEPATH WINELIB_NAME_AW(INSTALLPROPERTY_MEDIAPACKAGEPATH) + +#define INSTALLPROPERTY_DISKPROMPTA "DiskPrompt" +static const WCHAR INSTALLPROPERTY_DISKPROMPTW[] = {'D','i','s','k','P','r','o','m','p','t',0}; +#define INSTALLPROPERTY_DISKPROMPT WINELIB_NAME_AW(INSTALLPROPERTY_DISKPROMPT) + +typedef INT (CALLBACK *INSTALLUI_HANDLERA)(LPVOID, UINT, LPCSTR); +typedef INT (CALLBACK *INSTALLUI_HANDLERW)(LPVOID, UINT, LPCWSTR); +typedef INT (CALLBACK *INSTALLUI_HANDLER_RECORD)(LPVOID, UINT, MSIHANDLE); +typedef INSTALLUI_HANDLER_RECORD* PINSTALLUI_HANDLER_RECORD; + +UINT WINAPI MsiAdvertiseProductA(LPCSTR, LPCSTR, LPCSTR, LANGID); +UINT WINAPI MsiAdvertiseProductW(LPCWSTR, LPCWSTR, LPCWSTR, LANGID); +#define MsiAdvertiseProduct WINELIB_NAME_AW(MsiAdvertiseProduct) + +UINT WINAPI MsiAdvertiseProductExA(LPCSTR, LPCSTR, LPCSTR, LANGID, DWORD, DWORD); +UINT WINAPI MsiAdvertiseProductExW(LPCWSTR, LPCWSTR, LPCWSTR, LANGID, DWORD, DWORD); +#define MsiAdvertiseProductEx WINELIB_NAME_AW(MsiAdvertiseProductEx) + +UINT WINAPI MsiInstallProductA(LPCSTR, LPCSTR); +UINT WINAPI MsiInstallProductW(LPCWSTR, LPCWSTR); +#define MsiInstallProduct WINELIB_NAME_AW(MsiInstallProduct) + +UINT WINAPI MsiReinstallProductA(LPCSTR, DWORD); +UINT WINAPI MsiReinstallProductW(LPCWSTR, DWORD); +#define MsiReinstallProduct WINELIB_NAME_AW(MsiReinstallProduct) + +UINT WINAPI MsiApplyPatchA(LPCSTR, LPCSTR, INSTALLTYPE, LPCSTR); +UINT WINAPI MsiApplyPatchW(LPCWSTR, LPCWSTR, INSTALLTYPE, LPCWSTR); +#define MsiApplyPatch WINELIB_NAME_AW(MsiApplyPatch) + +UINT WINAPI MsiEnumComponentCostsA(MSIHANDLE, LPCSTR, DWORD, INSTALLSTATE, LPSTR, LPDWORD, LPINT, LPINT); +UINT WINAPI MsiEnumComponentCostsW(MSIHANDLE, LPCWSTR, DWORD, INSTALLSTATE, LPWSTR, LPDWORD, LPINT, LPINT); +#define MsiEnumComponentCosts WINELIB_NAME_AW(MsiEnumComponentCosts) + +UINT WINAPI MsiEnumProductsA(DWORD, LPSTR); +UINT WINAPI MsiEnumProductsW(DWORD, LPWSTR); +#define MsiEnumProducts WINELIB_NAME_AW(MsiEnumProducts) + +UINT WINAPI MsiEnumProductsExA(LPCSTR, LPCSTR, DWORD, DWORD, CHAR[39], MSIINSTALLCONTEXT*, LPSTR, LPDWORD); +UINT WINAPI MsiEnumProductsExW(LPCWSTR, LPCWSTR, DWORD, DWORD, WCHAR[39], MSIINSTALLCONTEXT*, LPWSTR, LPDWORD); +#define MsiEnumProductsEx WINELIB_NAME_AW(MsiEnumProductsEx) + +UINT WINAPI MsiEnumFeaturesA(LPCSTR, DWORD, LPSTR, LPSTR); +UINT WINAPI MsiEnumFeaturesW(LPCWSTR, DWORD, LPWSTR, LPWSTR); +#define MsiEnumFeatures WINELIB_NAME_AW(MsiEnumFeatures) + +UINT WINAPI MsiEnumComponentsA(DWORD, LPSTR); +UINT WINAPI MsiEnumComponentsW(DWORD, LPWSTR); +#define MsiEnumComponents WINELIB_NAME_AW(MsiEnumComponents) + +UINT WINAPI MsiEnumComponentsExA(LPCSTR, DWORD, DWORD, CHAR[39], + MSIINSTALLCONTEXT *, LPSTR, LPDWORD); +UINT WINAPI MsiEnumComponentsExW(LPCWSTR, DWORD, DWORD, WCHAR[39], + MSIINSTALLCONTEXT *, LPWSTR, LPDWORD); +#define MsiEnumComponentsEx WINELIB_NAME_AW(MsiEnumComponentsEx) + +UINT WINAPI MsiEnumClientsA(LPCSTR, DWORD, LPSTR); +UINT WINAPI MsiEnumClientsW(LPCWSTR, DWORD, LPWSTR); +#define MsiEnumClients WINELIB_NAME_AW(MsiEnumClients) + +UINT WINAPI MsiOpenPackageA(LPCSTR, MSIHANDLE*); +UINT WINAPI MsiOpenPackageW(LPCWSTR, MSIHANDLE*); +#define MsiOpenPackage WINELIB_NAME_AW(MsiOpenPackage) + +UINT WINAPI MsiOpenPackageExA(LPCSTR, DWORD, MSIHANDLE*); +UINT WINAPI MsiOpenPackageExW(LPCWSTR, DWORD, MSIHANDLE*); +#define MsiOpenPackageEx WINELIB_NAME_AW(MsiOpenPackageEx) + +UINT WINAPI MsiOpenProductA(LPCSTR, MSIHANDLE*); +UINT WINAPI MsiOpenProductW(LPCWSTR, MSIHANDLE*); +#define MsiOpenProduct WINELIB_NAME_AW(MsiOpenProduct) + +UINT WINAPI MsiGetProductPropertyA(MSIHANDLE,LPCSTR,LPSTR,LPDWORD); +UINT WINAPI MsiGetProductPropertyW(MSIHANDLE,LPCWSTR,LPWSTR,LPDWORD); +#define MsiGetProductProperty WINELIB_NAME_AW(MsiGetProductProperty) + +UINT WINAPI MsiVerifyPackageA(LPCSTR); +UINT WINAPI MsiVerifyPackageW(LPCWSTR); +#define MsiVerifyPackage WINELIB_NAME_AW(MsiVerifyPackage) + +UINT WINAPI MsiQueryComponentStateA(LPCSTR,LPCSTR,MSIINSTALLCONTEXT,LPCSTR,INSTALLSTATE*); +UINT WINAPI MsiQueryComponentStateW(LPCWSTR,LPCWSTR,MSIINSTALLCONTEXT,LPCWSTR,INSTALLSTATE*); +#define MsiQueryComponentState WINELIB_NAME_AW(MsiQueryComponentState) + +INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR); +INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR); +#define MsiQueryProductState WINELIB_NAME_AW(MsiQueryProductState) + +UINT WINAPI MsiConfigureProductA(LPCSTR, int, INSTALLSTATE); +UINT WINAPI MsiConfigureProductW(LPCWSTR, int, INSTALLSTATE); +#define MsiConfigureProduct WINELIB_NAME_AW(MsiConfigureProduct); + +UINT WINAPI MsiConfigureProductExA(LPCSTR, int, INSTALLSTATE, LPCSTR); +UINT WINAPI MsiConfigureProductExW(LPCWSTR, int, INSTALLSTATE, LPCWSTR); +#define MsiConfigureProductEx WINELIB_NAME_AW(MsiConfigureProductEx); + +UINT WINAPI MsiConfigureFeatureA(LPCSTR, LPCSTR, INSTALLSTATE); +UINT WINAPI MsiConfigureFeatureW(LPCWSTR, LPCWSTR, INSTALLSTATE); +#define MsiConfigureFeature WINELIB_NAME_AW(MsiConfigureFeature); + +UINT WINAPI MsiGetProductCodeA(LPCSTR, LPSTR); +UINT WINAPI MsiGetProductCodeW(LPCWSTR, LPWSTR); +#define MsiGetProductCode WINELIB_NAME_AW(MsiGetProductCode) + +UINT WINAPI MsiGetProductInfoA(LPCSTR, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiGetProductInfoW(LPCWSTR, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetProductInfo WINELIB_NAME_AW(MsiGetProductInfo) + +UINT WINAPI MsiGetProductInfoExA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiGetProductInfoExW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetProductInfoEx WINELIB_NAME_AW(MsiGetProductInfoEx) + +UINT WINAPI MsiGetPatchInfoExA(LPCSTR, LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiGetPatchInfoExW(LPCWSTR, LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetPatchInfoEx WINELIB_NAME_AW(MsiGetPatchInfoEx) + +UINT WINAPI MsiGetPatchInfoA(LPCSTR, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiGetPatchInfoW(LPCWSTR, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetPatchInfo WINELIB_NAME_AW(MsiGetPatchInfo) + +UINT WINAPI MsiEnableLogA(DWORD, LPCSTR, DWORD); +UINT WINAPI MsiEnableLogW(DWORD, LPCWSTR, DWORD); +#define MsiEnableLog WINELIB_NAME_AW(MsiEnableLog) + +INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA, DWORD, LPVOID); +INSTALLUI_HANDLERW WINAPI MsiSetExternalUIW(INSTALLUI_HANDLERW, DWORD, LPVOID); +#define MsiSetExternalUI WINELIB_NAME_AW(MsiSetExternalUI) + +INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR, LPCSTR, LPSTR, LPDWORD); +INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetComponentPath WINELIB_NAME_AW(MsiGetComponentPath) + +INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR, LPCSTR); +INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR, LPCWSTR); +#define MsiQueryFeatureState WINELIB_NAME_AW(MsiQueryFeatureState) + +UINT WINAPI MsiGetFeatureInfoA(MSIHANDLE, LPCSTR, LPDWORD, LPSTR, LPDWORD, LPSTR, LPDWORD); +UINT WINAPI MsiGetFeatureInfoW(MSIHANDLE, LPCWSTR, LPDWORD, LPWSTR, LPDWORD, LPWSTR, LPDWORD); +#define MsiGetFeatureInfo WINELIB_NAME_AW(MsiGetFeatureInfo) + +UINT WINAPI MsiGetFeatureUsageA(LPCSTR, LPCSTR, LPDWORD, LPWORD); +UINT WINAPI MsiGetFeatureUsageW(LPCWSTR, LPCWSTR, LPDWORD, LPWORD); +#define MsiGetFeatureUsage WINELIB_NAME_AW(MsiGetFeatureUsage) + +UINT WINAPI MsiEnumRelatedProductsA(LPCSTR, DWORD, DWORD, LPSTR); +UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR, DWORD, DWORD, LPWSTR); +#define MsiEnumRelatedProducts WINELIB_NAME_AW(MsiEnumRelatedProducts) + +UINT WINAPI MsiProvideAssemblyA(LPCSTR, LPCSTR, DWORD, DWORD, LPSTR, LPDWORD); +UINT WINAPI MsiProvideAssemblyW(LPCWSTR, LPCWSTR, DWORD, DWORD, LPWSTR, LPDWORD); +#define MsiProvideAssembly WINELIB_NAME_AW(MsiProvideAssembly) + +UINT WINAPI MsiEnumComponentQualifiersA(LPCSTR, DWORD, LPSTR, LPDWORD, LPSTR, LPDWORD); +UINT WINAPI MsiEnumComponentQualifiersW(LPCWSTR, DWORD, LPWSTR, LPDWORD, LPWSTR, LPDWORD); +#define MsiEnumComponentQualifiers WINELIB_NAME_AW(MsiEnumComponentQualifiers) + +UINT WINAPI MsiGetFileVersionA(LPCSTR, LPSTR, LPDWORD, LPSTR, LPDWORD); +UINT WINAPI MsiGetFileVersionW(LPCWSTR, LPWSTR, LPDWORD, LPWSTR, LPDWORD); +#define MsiGetFileVersion WINELIB_NAME_AW(MsiGetFileVersion) + +UINT WINAPI MsiMessageBoxA(HWND, LPCSTR, LPCSTR, UINT, WORD, DWORD); +UINT WINAPI MsiMessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT, WORD, DWORD); +#define MsiMessageBox WINELIB_NAME_AW(MsiMessageBox) + +UINT WINAPI MsiProvideQualifiedComponentExA(LPCSTR, LPCSTR, DWORD, LPCSTR, DWORD, DWORD, LPSTR, LPDWORD); +UINT WINAPI MsiProvideQualifiedComponentExW(LPCWSTR, LPCWSTR, DWORD, LPCWSTR, DWORD, DWORD, LPWSTR, LPDWORD); +#define MsiProvideQualifiedComponentEx WINELIB_NAME_AW(MsiProvideQualifiedComponentEx) + +UINT WINAPI MsiProvideQualifiedComponentA(LPCSTR, LPCSTR, DWORD, LPSTR, LPDWORD); +UINT WINAPI MsiProvideQualifiedComponentW(LPCWSTR, LPCWSTR, DWORD, LPWSTR, LPDWORD); +#define MsiProvideQualifiedComponent WINELIB_NAME_AW(MsiProvideQualifiedComponent) + +USERINFOSTATE WINAPI MsiGetUserInfoA(LPCSTR, LPSTR, LPDWORD, LPSTR, LPDWORD, LPSTR, LPDWORD); +USERINFOSTATE WINAPI MsiGetUserInfoW(LPCWSTR, LPWSTR, LPDWORD, LPWSTR, LPDWORD, LPWSTR, LPDWORD); +#define MsiGetUserInfo WINELIB_NAME_AW(MsiGetUserInfo) + +UINT WINAPI MsiCollectUserInfoA(LPCSTR); +UINT WINAPI MsiCollectUserInfoW(LPCWSTR); +#define MsiCollectUserInfo WINELIB_NAME_AW(MsiCollectUserInfo) + +UINT WINAPI MsiReinstallFeatureA(LPCSTR, LPCSTR, DWORD); +UINT WINAPI MsiReinstallFeatureW(LPCWSTR, LPCWSTR, DWORD); +#define MsiReinstallFeature WINELIB_NAME_AW(MsiReinstallFeature) + +UINT WINAPI MsiGetShortcutTargetA(LPCSTR, LPSTR, LPSTR, LPSTR); +UINT WINAPI MsiGetShortcutTargetW(LPCWSTR, LPWSTR, LPWSTR, LPWSTR); +#define MsiGetShortcutTarget WINELIB_NAME_AW(MsiGetShortcutTarget) + +INSTALLSTATE WINAPI MsiUseFeatureW(LPCWSTR, LPCWSTR); +INSTALLSTATE WINAPI MsiUseFeatureA(LPCSTR, LPCSTR); +#define MsiUseFeature WINELIB_NAME_AW(MsiUseFeature) + +INSTALLSTATE WINAPI MsiUseFeatureExW(LPCWSTR, LPCWSTR, DWORD, DWORD); +INSTALLSTATE WINAPI MsiUseFeatureExA(LPCSTR, LPCSTR, DWORD, DWORD); +#define MsiUseFeatureEx WINELIB_NAME_AW(MsiUseFeatureEx) + +HRESULT WINAPI MsiGetFileSignatureInformationA(LPCSTR, DWORD, PCCERT_CONTEXT*, LPBYTE, LPDWORD); +HRESULT WINAPI MsiGetFileSignatureInformationW(LPCWSTR, DWORD, PCCERT_CONTEXT*, LPBYTE, LPDWORD); +#define MsiGetFileSignatureInformation WINELIB_NAME_AW(MsiGetFileSignatureInformation) + +INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR, LPSTR, LPDWORD); +INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR, LPWSTR, LPDWORD); +#define MsiLocateComponent WINELIB_NAME_AW(MsiLocateComponent) + +UINT WINAPI MsiSourceListAddSourceA(LPCSTR, LPCSTR, DWORD, LPCSTR); +UINT WINAPI MsiSourceListAddSourceW(LPCWSTR, LPCWSTR, DWORD, LPCWSTR); +#define MsiSourceListAddSource WINELIB_NAME_AW(MsiSourceListAddSource) + +UINT WINAPI MsiSourceListEnumMediaDisksA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPDWORD, + LPSTR, LPDWORD, LPSTR, LPDWORD); +UINT WINAPI MsiSourceListEnumMediaDisksW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPDWORD, + LPWSTR, LPDWORD, LPWSTR, LPDWORD); +#define MsiSourceListEnumMediaDisks WINELIB_NAME_AW(MsiSourceListEnumMediaDisks) + +UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPSTR, LPDWORD); +UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPWSTR, LPDWORD); +#define MsiSourceListEnumSources WINELIB_NAME_AW(MsiSourceListEnumSources) + +UINT WINAPI MsiSourceListClearSourceA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR); +UINT WINAPI MsiSourceListClearSourceW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, LPCWSTR); +#define MsiSourceListClearSource WINELIB_NAME_AW(MsiSourceListClearSource) + +UINT WINAPI MsiSourceListClearAllA(LPCSTR, LPCSTR, DWORD); +UINT WINAPI MsiSourceListClearAllW(LPCWSTR, LPCWSTR, DWORD); +#define MsiSourceListClearAll WINELIB_NAME_AW(MsiSourceListClearAll) + +UINT WINAPI MsiSourceListGetInfoA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiSourceListGetInfoW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, LPCWSTR, LPWSTR, LPDWORD); +#define MsiSourceListGetInfo WINELIB_NAME_AW(MsiSourceListGetInfo) + +UINT WINAPI MsiSourceListSetInfoA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPCSTR); +UINT WINAPI MsiSourceListSetInfoW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, LPCWSTR, LPCWSTR); +#define MsiSourceListSetInfo WINELIB_NAME_AW(MsiSourceListSetInfo) + +UINT WINAPI MsiSourceListAddSourceExA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, DWORD); +UINT WINAPI MsiSourceListAddSourceExW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, LPCWSTR, DWORD); +#define MsiSourceListAddSourceEx WINELIB_NAME_AW(MsiSourceListAddSourceEx) + +UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPCSTR, LPCSTR); +UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, DWORD, LPCWSTR, LPCWSTR); +#define MsiSourceListAddMediaDisk WINELIB_NAME_AW(MsiSourceListAddMediaDisk) + +UINT WINAPI MsiEnumPatchesA(LPCSTR, DWORD, LPSTR, LPSTR, LPDWORD); +UINT WINAPI MsiEnumPatchesW(LPCWSTR, DWORD, LPWSTR, LPWSTR, LPDWORD); +#define MsiEnumPatches WINELIB_NAME_AW(MsiEnumPatches) + +UINT WINAPI MsiEnumPatchesExA(LPCSTR, LPCSTR, DWORD, DWORD, DWORD, LPSTR, LPSTR, + MSIINSTALLCONTEXT*, LPSTR, LPDWORD); +UINT WINAPI MsiEnumPatchesExW(LPCWSTR, LPCWSTR, DWORD, DWORD, DWORD, LPWSTR, LPWSTR, + MSIINSTALLCONTEXT*, LPWSTR, LPDWORD); +#define MsiEnumPatchesEx WINELIB_NAME_AW(MsiEnumPatchesEx) + +UINT WINAPI MsiGetFileHashA(LPCSTR, DWORD, PMSIFILEHASHINFO); +UINT WINAPI MsiGetFileHashW(LPCWSTR, DWORD, PMSIFILEHASHINFO); +#define MsiGetFileHash WINELIB_NAME_AW(MsiGetFileHash) + +UINT WINAPI MsiAdvertiseScriptA(LPCSTR, DWORD, PHKEY, BOOL); +UINT WINAPI MsiAdvertiseScriptW(LPCWSTR, DWORD, PHKEY, BOOL); +#define MsiAdvertiseScript WINELIB_NAME_AW(MsiAdvertiseScript) + +UINT WINAPI MsiIsProductElevatedA(LPCSTR, BOOL *); +UINT WINAPI MsiIsProductElevatedW(LPCWSTR, BOOL *); +#define MsiIsProductElevated WINELIB_NAME_AW(MsiIsProductElevated) + +UINT WINAPI MsiDatabaseMergeA(MSIHANDLE, MSIHANDLE, LPCSTR); +UINT WINAPI MsiDatabaseMergeW(MSIHANDLE, MSIHANDLE, LPCWSTR); +#define MsiDatabaseMerge WINELIB_NAME_AW(MsiDatabaseMerge) + +UINT WINAPI MsiInstallMissingComponentA(LPCSTR, LPCSTR, INSTALLSTATE); +UINT WINAPI MsiInstallMissingComponentW(LPCWSTR, LPCWSTR, INSTALLSTATE); +#define MsiInstallMissingComponent WINELIB_NAME_AW(MsiInstallMissingComponent) + +UINT WINAPI MsiDetermineApplicablePatchesA(LPCSTR, DWORD, PMSIPATCHSEQUENCEINFOA); +UINT WINAPI MsiDetermineApplicablePatchesW(LPCWSTR, DWORD, PMSIPATCHSEQUENCEINFOW); +#define MsiDetermineApplicablePatches WINELIB_NAME_AW(MsiDetermineApplicablePatches) + +UINT WINAPI MsiDeterminePatchSequenceA(LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, PMSIPATCHSEQUENCEINFOA); +UINT WINAPI MsiDeterminePatchSequenceW(LPCWSTR, LPCWSTR, MSIINSTALLCONTEXT, DWORD, PMSIPATCHSEQUENCEINFOW); +#define MsiDeterminePatchSequence WINELIB_NAME_AW(MsiDeterminePatchSequence) + +UINT WINAPI MsiApplyMultiplePatchesA(LPCSTR, LPCSTR, LPCSTR); +UINT WINAPI MsiApplyMultiplePatchesW(LPCWSTR, LPCWSTR, LPCWSTR); +#define MsiApplyMultiplePatches WINELIB_NAME_AW(MsiApplyMultiplePatches) + +UINT WINAPI MsiBeginTransactionA(LPCSTR, DWORD, MSIHANDLE *, HANDLE *); +UINT WINAPI MsiBeginTransactionW(LPCWSTR, DWORD, MSIHANDLE *, HANDLE *); +#define MsiBeginTransaction WINELIB_NAME_AW(MsiBeginTransaction) + +UINT WINAPI MsiEndTransaction(DWORD); + +/* Non Unicode */ +UINT WINAPI MsiCloseHandle(MSIHANDLE); +UINT WINAPI MsiCloseAllHandles(void); +INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL, HWND*); +UINT WINAPI MsiSetExternalUIRecord(INSTALLUI_HANDLER_RECORD, DWORD, LPVOID, PINSTALLUI_HANDLER_RECORD); + +#ifdef __cplusplus +} +#endif + +#endif /* __MSI_H */ diff --git a/libmsi/msi.rc b/libmsi/msi.rc new file mode 100644 index 0000000..359ca35 --- /dev/null +++ b/libmsi/msi.rc @@ -0,0 +1,85 @@ +/* + * Resources for MSI + * + * Copyright 2005 Mike McCormack + * + * 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 "windef.h" + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + 4 "The specified installation package could not be opened. Please check the file path and try again." + 5 "path %s not found" + 9 "insert disk %s" + 10 "Windows Installer %s\n\n" \ + "Usage:\n" \ + "msiexec command {required parameter} [optional parameter]\n\n" \ + "Install a product:\n" \ + "\t/i {package|product_code} [property]\n" \ + "\t/package {package|product_code} [property]\n" \ + "\t/a package [property]\n" \ + "Repair an installation:\n" \ + "\t/f[p|o|e|d|c|a|u|m|s|v] {package|product_code}\n" \ + "Uninstall a product:\n" \ + "\t/uninstall {package|product_code} [property]\n" \ + "\t/x {package|product_code} [property]\n" \ + "Advertise a product:\n" \ + "\t/j[u|m] package [/t transform] [/g languageid]\n" \ + "Apply a patch:\n" \ + "\t/p patch_package [property]\n" \ + "\t/p patch_package /a package [property]\n" \ + "Log and user interface modifiers for the above commands:\n" \ + "\t/l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] log_file\n" \ + "\t/q{|n|b|r|f|n+|b+|b-}\n" \ + "Register the MSI Service:\n" \ + "\t/y\n" \ + "Unregister the MSI Service:\n" \ + "\t/z\n" \ + "Display this help:\n" \ + "\t/help\n" \ + "\t/?\n" + 11 "enter which folder contains %s" + 12 "install source for feature missing" + 13 "network drive for feature missing" + 14 "feature from:" + 15 "choose which folder contains %s" +} + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +/* @makedep: msiserver.rgs */ +1 WINE_REGISTRY msiserver.rgs + +/* @makedep: instadvert.bmp */ +0x1001 BITMAP instadvert.bmp + +/* @makedep: instabsent.bmp */ +0x1002 BITMAP instabsent.bmp + +/* @makedep: instlocal.bmp */ +0x1003 BITMAP instlocal.bmp + +#define WINE_FILEDESCRIPTION_STR "Wine MSI dll" +#define WINE_FILENAME_STR "msi.dll" +#define WINE_FILEVERSION 4,5,6001,22299 +#define WINE_FILEVERSION_STR "4.5.6001.22299" +#define WINE_PRODUCTVERSION 4,5,6001,22299 +#define WINE_PRODUCTVERSION_STR "4.5.6001.22299" + +#include "wine/wine_common_ver.rc" diff --git a/libmsi/msi_main.c b/libmsi/msi_main.c new file mode 100644 index 0000000..de0534a --- /dev/null +++ b/libmsi/msi_main.c @@ -0,0 +1,245 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2006 Mike McCormack 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 +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "oleauto.h" +#include "rpcproxy.h" +#include "msipriv.h" +#include "msiserver.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static LONG dll_count; + +/* the UI level */ +INSTALLUILEVEL gUILevel = INSTALLUILEVEL_BASIC; +HWND gUIhwnd = 0; +INSTALLUI_HANDLERA gUIHandlerA = NULL; +INSTALLUI_HANDLERW gUIHandlerW = NULL; +INSTALLUI_HANDLER_RECORD gUIHandlerRecord = NULL; +DWORD gUIFilter = 0; +LPVOID gUIContext = NULL; +WCHAR *gszLogFile = NULL; +HINSTANCE msi_hInstance; + + +/* + * Dll lifetime tracking declaration + */ +static void LockModule(void) +{ + InterlockedIncrement(&dll_count); +} + +static void UnlockModule(void) +{ + InterlockedDecrement(&dll_count); +} + +/****************************************************************** + * DllMain + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + msi_hInstance = hinstDLL; + DisableThreadLibraryCalls(hinstDLL); + IsWow64Process( GetCurrentProcess(), &is_wow64 ); + break; + case DLL_PROCESS_DETACH: + msi_dialog_unregister_class(); + msi_free_handle_table(); + msi_free( gszLogFile ); + break; + } + return TRUE; +} + +typedef struct tagIClassFactoryImpl { + IClassFactory IClassFactory_iface; + HRESULT (*create_object)( IUnknown*, LPVOID* ); +} IClassFactoryImpl; + +static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface); +} + +static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface, + REFIID riid,LPVOID *ppobj) +{ + IClassFactoryImpl *This = impl_from_IClassFactory(iface); + + TRACE("%p %s %p\n",This,debugstr_guid(riid),ppobj); + + if( IsEqualCLSID( riid, &IID_IUnknown ) || + IsEqualCLSID( riid, &IID_IClassFactory ) ) + { + IClassFactory_AddRef( iface ); + *ppobj = iface; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface) +{ + LockModule(); + return 2; +} + +static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface) +{ + UnlockModule(); + return 1; +} + +static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface, + LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj) +{ + IClassFactoryImpl *This = impl_from_IClassFactory(iface); + IUnknown *unk = NULL; + HRESULT r; + + TRACE("%p %p %s %p\n", This, pOuter, debugstr_guid(riid), ppobj); + + r = This->create_object( pOuter, (LPVOID*) &unk ); + if (SUCCEEDED(r)) + { + r = IUnknown_QueryInterface( unk, riid, ppobj ); + IUnknown_Release( unk ); + } + return r; +} + +static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface, BOOL dolock) +{ + TRACE("%p %d\n", iface, dolock); + + if (dolock) + LockModule(); + else + UnlockModule(); + + return S_OK; +} + +static const IClassFactoryVtbl MsiCF_Vtbl = +{ + MsiCF_QueryInterface, + MsiCF_AddRef, + MsiCF_Release, + MsiCF_CreateInstance, + MsiCF_LockServer +}; + +static IClassFactoryImpl MsiServer_CF = { { &MsiCF_Vtbl }, create_msiserver }; +static IClassFactoryImpl WineMsiCustomRemote_CF = { { &MsiCF_Vtbl }, create_msi_custom_remote }; +static IClassFactoryImpl WineMsiRemotePackage_CF = { { &MsiCF_Vtbl }, create_msi_remote_package }; + +/****************************************************************** + * DllGetClassObject [MSI.@] + */ +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + TRACE("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if ( IsEqualCLSID (rclsid, &CLSID_MsiInstaller) ) + { + *ppv = &MsiServer_CF; + return S_OK; + } + + if ( IsEqualCLSID (rclsid, &CLSID_WineMsiRemoteCustomAction) ) + { + *ppv = &WineMsiCustomRemote_CF; + return S_OK; + } + + if ( IsEqualCLSID (rclsid, &CLSID_WineMsiRemotePackage) ) + { + *ppv = &WineMsiRemotePackage_CF; + return S_OK; + } + + if( IsEqualCLSID (rclsid, &CLSID_MsiServerMessage) || + IsEqualCLSID (rclsid, &CLSID_MsiServer) || + IsEqualCLSID (rclsid, &CLSID_PSFactoryBuffer) || + IsEqualCLSID (rclsid, &CLSID_MsiServerX3) ) + { + FIXME("create %s object\n", debugstr_guid( rclsid )); + } + + return CLASS_E_CLASSNOTAVAILABLE; +} + +/****************************************************************** + * DllGetVersion [MSI.@] + */ +HRESULT WINAPI DllGetVersion(DLLVERSIONINFO *pdvi) +{ + TRACE("%p\n",pdvi); + + if (pdvi->cbSize < sizeof(DLLVERSIONINFO)) + return E_INVALIDARG; + + pdvi->dwMajorVersion = MSI_MAJORVERSION; + pdvi->dwMinorVersion = MSI_MINORVERSION; + pdvi->dwBuildNumber = MSI_BUILDNUMBER; + pdvi->dwPlatformID = DLLVER_PLATFORM_WINDOWS; + + return S_OK; +} + +/****************************************************************** + * DllCanUnloadNow [MSI.@] + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + return dll_count == 0 ? S_OK : S_FALSE; +} + +/*********************************************************************** + * DllRegisterServer (MSI.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + return __wine_register_resources( msi_hInstance ); +} + +/*********************************************************************** + * DllUnregisterServer (MSI.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + return __wine_unregister_resources( msi_hInstance ); +} diff --git a/libmsi/msipriv.h b/libmsi/msipriv.h new file mode 100644 index 0000000..73bccec --- /dev/null +++ b/libmsi/msipriv.h @@ -0,0 +1,1249 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack for CodeWeavers + * Copyright 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 + */ + +#ifndef __WINE_MSI_PRIVATE__ +#define __WINE_MSI_PRIVATE__ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "fdi.h" +#include "msi.h" +#include "msiquery.h" +#include "msidefs.h" +#include "objbase.h" +#include "objidl.h" +#include "fusion.h" +#include "winnls.h" +#include "winver.h" +#include "wine/list.h" +#include "wine/debug.h" + +static const BOOL is_64bit = sizeof(void *) > sizeof(int); +BOOL is_wow64; + +#define MSI_DATASIZEMASK 0x00ff +#define MSITYPE_VALID 0x0100 +#define MSITYPE_LOCALIZABLE 0x200 +#define MSITYPE_STRING 0x0800 +#define MSITYPE_NULLABLE 0x1000 +#define MSITYPE_KEY 0x2000 +#define MSITYPE_TEMPORARY 0x4000 +#define MSITYPE_UNKNOWN 0x8000 + +#define MAX_STREAM_NAME_LEN 62 +#define LONG_STR_BYTES 3 + +/* Install UI level mask for AND operation to exclude flags */ +#define INSTALLUILEVEL_MASK 0x0007 + +#define MSITYPE_IS_BINARY(type) (((type) & ~MSITYPE_NULLABLE) == (MSITYPE_STRING|MSITYPE_VALID)) + +struct tagMSITABLE; +typedef struct tagMSITABLE MSITABLE; + +struct string_table; +typedef struct string_table string_table; + +struct tagMSIOBJECTHDR; +typedef struct tagMSIOBJECTHDR MSIOBJECTHDR; + +typedef VOID (*msihandledestructor)( MSIOBJECTHDR * ); + +struct tagMSIOBJECTHDR +{ + UINT magic; + UINT type; + LONG refcount; + msihandledestructor destructor; +}; + +#define MSI_INITIAL_MEDIA_TRANSFORM_OFFSET 10000 +#define MSI_INITIAL_MEDIA_TRANSFORM_DISKID 30000 + +typedef struct tagMSIDATABASE +{ + MSIOBJECTHDR hdr; + IStorage *storage; + string_table *strings; + UINT bytes_per_strref; + LPWSTR path; + LPWSTR deletefile; + LPCWSTR mode; + UINT media_transform_offset; + UINT media_transform_disk_id; + struct list tables; + struct list transforms; + struct list streams; +} MSIDATABASE; + +typedef struct tagMSIVIEW MSIVIEW; + +typedef struct tagMSIQUERY +{ + MSIOBJECTHDR hdr; + MSIVIEW *view; + UINT row; + MSIDATABASE *db; + struct list mem; +} MSIQUERY; + +/* maybe we can use a Variant instead of doing it ourselves? */ +typedef struct tagMSIFIELD +{ + UINT type; + union + { + INT iVal; + INT_PTR pVal; + LPWSTR szwVal; + IStream *stream; + } u; +} MSIFIELD; + +typedef struct tagMSIRECORD +{ + MSIOBJECTHDR hdr; + UINT count; /* as passed to MsiCreateRecord */ + MSIFIELD fields[1]; /* nb. array size is count+1 */ +} MSIRECORD; + +typedef struct tagMSISOURCELISTINFO +{ + struct list entry; + DWORD context; + DWORD options; + LPCWSTR property; + LPWSTR value; +} MSISOURCELISTINFO; + +typedef struct tagMSIMEDIADISK +{ + struct list entry; + DWORD context; + DWORD options; + DWORD disk_id; + LPWSTR volume_label; + LPWSTR disk_prompt; +} MSIMEDIADISK; + +typedef struct tagMSIMEDIAINFO +{ + UINT disk_id; + UINT type; + UINT last_sequence; + LPWSTR disk_prompt; + LPWSTR cabinet; + LPWSTR first_volume; + LPWSTR volume_label; + BOOL is_continuous; + BOOL is_extracted; + WCHAR sourcedir[MAX_PATH]; +} MSIMEDIAINFO; + +typedef struct tagMSICABINETSTREAM +{ + struct list entry; + UINT disk_id; + IStorage *storage; + WCHAR *stream; +} MSICABINETSTREAM; + +typedef struct tagMSIPATCHINFO +{ + struct list entry; + LPWSTR patchcode; + LPWSTR products; + LPWSTR transforms; + LPWSTR filename; + LPWSTR localfile; + MSIPATCHSTATE state; + BOOL delete_on_close; +} MSIPATCHINFO; + +typedef struct tagMSIBINARY +{ + struct list entry; + WCHAR *source; + WCHAR *tmpfile; + HMODULE module; +} MSIBINARY; + +typedef struct _column_info +{ + LPCWSTR table; + LPCWSTR column; + INT type; + BOOL temporary; + struct expr *val; + struct _column_info *next; +} column_info; + +typedef const struct tagMSICOLUMNHASHENTRY *MSIITERHANDLE; + +typedef struct tagMSIVIEWOPS +{ + /* + * fetch_int - reads one integer from {row,col} in the table + * + * This function should be called after the execute method. + * Data returned by the function should not change until + * close or delete is called. + * To get a string value, query the database's string table with + * the integer value returned from this function. + */ + UINT (*fetch_int)( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ); + + /* + * fetch_stream - gets a stream from {row,col} in the table + * + * This function is similar to fetch_int, except fetches a + * stream instead of an integer. + */ + UINT (*fetch_stream)( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ); + + /* + * get_row - gets values from a row + * + */ + UINT (*get_row)( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ); + + /* + * set_row - sets values in a row as specified by mask + * + * Similar semantics to fetch_int + */ + UINT (*set_row)( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ); + + /* + * Inserts a new row into the database from the records contents + */ + UINT (*insert_row)( struct tagMSIVIEW *view, MSIRECORD *record, UINT row, BOOL temporary ); + + /* + * Deletes a row from the database + */ + UINT (*delete_row)( struct tagMSIVIEW *view, UINT row ); + + /* + * execute - loads the underlying data into memory so it can be read + */ + UINT (*execute)( struct tagMSIVIEW *view, MSIRECORD *record ); + + /* + * close - clears the data read by execute from memory + */ + UINT (*close)( struct tagMSIVIEW *view ); + + /* + * get_dimensions - returns the number of rows or columns in a table. + * + * The number of rows can only be queried after the execute method + * is called. The number of columns can be queried at any time. + */ + UINT (*get_dimensions)( struct tagMSIVIEW *view, UINT *rows, UINT *cols ); + + /* + * get_column_info - returns the name and type of a specific column + * + * The column information can be queried at any time. + */ + UINT (*get_column_info)( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, UINT *type, + BOOL *temporary, LPCWSTR *table_name ); + + /* + * modify - not yet implemented properly + */ + UINT (*modify)( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *record, UINT row ); + + /* + * delete - destroys the structure completely + */ + UINT (*delete)( struct tagMSIVIEW * ); + + /* + * find_matching_rows - iterates through rows that match a value + * + * If the column type is a string then a string ID should be passed in. + * If the value to be looked up is an integer then no transformation of + * the input value is required, except if the column is a string, in which + * case a string ID should be passed in. + * The handle is an input/output parameter that keeps track of the current + * position in the iteration. It must be initialised to zero before the + * first call and continued to be passed in to subsequent calls. + */ + UINT (*find_matching_rows)( struct tagMSIVIEW *view, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle ); + + /* + * add_ref - increases the reference count of the table + */ + UINT (*add_ref)( struct tagMSIVIEW *view ); + + /* + * release - decreases the reference count of the table + */ + UINT (*release)( struct tagMSIVIEW *view ); + + /* + * add_column - adds a column to the table + */ + UINT (*add_column)( struct tagMSIVIEW *view, LPCWSTR table, UINT number, LPCWSTR column, UINT type, BOOL hold ); + + /* + * remove_column - removes the column represented by table name and column number from the table + */ + UINT (*remove_column)( struct tagMSIVIEW *view, LPCWSTR table, UINT number ); + + /* + * sort - orders the table by columns + */ + UINT (*sort)( struct tagMSIVIEW *view, column_info *columns ); + + /* + * drop - drops the table from the database + */ + UINT (*drop)( struct tagMSIVIEW *view ); +} MSIVIEWOPS; + +struct tagMSIVIEW +{ + MSIOBJECTHDR hdr; + const MSIVIEWOPS *ops; + MSIDBERROR error; + const WCHAR *error_column; +}; + +struct msi_dialog_tag; +typedef struct msi_dialog_tag msi_dialog; + +enum platform +{ + PLATFORM_INTEL, + PLATFORM_INTEL64, + PLATFORM_X64, + PLATFORM_ARM +}; + +enum clr_version +{ + CLR_VERSION_V10, + CLR_VERSION_V11, + CLR_VERSION_V20, + CLR_VERSION_V40, + CLR_VERSION_MAX +}; + +typedef struct tagMSIPACKAGE +{ + MSIOBJECTHDR hdr; + MSIDATABASE *db; + INT version; + enum platform platform; + UINT num_langids; + LANGID *langids; + struct list patches; + struct list components; + struct list features; + struct list files; + struct list filepatches; + struct list tempfiles; + struct list folders; + struct list binaries; + struct list cabinet_streams; + LPWSTR ActionFormat; + LPWSTR LastAction; + UINT action_progress_increment; + HANDLE log_file; + IAssemblyCache *cache_net[CLR_VERSION_MAX]; + IAssemblyCache *cache_sxs; + + struct list classes; + struct list extensions; + struct list progids; + struct list mimes; + struct list appids; + + struct tagMSISCRIPT *script; + + struct list RunningActions; + + LPWSTR BaseURL; + LPWSTR PackagePath; + LPWSTR ProductCode; + LPWSTR localfile; + BOOL delete_on_close; + + INSTALLUILEVEL ui_level; + UINT CurrentInstallState; + msi_dialog *dialog; + LPWSTR next_dialog; + float center_x; + float center_y; + + UINT WordCount; + UINT Context; + + struct list subscriptions; + + struct list sourcelist_info; + struct list sourcelist_media; + + unsigned char scheduled_action_running : 1; + unsigned char commit_action_running : 1; + unsigned char rollback_action_running : 1; + unsigned char need_reboot_at_end : 1; + unsigned char need_reboot_now : 1; + unsigned char need_rollback : 1; +} MSIPACKAGE; + +typedef struct tagMSIPREVIEW +{ + MSIOBJECTHDR hdr; + MSIPACKAGE *package; + msi_dialog *dialog; +} MSIPREVIEW; + +#define MSI_MAX_PROPS 20 + +typedef struct tagMSISUMMARYINFO +{ + MSIOBJECTHDR hdr; + IStorage *storage; + DWORD update_count; + PROPVARIANT property[MSI_MAX_PROPS]; +} MSISUMMARYINFO; + +typedef struct tagMSIFEATURE +{ + struct list entry; + LPWSTR Feature; + LPWSTR Feature_Parent; + LPWSTR Title; + LPWSTR Description; + INT Display; + INT Level; + LPWSTR Directory; + INT Attributes; + INSTALLSTATE Installed; + INSTALLSTATE ActionRequest; + INSTALLSTATE Action; + struct list Children; + struct list Components; +} MSIFEATURE; + +typedef struct tagMSIASSEMBLY +{ + LPWSTR feature; + LPWSTR manifest; + LPWSTR application; + DWORD attributes; + LPWSTR display_name; + LPWSTR tempdir; + BOOL installed; + BOOL clr_version[CLR_VERSION_MAX]; +} MSIASSEMBLY; + +typedef struct tagMSICOMPONENT +{ + struct list entry; + LPWSTR Component; + LPWSTR ComponentId; + LPWSTR Directory; + INT Attributes; + LPWSTR Condition; + LPWSTR KeyPath; + INSTALLSTATE Installed; + INSTALLSTATE ActionRequest; + INSTALLSTATE Action; + BOOL ForceLocalState; + BOOL Enabled; + INT Cost; + INT RefCount; + LPWSTR FullKeypath; + LPWSTR AdvertiseString; + MSIASSEMBLY *assembly; + int num_clients; + + unsigned int anyAbsent:1; + unsigned int hasAdvertiseFeature:1; + unsigned int hasLocalFeature:1; + unsigned int hasSourceFeature:1; +} MSICOMPONENT; + +typedef struct tagComponentList +{ + struct list entry; + MSICOMPONENT *component; +} ComponentList; + +typedef struct tagFeatureList +{ + struct list entry; + MSIFEATURE *feature; +} FeatureList; + +enum folder_state +{ + FOLDER_STATE_UNINITIALIZED, + FOLDER_STATE_EXISTS, + FOLDER_STATE_CREATED, + FOLDER_STATE_CREATED_PERSISTENT, + FOLDER_STATE_REMOVED +}; + +typedef struct tagMSIFOLDER +{ + struct list entry; + struct list children; + LPWSTR Directory; + LPWSTR Parent; + LPWSTR TargetDefault; + LPWSTR SourceLongPath; + LPWSTR SourceShortPath; + LPWSTR ResolvedTarget; + LPWSTR ResolvedSource; + enum folder_state State; + BOOL persistent; + INT Cost; + INT Space; +} MSIFOLDER; + +typedef struct tagFolderList +{ + struct list entry; + MSIFOLDER *folder; +} FolderList; + +typedef enum _msi_file_state { + msifs_invalid, + msifs_missing, + msifs_overwrite, + msifs_present, + msifs_installed, + msifs_skipped, + msifs_hashmatch +} msi_file_state; + +typedef struct tagMSIFILE +{ + struct list entry; + LPWSTR File; + MSICOMPONENT *Component; + LPWSTR FileName; + LPWSTR ShortName; + LPWSTR LongName; + INT FileSize; + LPWSTR Version; + LPWSTR Language; + INT Attributes; + INT Sequence; + msi_file_state state; + LPWSTR TargetPath; + BOOL IsCompressed; + MSIFILEHASHINFO hash; + UINT disk_id; +} MSIFILE; + +typedef struct tagMSITEMPFILE +{ + struct list entry; + LPWSTR Path; +} MSITEMPFILE; + +typedef struct tagMSIFILEPATCH +{ + struct list entry; + MSIFILE *File; + INT Sequence; + INT PatchSize; + INT Attributes; + BOOL IsApplied; +} MSIFILEPATCH; + +typedef struct tagMSIAPPID +{ + struct list entry; + LPWSTR AppID; /* Primary key */ + LPWSTR RemoteServerName; + LPWSTR LocalServer; + LPWSTR ServiceParameters; + LPWSTR DllSurrogate; + BOOL ActivateAtStorage; + BOOL RunAsInteractiveUser; +} MSIAPPID; + +typedef struct tagMSIPROGID MSIPROGID; + +typedef struct tagMSICLASS +{ + struct list entry; + LPWSTR clsid; /* Primary Key */ + LPWSTR Context; /* Primary Key */ + MSICOMPONENT *Component; + MSIPROGID *ProgID; + LPWSTR ProgIDText; + LPWSTR Description; + MSIAPPID *AppID; + LPWSTR FileTypeMask; + LPWSTR IconPath; + LPWSTR DefInprocHandler; + LPWSTR DefInprocHandler32; + LPWSTR Argument; + MSIFEATURE *Feature; + INT Attributes; + /* not in the table, set during installation */ + BOOL Installed; +} MSICLASS; + +typedef struct tagMSIMIME MSIMIME; + +typedef struct tagMSIEXTENSION +{ + struct list entry; + LPWSTR Extension; /* Primary Key */ + MSICOMPONENT *Component; + MSIPROGID *ProgID; + LPWSTR ProgIDText; + MSIMIME *Mime; + MSIFEATURE *Feature; + /* not in the table, set during installation */ + BOOL Installed; + struct list verbs; +} MSIEXTENSION; + +struct tagMSIPROGID +{ + struct list entry; + LPWSTR ProgID; /* Primary Key */ + MSIPROGID *Parent; + MSICLASS *Class; + LPWSTR Description; + LPWSTR IconPath; + /* not in the table, set during installation */ + BOOL InstallMe; + MSIPROGID *CurVer; + MSIPROGID *VersionInd; +}; + +typedef struct tagMSIVERB +{ + struct list entry; + LPWSTR Verb; + INT Sequence; + LPWSTR Command; + LPWSTR Argument; +} MSIVERB; + +struct tagMSIMIME +{ + struct list entry; + LPWSTR ContentType; /* Primary Key */ + MSIEXTENSION *Extension; + LPWSTR suffix; + LPWSTR clsid; + MSICLASS *Class; + /* not in the table, set during installation */ + BOOL InstallMe; +}; + +enum SCRIPTS +{ + SCRIPT_NONE = -1, + SCRIPT_INSTALL = 0, + SCRIPT_COMMIT = 1, + SCRIPT_ROLLBACK = 2, + SCRIPT_MAX = 3 +}; + +#define SEQUENCE_UI 0x1 +#define SEQUENCE_EXEC 0x2 +#define SEQUENCE_INSTALL 0x10 + +typedef struct tagMSISCRIPT +{ + LPWSTR *Actions[SCRIPT_MAX]; + UINT ActionCount[SCRIPT_MAX]; + BOOL ExecuteSequenceRun; + BOOL CurrentlyScripting; + UINT InWhatSequence; + LPWSTR *UniqueActions; + UINT UniqueActionsCount; +} MSISCRIPT; + +#define MSIHANDLETYPE_ANY 0 +#define MSIHANDLETYPE_DATABASE 1 +#define MSIHANDLETYPE_SUMMARYINFO 2 +#define MSIHANDLETYPE_VIEW 3 +#define MSIHANDLETYPE_RECORD 4 +#define MSIHANDLETYPE_PACKAGE 5 +#define MSIHANDLETYPE_PREVIEW 6 + +#define MSI_MAJORVERSION 4 +#define MSI_MINORVERSION 5 +#define MSI_BUILDNUMBER 6001 + +#define GUID_SIZE 39 +#define SQUISH_GUID_SIZE 33 + +#define MSIHANDLE_MAGIC 0x4d434923 + +/* handle unicode/ascii output in the Msi* API functions */ +typedef struct { + BOOL unicode; + union { + LPSTR a; + LPWSTR w; + } str; +} awstring; + +typedef struct { + BOOL unicode; + union { + LPCSTR a; + LPCWSTR w; + } str; +} awcstring; + +UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz ) DECLSPEC_HIDDEN; + +/* msi server interface */ +extern HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj ) DECLSPEC_HIDDEN; +extern HRESULT create_msi_remote_package( IUnknown *pOuter, LPVOID *ppObj ) DECLSPEC_HIDDEN; +extern HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj ) DECLSPEC_HIDDEN; +extern IUnknown *msi_get_remote(MSIHANDLE handle) DECLSPEC_HIDDEN; + +/* handle functions */ +extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type) DECLSPEC_HIDDEN; +extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * ) DECLSPEC_HIDDEN; +extern MSIHANDLE alloc_msi_remote_handle( IUnknown *unk ) DECLSPEC_HIDDEN; +extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy ) DECLSPEC_HIDDEN; +extern void msiobj_addref(MSIOBJECTHDR *) DECLSPEC_HIDDEN; +extern int msiobj_release(MSIOBJECTHDR *) DECLSPEC_HIDDEN; +extern void msiobj_lock(MSIOBJECTHDR *) DECLSPEC_HIDDEN; +extern void msiobj_unlock(MSIOBJECTHDR *) DECLSPEC_HIDDEN; +extern void msi_free_handle_table(void) DECLSPEC_HIDDEN; + +extern void free_cached_tables( MSIDATABASE *db ) DECLSPEC_HIDDEN; +extern UINT MSI_CommitTables( MSIDATABASE *db ) DECLSPEC_HIDDEN; + + +/* string table functions */ +enum StringPersistence +{ + StringPersistent = 0, + StringNonPersistent = 1 +}; + +extern BOOL msi_addstringW( string_table *st, const WCHAR *data, int len, USHORT refcount, enum StringPersistence persistence ) DECLSPEC_HIDDEN; +extern UINT msi_string2idW( const string_table *st, LPCWSTR buffer, UINT *id ) DECLSPEC_HIDDEN; +extern VOID msi_destroy_stringtable( string_table *st ) DECLSPEC_HIDDEN; +extern const WCHAR *msi_string_lookup_id( const string_table *st, UINT id ) DECLSPEC_HIDDEN; +extern HRESULT msi_init_string_table( IStorage *stg ) DECLSPEC_HIDDEN; +extern string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref ) DECLSPEC_HIDDEN; +extern UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref ) DECLSPEC_HIDDEN; +extern UINT msi_get_string_table_codepage( const string_table *st ) DECLSPEC_HIDDEN; +extern UINT msi_set_string_table_codepage( string_table *st, UINT codepage ) DECLSPEC_HIDDEN; + +extern BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) DECLSPEC_HIDDEN; +extern MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ) DECLSPEC_HIDDEN; + +extern UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table, + BYTE **pdata, UINT *psz ) DECLSPEC_HIDDEN; +extern UINT write_stream_data( IStorage *stg, LPCWSTR stname, + LPCVOID data, UINT sz, BOOL bTable ) DECLSPEC_HIDDEN; + +/* transform functions */ +extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) DECLSPEC_HIDDEN; +extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, + LPCWSTR szTransformFile, int iErrorCond ) DECLSPEC_HIDDEN; +extern void append_storage_to_db( MSIDATABASE *db, IStorage *stg ) DECLSPEC_HIDDEN; +extern UINT msi_apply_transforms( MSIPACKAGE *package ) DECLSPEC_HIDDEN; + +/* patch functions */ +extern UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) DECLSPEC_HIDDEN; +extern UINT msi_apply_patches( MSIPACKAGE *package ) DECLSPEC_HIDDEN; +extern UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code ) DECLSPEC_HIDDEN; +extern void msi_free_patchinfo( MSIPATCHINFO *patch ) DECLSPEC_HIDDEN; + +/* action internals */ +extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR) DECLSPEC_HIDDEN; +extern UINT ACTION_ForceReboot(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable ) DECLSPEC_HIDDEN; +extern UINT MSI_SetFeatureStates( MSIPACKAGE *package ) DECLSPEC_HIDDEN; +extern UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine, BOOL preserve_case ) DECLSPEC_HIDDEN; +extern UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action ) DECLSPEC_HIDDEN; +extern INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp ) DECLSPEC_HIDDEN; +extern INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature ) DECLSPEC_HIDDEN; +extern UINT msi_load_all_components( MSIPACKAGE *package ) DECLSPEC_HIDDEN; +extern UINT msi_load_all_features( MSIPACKAGE *package ) DECLSPEC_HIDDEN; +extern UINT msi_validate_product_id( MSIPACKAGE *package ) DECLSPEC_HIDDEN; + +/* record internals */ +extern void MSI_CloseRecord( MSIOBJECTHDR * ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetIStream( MSIRECORD *, UINT, IStream *) DECLSPEC_HIDDEN; +extern UINT MSI_RecordGetIStream( MSIRECORD *, UINT, IStream **) DECLSPEC_HIDDEN; +extern const WCHAR *MSI_RecordGetString( const MSIRECORD *, UINT ) DECLSPEC_HIDDEN; +extern MSIRECORD *MSI_CreateRecord( UINT ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetInteger( MSIRECORD *, UINT, int ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetIntPtr( MSIRECORD *, UINT, INT_PTR ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetStringW( MSIRECORD *, UINT, LPCWSTR ) DECLSPEC_HIDDEN; +extern BOOL MSI_RecordIsNull( MSIRECORD *, UINT ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordGetStringW( MSIRECORD * , UINT, LPWSTR, LPDWORD) DECLSPEC_HIDDEN; +extern UINT MSI_RecordGetStringA( MSIRECORD *, UINT, LPSTR, LPDWORD) DECLSPEC_HIDDEN; +extern int MSI_RecordGetInteger( MSIRECORD *, UINT ) DECLSPEC_HIDDEN; +extern INT_PTR MSI_RecordGetIntPtr( MSIRECORD *, UINT ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordReadStream( MSIRECORD *, UINT, char *, LPDWORD) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetStream(MSIRECORD *, UINT, IStream *) DECLSPEC_HIDDEN; +extern UINT MSI_RecordGetFieldCount( const MSIRECORD *rec ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordStreamToFile( MSIRECORD *, UINT, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordSetStreamFromFileW( MSIRECORD *, UINT, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT MSI_RecordCopyField( MSIRECORD *, UINT, MSIRECORD *, UINT ) DECLSPEC_HIDDEN; +extern MSIRECORD *MSI_CloneRecord( MSIRECORD * ) DECLSPEC_HIDDEN; +extern BOOL MSI_RecordsAreEqual( MSIRECORD *, MSIRECORD * ) DECLSPEC_HIDDEN; +extern BOOL MSI_RecordsAreFieldsEqual(MSIRECORD *a, MSIRECORD *b, UINT field) DECLSPEC_HIDDEN; + +/* stream internals */ +extern void enum_stream_names( IStorage *stg ) DECLSPEC_HIDDEN; +extern LPWSTR encode_streamname(BOOL bTable, LPCWSTR in) DECLSPEC_HIDDEN; +extern BOOL decode_streamname(LPCWSTR in, LPWSTR out) DECLSPEC_HIDDEN; + +/* database internals */ +extern UINT msi_get_raw_stream( MSIDATABASE *, LPCWSTR, IStream ** ) DECLSPEC_HIDDEN; +extern UINT msi_clone_open_stream( MSIDATABASE *, IStorage *, const WCHAR *, IStream ** ) DECLSPEC_HIDDEN; +void msi_destroy_stream( MSIDATABASE *, const WCHAR * ) DECLSPEC_HIDDEN; +extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** ) DECLSPEC_HIDDEN; +extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** ) DECLSPEC_HIDDEN; +extern UINT MSI_OpenQuery( MSIDATABASE *, MSIQUERY **, LPCWSTR, ... ) DECLSPEC_HIDDEN; +typedef UINT (*record_func)( MSIRECORD *, LPVOID ); +extern UINT MSI_IterateRecords( MSIQUERY *, LPDWORD, record_func, LPVOID ) DECLSPEC_HIDDEN; +extern MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR query, ... ) DECLSPEC_HIDDEN; +extern UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *, LPCWSTR, MSIRECORD ** ) DECLSPEC_HIDDEN; + +/* view internals */ +extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * ) DECLSPEC_HIDDEN; +extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** ) DECLSPEC_HIDDEN; +extern UINT MSI_ViewClose( MSIQUERY* ) DECLSPEC_HIDDEN; +extern UINT MSI_ViewGetColumnInfo(MSIQUERY *, MSICOLINFO, MSIRECORD **) DECLSPEC_HIDDEN; +extern UINT MSI_ViewModify( MSIQUERY *, MSIMODIFY, MSIRECORD * ) DECLSPEC_HIDDEN; +extern UINT VIEW_find_column( MSIVIEW *, LPCWSTR, LPCWSTR, UINT * ) DECLSPEC_HIDDEN; +extern UINT msi_view_get_row(MSIDATABASE *, MSIVIEW *, UINT, MSIRECORD **) DECLSPEC_HIDDEN; + +/* install internals */ +extern UINT MSI_SetInstallLevel( MSIPACKAGE *package, int iInstallLevel ) DECLSPEC_HIDDEN; + +/* package internals */ +extern MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT MSI_OpenPackageW( LPCWSTR szPackage, MSIPACKAGE **pPackage ) DECLSPEC_HIDDEN; +extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN; +extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD * ) DECLSPEC_HIDDEN; +extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT MSI_GetComponentStateW( MSIPACKAGE *, LPCWSTR, INSTALLSTATE *, INSTALLSTATE * ) DECLSPEC_HIDDEN; +extern UINT MSI_GetFeatureStateW( MSIPACKAGE *, LPCWSTR, INSTALLSTATE *, INSTALLSTATE * ) DECLSPEC_HIDDEN; +extern UINT MSI_SetFeatureStateW(MSIPACKAGE*, LPCWSTR, INSTALLSTATE ) DECLSPEC_HIDDEN; +extern UINT msi_download_file( LPCWSTR szUrl, LPWSTR filename ) DECLSPEC_HIDDEN; +extern UINT msi_package_add_info(MSIPACKAGE *, DWORD, DWORD, LPCWSTR, LPWSTR) DECLSPEC_HIDDEN; +extern UINT msi_package_add_media_disk(MSIPACKAGE *, DWORD, DWORD, DWORD, LPWSTR, LPWSTR) DECLSPEC_HIDDEN; +extern UINT msi_clone_properties(MSIPACKAGE *) DECLSPEC_HIDDEN; +extern UINT msi_set_context(MSIPACKAGE *) DECLSPEC_HIDDEN; +extern void msi_adjust_privilege_properties(MSIPACKAGE *) DECLSPEC_HIDDEN; +extern UINT MSI_GetFeatureCost(MSIPACKAGE *, MSIFEATURE *, MSICOSTTREE, INSTALLSTATE, LPINT) DECLSPEC_HIDDEN; + +/* for deformating */ +extern UINT MSI_FormatRecordW( MSIPACKAGE *, MSIRECORD *, LPWSTR, LPDWORD ) DECLSPEC_HIDDEN; + +/* registry data encoding/decoding functions */ +extern BOOL unsquash_guid(LPCWSTR in, LPWSTR out) DECLSPEC_HIDDEN; +extern BOOL squash_guid(LPCWSTR in, LPWSTR out) DECLSPEC_HIDDEN; +extern BOOL encode_base85_guid(GUID *,LPWSTR) DECLSPEC_HIDDEN; +extern BOOL decode_base85_guid(LPCWSTR,GUID*) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUninstallKey(const WCHAR *, enum platform, HKEY *, BOOL) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUninstallKey(const WCHAR *, enum platform) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenProductKey(LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT context, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context, + HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context, + HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid, + HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, + LPCWSTR szUserSid, HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserDataPatchKey(LPCWSTR szPatch, MSIINSTALLCONTEXT dwContext, + HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserDataProductPatchesKey(LPCWSTR product, MSIINSTALLCONTEXT context, + HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenInstallProps(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, + LPCWSTR szUserSid, HKEY *key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteProductKey(LPCWSTR szProduct) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserDataPatchKey(LPCWSTR patch, MSIINSTALLCONTEXT context) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserFeaturesKey(LPCWSTR szProduct) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteUserUpgradeCodesKey(LPCWSTR szUpgradeCode) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteClassesUpgradeCodesKey(LPCWSTR szUpgradeCode) DECLSPEC_HIDDEN; +extern UINT MSIREG_OpenClassesUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteLocalClassesProductKey(LPCWSTR szProductCode) DECLSPEC_HIDDEN; +extern UINT MSIREG_DeleteLocalClassesFeaturesKey(LPCWSTR szProductCode) DECLSPEC_HIDDEN; +extern UINT msi_locate_product(LPCWSTR szProduct, MSIINSTALLCONTEXT *context) DECLSPEC_HIDDEN; +extern LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name ) DECLSPEC_HIDDEN; +extern BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val) DECLSPEC_HIDDEN; + +extern DWORD msi_version_str_to_dword(LPCWSTR p) DECLSPEC_HIDDEN; +extern void msi_parse_version_string(LPCWSTR, PDWORD, PDWORD) DECLSPEC_HIDDEN; +extern VS_FIXEDFILEINFO *msi_get_disk_file_version(LPCWSTR) DECLSPEC_HIDDEN; +extern int msi_compare_file_versions(VS_FIXEDFILEINFO *, const WCHAR *) DECLSPEC_HIDDEN; +extern int msi_compare_font_versions(const WCHAR *, const WCHAR *) DECLSPEC_HIDDEN; +extern DWORD msi_get_disk_file_size(LPCWSTR) DECLSPEC_HIDDEN; +extern BOOL msi_file_hash_matches(MSIFILE *) DECLSPEC_HIDDEN; + +extern LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) DECLSPEC_HIDDEN; +extern LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) DECLSPEC_HIDDEN; +extern LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val ) DECLSPEC_HIDDEN; +extern LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val ) DECLSPEC_HIDDEN; + +/* msi dialog interface */ +typedef UINT (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, msi_dialog* ); +extern msi_dialog *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog*, msi_dialog_event_handler ) DECLSPEC_HIDDEN; +extern UINT msi_dialog_run_message_loop( msi_dialog* ) DECLSPEC_HIDDEN; +extern void msi_dialog_end_dialog( msi_dialog* ) DECLSPEC_HIDDEN; +extern void msi_dialog_check_messages( HANDLE ) DECLSPEC_HIDDEN; +extern void msi_dialog_destroy( msi_dialog* ) DECLSPEC_HIDDEN; +extern void msi_dialog_unregister_class( void ) DECLSPEC_HIDDEN; +extern void msi_dialog_handle_event( msi_dialog*, LPCWSTR, LPCWSTR, MSIRECORD * ) DECLSPEC_HIDDEN; +extern UINT msi_dialog_reset( msi_dialog *dialog ) DECLSPEC_HIDDEN; +extern UINT msi_dialog_directorylist_up( msi_dialog *dialog ) DECLSPEC_HIDDEN; +extern msi_dialog *msi_dialog_get_parent( msi_dialog *dialog ) DECLSPEC_HIDDEN; +extern LPWSTR msi_dialog_get_name( msi_dialog *dialog ) DECLSPEC_HIDDEN; +extern UINT msi_spawn_error_dialog( MSIPACKAGE*, LPWSTR, LPWSTR ) DECLSPEC_HIDDEN; + +/* summary information */ +extern MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount ) DECLSPEC_HIDDEN; +extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) DECLSPEC_HIDDEN; +extern INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ) DECLSPEC_HIDDEN; +extern LPWSTR msi_get_suminfo_product( IStorage *stg ) DECLSPEC_HIDDEN; +extern UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns ) DECLSPEC_HIDDEN; + +/* undocumented functions */ +UINT WINAPI MsiCreateAndVerifyInstallerDirectory( DWORD ); +UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR, LPWSTR, LPWSTR, LPWSTR, LPDWORD ); +UINT WINAPI MsiDecomposeDescriptorA( LPCSTR, LPSTR, LPSTR, LPSTR, LPDWORD ); +LANGID WINAPI MsiLoadStringW( MSIHANDLE, UINT, LPWSTR, int, LANGID ); +LANGID WINAPI MsiLoadStringA( MSIHANDLE, UINT, LPSTR, int, LANGID ); + +/* UI globals */ +extern INSTALLUILEVEL gUILevel DECLSPEC_HIDDEN; +extern HWND gUIhwnd DECLSPEC_HIDDEN; +extern INSTALLUI_HANDLERA gUIHandlerA DECLSPEC_HIDDEN; +extern INSTALLUI_HANDLERW gUIHandlerW DECLSPEC_HIDDEN; +extern INSTALLUI_HANDLER_RECORD gUIHandlerRecord DECLSPEC_HIDDEN; +extern DWORD gUIFilter DECLSPEC_HIDDEN; +extern LPVOID gUIContext DECLSPEC_HIDDEN; +extern WCHAR *gszLogFile DECLSPEC_HIDDEN; +extern HINSTANCE msi_hInstance DECLSPEC_HIDDEN; + +/* action related functions */ +extern UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script) DECLSPEC_HIDDEN; +extern UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script) DECLSPEC_HIDDEN; +extern void ACTION_FinishCustomActions( const MSIPACKAGE* package) DECLSPEC_HIDDEN; +extern UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action, UINT script, BOOL execute) DECLSPEC_HIDDEN; + +/* actions in other modules */ +extern UINT ACTION_AppSearch(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_CCPSearch(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_InstallFiles(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_PatchFiles( MSIPACKAGE *package ) DECLSPEC_HIDDEN; +extern UINT ACTION_RemoveFiles(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_MoveFiles(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_DuplicateFiles(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RemoveDuplicateFiles(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_RegisterFonts(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_UnregisterClassInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_UnregisterExtensionInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_UnregisterFonts(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_UnregisterMIMEInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_UnregisterProgIdInfo(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_MsiPublishAssemblies(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern UINT ACTION_MsiUnpublishAssemblies(MSIPACKAGE *package) DECLSPEC_HIDDEN; + +/* Helpers */ +extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data ) DECLSPEC_HIDDEN; +extern WCHAR *msi_dup_record_field(MSIRECORD *row, INT index) DECLSPEC_HIDDEN; +extern LPWSTR msi_dup_property( MSIDATABASE *db, LPCWSTR prop ) DECLSPEC_HIDDEN; +extern UINT msi_set_property( MSIDATABASE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN; +extern UINT msi_get_property( MSIDATABASE *, LPCWSTR, LPWSTR, LPDWORD ) DECLSPEC_HIDDEN; +extern int msi_get_property_int( MSIDATABASE *package, LPCWSTR prop, int def ) DECLSPEC_HIDDEN; +extern WCHAR *msi_resolve_source_folder(MSIPACKAGE *package, const WCHAR *name, MSIFOLDER **folder) DECLSPEC_HIDDEN; +extern void msi_resolve_target_folder(MSIPACKAGE *package, const WCHAR *name, BOOL load_prop) DECLSPEC_HIDDEN; +extern WCHAR *msi_normalize_path(const WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR *msi_resolve_file_source(MSIPACKAGE *package, MSIFILE *file) DECLSPEC_HIDDEN; +extern const WCHAR *msi_get_target_folder(MSIPACKAGE *package, const WCHAR *name) DECLSPEC_HIDDEN; +extern void msi_reset_folders( MSIPACKAGE *package, BOOL source ) DECLSPEC_HIDDEN; +extern MSICOMPONENT *msi_get_loaded_component(MSIPACKAGE *package, const WCHAR *Component) DECLSPEC_HIDDEN; +extern MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE *package, const WCHAR *Feature) DECLSPEC_HIDDEN; +extern MSIFILE *msi_get_loaded_file(MSIPACKAGE *package, const WCHAR *file) DECLSPEC_HIDDEN; +extern MSIFILEPATCH *msi_get_loaded_filepatch(MSIPACKAGE* package, const WCHAR *key) DECLSPEC_HIDDEN; +extern MSIFOLDER *msi_get_loaded_folder(MSIPACKAGE *package, const WCHAR *dir) DECLSPEC_HIDDEN; +extern int msi_track_tempfile(MSIPACKAGE *package, const WCHAR *path) DECLSPEC_HIDDEN; +extern void msi_free_action_script(MSIPACKAGE *package, UINT script) DECLSPEC_HIDDEN; +extern WCHAR *msi_build_icon_path(MSIPACKAGE *, const WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR *msi_build_directory_name(DWORD , ...) DECLSPEC_HIDDEN; +extern BOOL msi_create_full_path(const WCHAR *path) DECLSPEC_HIDDEN; +extern void msi_reduce_to_long_filename(WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR *msi_create_component_advertise_string(MSIPACKAGE *, MSICOMPONENT *, const WCHAR *) DECLSPEC_HIDDEN; +extern void ACTION_UpdateComponentStates(MSIPACKAGE *package, MSIFEATURE *feature) DECLSPEC_HIDDEN; +extern UINT msi_register_unique_action(MSIPACKAGE *, const WCHAR *) DECLSPEC_HIDDEN; +extern BOOL msi_action_is_unique(const MSIPACKAGE *, const WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR *msi_build_error_string(MSIPACKAGE *, UINT, DWORD, ...) DECLSPEC_HIDDEN; +extern UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid, + MSIINSTALLCONTEXT context, DWORD options, LPCWSTR value) DECLSPEC_HIDDEN; +extern UINT msi_create_empty_local_file(LPWSTR path, LPCWSTR suffix) DECLSPEC_HIDDEN; +extern UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace) DECLSPEC_HIDDEN; +extern MSIASSEMBLY *msi_load_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN; +extern UINT msi_install_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN; +extern UINT msi_uninstall_assembly(MSIPACKAGE *, MSICOMPONENT *) DECLSPEC_HIDDEN; +extern BOOL msi_init_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN; +extern void msi_destroy_assembly_caches(MSIPACKAGE *) DECLSPEC_HIDDEN; +extern WCHAR *msi_font_version_from_file(const WCHAR *) DECLSPEC_HIDDEN; +extern WCHAR **msi_split_string(const WCHAR *, WCHAR) DECLSPEC_HIDDEN; + +/* media */ + +typedef BOOL (*PMSICABEXTRACTCB)(MSIPACKAGE *, LPCWSTR, DWORD, LPWSTR *, DWORD *, PVOID); + +#define MSICABEXTRACT_BEGINEXTRACT 0x01 +#define MSICABEXTRACT_FILEEXTRACTED 0x02 + +typedef struct +{ + MSIPACKAGE* package; + MSIMEDIAINFO *mi; + PMSICABEXTRACTCB cb; + LPWSTR curfile; + PVOID user; +} MSICABDATA; + +extern UINT ready_media(MSIPACKAGE *package, BOOL compressed, MSIMEDIAINFO *mi) DECLSPEC_HIDDEN; +extern UINT msi_load_media_info(MSIPACKAGE *package, UINT Sequence, MSIMEDIAINFO *mi) DECLSPEC_HIDDEN; +extern void msi_free_media_info(MSIMEDIAINFO *mi) DECLSPEC_HIDDEN; +extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data) DECLSPEC_HIDDEN; +extern UINT msi_add_cabinet_stream(MSIPACKAGE *, UINT, IStorage *, const WCHAR *) DECLSPEC_HIDDEN; + +/* control event stuff */ +extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event, + MSIRECORD *data) DECLSPEC_HIDDEN; +extern VOID ControlEvent_CleanupDialogSubscriptions(MSIPACKAGE *package, LPWSTR dialog) DECLSPEC_HIDDEN; +extern VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package) DECLSPEC_HIDDEN; +extern VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, msi_dialog *dialog, + LPCWSTR event, LPCWSTR control, LPCWSTR attribute) DECLSPEC_HIDDEN; + +/* OLE automation */ +extern HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj) DECLSPEC_HIDDEN; +extern HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch) DECLSPEC_HIDDEN; +extern HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid) DECLSPEC_HIDDEN; + +/* Scripting */ +extern DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action) DECLSPEC_HIDDEN; + +/* User interface messages from the actions */ +extern void msi_ui_progress(MSIPACKAGE *, int, int, int, int) DECLSPEC_HIDDEN; +extern void msi_ui_actiondata(MSIPACKAGE *, const WCHAR *, MSIRECORD *) DECLSPEC_HIDDEN; + +/* common strings */ +static const WCHAR szSourceDir[] = {'S','o','u','r','c','e','D','i','r',0}; +static const WCHAR szSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0}; +static const WCHAR szRootDrive[] = {'R','O','O','T','D','R','I','V','E',0}; +static const WCHAR szTargetDir[] = {'T','A','R','G','E','T','D','I','R',0}; +static const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0}; +static const WCHAR szAllSid[] = {'S','-','1','-','1','-','0',0}; +static const WCHAR szEmpty[] = {0}; +static const WCHAR szAll[] = {'A','L','L',0}; +static const WCHAR szOne[] = {'1',0}; +static const WCHAR szZero[] = {'0',0}; +static const WCHAR szSpace[] = {' ',0}; +static const WCHAR szBackSlash[] = {'\\',0}; +static const WCHAR szForwardSlash[] = {'/',0}; +static const WCHAR szDot[] = {'.',0}; +static const WCHAR szDotDot[] = {'.','.',0}; +static const WCHAR szSemiColon[] = {';',0}; +static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0}; +static const WCHAR szPatches[] = {'P','a','t','c','h','e','s',0}; +static const WCHAR szState[] = {'S','t','a','t','e',0}; +static const WCHAR szMsi[] = {'m','s','i',0}; +static const WCHAR szPatch[] = {'P','A','T','C','H',0}; +static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0}; +static const WCHAR szInstalled[] = {'I','n','s','t','a','l','l','e','d',0}; +static const WCHAR szReinstall[] = {'R','E','I','N','S','T','A','L','L',0}; +static const WCHAR szReinstallMode[] = {'R','E','I','N','S','T','A','L','L','M','O','D','E',0}; +static const WCHAR szRemove[] = {'R','E','M','O','V','E',0}; +static const WCHAR szUserSID[] = {'U','s','e','r','S','I','D',0}; +static const WCHAR szProductCode[] = {'P','r','o','d','u','c','t','C','o','d','e',0}; +static const WCHAR szRegisterClassInfo[] = {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0}; +static const WCHAR szRegisterProgIdInfo[] = {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0}; +static const WCHAR szRegisterExtensionInfo[] = {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0}; +static const WCHAR szRegisterMIMEInfo[] = {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0}; +static const WCHAR szDuplicateFiles[] = {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0}; +static const WCHAR szRemoveDuplicateFiles[] = {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0}; +static const WCHAR szInstallFiles[] = {'I','n','s','t','a','l','l','F','i','l','e','s',0}; +static const WCHAR szPatchFiles[] = {'P','a','t','c','h','F','i','l','e','s',0}; +static const WCHAR szRemoveFiles[] = {'R','e','m','o','v','e','F','i','l','e','s',0}; +static const WCHAR szFindRelatedProducts[] = {'F','i','n','d','R','e','l','a','t','e','d','P','r','o','d','u','c','t','s',0}; +static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0}; +static const WCHAR szCustomActionData[] = {'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0}; +static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0}; +static const WCHAR szProductID[] = {'P','r','o','d','u','c','t','I','D',0}; +static const WCHAR szPIDTemplate[] = {'P','I','D','T','e','m','p','l','a','t','e',0}; +static const WCHAR szPIDKEY[] = {'P','I','D','K','E','Y',0}; +static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0}; +static const WCHAR szSumInfo[] = {5 ,'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0}; +static const WCHAR szHCR[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T','\\',0}; +static const WCHAR szHCU[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',0}; +static const WCHAR szHLM[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E','\\',0}; +static const WCHAR szHU[] = {'H','K','E','Y','_','U','S','E','R','S','\\',0}; +static const WCHAR szWindowsFolder[] = {'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; +static const WCHAR szAppSearch[] = {'A','p','p','S','e','a','r','c','h',0}; +static const WCHAR szMoveFiles[] = {'M','o','v','e','F','i','l','e','s',0}; +static const WCHAR szCCPSearch[] = {'C','C','P','S','e','a','r','c','h',0}; +static const WCHAR szUnregisterClassInfo[] = {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0}; +static const WCHAR szUnregisterExtensionInfo[] = {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0}; +static const WCHAR szUnregisterMIMEInfo[] = {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0}; +static const WCHAR szUnregisterProgIdInfo[] = {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0}; +static const WCHAR szRegisterFonts[] = {'R','e','g','i','s','t','e','r','F','o','n','t','s',0}; +static const WCHAR szUnregisterFonts[] = {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0}; +static const WCHAR szCLSID[] = {'C','L','S','I','D',0}; +static const WCHAR szProgID[] = {'P','r','o','g','I','D',0}; +static const WCHAR szVIProgID[] = {'V','e','r','s','i','o','n','I','n','d','e','p','e','n','d','e','n','t','P','r','o','g','I','D',0}; +static const WCHAR szAppID[] = {'A','p','p','I','D',0}; +static const WCHAR szDefaultIcon[] = {'D','e','f','a','u','l','t','I','c','o','n',0}; +static const WCHAR szInprocHandler[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r',0}; +static const WCHAR szInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0}; +static const WCHAR szMIMEDatabase[] = {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\','C','o','n','t','e','n','t',' ','T','y','p','e','\\',0}; +static const WCHAR szLocalPackage[] = {'L','o','c','a','l','P','a','c','k','a','g','e',0}; +static const WCHAR szOriginalDatabase[] = {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0}; +static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0}; +static const WCHAR szAdminUser[] = {'A','d','m','i','n','U','s','e','r',0}; +static const WCHAR szIntel[] = {'I','n','t','e','l',0}; +static const WCHAR szIntel64[] = {'I','n','t','e','l','6','4',0}; +static const WCHAR szX64[] = {'x','6','4',0}; +static const WCHAR szAMD64[] = {'A','M','D','6','4',0}; +static const WCHAR szARM[] = {'A','r','m',0}; +static const WCHAR szWow6432NodeCLSID[] = {'W','o','w','6','4','3','2','N','o','d','e','\\','C','L','S','I','D',0}; +static const WCHAR szWow6432Node[] = {'W','o','w','6','4','3','2','N','o','d','e',0}; +static const WCHAR szStreams[] = {'_','S','t','r','e','a','m','s',0}; +static const WCHAR szStorages[] = {'_','S','t','o','r','a','g','e','s',0}; +static const WCHAR szMsiPublishAssemblies[] = {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0}; +static const WCHAR szCostingComplete[] = {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0}; +static const WCHAR szTempFolder[] = {'T','e','m','p','F','o','l','d','e','r',0}; +static const WCHAR szDatabase[] = {'D','A','T','A','B','A','S','E',0}; +static const WCHAR szCRoot[] = {'C',':','\\',0}; +static const WCHAR szProductLanguage[] = {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0}; +static const WCHAR szProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0}; +static const WCHAR szWindowsInstaller[] = {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0}; +static const WCHAR szStringData[] = {'_','S','t','r','i','n','g','D','a','t','a',0}; +static const WCHAR szStringPool[] = {'_','S','t','r','i','n','g','P','o','o','l',0}; +static const WCHAR szInstallLevel[] = {'I','N','S','T','A','L','L','L','E','V','E','L',0}; +static const WCHAR szCostInitialize[] = {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0}; +static const WCHAR szAppDataFolder[] = {'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; +static const WCHAR szRollbackDisabled[] = {'R','o','l','l','b','a','c','k','D','i','s','a','b','l','e','d',0}; +static const WCHAR szName[] = {'N','a','m','e',0}; +static const WCHAR szData[] = {'D','a','t','a',0}; +static const WCHAR szLangResource[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0}; +static const WCHAR szInstallLocation[] = {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0}; + +/* memory allocation macro functions */ +static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1); +static inline void *msi_alloc( size_t len ) +{ + return HeapAlloc( GetProcessHeap(), 0, len ); +} + +static void *msi_alloc_zero( size_t len ) __WINE_ALLOC_SIZE(1); +static inline void *msi_alloc_zero( size_t len ) +{ + return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len ); +} + +static void *msi_realloc( void *mem, size_t len ) __WINE_ALLOC_SIZE(2); +static inline void *msi_realloc( void *mem, size_t len ) +{ + return HeapReAlloc( GetProcessHeap(), 0, mem, len ); +} + +static void *msi_realloc_zero( void *mem, size_t len ) __WINE_ALLOC_SIZE(2); +static inline void *msi_realloc_zero( void *mem, size_t len ) +{ + return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len ); +} + +static inline BOOL msi_free( void *mem ) +{ + return HeapFree( GetProcessHeap(), 0, mem ); +} + +static inline char *strdupWtoA( LPCWSTR str ) +{ + LPSTR ret = NULL; + DWORD len; + + if (!str) return ret; + len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); + ret = msi_alloc( len ); + if (ret) + WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); + return ret; +} + +static inline LPWSTR strdupAtoW( LPCSTR str ) +{ + LPWSTR ret = NULL; + DWORD len; + + if (!str) return ret; + len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + ret = msi_alloc( len * sizeof(WCHAR) ); + if (ret) + MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + return ret; +} + +static inline LPWSTR strdupW( LPCWSTR src ) +{ + LPWSTR dest; + if (!src) return NULL; + dest = msi_alloc( (lstrlenW(src)+1)*sizeof(WCHAR) ); + if (dest) + lstrcpyW(dest, src); + return dest; +} + +#endif /* __WINE_MSI_PRIVATE__ */ diff --git a/libmsi/msiquery.c b/libmsi/msiquery.c new file mode 100644 index 0000000..433b3c7 --- /dev/null +++ b/libmsi/msiquery.c @@ -0,0 +1,1053 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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 "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" +#include "msiserver.h" + +#include "initguid.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static void MSI_CloseView( MSIOBJECTHDR *arg ) +{ + MSIQUERY *query = (MSIQUERY*) arg; + struct list *ptr, *t; + + if( query->view && query->view->ops->delete ) + query->view->ops->delete( query->view ); + msiobj_release( &query->db->hdr ); + + LIST_FOR_EACH_SAFE( ptr, t, &query->mem ) + { + msi_free( ptr ); + } +} + +UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, LPCWSTR table_name, UINT *n ) +{ + LPCWSTR col_name, haystack_table_name; + UINT i, count, r; + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=1; i<=count; i++ ) + { + INT x; + + r = table->ops->get_column_info( table, i, &col_name, NULL, + NULL, &haystack_table_name ); + if( r != ERROR_SUCCESS ) + return r; + x = strcmpW( name, col_name ); + if( table_name ) + x |= strcmpW( table_name, haystack_table_name ); + if( !x ) + { + *n = i; + return ERROR_SUCCESS; + } + } + return ERROR_INVALID_PARAMETER; +} + +UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb, + LPCSTR szQuery, MSIHANDLE *phView) +{ + UINT r; + LPWSTR szwQuery; + + TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView); + + if( szQuery ) + { + szwQuery = strdupAtoW( szQuery ); + if( !szwQuery ) + return ERROR_FUNCTION_FAILED; + } + else + szwQuery = NULL; + + r = MsiDatabaseOpenViewW( hdb, szwQuery, phView); + + msi_free( szwQuery ); + return r; +} + +UINT MSI_DatabaseOpenViewW(MSIDATABASE *db, + LPCWSTR szQuery, MSIQUERY **pView) +{ + MSIQUERY *query; + UINT r; + + TRACE("%s %p\n", debugstr_w(szQuery), pView); + + if( !szQuery) + return ERROR_INVALID_PARAMETER; + + /* pre allocate a handle to hold a pointer to the view */ + query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), + MSI_CloseView ); + if( !query ) + return ERROR_FUNCTION_FAILED; + + msiobj_addref( &db->hdr ); + query->db = db; + list_init( &query->mem ); + + r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem ); + if( r == ERROR_SUCCESS ) + { + msiobj_addref( &query->hdr ); + *pView = query; + } + + msiobj_release( &query->hdr ); + return r; +} + +UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... ) +{ + UINT r; + int size = 100, res; + LPWSTR query; + + /* construct the string */ + for (;;) + { + va_list va; + query = msi_alloc( size*sizeof(WCHAR) ); + va_start(va, fmt); + res = vsnprintfW(query, size, fmt, va); + va_end(va); + if (res == -1) size *= 2; + else if (res >= size) size = res + 1; + else break; + msi_free( query ); + } + /* perform the query */ + r = MSI_DatabaseOpenViewW(db, query, view); + msi_free(query); + return r; +} + +UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count, + record_func func, LPVOID param ) +{ + MSIRECORD *rec = NULL; + UINT r, n = 0, max = 0; + + r = MSI_ViewExecute( view, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + if( count ) + max = *count; + + /* iterate a query */ + for( n = 0; (max == 0) || (n < max); n++ ) + { + r = MSI_ViewFetch( view, &rec ); + if( r != ERROR_SUCCESS ) + break; + if (func) + r = func( rec, param ); + msiobj_release( &rec->hdr ); + if( r != ERROR_SUCCESS ) + break; + } + + MSI_ViewClose( view ); + + if( count ) + *count = n; + + if( r == ERROR_NO_MORE_ITEMS ) + r = ERROR_SUCCESS; + + return r; +} + +/* return a single record from a query */ +MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... ) +{ + MSIRECORD *rec = NULL; + MSIQUERY *view = NULL; + UINT r; + int size = 100, res; + LPWSTR query; + + /* construct the string */ + for (;;) + { + va_list va; + query = msi_alloc( size*sizeof(WCHAR) ); + va_start(va, fmt); + res = vsnprintfW(query, size, fmt, va); + va_end(va); + if (res == -1) size *= 2; + else if (res >= size) size = res + 1; + else break; + msi_free( query ); + } + /* perform the query */ + r = MSI_DatabaseOpenViewW(db, query, &view); + msi_free(query); + + if( r == ERROR_SUCCESS ) + { + MSI_ViewExecute( view, NULL ); + MSI_ViewFetch( view, &rec ); + MSI_ViewClose( view ); + msiobj_release( &view->hdr ); + } + return rec; +} + +UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb, + LPCWSTR szQuery, MSIHANDLE *phView) +{ + MSIDATABASE *db; + MSIQUERY *query = NULL; + UINT ret; + + TRACE("%s %p\n", debugstr_w(szQuery), phView); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + HRESULT hr; + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemoteDatabase_OpenView( remote_database, szQuery, phView ); + IWineMsiRemoteDatabase_Release( remote_database ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = MSI_DatabaseOpenViewW( db, szQuery, &query ); + if( ret == ERROR_SUCCESS ) + { + *phView = alloc_msihandle( &query->hdr ); + if (! *phView) + ret = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &query->hdr ); + } + msiobj_release( &db->hdr ); + + return ret; +} + +UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec) +{ + UINT row_count = 0, col_count = 0, i, ival, ret, type; + + TRACE("%p %p %d %p\n", db, view, row, rec); + + ret = view->ops->get_dimensions(view, &row_count, &col_count); + if (ret) + return ret; + + if (!col_count) + return ERROR_INVALID_PARAMETER; + + if (row >= row_count) + return ERROR_NO_MORE_ITEMS; + + *rec = MSI_CreateRecord(col_count); + if (!*rec) + return ERROR_FUNCTION_FAILED; + + for (i = 1; i <= col_count; i++) + { + ret = view->ops->get_column_info(view, i, NULL, &type, NULL, NULL); + if (ret) + { + ERR("Error getting column type for %d\n", i); + continue; + } + + if (MSITYPE_IS_BINARY(type)) + { + IStream *stm = NULL; + + ret = view->ops->fetch_stream(view, row, i, &stm); + if ((ret == ERROR_SUCCESS) && stm) + { + MSI_RecordSetIStream(*rec, i, stm); + IStream_Release(stm); + } + else + WARN("failed to get stream\n"); + + continue; + } + + ret = view->ops->fetch_int(view, row, i, &ival); + if (ret) + { + ERR("Error fetching data for %d\n", i); + continue; + } + + if (! (type & MSITYPE_VALID)) + ERR("Invalid type!\n"); + + /* check if it's nul (0) - if so, don't set anything */ + if (!ival) + continue; + + if (type & MSITYPE_STRING) + { + LPCWSTR sval; + + sval = msi_string_lookup_id(db->strings, ival); + MSI_RecordSetStringW(*rec, i, sval); + } + else + { + if ((type & MSI_DATASIZEMASK) == 2) + MSI_RecordSetInteger(*rec, i, ival - (1<<15)); + else + MSI_RecordSetInteger(*rec, i, ival - (1<<31)); + } + } + + return ERROR_SUCCESS; +} + +UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec) +{ + MSIVIEW *view; + UINT r; + + TRACE("%p %p\n", query, prec ); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + + r = msi_view_get_row(query->db, view, query->row, prec); + if (r == ERROR_SUCCESS) + { + query->row ++; + MSI_RecordSetIntPtr(*prec, 0, (INT_PTR)query); + } + + return r; +} + +UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record) +{ + MSIQUERY *query; + MSIRECORD *rec = NULL; + UINT ret; + + TRACE("%d %p\n", hView, record); + + if( !record ) + return ERROR_INVALID_PARAMETER; + *record = 0; + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + ret = MSI_ViewFetch( query, &rec ); + if( ret == ERROR_SUCCESS ) + { + *record = alloc_msihandle( &rec->hdr ); + if (! *record) + ret = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &rec->hdr ); + } + msiobj_release( &query->hdr ); + return ret; +} + +UINT MSI_ViewClose(MSIQUERY *query) +{ + MSIVIEW *view; + + TRACE("%p\n", query ); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->close ) + return ERROR_FUNCTION_FAILED; + + return view->ops->close( view ); +} + +UINT WINAPI MsiViewClose(MSIHANDLE hView) +{ + MSIQUERY *query; + UINT ret; + + TRACE("%d\n", hView ); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + ret = MSI_ViewClose( query ); + msiobj_release( &query->hdr ); + return ret; +} + +UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec ) +{ + MSIVIEW *view; + + TRACE("%p %p\n", query, rec); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->execute ) + return ERROR_FUNCTION_FAILED; + query->row = 0; + + return view->ops->execute( view, rec ); +} + +UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec) +{ + MSIQUERY *query; + MSIRECORD *rec = NULL; + UINT ret; + + TRACE("%d %d\n", hView, hRec); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + if( hRec ) + { + rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD ); + if( !rec ) + { + ret = ERROR_INVALID_HANDLE; + goto out; + } + } + + msiobj_lock( &rec->hdr ); + ret = MSI_ViewExecute( query, rec ); + msiobj_unlock( &rec->hdr ); + +out: + msiobj_release( &query->hdr ); + if( rec ) + msiobj_release( &rec->hdr ); + + return ret; +} + +static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, + UINT type, BOOL temporary ) +{ + static const WCHAR fmt[] = { '%','d',0 }; + WCHAR szType[0x10]; + + if (MSITYPE_IS_BINARY(type)) + szType[0] = 'v'; + else if (type & MSITYPE_LOCALIZABLE) + szType[0] = 'l'; + else if (type & MSITYPE_UNKNOWN) + szType[0] = 'f'; + else if (type & MSITYPE_STRING) + { + if (temporary) + szType[0] = 'g'; + else + szType[0] = 's'; + } + else + { + if (temporary) + szType[0] = 'j'; + else + szType[0] = 'i'; + } + + if (type & MSITYPE_NULLABLE) + szType[0] &= ~0x20; + + sprintfW( &szType[1], fmt, (type&0xff) ); + + TRACE("type %04x -> %s\n", type, debugstr_w(szType) ); + + return MSI_RecordSetStringW( rec, field, szType ); +} + +UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec ) +{ + UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type; + MSIRECORD *rec; + MSIVIEW *view = query->view; + LPCWSTR name; + BOOL temporary; + + if( !view ) + return ERROR_FUNCTION_FAILED; + + if( !view->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + + r = view->ops->get_dimensions( view, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + if( !count ) + return ERROR_INVALID_PARAMETER; + + rec = MSI_CreateRecord( count ); + if( !rec ) + return ERROR_FUNCTION_FAILED; + + for( i=0; i<count; i++ ) + { + name = NULL; + r = view->ops->get_column_info( view, i+1, &name, &type, &temporary, NULL ); + if( r != ERROR_SUCCESS ) + continue; + if (info == MSICOLINFO_NAMES) + MSI_RecordSetStringW( rec, i+1, name ); + else + msi_set_record_type_string( rec, i+1, type, temporary ); + } + *prec = rec; + return ERROR_SUCCESS; +} + +UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec) +{ + MSIQUERY *query = NULL; + MSIRECORD *rec = NULL; + UINT r; + + TRACE("%d %d %p\n", hView, info, hRec); + + if( !hRec ) + return ERROR_INVALID_PARAMETER; + + if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES ) + return ERROR_INVALID_PARAMETER; + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + r = MSI_ViewGetColumnInfo( query, info, &rec ); + if ( r == ERROR_SUCCESS ) + { + *hRec = alloc_msihandle( &rec->hdr ); + if ( !*hRec ) + r = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &rec->hdr ); + } + + msiobj_release( &query->hdr ); + + return r; +} + +UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec ) +{ + MSIVIEW *view = NULL; + UINT r; + + if ( !query || !rec ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if ( !view || !view->ops->modify) + return ERROR_FUNCTION_FAILED; + + if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetIntPtr( rec, 0 ) != (INT_PTR)query ) + return ERROR_FUNCTION_FAILED; + + r = view->ops->modify( view, mode, rec, query->row ); + if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS) + query->row--; + + return r; +} + +UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode, + MSIHANDLE hRecord) +{ + MSIQUERY *query = NULL; + MSIRECORD *rec = NULL; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%d %x %d\n", hView, eModifyMode, hRecord); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + r = MSI_ViewModify( query, eModifyMode, rec ); + + msiobj_release( &query->hdr ); + if( rec ) + msiobj_release( &rec->hdr ); + + return r; +} + +MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR buffer, LPDWORD buflen ) +{ + MSIQUERY *query; + const WCHAR *column; + MSIDBERROR r; + DWORD len; + + TRACE("%u %p %p\n", handle, buffer, buflen); + + if (!buflen) + return MSIDBERROR_INVALIDARG; + + query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); + if( !query ) + return MSIDBERROR_INVALIDARG; + + if ((r = query->view->error)) column = query->view->error_column; + else column = szEmpty; + + len = strlenW( column ); + if (buffer) + { + if (*buflen > len) + strcpyW( buffer, column ); + else + r = MSIDBERROR_MOREDATA; + } + *buflen = len; + msiobj_release( &query->hdr ); + return r; +} + +MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR buffer, LPDWORD buflen ) +{ + MSIQUERY *query; + const WCHAR *column; + MSIDBERROR r; + DWORD len; + + TRACE("%u %p %p\n", handle, buffer, buflen); + + if (!buflen) + return MSIDBERROR_INVALIDARG; + + query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); + if (!query) + return MSIDBERROR_INVALIDARG; + + if ((r = query->view->error)) column = query->view->error_column; + else column = szEmpty; + + len = WideCharToMultiByte( CP_ACP, 0, column, -1, NULL, 0, NULL, NULL ); + if (buffer) + { + if (*buflen >= len) + WideCharToMultiByte( CP_ACP, 0, column, -1, buffer, *buflen, NULL, NULL ); + else + r = MSIDBERROR_MOREDATA; + } + *buflen = len - 1; + msiobj_release( &query->hdr ); + return r; +} + +MSIHANDLE WINAPI MsiGetLastErrorRecord( void ) +{ + FIXME("\n"); + return 0; +} + +UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, + LPCWSTR szTransformFile, int iErrorCond ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + IStorage *stg = NULL; + STATSTG stat; + + TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond); + + r = StgOpenStorage( szTransformFile, NULL, + STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + if ( FAILED(r) ) + { + WARN("failed to open transform 0x%08x\n", r); + return ret; + } + + r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); + if ( FAILED( r ) ) + goto end; + + if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) ) + goto end; + + if( TRACE_ON( msi ) ) + enum_stream_names( stg ); + + ret = msi_table_apply_transform( db, stg ); + +end: + IStorage_Release( stg ); + + return ret; +} + +UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, + LPCWSTR szTransformFile, int iErrorCond) +{ + MSIDATABASE *db; + UINT r; + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n"); + + return ERROR_SUCCESS; + } + + r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, + LPCSTR szTransformFile, int iErrorCond) +{ + LPWSTR wstr; + UINT ret; + + TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond); + + wstr = strdupAtoW( szTransformFile ); + if( szTransformFile && !wstr ) + return ERROR_NOT_ENOUGH_MEMORY; + + ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond); + + msi_free( wstr ); + + return ret; +} + +UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%d %d %s %d %d\n", hdb, hdbref, + debugstr_a(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCWSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%d %d %s %d %d\n", hdb, hdbref, + debugstr_w(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb ) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%d\n", hdb); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("not allowed during a custom action!\n"); + + return ERROR_SUCCESS; + } + + if (db->mode == MSIDBOPEN_READONLY) + { + msiobj_release( &db->hdr ); + return ERROR_SUCCESS; + } + + /* FIXME: lock the database */ + + r = MSI_CommitTables( db ); + if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n"); + + /* FIXME: unlock the database */ + + msiobj_release( &db->hdr ); + + if (r == ERROR_SUCCESS) + { + msi_free( db->deletefile ); + db->deletefile = NULL; + } + + return r; +} + +struct msi_primary_key_record_info +{ + DWORD n; + MSIRECORD *rec; +}; + +static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param ) +{ + struct msi_primary_key_record_info *info = param; + LPCWSTR name, table; + DWORD type; + + type = MSI_RecordGetInteger( rec, 4 ); + if( type & MSITYPE_KEY ) + { + info->n++; + if( info->rec ) + { + if ( info->n == 1 ) + { + table = MSI_RecordGetString( rec, 1 ); + MSI_RecordSetStringW( info->rec, 0, table); + } + + name = MSI_RecordGetString( rec, 3 ); + MSI_RecordSetStringW( info->rec, info->n, name ); + } + } + + return ERROR_SUCCESS; +} + +UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db, + LPCWSTR table, MSIRECORD **prec ) +{ + static const WCHAR sql[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ', + 'w','h','e','r','e',' ', + '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 }; + struct msi_primary_key_record_info info; + MSIQUERY *query = NULL; + UINT r; + + if (!TABLE_Exists( db, table )) + return ERROR_INVALID_TABLE; + + r = MSI_OpenQuery( db, &query, sql, table ); + if( r != ERROR_SUCCESS ) + return r; + + /* count the number of primary key records */ + info.n = 0; + info.rec = 0; + r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); + if( r == ERROR_SUCCESS ) + { + TRACE("Found %d primary keys\n", info.n ); + + /* allocate a record and fill in the names of the tables */ + info.rec = MSI_CreateRecord( info.n ); + info.n = 0; + r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); + if( r == ERROR_SUCCESS ) + *prec = info.rec; + else + msiobj_release( &info.rec->hdr ); + } + msiobj_release( &query->hdr ); + + return r; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb, + LPCWSTR table, MSIHANDLE* phRec ) +{ + MSIRECORD *rec = NULL; + MSIDATABASE *db; + UINT r; + + TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + HRESULT hr; + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, table, phRec ); + IWineMsiRemoteDatabase_Release( remote_database ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); + if( r == ERROR_SUCCESS ) + { + *phRec = alloc_msihandle( &rec->hdr ); + if (! *phRec) + r = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &rec->hdr ); + } + msiobj_release( &db->hdr ); + + return r; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, + LPCSTR table, MSIHANDLE* phRec) +{ + LPWSTR szwTable = NULL; + UINT r; + + TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec); + + if( table ) + { + szwTable = strdupAtoW( table ); + if( !szwTable ) + return ERROR_OUTOFMEMORY; + } + r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec ); + msi_free( szwTable ); + + return r; +} + +MSICONDITION WINAPI MsiDatabaseIsTablePersistentA( + MSIHANDLE hDatabase, LPCSTR szTableName) +{ + LPWSTR szwTableName = NULL; + MSICONDITION r; + + TRACE("%x %s\n", hDatabase, debugstr_a(szTableName)); + + if( szTableName ) + { + szwTableName = strdupAtoW( szTableName ); + if( !szwTableName ) + return MSICONDITION_ERROR; + } + r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName ); + msi_free( szwTableName ); + + return r; +} + +MSICONDITION WINAPI MsiDatabaseIsTablePersistentW( + MSIHANDLE hDatabase, LPCWSTR szTableName) +{ + MSIDATABASE *db; + MSICONDITION r; + + TRACE("%x %s\n", hDatabase, debugstr_w(szTableName)); + + db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + HRESULT hr; + MSICONDITION condition; + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase ); + if ( !remote_database ) + return MSICONDITION_ERROR; + + hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database, + szTableName, &condition ); + IWineMsiRemoteDatabase_Release( remote_database ); + + if (FAILED(hr)) + return MSICONDITION_ERROR; + + return condition; + } + + r = MSI_DatabaseIsTablePersistent( db, szTableName ); + + msiobj_release( &db->hdr ); + + return r; +} diff --git a/libmsi/msiquery.h b/libmsi/msiquery.h new file mode 100644 index 0000000..99c1439 --- /dev/null +++ b/libmsi/msiquery.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2002,2003 Mike McCormack + * + * 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 + */ + +#ifndef _MSIQUERY_H +#define _MSIQUERY_H + +#include <msi.h> + +typedef enum tagMSICONDITION +{ + MSICONDITION_FALSE = 0, + MSICONDITION_TRUE = 1, + MSICONDITION_NONE = 2, + MSICONDITION_ERROR = 3, +} MSICONDITION; + +#define MSI_NULL_INTEGER 0x80000000 + +typedef enum tagMSICOLINFO +{ + MSICOLINFO_NAMES = 0, + MSICOLINFO_TYPES = 1 +} MSICOLINFO; + +typedef enum tagMSICOSTTREE +{ + MSICOSTTREE_SELFONLY = 0, + MSICOSTTREE_CHILDREN = 1, + MSICOSTTREE_PARENTS = 2, + MSICOSTTREE_PRODUCT = 3, +} MSICOSTTREE; + +typedef enum tagMSIMODIFY +{ + MSIMODIFY_SEEK = -1, + MSIMODIFY_REFRESH = 0, + MSIMODIFY_INSERT = 1, + MSIMODIFY_UPDATE = 2, + MSIMODIFY_ASSIGN = 3, + MSIMODIFY_REPLACE = 4, + MSIMODIFY_MERGE = 5, + MSIMODIFY_DELETE = 6, + MSIMODIFY_INSERT_TEMPORARY = 7, + MSIMODIFY_VALIDATE = 8, + MSIMODIFY_VALIDATE_NEW = 9, + MSIMODIFY_VALIDATE_FIELD = 10, + MSIMODIFY_VALIDATE_DELETE = 11 +} MSIMODIFY; + +#ifndef WINE_NO_UNICODE_MACROS +#define MSIDBOPEN_READONLY (LPCTSTR)0 +#define MSIDBOPEN_TRANSACT (LPCTSTR)1 +#define MSIDBOPEN_DIRECT (LPCTSTR)2 +#define MSIDBOPEN_CREATE (LPCTSTR)3 +#define MSIDBOPEN_CREATEDIRECT (LPCTSTR)4 +#else +#define MSIDBOPEN_READONLY (LPCWSTR)0 +#define MSIDBOPEN_TRANSACT (LPCWSTR)1 +#define MSIDBOPEN_DIRECT (LPCWSTR)2 +#define MSIDBOPEN_CREATE (LPCWSTR)3 +#define MSIDBOPEN_CREATEDIRECT (LPCWSTR)4 +#endif + +#define MSIDBOPEN_PATCHFILE 32 / sizeof(*MSIDBOPEN_READONLY) + +typedef enum tagMSIRUNMODE +{ + MSIRUNMODE_ADMIN = 0, + MSIRUNMODE_ADVERTISE = 1, + MSIRUNMODE_MAINTENANCE = 2, + MSIRUNMODE_ROLLBACKENABLED = 3, + MSIRUNMODE_LOGENABLED = 4, + MSIRUNMODE_OPERATIONS = 5, + MSIRUNMODE_REBOOTATEND = 6, + MSIRUNMODE_REBOOTNOW = 7, + MSIRUNMODE_CABINET = 8, + MSIRUNMODE_SOURCESHORTNAMES = 9, + MSIRUNMODE_TARGETSHORTNAMES = 10, + MSIRUNMODE_RESERVED11 = 11, + MSIRUNMODE_WINDOWS9X = 12, + MSIRUNMODE_ZAWENABLED = 13, + MSIRUNMODE_RESERVED14 = 14, + MSIRUNMODE_RESERVED15 = 15, + MSIRUNMODE_SCHEDULED = 16, + MSIRUNMODE_ROLLBACK = 17, + MSIRUNMODE_COMMIT = 18 +} MSIRUNMODE; + +typedef enum tagMSIDBERROR +{ + MSIDBERROR_INVALIDARG = -3, + MSIDBERROR_MOREDATA = -2, + MSIDBERROR_FUNCTIONERROR = -1, + MSIDBERROR_NOERROR = 0, + MSIDBERROR_DUPLICATEKEY = 1, + MSIDBERROR_REQUIRED = 2, + MSIDBERROR_BADLINK = 3, + MSIDBERROR_OVERFLOW = 4, + MSIDBERROR_UNDERFLOW = 5, + MSIDBERROR_NOTINSET = 6, + MSIDBERROR_BADVERSION = 7, + MSIDBERROR_BADCASE = 8, + MSIDBERROR_BADGUID = 9, + MSIDBERROR_BADWILDCARD = 10, + MSIDBERROR_BADIDENTIFIER = 11, + MSIDBERROR_BADLANGUAGE = 12, + MSIDBERROR_BADFILENAME = 13, + MSIDBERROR_BADPATH = 14, + MSIDBERROR_BADCONDITION = 15, + MSIDBERROR_BADFORMATTED = 16, + MSIDBERROR_BADTEMPLATE = 17, + MSIDBERROR_BADDEFAULTDIR = 18, + MSIDBERROR_BADREGPATH = 19, + MSIDBERROR_BADCUSTOMSOURCE = 20, + MSIDBERROR_BADPROPERTY = 21, + MSIDBERROR_MISSINGDATA = 22, + MSIDBERROR_BADCATEGORY = 23, + MSIDBERROR_BADKEYTABLE = 24, + MSIDBERROR_BADMAXMINVALUES = 25, + MSIDBERROR_BADCABINET = 26, + MSIDBERROR_BADSHORTCUT= 27, + MSIDBERROR_STRINGOVERFLOW = 28, + MSIDBERROR_BADLOCALIZEATTRIB = 29 +} MSIDBERROR; + +typedef enum tagMSIDBSTATE +{ + MSIDBSTATE_ERROR = -1, + MSIDBSTATE_READ = 0, + MSIDBSTATE_WRITE = 1 +} MSIDBSTATE; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* view manipulation */ +UINT WINAPI MsiViewFetch(MSIHANDLE,MSIHANDLE*); +UINT WINAPI MsiViewExecute(MSIHANDLE,MSIHANDLE); +UINT WINAPI MsiViewClose(MSIHANDLE); +UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE,LPCSTR,MSIHANDLE*); +UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE,LPCWSTR,MSIHANDLE*); +#define MsiDatabaseOpenView WINELIB_NAME_AW(MsiDatabaseOpenView) +MSIDBERROR WINAPI MsiViewGetErrorA(MSIHANDLE,LPSTR,LPDWORD); +MSIDBERROR WINAPI MsiViewGetErrorW(MSIHANDLE,LPWSTR,LPDWORD); +#define MsiViewGetError WINELIB_NAME_AW(MsiViewGetError) + +MSIDBSTATE WINAPI MsiGetDatabaseState(MSIHANDLE); + +/* record manipulation */ +MSIHANDLE WINAPI MsiCreateRecord(UINT); +UINT WINAPI MsiRecordClearData(MSIHANDLE); +UINT WINAPI MsiRecordSetInteger(MSIHANDLE,UINT,int); +UINT WINAPI MsiRecordSetStringA(MSIHANDLE,UINT,LPCSTR); +UINT WINAPI MsiRecordSetStringW(MSIHANDLE,UINT,LPCWSTR); +#define MsiRecordSetString WINELIB_NAME_AW(MsiRecordSetString) +UINT WINAPI MsiRecordGetStringA(MSIHANDLE,UINT,LPSTR,LPDWORD); +UINT WINAPI MsiRecordGetStringW(MSIHANDLE,UINT,LPWSTR,LPDWORD); +#define MsiRecordGetString WINELIB_NAME_AW(MsiRecordGetString) +UINT WINAPI MsiRecordGetFieldCount(MSIHANDLE); +int WINAPI MsiRecordGetInteger(MSIHANDLE,UINT); +UINT WINAPI MsiRecordDataSize(MSIHANDLE,UINT); +BOOL WINAPI MsiRecordIsNull(MSIHANDLE,UINT); +UINT WINAPI MsiFormatRecordA(MSIHANDLE,MSIHANDLE,LPSTR,LPDWORD); +UINT WINAPI MsiFormatRecordW(MSIHANDLE,MSIHANDLE,LPWSTR,LPDWORD); +#define MsiFormatRecord WINELIB_NAME_AW(MsiFormatRecord) +UINT WINAPI MsiRecordSetStreamA(MSIHANDLE,UINT,LPCSTR); +UINT WINAPI MsiRecordSetStreamW(MSIHANDLE,UINT,LPCWSTR); +#define MsiRecordSetStream WINELIB_NAME_AW(MsiRecordSetStream) +UINT WINAPI MsiRecordReadStream(MSIHANDLE,UINT,char*,LPDWORD); + +UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE,LPCSTR,MSIHANDLE*); +UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE,LPCWSTR,MSIHANDLE*); +#define MsiDatabaseGetPrimaryKeys WINELIB_NAME_AW(MsiDatabaseGetPrimaryKeys) + +/* installing */ +UINT WINAPI MsiDoActionA(MSIHANDLE,LPCSTR ); +UINT WINAPI MsiDoActionW(MSIHANDLE,LPCWSTR ); +#define MsiDoAction WINELIB_NAME_AW(MsiDoAction) + +/* database transforms */ +UINT WINAPI MsiDatabaseApplyTransformA(MSIHANDLE,LPCSTR,int); +UINT WINAPI MsiDatabaseApplyTransformW(MSIHANDLE,LPCWSTR,int); +#define MsiDatabaseApplyTransform WINELIB_NAME_AW(MsiDatabaseApplyTransform) +UINT WINAPI MsiDatabaseGenerateTransformA(MSIHANDLE,MSIHANDLE,LPCSTR,int,int); +UINT WINAPI MsiDatabaseGenerateTransformW(MSIHANDLE,MSIHANDLE,LPCWSTR,int,int); +#define MsiDatabaseGenerateTransform WINELIB_NAME_AW(MsiDatabaseGenerateTransform) + +UINT WINAPI MsiDatabaseCommit(MSIHANDLE); + +/* install state */ +UINT WINAPI MsiGetFeatureStateA(MSIHANDLE,LPCSTR,INSTALLSTATE*,INSTALLSTATE*); +UINT WINAPI MsiGetFeatureStateW(MSIHANDLE,LPCWSTR,INSTALLSTATE*,INSTALLSTATE*); +#define MsiGetFeatureState WINELIB_NAME_AW(MsiGetFeatureState) +UINT WINAPI MsiGetFeatureValidStatesA(MSIHANDLE,LPCSTR,LPDWORD); +UINT WINAPI MsiGetFeatureValidStatesW(MSIHANDLE,LPCWSTR,LPDWORD); +#define MsiGetFeatureValidStates WINELIB_NAME_AW(MsiGetFeatureValidStates) +UINT WINAPI MsiSetComponentStateA(MSIHANDLE,LPCSTR,INSTALLSTATE); +UINT WINAPI MsiSetComponentStateW(MSIHANDLE,LPCWSTR,INSTALLSTATE); +#define MsiSetComponentState WINELIB_NAME_AW(MsiSetComponentState) +UINT WINAPI MsiGetComponentStateA(MSIHANDLE,LPCSTR,INSTALLSTATE*,INSTALLSTATE*); +UINT WINAPI MsiGetComponentStateW(MSIHANDLE,LPCWSTR,INSTALLSTATE*,INSTALLSTATE*); +#define MsiGetComponentState WINELIB_NAME_AW(MsiGetComponentState) + +MSICONDITION WINAPI MsiEvaluateConditionA(MSIHANDLE,LPCSTR); +MSICONDITION WINAPI MsiEvaluateConditionW(MSIHANDLE,LPCWSTR); +#define MsiEvaluateCondition WINELIB_NAME_AW(MsiEvaluateCondition) + +/* property functions */ +UINT WINAPI MsiGetPropertyA(MSIHANDLE, LPCSTR, LPSTR, LPDWORD); +UINT WINAPI MsiGetPropertyW(MSIHANDLE, LPCWSTR, LPWSTR, LPDWORD); +#define MsiGetProperty WINELIB_NAME_AW(MsiGetProperty) + +UINT WINAPI MsiSetPropertyA(MSIHANDLE, LPCSTR, LPCSTR); +UINT WINAPI MsiSetPropertyW(MSIHANDLE, LPCWSTR, LPCWSTR); +#define MsiSetProperty WINELIB_NAME_AW(MsiSetProperty) + +UINT WINAPI MsiGetTargetPathA(MSIHANDLE,LPCSTR,LPSTR,LPDWORD); +UINT WINAPI MsiGetTargetPathW(MSIHANDLE,LPCWSTR,LPWSTR,LPDWORD); +#define MsiGetTargetPath WINELIB_NAME_AW(MsiGetTargetPath) + +UINT WINAPI MsiSetTargetPathA(MSIHANDLE, LPCSTR, LPCSTR); +UINT WINAPI MsiSetTargetPathW(MSIHANDLE, LPCWSTR, LPCWSTR); +#define MsiSetTargetPath WINELIB_NAME_AW(MsiSetTargetPath) + +UINT WINAPI MsiGetSourcePathA(MSIHANDLE,LPCSTR,LPSTR,LPDWORD); +UINT WINAPI MsiGetSourcePathW(MSIHANDLE,LPCWSTR,LPWSTR,LPDWORD); +#define MsiGetSourcePath WINELIB_NAME_AW(MsiGetSourcePath) + +MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE); + +UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE, MSICOLINFO, MSIHANDLE*); +INT WINAPI MsiProcessMessage(MSIHANDLE, INSTALLMESSAGE, MSIHANDLE); + +UINT WINAPI MsiSetFeatureAttributesA(MSIHANDLE, LPCSTR, DWORD); +UINT WINAPI MsiSetFeatureAttributesW(MSIHANDLE, LPCWSTR, DWORD); +#define MsiSetFeatureAttributes WINELIB_NAME_AW(MsiSetFeatureAttributes) + +UINT WINAPI MsiSetFeatureStateA(MSIHANDLE, LPCSTR, INSTALLSTATE); +UINT WINAPI MsiSetFeatureStateW(MSIHANDLE, LPCWSTR, INSTALLSTATE); +#define MsiSetFeatureState WINELIB_NAME_AW(MsiSetFeatureState) + +UINT WINAPI MsiPreviewDialogA(MSIHANDLE, LPCSTR); +UINT WINAPI MsiPreviewDialogW(MSIHANDLE, LPCWSTR); +#define MsiPreviewDialog WINELIB_NAME_AW(MsiPreviewDialog) + +UINT WINAPI MsiPreviewBillboardA(MSIHANDLE, LPCSTR, LPCSTR); +UINT WINAPI MsiPreviewBillboardW(MSIHANDLE, LPCWSTR, LPCWSTR); +#define MsiPreviewBillboard WINELIB_NAME_AW(MsiPreviewBillboard) + +UINT WINAPI MsiCreateTransformSummaryInfoA(MSIHANDLE, MSIHANDLE, LPCSTR, int, int); +UINT WINAPI MsiCreateTransformSummaryInfoW(MSIHANDLE, MSIHANDLE, LPCWSTR, int, int); +#define MsiCreateTransformSummaryInfo WINELIB_NAME_AW(MsiCreateTransformSummaryInfo) + +UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE, LPCSTR, UINT, MSIHANDLE *); +UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE, LPCWSTR, UINT, MSIHANDLE *); +#define MsiGetSummaryInformation WINELIB_NAME_AW(MsiGetSummaryInformation) + +UINT WINAPI MsiSummaryInfoGetPropertyA(MSIHANDLE,UINT,PUINT,LPINT,FILETIME*,LPSTR,LPDWORD); +UINT WINAPI MsiSummaryInfoGetPropertyW(MSIHANDLE,UINT,PUINT,LPINT,FILETIME*,LPWSTR,LPDWORD); +#define MsiSummaryInfoGetProperty WINELIB_NAME_AW(MsiSummaryInfoGetProperty) + +UINT WINAPI MsiSummaryInfoSetPropertyA(MSIHANDLE, UINT, UINT, INT, FILETIME*, LPCSTR); +UINT WINAPI MsiSummaryInfoSetPropertyW(MSIHANDLE, UINT, UINT, INT, FILETIME*, LPCWSTR); +#define MsiSummaryInfoSetProperty WINELIB_NAME_AW(MsiSummaryInfoSetProperty) + +UINT WINAPI MsiDatabaseExportA(MSIHANDLE, LPCSTR, LPCSTR, LPCSTR); +UINT WINAPI MsiDatabaseExportW(MSIHANDLE, LPCWSTR, LPCWSTR, LPCWSTR); +#define MsiDatabaseExport WINELIB_NAME_AW(MsiDatabaseExport) + +UINT WINAPI MsiDatabaseImportA(MSIHANDLE, LPCSTR, LPCSTR); +UINT WINAPI MsiDatabaseImportW(MSIHANDLE, LPCWSTR, LPCWSTR); +#define MsiDatabaseImport WINELIB_NAME_AW(MsiDatabaseImport) + +UINT WINAPI MsiOpenDatabaseW(LPCWSTR, LPCWSTR, MSIHANDLE*); +UINT WINAPI MsiOpenDatabaseA(LPCSTR, LPCSTR, MSIHANDLE*); +#define MsiOpenDatabase WINELIB_NAME_AW(MsiOpenDatabase) + +MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(MSIHANDLE, LPCSTR); +MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(MSIHANDLE, LPCWSTR); +#define MsiDatabaseIsTablePersistent WINELIB_NAME_AW(MsiDatabaseIsTablePersistent) + +UINT WINAPI MsiSequenceA(MSIHANDLE, LPCSTR, INT); +UINT WINAPI MsiSequenceW(MSIHANDLE, LPCWSTR, INT); +#define MsiSequence WINELIB_NAME_AW(MsiSequence) + +UINT WINAPI MsiSummaryInfoPersist(MSIHANDLE); +UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE,PUINT); + +UINT WINAPI MsiEnableUIPreview(MSIHANDLE, MSIHANDLE*); +BOOL WINAPI MsiGetMode(MSIHANDLE, MSIRUNMODE); +UINT WINAPI MsiSetMode(MSIHANDLE, MSIRUNMODE, BOOL); + +UINT WINAPI MsiViewModify(MSIHANDLE, MSIMODIFY, MSIHANDLE); + +UINT WINAPI MsiGetFeatureCostA(MSIHANDLE, LPCSTR, MSICOSTTREE, INSTALLSTATE, LPINT); +UINT WINAPI MsiGetFeatureCostW(MSIHANDLE, LPCWSTR, MSICOSTTREE, INSTALLSTATE, LPINT); +#define MsiGetFeatureCost WINELIB_NAME_AW(MsiGetFeatureCost) + +LANGID WINAPI MsiGetLanguage(MSIHANDLE); + +UINT WINAPI MsiSetInstallLevel(MSIHANDLE, int); + +MSIHANDLE WINAPI MsiGetLastErrorRecord(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _MSIQUERY_H */ diff --git a/libmsi/msiserver.idl b/libmsi/msiserver.idl new file mode 100644 index 0000000..669d7f2 --- /dev/null +++ b/libmsi/msiserver.idl @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2007 Mike McCormack + * Copyright (C) 2007 Misha Koshelev + * + * 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 "msiserver_dispids.h" +import "unknwn.idl"; +import "wtypes.idl"; +import "objidl.idl"; +import "oaidl.idl"; + +cpp_quote("#if 0") +typedef unsigned long MSIHANDLE; +typedef int INSTALLMESSAGE; +typedef int MSICONDITION; +typedef int MSIRUNMODE; +typedef int INSTALLSTATE; +cpp_quote("#endif") + +[ + uuid(7BDE2046-D03B-4ffc-B84C-A098F38CFF0B), + oleautomation, + object +] +interface IWineMsiRemoteDatabase : IUnknown +{ + HRESULT IsTablePersistent( [in] LPCWSTR table, [out] MSICONDITION *persistent ); + HRESULT GetPrimaryKeys( [in] LPCWSTR table, [out] MSIHANDLE *keys ); + HRESULT GetSummaryInformation( [in] UINT updatecount, [out] MSIHANDLE *suminfo ); + HRESULT OpenView( [in] LPCWSTR query, [out] MSIHANDLE *view ); + HRESULT SetMsiHandle( [in] MSIHANDLE handle ); +} + +[ + uuid(902B3592-9D08-4dfd-A593-D07C52546421), + oleautomation, + object +] +interface IWineMsiRemotePackage : IUnknown +{ + HRESULT SetMsiHandle( [in] MSIHANDLE handle ); + HRESULT GetActiveDatabase( [out] MSIHANDLE *handle ); + HRESULT GetProperty( [in] BSTR property, [out, size_is(*size)] BSTR value, [in, out] DWORD *size ); + HRESULT SetProperty( [in] BSTR property, [in] BSTR value ); + HRESULT ProcessMessage( [in] INSTALLMESSAGE message, [in] MSIHANDLE record ); + HRESULT DoAction( [in] BSTR action ); + HRESULT Sequence( [in] BSTR table, [in] int sequence ); + HRESULT GetTargetPath( [in] BSTR folder, [out, size_is(*size)] BSTR value, [in, out] DWORD *size ); + HRESULT SetTargetPath( [in] BSTR folder, [in] BSTR value ); + HRESULT GetSourcePath( [in] BSTR folder, [out, size_is(*size)] BSTR value, [in, out] DWORD *size ); + HRESULT GetMode( [in] MSIRUNMODE mode, [out] BOOL *ret ); + HRESULT SetMode( [in] MSIRUNMODE mode, [in] BOOL state ); + HRESULT GetFeatureState( [in] BSTR feature, [out] INSTALLSTATE *installed, [out] INSTALLSTATE *action ); + HRESULT SetFeatureState( [in] BSTR feature, [in] INSTALLSTATE state ); + HRESULT GetComponentState( [in] BSTR component, [out] INSTALLSTATE *installed, [out] INSTALLSTATE *action ); + HRESULT SetComponentState( [in] BSTR component, [in] INSTALLSTATE state ); + HRESULT GetLanguage( [out] LANGID *language ); + HRESULT SetInstallLevel( [in] int level ); + HRESULT FormatRecord( [in] MSIHANDLE record, [out] BSTR *value ); + HRESULT EvaluateCondition( [in] BSTR condition ); + HRESULT GetFeatureCost( [in] BSTR feature, [in] INT cost_tree, [in] INSTALLSTATE state, [out] INT *cost ); + HRESULT EnumComponentCosts( [in] BSTR component, [in] DWORD index, [in] INSTALLSTATE state, + [out, size_is(*buflen)] BSTR drive, [in, out] DWORD *buflen, [out] INT *cost, [out] INT *temp ); +} + +[ + uuid(56D58B64-8780-4c22-A8BC-8B0B29E4A9F8), + oleautomation, + object +] +interface IWineMsiRemoteCustomAction : IUnknown +{ + HRESULT GetActionInfo( [in] LPCGUID guid, [out] INT *type, [out] MSIHANDLE *handle, [out] BSTR *dllname, + [out] BSTR *function, [out] IWineMsiRemotePackage **package ); +} + +[ + uuid(000c101c-0000-0000-c000-000000000046), + oleautomation, + object +] +interface IMsiServer : IUnknown +{ + /* FIXME: methods */ +} + +[ + uuid(000c101d-0000-0000-c000-000000000046), + oleautomation, + object +] +interface IMsiMessage : IUnknown +{ + /* FIXME: methods */ +} + +[ + uuid(000c1025-0000-0000-c000-000000000046), + oleautomation, + object +] +interface IMsiCustomAction : IUnknown +{ + /* FIXME: methods */ +} + +[ + uuid(000c1033-0000-0000-c000-000000000046), + oleautomation, + object +] +interface IMsiRemoteAPI : IUnknown +{ + /* FIXME: methods */ +} + +[ + helpstring("Msi install server"), + progid("IMsiServer"), + uuid(000c101c-0000-0000-c000-000000000046) +] +coclass MsiServer { interface IMsiServer; } + +[ + helpstring("Microsoft Windows Installer Message RPC"), + progid("WindowsInstaller.Message"), + uuid(000c101d-0000-0000-c000-000000000046) +] +coclass MsiServerMessage { interface IMsiMessage; } + +[ + threading(both), + uuid(000c103e-0000-0000-c000-000000000046) +] +coclass PSFactoryBuffer { interface IPSFactoryBuffer; } + +[ + uuid(000c1082-0000-0000-c000-000000000046) +] +coclass MsiTransform { } + +[ + uuid(000c1084-0000-0000-c000-000000000046) +] +coclass MsiDatabase { } + +[ + uuid(000c1086-0000-0000-c000-000000000046) +] +coclass MsiPatch { } + +[ + threading(apartment), + uuid(000c1094-0000-0000-c000-000000000046) +] +/* FIXME: unidentified class */ +coclass MsiServerX3 { interface IMsiServer; } + +[ + uuid(ba26e6fa-4f27-4f56-953a-3f90272018aa) +] +coclass WineMsiRemoteCustomAction { interface WineMsiRemoteCustomAction; } + +[ + uuid(902b3592-9d08-4dfd-a593-d07c52546421) +] +coclass WineMsiRemotePackage { interface WineMsiRemotePackage; } + + +[ uuid(000C1092-0000-0000-C000-000000000046), version(1.0) ] +library WindowsInstaller +{ + dispinterface Installer; + dispinterface Record; + dispinterface Session; + dispinterface Database; + dispinterface SummaryInfo; + dispinterface View; + dispinterface UIPreview; + dispinterface FeatureInfo; + dispinterface RecordList; + dispinterface StringList; + dispinterface Product; + dispinterface Patch; + + typedef enum { + msiInstallStateNotUsed = -7, + msiInstallStateBadConfig = -6, + msiInstallStateIncomplete = -5, + msiInstallStateSourceAbsent = -4, + msiInstallStateInvalidArg = -2, + msiInstallStateUnknown = -1, + msiInstallStateBroken = 0, + msiInstallStateAdvertised = 1, + msiInstallStateRemoved = 1, + msiInstallStateAbsent = 2, + msiInstallStateLocal = 3, + msiInstallStateSource = 4, + msiInstallStateDefault = 5 + } MsiInstallState; + + typedef enum { + msiOpenDatabaseModeReadOnly = 0, + msiOpenDatabaseModeTransact = 1, + msiOpenDatabaseModeDirect = 2, + msiOpenDatabaseModeCreate = 3, + msiOpenDatabaseModeCreateDirect = 4, + msiOpenDatabaseModePatchFile = 32 + } MsiOpenDatabaseMode; + + typedef enum { + msiUILevelNoChange = 0, + msiUILevelDefault = 1, + msiUILevelNone = 2, + msiUILevelBasic = 3, + msiUILevelReduced = 4, + msiUILevelFull = 5, + msiUILevelHideCancel = 32, + msiUILevelProgressOnly = 64, + msiUILevelEndDialog = 128, + msiUILevelSourceResOnly = 256 + } MsiUILevel; + + [ uuid(000C1090-0000-0000-C000-000000000046) ] + dispinterface Installer + { + properties: + [id(DISPID_INSTALLER_UILEVEL)] + MsiUILevel UILevel; + methods: + [id(DISPID_INSTALLER_CREATERECORD)] + Record *CreateRecord([in] long Count); + [id(DISPID_INSTALLER_OPENPACKAGE)] + Session* OpenPackage( + [in] VARIANT PackagePath, + [in, optional, defaultvalue(0)] long Options); + [id(DISPID_INSTALLER_OPENPRODUCT)] + Session* OpenProduct( + [in] BSTR ProductCode); + [id(DISPID_INSTALLER_SUMMARYINFORMATION)] + SummaryInfo* SummaryInformation( + [in] BSTR PackagePath, + [in, optional, defaultvalue(0)] long UpdateCount); + [id(DISPID_INSTALLER_OPENDATABASE)] + Database *OpenDatabase( + [in] BSTR DatabasePath, + [in] VARIANT OpenMode); + [id(DISPID_INSTALLER_ENABLELOG)] + void EnableLog( + [in] BSTR LogMode, + [in] BSTR LogFile); + [id(DISPID_INSTALLER_INSTALLPRODUCT)] + void InstallProduct( + [in] BSTR PackagePath, + [in, optional, defaultvalue("0")] BSTR PropertyValues); + [id(DISPID_INSTALLER_VERSION)] + BSTR Version(); + [id(DISPID_INSTALLER_LASTERRORRECORD)] + Record* LastErrorRecord(); + [id(DISPID_INSTALLER_REGISTRYVALUE), propget] + BSTR RegistryValue( + [in] VARIANT Root, + [in] BSTR Key, + [in, optional] VARIANT Value); + [id(DISPID_INSTALLER_ENVIRONMENT), propget] + BSTR Environment([in] BSTR Variable); + [id(DISPID_INSTALLER_ENVIRONMENT), propput] + void Environment( + [in] BSTR Variable, + [in] BSTR rhs); + [id(DISPID_INSTALLER_FILEATTRIBUTES)] + long FileAttributes([in] BSTR FilePath); + [id(DISPID_INSTALLER_FILESIZE)] + long FileSize([in] BSTR FilePath); + [id(DISPID_INSTALLER_FILEVERSION)] + BSTR FileVersion( + [in] BSTR FilePath, + [in, optional] VARIANT Language); + [id(DISPID_INSTALLER_PRODUCTSTATE), propget] + MsiInstallState ProductState( + [in] BSTR Product); + [id(DISPID_INSTALLER_PRODUCTINFO), propget] + BSTR ProductInfo( + [in] BSTR Product, + [in] BSTR Attribute); + [id(DISPID_INSTALLER_PRODUCTS), propget] + StringList *Products(); + [id(DISPID_INSTALLER_RELATEDPRODUCTS), propget] + StringList *RelatedProducts( + [in] BSTR UpgradeCode); + } + + [ uuid(000C1093-0000-0000-C000-000000000046) ] + dispinterface Record + { + properties: + methods: + [id(DISPID_RECORD_STRINGDATA), propget] + BSTR StringData([in] long Field); + [id(DISPID_RECORD_STRINGDATA), propput] + void StringData( + [in] long Field, + [in] BSTR rhs); + [id(DISPID_RECORD_INTEGERDATA), propget] + long IntegerData([in] long Field); + [id(DISPID_RECORD_INTEGERDATA), propput] + void IntegerData( + [in] long Field, + [in] long rhs); + [id(DISPID_RECORD_FIELDCOUNT), propget] + long FieldCount(); + } + + [ uuid(000C1095-0000-0000-C000-000000000046) ] + dispinterface StringList + { + properties: + methods: + [id(DISPID_LIST__NEWENUM)] + IUnknown _NewEnum(); + [id(DISPID_LIST_ITEM), propget] + BSTR Item(long Index); + [id(DISPID_LIST_COUNT), propget] + long Count(); + } + + [ uuid(000C1096-0000-0000-C000-000000000046) ] + dispinterface RecordList + { + properties: + methods: + } + + [ uuid(000C109A-0000-0000-C000-000000000046) ] + dispinterface UIPreview + { + properties: + methods: + } + + [ uuid(000C109B-0000-0000-C000-000000000046) ] + dispinterface SummaryInfo + { + properties: + methods: + [id(DISPID_SUMMARYINFO_PROPERTY), propget] + VARIANT Property([in] long Pid); + [id(DISPID_SUMMARYINFO_PROPERTY), propput] + void Property( + [in] long Pid, + [in] VARIANT rhs); + [id(DISPID_SUMMARYINFO_PROPERTYCOUNT), propget] + long PropertyCount(); + } + + typedef enum { + msiViewModifySeek = -1, + msiViewModifyRefresh = 0, + msiViewModifyInsert = 1, + msiViewModifyUpdate = 2, + msiViewModifyAssign = 3, + msiViewModifyReplace = 4, + msiViewModifyMerge = 5, + msiViewModifyDelete = 6, + msiViewModifyInsertTemporary = 7, + msiViewModifyValidate = 8, + msiViewModifyValidateNew = 9, + msiViewModifyValidateField = 10, + msiViewModifyValidateDelete = 11, + } _MsiViewModify; /* Added underscore to avoid conflict with function name */ + + [ uuid(000C109C-0000-0000-C000-000000000046) ] + dispinterface View + { + properties: + methods: + [id(DISPID_VIEW_EXECUTE)] + void Execute([in, optional, defaultvalue(0)] Record *Params); + [id(DISPID_VIEW_FETCH)] + Record* Fetch(); + [id(DISPID_VIEW_MODIFY)] + void Modify( + [in] _MsiViewModify Mode, + Record *Record); + [id(DISPID_VIEW_CLOSE)] + void Close(); + } + + [ uuid(000C109D-0000-0000-C000-000000000046) ] + dispinterface Database + { + properties: + methods: + [id(DISPID_DATABASE_OPENVIEW)] + View* OpenView([in] BSTR Sql); + [id(DISPID_DATABASE_SUMMARYINFORMATION), propget] + SummaryInfo *SummaryInformation([in, optional, defaultvalue(0)] long UpdateCount); + } + + typedef enum { + msiDoActionStatusNoAction = 0, + msiDoActionStatusSuccess = 1, + msiDoActionStatusUserExit = 2, + msiDoActionStatusFailure = 3, + msiDoActionStatusSuspend = 4, + msiDoActionStatusFinished = 5, + msiDoActionStatusWrongState = 6, + msiDoActionStatusBadActionData = 7 + } MsiDoActionStatus; + + typedef enum { + msiRunModeAdmin = 0, + msiRunModeAdvertise = 1, + msiRunModeMaintenance = 2, + msiRunModeRollbackEnabled = 3, + msiRunModeLogEnabled = 4, + msiRunModeOperations = 5, + msiRunModeRebootAtEnd = 6, + msiRunModeRebootNow = 7, + msiRunModeCabinet = 8, + msiRunModeSourceShortNames = 9, + msiRunModeTargetShortNames = 10, + msiRunModeWindows9x = 12, + msiRunModeZawEnabled = 13, + msiRunModeScheduled = 16, + msiRunModeRollback = 17, + msiRunModeCommit = 18 + } MsiRunMode; + + typedef enum { + msiEvaluateConditionFalse = 0, + msiEvaluateConditionTrue = 1, + msiEvaluateConditionNone = 2, + msiEvaluateConditionError = 3 + } _MsiEvaluateCondition; /* Added underscore to avoid conflict with function name */ + + typedef enum { + msiMessageStatusError = -1, + msiMessageStatusNone = 0, + msiMessageStatusOk = 1, + msiMessageStatusCancel = 2, + msiMessageStatusAbort = 3, + msiMessageStatusRetry = 4, + msiMessageStatusIgnore = 5, + msiMessageStatusYes = 6, + msiMessageStatusNo = 7 + } MsiMessageStatus; + + typedef enum { + msiMessageTypeFatalExit = 0, + msiMessageTypeError = 0x01000000, + msiMessageTypeWarning = 0x02000000, + msiMessageTypeUser = 0x03000000, + msiMessageTypeInfo = 0x04000000, + msiMessageTypeFilesInUse = 0x05000000, + msiMessageTypeResolveSource = 0x06000000, + msiMessageTypeOutOfDiskSpace = 0x07000000, + msiMessageTypeActionStart = 0x08000000, + msiMessageTypeActionData = 0x09000000, + msiMessageTypeProgress = 0x0a000000, + msiMessageTypeCommonData = 0x0b000000, + msiMessageTypeOk = 0, + msiMessageTypeOkCancel = 1, + msiMessageTypeAbortRetryIgnore = 2, + msiMessageTypeYesNoCancel = 3, + msiMessageTypeYesNo = 4, + msiMessageTypeRetryCancel = 5, + msiMessageTypeDefault1 = 0, + msiMessageTypeDefault2 = 256, + msiMessageTypeDefault3 = 512 + } MsiMessageType; + + [ uuid(000C109E-0000-0000-C000-000000000046) ] + dispinterface Session + { + properties: + methods: + [id(DISPID_SESSION_INSTALLER), propget] + Installer *Installer(); + [id(DISPID_SESSION_PROPERTY), propget] + BSTR Property([in] BSTR Name); + [id(DISPID_SESSION_PROPERTY), propput] + void Property( + [in] BSTR Name, + [in] BSTR rhs); + [id(DISPID_SESSION_LANGUAGE), propget] + long Language(); + [id(DISPID_SESSION_MODE), propget] + VARIANT_BOOL Mode([in] MsiRunMode Flag); + [id(DISPID_SESSION_MODE), propput] + void Mode( + [in] MsiRunMode Flag, + [in] VARIANT_BOOL rhs); + [id(DISPID_SESSION_DATABASE), propget] + Database* Database(); + [id(DISPID_SESSION_DOACTION)] + MsiDoActionStatus DoAction([in] BSTR Action); + [id(DISPID_SESSION_EVALUATECONDITION)] + _MsiEvaluateCondition EvaluateCondition([in] BSTR Expression); + [id(DISPID_SESSION_MESSAGE)] + MsiMessageStatus Message( + [in] MsiMessageType Kind, + [in] Record *Record); + [id(DISPID_SESSION_FEATURECURRENTSTATE), propget] + MsiInstallState FeatureCurrentState([in] BSTR Feature); + [id(DISPID_SESSION_FEATUREREQUESTSTATE), propget] + MsiInstallState FeatureRequestState([in] BSTR Feature); + [id(DISPID_SESSION_FEATUREREQUESTSTATE), propput] + void FeatureRequestState( + [in] BSTR Feature, + [in] MsiInstallState rhs); + [id(DISPID_SESSION_SETINSTALLLEVEL)] + void SetInstallLevel([in] long Level); + } + + [ uuid(000C109F-0000-0000-C000-000000000046) ] + dispinterface FeatureInfo + { + properties: + methods: + } + + [ uuid(000C10A0-0000-0000-C000-000000000046) ] + dispinterface Product + { + properties: + methods: + } + + [ uuid(000C10A1-0000-0000-C000-000000000046) ] + dispinterface Patch + { + properties: + methods: + } + + [ + helpstring("Microsoft Windows Installer"), + threading(apartment), + progid("WindowsInstaller.Installer"), + uuid(000c1090-0000-0000-c000-000000000046) + ] + coclass MsiInstaller { interface Installer; } +} diff --git a/libmsi/msiserver.rgs b/libmsi/msiserver.rgs new file mode 100644 index 0000000..9f408db --- /dev/null +++ b/libmsi/msiserver.rgs @@ -0,0 +1,8 @@ +HKCR +{ + NoRemove CLSID + { + '{000C101D-0000-0000-C000-000000000046}' { DllVersion = s '3.1.4000' } + '{000C1090-0000-0000-C000-000000000046}' { InProcHandler32 = s 'ole32.dll' } + } +} diff --git a/libmsi/msiserver_dispids.h b/libmsi/msiserver_dispids.h new file mode 100644 index 0000000..d98d784 --- /dev/null +++ b/libmsi/msiserver_dispids.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 Misha Koshelev + * + * 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 + */ + +#define DISPID_INSTALLER_CREATERECORD 1 +#define DISPID_INSTALLER_OPENPACKAGE 2 +#define DISPID_INSTALLER_OPENPRODUCT 3 +#define DISPID_INSTALLER_OPENDATABASE 4 +#define DISPID_INSTALLER_SUMMARYINFORMATION 5 +#define DISPID_INSTALLER_UILEVEL 6 +#define DISPID_INSTALLER_ENABLELOG 7 +#define DISPID_INSTALLER_INSTALLPRODUCT 8 +#define DISPID_INSTALLER_VERSION 9 +#define DISPID_INSTALLER_LASTERRORRECORD 10 +#define DISPID_INSTALLER_REGISTRYVALUE 11 +#define DISPID_INSTALLER_ENVIRONMENT 12 +#define DISPID_INSTALLER_FILEATTRIBUTES 13 +#define DISPID_INSTALLER_FILESIZE 15 +#define DISPID_INSTALLER_FILEVERSION 16 +#define DISPID_INSTALLER_PRODUCTSTATE 17 +#define DISPID_INSTALLER_PRODUCTINFO 18 +#define DISPID_INSTALLER_PRODUCTS 35 +#define DISPID_INSTALLER_RELATEDPRODUCTS 40 + +#define DISPID_RECORD_FIELDCOUNT 0 +#define DISPID_RECORD_STRINGDATA 1 +#define DISPID_RECORD_INTEGERDATA 2 + +#define DISPID_LIST__NEWENUM -4 +#define DISPID_LIST_ITEM 0 +#define DISPID_LIST_COUNT 1 + +#define DISPID_VIEW_EXECUTE 1 +#define DISPID_VIEW_FETCH 2 +#define DISPID_VIEW_MODIFY 3 +#define DISPID_VIEW_CLOSE 4 + +#define DISPID_DATABASE_SUMMARYINFORMATION 2 +#define DISPID_DATABASE_OPENVIEW 3 + +#define DISPID_SESSION_INSTALLER 1 +#define DISPID_SESSION_PROPERTY 2 +#define DISPID_SESSION_LANGUAGE 3 +#define DISPID_SESSION_MODE 4 +#define DISPID_SESSION_DATABASE 5 +#define DISPID_SESSION_DOACTION 8 +#define DISPID_SESSION_EVALUATECONDITION 10 +#define DISPID_SESSION_MESSAGE 12 +#define DISPID_SESSION_FEATURECURRENTSTATE 13 +#define DISPID_SESSION_FEATUREREQUESTSTATE 14 +#define DISPID_SESSION_SETINSTALLLEVEL 19 + +#define DISPID_SUMMARYINFO_PROPERTY 1 +#define DISPID_SUMMARYINFO_PROPERTYCOUNT 2 diff --git a/libmsi/package.c b/libmsi/package.c new file mode 100644 index 0000000..a050b2e --- /dev/null +++ b/libmsi/package.c @@ -0,0 +1,2674 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 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 + */ + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#define COBJMACROS + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wingdi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objidl.h" +#include "wincrypt.h" +#include "winuser.h" +#include "wininet.h" +#include "winver.h" +#include "urlmon.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "objbase.h" +#include "msidefs.h" +#include "sddl.h" + +#include "msipriv.h" +#include "msiserver.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static void remove_tracked_tempfiles( MSIPACKAGE *package ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &package->tempfiles ) + { + MSITEMPFILE *temp = LIST_ENTRY( item, MSITEMPFILE, entry ); + + list_remove( &temp->entry ); + TRACE("deleting temp file %s\n", debugstr_w( temp->Path )); + DeleteFileW( temp->Path ); + msi_free( temp->Path ); + msi_free( temp ); + } +} + +static void free_feature( MSIFEATURE *feature ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &feature->Children ) + { + FeatureList *fl = LIST_ENTRY( item, FeatureList, entry ); + list_remove( &fl->entry ); + msi_free( fl ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &feature->Components ) + { + ComponentList *cl = LIST_ENTRY( item, ComponentList, entry ); + list_remove( &cl->entry ); + msi_free( cl ); + } + msi_free( feature->Feature ); + msi_free( feature->Feature_Parent ); + msi_free( feature->Directory ); + msi_free( feature->Description ); + msi_free( feature->Title ); + msi_free( feature ); +} + +static void free_folder( MSIFOLDER *folder ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &folder->children ) + { + FolderList *fl = LIST_ENTRY( item, FolderList, entry ); + list_remove( &fl->entry ); + msi_free( fl ); + } + msi_free( folder->Parent ); + msi_free( folder->Directory ); + msi_free( folder->TargetDefault ); + msi_free( folder->SourceLongPath ); + msi_free( folder->SourceShortPath ); + msi_free( folder->ResolvedTarget ); + msi_free( folder->ResolvedSource ); + msi_free( folder ); +} + +static void free_extension( MSIEXTENSION *ext ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &ext->verbs ) + { + MSIVERB *verb = LIST_ENTRY( item, MSIVERB, entry ); + + list_remove( &verb->entry ); + msi_free( verb->Verb ); + msi_free( verb->Command ); + msi_free( verb->Argument ); + msi_free( verb ); + } + + msi_free( ext->Extension ); + msi_free( ext->ProgIDText ); + msi_free( ext ); +} + +static void free_assembly( MSIASSEMBLY *assembly ) +{ + msi_free( assembly->feature ); + msi_free( assembly->manifest ); + msi_free( assembly->application ); + msi_free( assembly->display_name ); + if (assembly->tempdir) RemoveDirectoryW( assembly->tempdir ); + msi_free( assembly->tempdir ); + msi_free( assembly ); +} + +void msi_free_action_script( MSIPACKAGE *package, UINT script ) +{ + UINT i; + for (i = 0; i < package->script->ActionCount[script]; i++) + msi_free( package->script->Actions[script][i] ); + + msi_free( package->script->Actions[script] ); + package->script->Actions[script] = NULL; + package->script->ActionCount[script] = 0; +} + +static void free_package_structures( MSIPACKAGE *package ) +{ + INT i; + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &package->features ) + { + MSIFEATURE *feature = LIST_ENTRY( item, MSIFEATURE, entry ); + list_remove( &feature->entry ); + free_feature( feature ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->folders ) + { + MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry ); + list_remove( &folder->entry ); + free_folder( folder ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->components ) + { + MSICOMPONENT *comp = LIST_ENTRY( item, MSICOMPONENT, entry ); + + list_remove( &comp->entry ); + msi_free( comp->Component ); + msi_free( comp->ComponentId ); + msi_free( comp->Directory ); + msi_free( comp->Condition ); + msi_free( comp->KeyPath ); + msi_free( comp->FullKeypath ); + if (comp->assembly) free_assembly( comp->assembly ); + msi_free( comp ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->files ) + { + MSIFILE *file = LIST_ENTRY( item, MSIFILE, entry ); + + list_remove( &file->entry ); + msi_free( file->File ); + msi_free( file->FileName ); + msi_free( file->ShortName ); + msi_free( file->LongName ); + msi_free( file->Version ); + msi_free( file->Language ); + msi_free( file->TargetPath ); + msi_free( file ); + } + + /* clean up extension, progid, class and verb structures */ + LIST_FOR_EACH_SAFE( item, cursor, &package->classes ) + { + MSICLASS *cls = LIST_ENTRY( item, MSICLASS, entry ); + + list_remove( &cls->entry ); + msi_free( cls->clsid ); + msi_free( cls->Context ); + msi_free( cls->Description ); + msi_free( cls->FileTypeMask ); + msi_free( cls->IconPath ); + msi_free( cls->DefInprocHandler ); + msi_free( cls->DefInprocHandler32 ); + msi_free( cls->Argument ); + msi_free( cls->ProgIDText ); + msi_free( cls ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->extensions ) + { + MSIEXTENSION *ext = LIST_ENTRY( item, MSIEXTENSION, entry ); + + list_remove( &ext->entry ); + free_extension( ext ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->progids ) + { + MSIPROGID *progid = LIST_ENTRY( item, MSIPROGID, entry ); + + list_remove( &progid->entry ); + msi_free( progid->ProgID ); + msi_free( progid->Description ); + msi_free( progid->IconPath ); + msi_free( progid ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->mimes ) + { + MSIMIME *mt = LIST_ENTRY( item, MSIMIME, entry ); + + list_remove( &mt->entry ); + msi_free( mt->suffix ); + msi_free( mt->clsid ); + msi_free( mt->ContentType ); + msi_free( mt ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->appids ) + { + MSIAPPID *appid = LIST_ENTRY( item, MSIAPPID, entry ); + + list_remove( &appid->entry ); + msi_free( appid->AppID ); + msi_free( appid->RemoteServerName ); + msi_free( appid->LocalServer ); + msi_free( appid->ServiceParameters ); + msi_free( appid->DllSurrogate ); + msi_free( appid ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->sourcelist_info ) + { + MSISOURCELISTINFO *info = LIST_ENTRY( item, MSISOURCELISTINFO, entry ); + + list_remove( &info->entry ); + msi_free( info->value ); + msi_free( info ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->sourcelist_media ) + { + MSIMEDIADISK *info = LIST_ENTRY( item, MSIMEDIADISK, entry ); + + list_remove( &info->entry ); + msi_free( info->volume_label ); + msi_free( info->disk_prompt ); + msi_free( info ); + } + + if (package->script) + { + for (i = 0; i < SCRIPT_MAX; i++) + msi_free_action_script( package, i ); + + for (i = 0; i < package->script->UniqueActionsCount; i++) + msi_free( package->script->UniqueActions[i] ); + + msi_free( package->script->UniqueActions ); + msi_free( package->script ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->binaries ) + { + MSIBINARY *binary = LIST_ENTRY( item, MSIBINARY, entry ); + + list_remove( &binary->entry ); + if (binary->module) + FreeLibrary( binary->module ); + if (!DeleteFileW( binary->tmpfile )) + ERR("failed to delete %s (%u)\n", debugstr_w(binary->tmpfile), GetLastError()); + msi_free( binary->source ); + msi_free( binary->tmpfile ); + msi_free( binary ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->cabinet_streams ) + { + MSICABINETSTREAM *cab = LIST_ENTRY( item, MSICABINETSTREAM, entry ); + + list_remove( &cab->entry ); + IStorage_Release( cab->storage ); + msi_free( cab->stream ); + msi_free( cab ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->patches ) + { + MSIPATCHINFO *patch = LIST_ENTRY( item, MSIPATCHINFO, entry ); + + list_remove( &patch->entry ); + if (patch->delete_on_close && !DeleteFileW( patch->localfile )) + { + ERR("failed to delete %s (%u)\n", debugstr_w(patch->localfile), GetLastError()); + } + msi_free_patchinfo( patch ); + } + + msi_free( package->BaseURL ); + msi_free( package->PackagePath ); + msi_free( package->ProductCode ); + msi_free( package->ActionFormat ); + msi_free( package->LastAction ); + msi_free( package->langids ); + + remove_tracked_tempfiles(package); + + /* cleanup control event subscriptions */ + ControlEvent_CleanupSubscriptions( package ); +} + +static void MSI_FreePackage( MSIOBJECTHDR *arg) +{ + MSIPACKAGE *package = (MSIPACKAGE *)arg; + + msi_destroy_assembly_caches( package ); + + if( package->dialog ) + msi_dialog_destroy( package->dialog ); + + msiobj_release( &package->db->hdr ); + free_package_structures(package); + CloseHandle( package->log_file ); + + if (package->delete_on_close) DeleteFileW( package->localfile ); + msi_free( package->localfile ); +} + +static UINT create_temp_property_table(MSIPACKAGE *package) +{ + static const WCHAR query[] = { + 'C','R','E','A','T','E',' ','T','A','B','L','E',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ','(',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ', + 'C','H','A','R','(','5','6',')',' ','N','O','T',' ','N','U','L','L',' ', + 'T','E','M','P','O','R','A','R','Y',',',' ', + '`','V','a','l','u','e','`',' ','C','H','A','R','(','9','8',')',' ', + 'N','O','T',' ','N','U','L','L',' ','T','E','M','P','O','R','A','R','Y', + ' ','P','R','I','M','A','R','Y',' ','K','E','Y',' ', + '`','_','P','r','o','p','e','r','t','y','`',')',' ','H','O','L','D',0}; + MSIQUERY *view; + UINT rc; + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return rc; + + rc = MSI_ViewExecute(view, 0); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + return rc; +} + +UINT msi_clone_properties(MSIPACKAGE *package) +{ + static const WCHAR query_select[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','r','o','p','e','r','t','y','`',0}; + static const WCHAR query_insert[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ', + '(','`','_','P','r','o','p','e','r','t','y','`',',','`','V','a','l','u','e','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; + static const WCHAR query_update[] = { + 'U','P','D','A','T','E',' ','`','_','P','r','o','p','e','r','t','y','`',' ', + 'S','E','T',' ','`','V','a','l','u','e','`',' ','=',' ','?',' ', + 'W','H','E','R','E',' ','`','_','P','r','o','p','e','r','t','y','`',' ','=',' ','?',0}; + MSIQUERY *view_select; + UINT rc; + + rc = MSI_DatabaseOpenViewW( package->db, query_select, &view_select ); + if (rc != ERROR_SUCCESS) + return rc; + + rc = MSI_ViewExecute( view_select, 0 ); + if (rc != ERROR_SUCCESS) + { + MSI_ViewClose( view_select ); + msiobj_release( &view_select->hdr ); + return rc; + } + + while (1) + { + MSIQUERY *view_insert, *view_update; + MSIRECORD *rec_select; + + rc = MSI_ViewFetch( view_select, &rec_select ); + if (rc != ERROR_SUCCESS) + break; + + rc = MSI_DatabaseOpenViewW( package->db, query_insert, &view_insert ); + if (rc != ERROR_SUCCESS) + { + msiobj_release( &rec_select->hdr ); + continue; + } + + rc = MSI_ViewExecute( view_insert, rec_select ); + MSI_ViewClose( view_insert ); + msiobj_release( &view_insert->hdr ); + if (rc != ERROR_SUCCESS) + { + MSIRECORD *rec_update; + + TRACE("insert failed, trying update\n"); + + rc = MSI_DatabaseOpenViewW( package->db, query_update, &view_update ); + if (rc != ERROR_SUCCESS) + { + WARN("open view failed %u\n", rc); + msiobj_release( &rec_select->hdr ); + continue; + } + + rec_update = MSI_CreateRecord( 2 ); + MSI_RecordCopyField( rec_select, 1, rec_update, 2 ); + MSI_RecordCopyField( rec_select, 2, rec_update, 1 ); + rc = MSI_ViewExecute( view_update, rec_update ); + if (rc != ERROR_SUCCESS) + WARN("update failed %u\n", rc); + + MSI_ViewClose( view_update ); + msiobj_release( &view_update->hdr ); + msiobj_release( &rec_update->hdr ); + } + + msiobj_release( &rec_select->hdr ); + } + + MSI_ViewClose( view_select ); + msiobj_release( &view_select->hdr ); + return rc; +} + +/* + * set_installed_prop + * + * Sets the "Installed" property to indicate that + * the product is installed for the current user. + */ +static UINT set_installed_prop( MSIPACKAGE *package ) +{ + HKEY hkey; + UINT r; + + if (!package->ProductCode) return ERROR_FUNCTION_FAILED; + + r = MSIREG_OpenUninstallKey( package->ProductCode, package->platform, &hkey, FALSE ); + if (r == ERROR_SUCCESS) + { + RegCloseKey( hkey ); + msi_set_property( package->db, szInstalled, szOne ); + } + return r; +} + +static UINT set_user_sid_prop( MSIPACKAGE *package ) +{ + SID_NAME_USE use; + LPWSTR user_name; + LPWSTR sid_str = NULL, dom = NULL; + DWORD size, dom_size; + PSID psid = NULL; + UINT r = ERROR_FUNCTION_FAILED; + + size = 0; + GetUserNameW( NULL, &size ); + + user_name = msi_alloc( (size + 1) * sizeof(WCHAR) ); + if (!user_name) + return ERROR_OUTOFMEMORY; + + if (!GetUserNameW( user_name, &size )) + goto done; + + size = 0; + dom_size = 0; + LookupAccountNameW( NULL, user_name, NULL, &size, NULL, &dom_size, &use ); + + psid = msi_alloc( size ); + dom = msi_alloc( dom_size*sizeof (WCHAR) ); + if (!psid || !dom) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + if (!LookupAccountNameW( NULL, user_name, psid, &size, dom, &dom_size, &use )) + goto done; + + if (!ConvertSidToStringSidW( psid, &sid_str )) + goto done; + + r = msi_set_property( package->db, szUserSID, sid_str ); + +done: + LocalFree( sid_str ); + msi_free( dom ); + msi_free( psid ); + msi_free( user_name ); + + return r; +} + +static LPWSTR get_fusion_filename(MSIPACKAGE *package) +{ + HKEY netsetup; + LONG res; + LPWSTR file = NULL; + DWORD index = 0, size; + WCHAR ver[MAX_PATH]; + WCHAR name[MAX_PATH]; + WCHAR windir[MAX_PATH]; + + static const WCHAR fusion[] = {'f','u','s','i','o','n','.','d','l','l',0}; + static const WCHAR sub[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'N','E','T',' ','F','r','a','m','e','w','o','r','k',' ','S','e','t','u','p','\\', + 'N','D','P',0 + }; + static const WCHAR subdir[] = { + 'M','i','c','r','o','s','o','f','t','.','N','E','T','\\', + 'F','r','a','m','e','w','o','r','k','\\',0 + }; + + res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, sub, 0, KEY_ENUMERATE_SUB_KEYS, &netsetup); + if (res != ERROR_SUCCESS) + return NULL; + + GetWindowsDirectoryW(windir, MAX_PATH); + + ver[0] = '\0'; + size = MAX_PATH; + while (RegEnumKeyExW(netsetup, index, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + index++; + + /* verify existence of fusion.dll .Net 3.0 does not install a new one */ + if (strcmpW( ver, name ) < 0) + { + LPWSTR check; + size = lstrlenW(windir) + lstrlenW(subdir) + lstrlenW(name) +lstrlenW(fusion) + 3; + check = msi_alloc(size * sizeof(WCHAR)); + + if (!check) + { + msi_free(file); + return NULL; + } + + lstrcpyW(check, windir); + lstrcatW(check, szBackSlash); + lstrcatW(check, subdir); + lstrcatW(check, name); + lstrcatW(check, szBackSlash); + lstrcatW(check, fusion); + + if(GetFileAttributesW(check) != INVALID_FILE_ATTRIBUTES) + { + msi_free(file); + file = check; + lstrcpyW(ver, name); + } + else + msi_free(check); + } + } + + RegCloseKey(netsetup); + return file; +} + +typedef struct tagLANGANDCODEPAGE +{ + WORD wLanguage; + WORD wCodePage; +} LANGANDCODEPAGE; + +static void set_msi_assembly_prop(MSIPACKAGE *package) +{ + UINT val_len; + DWORD size, handle; + LPVOID version = NULL; + WCHAR buf[MAX_PATH]; + LPWSTR fusion, verstr; + LANGANDCODEPAGE *translate; + + static const WCHAR netasm[] = { + 'M','s','i','N','e','t','A','s','s','e','m','b','l','y','S','u','p','p','o','r','t',0 + }; + static const WCHAR translation[] = { + '\\','V','a','r','F','i','l','e','I','n','f','o', + '\\','T','r','a','n','s','l','a','t','i','o','n',0 + }; + static const WCHAR verfmt[] = { + '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o', + '\\','%','0','4','x','%','0','4','x', + '\\','P','r','o','d','u','c','t','V','e','r','s','i','o','n',0 + }; + + fusion = get_fusion_filename(package); + if (!fusion) + return; + + size = GetFileVersionInfoSizeW(fusion, &handle); + if (!size) return; + + version = msi_alloc(size); + if (!version) return; + + if (!GetFileVersionInfoW(fusion, handle, size, version)) + goto done; + + if (!VerQueryValueW(version, translation, (LPVOID *)&translate, &val_len)) + goto done; + + sprintfW(buf, verfmt, translate[0].wLanguage, translate[0].wCodePage); + + if (!VerQueryValueW(version, buf, (LPVOID *)&verstr, &val_len)) + goto done; + + if (!val_len || !verstr) + goto done; + + msi_set_property(package->db, netasm, verstr); + +done: + msi_free(fusion); + msi_free(version); +} + +static VOID set_installer_properties(MSIPACKAGE *package) +{ + WCHAR *ptr; + OSVERSIONINFOEXW OSVersion; + MEMORYSTATUSEX msex; + DWORD verval, len; + WCHAR pth[MAX_PATH], verstr[11], bufstr[22]; + HDC dc; + HKEY hkey; + LPWSTR username, companyname; + SYSTEM_INFO sys_info; + SYSTEMTIME systemtime; + LANGID langid; + + static const WCHAR szCommonFilesFolder[] = {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR szProgramFilesFolder[] = {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR szCommonAppDataFolder[] = {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR szFavoritesFolder[] = {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0}; + static const WCHAR szFontsFolder[] = {'F','o','n','t','s','F','o','l','d','e','r',0}; + static const WCHAR szSendToFolder[] = {'S','e','n','d','T','o','F','o','l','d','e','r',0}; + static const WCHAR szStartMenuFolder[] = {'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR szStartupFolder[] = {'S','t','a','r','t','u','p','F','o','l','d','e','r',0}; + static const WCHAR szTemplateFolder[] = {'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0}; + static const WCHAR szDesktopFolder[] = {'D','e','s','k','t','o','p','F','o','l','d','e','r',0}; + static const WCHAR szProgramMenuFolder[] = {'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR szAdminToolsFolder[] = {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0}; + static const WCHAR szSystemFolder[] = {'S','y','s','t','e','m','F','o','l','d','e','r',0}; + static const WCHAR szSystem16Folder[] = {'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0}; + static const WCHAR szLocalAppDataFolder[] = {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR szMyPicturesFolder[] = {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0}; + static const WCHAR szPersonalFolder[] = {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0}; + static const WCHAR szWindowsVolume[] = {'W','i','n','d','o','w','s','V','o','l','u','m','e',0}; + static const WCHAR szPrivileged[] = {'P','r','i','v','i','l','e','g','e','d',0}; + static const WCHAR szVersion9x[] = {'V','e','r','s','i','o','n','9','X',0}; + static const WCHAR szVersionNT[] = {'V','e','r','s','i','o','n','N','T',0}; + static const WCHAR szMsiNTProductType[] = {'M','s','i','N','T','P','r','o','d','u','c','t','T','y','p','e',0}; + static const WCHAR szFormat[] = {'%','u',0}; + static const WCHAR szFormat2[] = {'%','u','.','%','u',0}; + static const WCHAR szWindowsBuild[] = {'W','i','n','d','o','w','s','B','u','i','l','d',0}; + static const WCHAR szServicePackLevel[] = {'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0}; + static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 }; + static const WCHAR szVersionDatabase[] = { 'V','e','r','s','i','o','n','D','a','t','a','b','a','s','e',0 }; + static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 }; + static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0}; + static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0}; + static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0}; + static const WCHAR szIntFormat[] = {'%','d',0}; + static const WCHAR szMsiAMD64[] = { 'M','s','i','A','M','D','6','4',0 }; + static const WCHAR szMsix64[] = { 'M','s','i','x','6','4',0 }; + static const WCHAR szSystem64Folder[] = { 'S','y','s','t','e','m','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szCommonFiles64Folder[] = { 'C','o','m','m','o','n','F','i','l','e','s','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szProgramFiles64Folder[] = { 'P','r','o','g','r','a','m','F','i','l','e','s','6','4','F','o','l','d','e','r',0 }; + static const WCHAR szVersionNT64[] = { 'V','e','r','s','i','o','n','N','T','6','4',0 }; + static const WCHAR szUserInfo[] = { + 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'M','S',' ','S','e','t','u','p',' ','(','A','C','M','E',')','\\', + 'U','s','e','r',' ','I','n','f','o',0 + }; + static const WCHAR szDefName[] = { 'D','e','f','N','a','m','e',0 }; + static const WCHAR szDefCompany[] = { 'D','e','f','C','o','m','p','a','n','y',0 }; + static const WCHAR szCurrentVersion[] = { + 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s',' ','N','T','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0 + }; + static const WCHAR szRegisteredUser[] = {'R','e','g','i','s','t','e','r','e','d','O','w','n','e','r',0}; + static const WCHAR szRegisteredOrganization[] = { + 'R','e','g','i','s','t','e','r','e','d','O','r','g','a','n','i','z','a','t','i','o','n',0 + }; + static const WCHAR szUSERNAME[] = {'U','S','E','R','N','A','M','E',0}; + static const WCHAR szCOMPANYNAME[] = {'C','O','M','P','A','N','Y','N','A','M','E',0}; + static const WCHAR szDate[] = {'D','a','t','e',0}; + static const WCHAR szTime[] = {'T','i','m','e',0}; + static const WCHAR szUserLanguageID[] = {'U','s','e','r','L','a','n','g','u','a','g','e','I','D',0}; + static const WCHAR szSystemLangID[] = {'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0}; + static const WCHAR szProductState[] = {'P','r','o','d','u','c','t','S','t','a','t','e',0}; + static const WCHAR szLogonUser[] = {'L','o','g','o','n','U','s','e','r',0}; + static const WCHAR szNetHoodFolder[] = {'N','e','t','H','o','o','d','F','o','l','d','e','r',0}; + static const WCHAR szPrintHoodFolder[] = {'P','r','i','n','t','H','o','o','d','F','o','l','d','e','r',0}; + static const WCHAR szRecentFolder[] = {'R','e','c','e','n','t','F','o','l','d','e','r',0}; + static const WCHAR szComputerName[] = {'C','o','m','p','u','t','e','r','N','a','m','e',0}; + + /* + * Other things that probably should be set: + * + * VirtualMemory ShellAdvSupport DefaultUIFont PackagecodeChanging + * CaptionHeight BorderTop BorderSide TextHeight RedirectedDllSupport + */ + + SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szCommonAppDataFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_FAVORITES, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szFavoritesFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szFontsFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_SENDTO, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szSendToFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_STARTMENU, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szStartMenuFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szStartupFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_TEMPLATES, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szTemplateFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szDesktopFolder, pth); + + /* FIXME: set to AllUsers profile path if ALLUSERS is set */ + SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szProgramMenuFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_ADMINTOOLS, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szAdminToolsFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szAppDataFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szSystemFolder, pth); + msi_set_property(package->db, szSystem16Folder, pth); + + SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szLocalAppDataFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szMyPicturesFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szPersonalFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szWindowsFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_PRINTHOOD, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szPrintHoodFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_NETHOOD, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szNetHoodFolder, pth); + + SHGetFolderPathW(NULL, CSIDL_RECENT, NULL, 0, pth); + strcatW(pth, szBackSlash); + msi_set_property(package->db, szRecentFolder, pth); + + /* Physical Memory is specified in MB. Using total amount. */ + msex.dwLength = sizeof(msex); + GlobalMemoryStatusEx( &msex ); + sprintfW( bufstr, szIntFormat, (int)(msex.ullTotalPhys / 1024 / 1024) ); + msi_set_property(package->db, szPhysicalMemory, bufstr); + + SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, pth); + ptr = strchrW(pth,'\\'); + if (ptr) *(ptr + 1) = 0; + msi_set_property(package->db, szWindowsVolume, pth); + + GetTempPathW(MAX_PATH,pth); + msi_set_property(package->db, szTempFolder, pth); + + /* in a wine environment the user is always admin and privileged */ + msi_set_property(package->db, szAdminUser, szOne); + msi_set_property(package->db, szPrivileged, szOne); + + /* set the os things */ + OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + GetVersionExW((OSVERSIONINFOW *)&OSVersion); + verval = OSVersion.dwMinorVersion + OSVersion.dwMajorVersion * 100; + sprintfW(verstr, szFormat, verval); + switch (OSVersion.dwPlatformId) + { + case VER_PLATFORM_WIN32_WINDOWS: + msi_set_property(package->db, szVersion9x, verstr); + break; + case VER_PLATFORM_WIN32_NT: + msi_set_property(package->db, szVersionNT, verstr); + sprintfW(verstr, szFormat,OSVersion.wProductType); + msi_set_property(package->db, szMsiNTProductType, verstr); + break; + } + sprintfW(verstr, szFormat, OSVersion.dwBuildNumber); + msi_set_property(package->db, szWindowsBuild, verstr); + sprintfW(verstr, szFormat, OSVersion.wServicePackMajor); + msi_set_property(package->db, szServicePackLevel, verstr); + + sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION); + msi_set_property( package->db, szVersionMsi, bufstr ); + sprintfW( bufstr, szFormat, MSI_MAJORVERSION * 100); + msi_set_property( package->db, szVersionDatabase, bufstr ); + + GetNativeSystemInfo( &sys_info ); + sprintfW( bufstr, szIntFormat, sys_info.wProcessorLevel ); + msi_set_property( package->db, szIntel, bufstr ); + if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + { + GetSystemDirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystemFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFilesFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFilesFolder, pth ); + } + else if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + { + msi_set_property( package->db, szMsiAMD64, bufstr ); + msi_set_property( package->db, szMsix64, bufstr ); + msi_set_property( package->db, szVersionNT64, verstr ); + + GetSystemDirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystem64Folder, pth ); + + GetSystemWow64DirectoryW( pth, MAX_PATH ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szSystemFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFiles64Folder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szProgramFilesFolder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFiles64Folder, pth ); + + SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES_COMMONX86, NULL, 0, pth ); + PathAddBackslashW( pth ); + msi_set_property( package->db, szCommonFilesFolder, pth ); + } + + /* Screen properties. */ + dc = GetDC(0); + sprintfW( bufstr, szIntFormat, GetDeviceCaps( dc, HORZRES ) ); + msi_set_property( package->db, szScreenX, bufstr ); + sprintfW( bufstr, szIntFormat, GetDeviceCaps( dc, VERTRES )); + msi_set_property( package->db, szScreenY, bufstr ); + sprintfW( bufstr, szIntFormat, GetDeviceCaps( dc, BITSPIXEL )); + msi_set_property( package->db, szColorBits, bufstr ); + ReleaseDC(0, dc); + + /* USERNAME and COMPANYNAME */ + username = msi_dup_property( package->db, szUSERNAME ); + companyname = msi_dup_property( package->db, szCOMPANYNAME ); + + if ((!username || !companyname) && + RegOpenKeyW( HKEY_CURRENT_USER, szUserInfo, &hkey ) == ERROR_SUCCESS) + { + if (!username && + (username = msi_reg_get_val_str( hkey, szDefName ))) + msi_set_property( package->db, szUSERNAME, username ); + if (!companyname && + (companyname = msi_reg_get_val_str( hkey, szDefCompany ))) + msi_set_property( package->db, szCOMPANYNAME, companyname ); + CloseHandle( hkey ); + } + if ((!username || !companyname) && + RegOpenKeyW( HKEY_LOCAL_MACHINE, szCurrentVersion, &hkey ) == ERROR_SUCCESS) + { + if (!username && + (username = msi_reg_get_val_str( hkey, szRegisteredUser ))) + msi_set_property( package->db, szUSERNAME, username ); + if (!companyname && + (companyname = msi_reg_get_val_str( hkey, szRegisteredOrganization ))) + msi_set_property( package->db, szCOMPANYNAME, companyname ); + CloseHandle( hkey ); + } + msi_free( username ); + msi_free( companyname ); + + if ( set_user_sid_prop( package ) != ERROR_SUCCESS) + ERR("Failed to set the UserSID property\n"); + + /* Date and time properties */ + GetSystemTime( &systemtime ); + if (GetDateFormatW( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systemtime, + NULL, bufstr, sizeof(bufstr)/sizeof(bufstr[0]) )) + msi_set_property( package->db, szDate, bufstr ); + else + ERR("Couldn't set Date property: GetDateFormat failed with error %d\n", GetLastError()); + + if (GetTimeFormatW( LOCALE_USER_DEFAULT, + TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, + &systemtime, NULL, bufstr, + sizeof(bufstr)/sizeof(bufstr[0]) )) + msi_set_property( package->db, szTime, bufstr ); + else + ERR("Couldn't set Time property: GetTimeFormat failed with error %d\n", GetLastError()); + + set_msi_assembly_prop( package ); + + langid = GetUserDefaultLangID(); + sprintfW(bufstr, szIntFormat, langid); + msi_set_property( package->db, szUserLanguageID, bufstr ); + + langid = GetSystemDefaultLangID(); + sprintfW(bufstr, szIntFormat, langid); + msi_set_property( package->db, szSystemLangID, bufstr ); + + sprintfW(bufstr, szIntFormat, MsiQueryProductStateW(package->ProductCode)); + msi_set_property( package->db, szProductState, bufstr ); + + len = 0; + if (!GetUserNameW( NULL, &len ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + WCHAR *username; + if ((username = msi_alloc( len * sizeof(WCHAR) ))) + { + if (GetUserNameW( username, &len )) + msi_set_property( package->db, szLogonUser, username ); + msi_free( username ); + } + } + len = 0; + if (!GetComputerNameW( NULL, &len ) && GetLastError() == ERROR_BUFFER_OVERFLOW) + { + WCHAR *computername; + if ((computername = msi_alloc( len * sizeof(WCHAR) ))) + { + if (GetComputerNameW( computername, &len )) + msi_set_property( package->db, szComputerName, computername ); + msi_free( computername ); + } + } +} + +static UINT msi_load_summary_properties( MSIPACKAGE *package ) +{ + UINT rc; + MSIHANDLE suminfo; + MSIHANDLE hdb = alloc_msihandle( &package->db->hdr ); + INT count; + DWORD len; + LPWSTR package_code; + static const WCHAR szPackageCode[] = { + 'P','a','c','k','a','g','e','C','o','d','e',0}; + + if (!hdb) { + ERR("Unable to allocate handle\n"); + return ERROR_OUTOFMEMORY; + } + + rc = MsiGetSummaryInformationW( hdb, NULL, 0, &suminfo ); + MsiCloseHandle(hdb); + if (rc != ERROR_SUCCESS) + { + ERR("Unable to open Summary Information\n"); + return rc; + } + + rc = MsiSummaryInfoGetPropertyW( suminfo, PID_PAGECOUNT, NULL, + &count, NULL, NULL, NULL ); + if (rc != ERROR_SUCCESS) + { + WARN("Unable to query page count: %d\n", rc); + goto done; + } + + /* load package code property */ + len = 0; + rc = MsiSummaryInfoGetPropertyW( suminfo, PID_REVNUMBER, NULL, + NULL, NULL, NULL, &len ); + if (rc != ERROR_MORE_DATA) + { + WARN("Unable to query revision number: %d\n", rc); + rc = ERROR_FUNCTION_FAILED; + goto done; + } + + len++; + package_code = msi_alloc( len * sizeof(WCHAR) ); + rc = MsiSummaryInfoGetPropertyW( suminfo, PID_REVNUMBER, NULL, + NULL, NULL, package_code, &len ); + if (rc != ERROR_SUCCESS) + { + WARN("Unable to query rev number: %d\n", rc); + goto done; + } + + msi_set_property( package->db, szPackageCode, package_code ); + msi_free( package_code ); + + /* load package attributes */ + count = 0; + MsiSummaryInfoGetPropertyW( suminfo, PID_WORDCOUNT, NULL, + &count, NULL, NULL, NULL ); + package->WordCount = count; + +done: + MsiCloseHandle(suminfo); + return rc; +} + +static MSIPACKAGE *msi_alloc_package( void ) +{ + MSIPACKAGE *package; + + package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE), + MSI_FreePackage ); + if( package ) + { + list_init( &package->components ); + list_init( &package->features ); + list_init( &package->files ); + list_init( &package->filepatches ); + list_init( &package->tempfiles ); + list_init( &package->folders ); + list_init( &package->subscriptions ); + list_init( &package->appids ); + list_init( &package->classes ); + list_init( &package->mimes ); + list_init( &package->extensions ); + list_init( &package->progids ); + list_init( &package->RunningActions ); + list_init( &package->sourcelist_info ); + list_init( &package->sourcelist_media ); + list_init( &package->patches ); + list_init( &package->binaries ); + list_init( &package->cabinet_streams ); + } + + return package; +} + +static UINT msi_load_admin_properties(MSIPACKAGE *package) +{ + BYTE *data; + UINT r, sz; + + static const WCHAR stmname[] = {'A','d','m','i','n','P','r','o','p','e','r','t','i','e','s',0}; + + r = read_stream_data(package->db->storage, stmname, FALSE, &data, &sz); + if (r != ERROR_SUCCESS) + return r; + + r = msi_parse_command_line(package, (WCHAR *)data, TRUE); + + msi_free(data); + return r; +} + +void msi_adjust_privilege_properties( MSIPACKAGE *package ) +{ + /* FIXME: this should depend on the user's privileges */ + if (msi_get_property_int( package->db, szAllUsers, 0 ) == 2) + { + TRACE("resetting ALLUSERS property from 2 to 1\n"); + msi_set_property( package->db, szAllUsers, szOne ); + } + msi_set_property( package->db, szAdminUser, szOne ); +} + +MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db, LPCWSTR base_url ) +{ + static const WCHAR fmtW[] = {'%','u',0}; + MSIPACKAGE *package; + WCHAR uilevel[11]; + UINT r; + + TRACE("%p\n", db); + + package = msi_alloc_package(); + if (package) + { + msiobj_addref( &db->hdr ); + package->db = db; + + package->WordCount = 0; + package->PackagePath = strdupW( db->path ); + package->BaseURL = strdupW( base_url ); + + create_temp_property_table( package ); + msi_clone_properties( package ); + msi_adjust_privilege_properties( package ); + + package->ProductCode = msi_dup_property( package->db, szProductCode ); + package->script = msi_alloc_zero( sizeof(MSISCRIPT) ); + + set_installed_prop( package ); + set_installer_properties( package ); + + package->ui_level = gUILevel; + sprintfW( uilevel, fmtW, gUILevel & INSTALLUILEVEL_MASK ); + msi_set_property(package->db, szUILevel, uilevel); + + r = msi_load_summary_properties( package ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &package->hdr ); + return NULL; + } + + if (package->WordCount & msidbSumInfoSourceTypeAdminImage) + msi_load_admin_properties( package ); + + package->log_file = INVALID_HANDLE_VALUE; + } + return package; +} + +UINT msi_download_file( LPCWSTR szUrl, LPWSTR filename ) +{ + LPINTERNET_CACHE_ENTRY_INFOW cache_entry; + DWORD size = 0; + HRESULT hr; + + /* call will always fail, because size is 0, + * but will return ERROR_FILE_NOT_FOUND first + * if the file doesn't exist + */ + GetUrlCacheEntryInfoW( szUrl, NULL, &size ); + if ( GetLastError() != ERROR_FILE_NOT_FOUND ) + { + cache_entry = msi_alloc( size ); + if ( !GetUrlCacheEntryInfoW( szUrl, cache_entry, &size ) ) + { + UINT error = GetLastError(); + msi_free( cache_entry ); + return error; + } + + lstrcpyW( filename, cache_entry->lpszLocalFileName ); + msi_free( cache_entry ); + return ERROR_SUCCESS; + } + + hr = URLDownloadToCacheFileW( NULL, szUrl, filename, MAX_PATH, 0, NULL ); + if ( FAILED(hr) ) + { + WARN("failed to download %s to cache file\n", debugstr_w(szUrl)); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +UINT msi_create_empty_local_file( LPWSTR path, LPCWSTR suffix ) +{ + static const WCHAR szInstaller[] = { + '\\','I','n','s','t','a','l','l','e','r','\\',0}; + static const WCHAR fmt[] = {'%','x',0}; + DWORD time, len, i, offset; + HANDLE handle; + + time = GetTickCount(); + GetWindowsDirectoryW( path, MAX_PATH ); + strcatW( path, szInstaller ); + CreateDirectoryW( path, NULL ); + + len = strlenW(path); + for (i = 0; i < 0x10000; i++) + { + offset = snprintfW( path + len, MAX_PATH - len, fmt, (time + i) & 0xffff ); + memcpy( path + len + offset, suffix, (strlenW( suffix ) + 1) * sizeof(WCHAR) ); + handle = CreateFileW( path, GENERIC_WRITE, 0, NULL, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + break; + } + if (GetLastError() != ERROR_FILE_EXISTS && + GetLastError() != ERROR_SHARING_VIOLATION) + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT msi_parse_summary( MSISUMMARYINFO *si, MSIPACKAGE *package ) +{ + WCHAR *template, *p, *q; + DWORD i, count; + + package->version = msi_suminfo_get_int32( si, PID_PAGECOUNT ); + TRACE("version: %d\n", package->version); + + template = msi_suminfo_dup_string( si, PID_TEMPLATE ); + if (!template) + return ERROR_SUCCESS; /* native accepts missing template property */ + + TRACE("template: %s\n", debugstr_w(template)); + + p = strchrW( template, ';' ); + if (!p) + { + WARN("invalid template string %s\n", debugstr_w(template)); + msi_free( template ); + return ERROR_PATCH_PACKAGE_INVALID; + } + *p = 0; + if ((q = strchrW( template, ',' ))) *q = 0; + if (!template[0] || !strcmpW( template, szIntel )) + package->platform = PLATFORM_INTEL; + else if (!strcmpW( template, szIntel64 )) + package->platform = PLATFORM_INTEL64; + else if (!strcmpW( template, szX64 ) || !strcmpW( template, szAMD64 )) + package->platform = PLATFORM_X64; + else if (!strcmpW( template, szARM )) + package->platform = PLATFORM_ARM; + else + { + WARN("unknown platform %s\n", debugstr_w(template)); + msi_free( template ); + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; + } + p++; + if (!*p) + { + msi_free( template ); + return ERROR_SUCCESS; + } + count = 1; + for (q = p; (q = strchrW( q, ',' )); q++) count++; + + package->langids = msi_alloc( count * sizeof(LANGID) ); + if (!package->langids) + { + msi_free( template ); + return ERROR_OUTOFMEMORY; + } + + i = 0; + while (*p) + { + q = strchrW( p, ',' ); + if (q) *q = 0; + package->langids[i] = atoiW( p ); + if (!q) break; + p = q + 1; + i++; + } + package->num_langids = i + 1; + + msi_free( template ); + return ERROR_SUCCESS; +} + +static UINT validate_package( MSIPACKAGE *package ) +{ + UINT i; + + if (package->platform == PLATFORM_INTEL64) + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; +#ifndef __arm__ + if (package->platform == PLATFORM_ARM) + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; +#endif + if (package->platform == PLATFORM_X64) + { + if (!is_64bit && !is_wow64) + return ERROR_INSTALL_PLATFORM_UNSUPPORTED; + if (package->version < 200) + return ERROR_INSTALL_PACKAGE_INVALID; + } + if (!package->num_langids) + { + return ERROR_SUCCESS; + } + for (i = 0; i < package->num_langids; i++) + { + LANGID langid = package->langids[i]; + + if (PRIMARYLANGID( langid ) == LANG_NEUTRAL) + { + langid = MAKELANGID( PRIMARYLANGID( GetSystemDefaultLangID() ), SUBLANGID( langid ) ); + } + if (SUBLANGID( langid ) == SUBLANG_NEUTRAL) + { + langid = MAKELANGID( PRIMARYLANGID( langid ), SUBLANGID( GetSystemDefaultLangID() ) ); + } + if (IsValidLocale( langid, LCID_INSTALLED )) + return ERROR_SUCCESS; + } + return ERROR_INSTALL_LANGUAGE_UNSUPPORTED; +} + +int msi_track_tempfile( MSIPACKAGE *package, const WCHAR *path ) +{ + MSITEMPFILE *temp; + + TRACE("%s\n", debugstr_w(path)); + + LIST_FOR_EACH_ENTRY( temp, &package->tempfiles, MSITEMPFILE, entry ) + { + if (!strcmpW( path, temp->Path )) return 0; + } + if (!(temp = msi_alloc_zero( sizeof (MSITEMPFILE) ))) return -1; + list_add_head( &package->tempfiles, &temp->entry ); + temp->Path = strdupW( path ); + return 0; +} + +static WCHAR *get_product_code( MSIDATABASE *db ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ', + 'F','R','O','M',' ','`','P','r','o','p','e','r','t','y','`',' ', + 'W','H','E','R','E',' ','`','P','r','o','p','e','r','t','y','`','=', + '\'','P','r','o','d','u','c','t','C','o','d','e','\'',0}; + MSIQUERY *view; + MSIRECORD *rec; + WCHAR *ret = NULL; + + if (MSI_DatabaseOpenViewW( db, query, &view ) != ERROR_SUCCESS) + { + return NULL; + } + if (MSI_ViewExecute( view, 0 ) != ERROR_SUCCESS) + { + MSI_ViewClose( view ); + msiobj_release( &view->hdr ); + return NULL; + } + if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + ret = strdupW( MSI_RecordGetString( rec, 1 ) ); + msiobj_release( &rec->hdr ); + } + MSI_ViewClose( view ); + msiobj_release( &view->hdr ); + return ret; +} + +static UINT get_registered_local_package( const WCHAR *product, const WCHAR *package, WCHAR *localfile ) +{ + MSIINSTALLCONTEXT context; + HKEY product_key, props_key; + WCHAR *registered_package = NULL, unsquashed[GUID_SIZE]; + UINT r; + + r = msi_locate_product( product, &context ); + if (r != ERROR_SUCCESS) + return r; + + r = MSIREG_OpenProductKey( product, NULL, context, &product_key, FALSE ); + if (r != ERROR_SUCCESS) + return r; + + r = MSIREG_OpenInstallProps( product, context, NULL, &props_key, FALSE ); + if (r != ERROR_SUCCESS) + { + RegCloseKey( product_key ); + return r; + } + r = ERROR_FUNCTION_FAILED; + registered_package = msi_reg_get_val_str( product_key, INSTALLPROPERTY_PACKAGECODEW ); + if (!registered_package) + goto done; + + unsquash_guid( registered_package, unsquashed ); + if (!strcmpiW( package, unsquashed )) + { + WCHAR *filename = msi_reg_get_val_str( props_key, INSTALLPROPERTY_LOCALPACKAGEW ); + if (!filename) + goto done; + + strcpyW( localfile, filename ); + msi_free( filename ); + r = ERROR_SUCCESS; + } +done: + msi_free( registered_package ); + RegCloseKey( props_key ); + RegCloseKey( product_key ); + return r; +} + +static WCHAR *get_package_code( MSIDATABASE *db ) +{ + WCHAR *ret; + MSISUMMARYINFO *si; + + if (!(si = MSI_GetSummaryInformationW( db->storage, 0 ))) + { + WARN("failed to load summary info\n"); + return NULL; + } + ret = msi_suminfo_dup_string( si, PID_REVNUMBER ); + msiobj_release( &si->hdr ); + return ret; +} + +static UINT get_local_package( const WCHAR *filename, WCHAR *localfile ) +{ + WCHAR *product_code, *package_code; + MSIDATABASE *db; + UINT r; + + if ((r = MSI_OpenDatabaseW( filename, MSIDBOPEN_READONLY, &db )) != ERROR_SUCCESS) + { + if (GetFileAttributesW( filename ) == INVALID_FILE_ATTRIBUTES) + return ERROR_FILE_NOT_FOUND; + return r; + } + if (!(product_code = get_product_code( db ))) + { + msiobj_release( &db->hdr ); + return ERROR_INSTALL_PACKAGE_INVALID; + } + if (!(package_code = get_package_code( db ))) + { + msi_free( product_code ); + msiobj_release( &db->hdr ); + return ERROR_INSTALL_PACKAGE_INVALID; + } + r = get_registered_local_package( product_code, package_code, localfile ); + msi_free( package_code ); + msi_free( product_code ); + msiobj_release( &db->hdr ); + return r; +} + +UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) +{ + static const WCHAR dotmsi[] = {'.','m','s','i',0}; + MSIDATABASE *db; + MSIPACKAGE *package; + MSIHANDLE handle; + LPWSTR ptr, base_url = NULL; + UINT r; + WCHAR localfile[MAX_PATH], cachefile[MAX_PATH]; + LPCWSTR file = szPackage; + DWORD index = 0; + MSISUMMARYINFO *si; + BOOL delete_on_close = FALSE; + + TRACE("%s %p\n", debugstr_w(szPackage), pPackage); + + localfile[0] = 0; + if( szPackage[0] == '#' ) + { + handle = atoiW(&szPackage[1]); + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + IWineMsiRemoteDatabase_Release( remote_database ); + WARN("MsiOpenPackage not allowed during a custom action!\n"); + + return ERROR_FUNCTION_FAILED; + } + } + else + { + if ( UrlIsW( szPackage, URLIS_URL ) ) + { + r = msi_download_file( szPackage, cachefile ); + if (r != ERROR_SUCCESS) + return r; + + file = cachefile; + + base_url = strdupW( szPackage ); + if (!base_url) + return ERROR_OUTOFMEMORY; + + ptr = strrchrW( base_url, '/' ); + if (ptr) *(ptr + 1) = '\0'; + } + r = get_local_package( file, localfile ); + if (r != ERROR_SUCCESS || GetFileAttributesW( localfile ) == INVALID_FILE_ATTRIBUTES) + { + r = msi_create_empty_local_file( localfile, dotmsi ); + if (r != ERROR_SUCCESS) + return r; + + if (!CopyFileW( file, localfile, FALSE )) + { + r = GetLastError(); + WARN("unable to copy package %s to %s (%u)\n", debugstr_w(file), debugstr_w(localfile), r); + DeleteFileW( localfile ); + return r; + } + delete_on_close = TRUE; + } + TRACE("opening package %s\n", debugstr_w( localfile )); + r = MSI_OpenDatabaseW( localfile, MSIDBOPEN_TRANSACT, &db ); + if (r != ERROR_SUCCESS) + return r; + } + package = MSI_CreatePackage( db, base_url ); + msi_free( base_url ); + msiobj_release( &db->hdr ); + if (!package) return ERROR_INSTALL_PACKAGE_INVALID; + package->localfile = strdupW( localfile ); + package->delete_on_close = delete_on_close; + + si = MSI_GetSummaryInformationW( db->storage, 0 ); + if (!si) + { + WARN("failed to load summary info\n"); + msiobj_release( &package->hdr ); + return ERROR_INSTALL_PACKAGE_INVALID; + } + r = msi_parse_summary( si, package ); + msiobj_release( &si->hdr ); + if (r != ERROR_SUCCESS) + { + WARN("failed to parse summary info %u\n", r); + msiobj_release( &package->hdr ); + return r; + } + r = validate_package( package ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &package->hdr ); + return r; + } + msi_set_property( package->db, szDatabase, db->path ); + + if( UrlIsW( szPackage, URLIS_URL ) ) + msi_set_property( package->db, szOriginalDatabase, szPackage ); + else if( szPackage[0] == '#' ) + msi_set_property( package->db, szOriginalDatabase, db->path ); + else + { + WCHAR fullpath[MAX_PATH]; + + GetFullPathNameW( szPackage, MAX_PATH, fullpath, NULL ); + msi_set_property( package->db, szOriginalDatabase, fullpath ); + } + msi_set_context( package ); + + while (1) + { + WCHAR patch_code[GUID_SIZE]; + r = MsiEnumPatchesExW( package->ProductCode, NULL, package->Context, + MSIPATCHSTATE_APPLIED, index, patch_code, NULL, NULL, NULL, NULL ); + if (r != ERROR_SUCCESS) + break; + + TRACE("found registered patch %s\n", debugstr_w(patch_code)); + + r = msi_apply_registered_patch( package, patch_code ); + if (r != ERROR_SUCCESS) + { + ERR("registered patch failed to apply %u\n", r); + msiobj_release( &package->hdr ); + return r; + } + index++; + } + if (index) + { + msi_clone_properties( package ); + msi_adjust_privilege_properties( package ); + } + if (gszLogFile) + package->log_file = CreateFileW( gszLogFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + *pPackage = package; + return ERROR_SUCCESS; +} + +UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) +{ + MSIPACKAGE *package = NULL; + UINT ret; + + TRACE("%s %08x %p\n", debugstr_w(szPackage), dwOptions, phPackage ); + + if( !szPackage || !phPackage ) + return ERROR_INVALID_PARAMETER; + + if ( !*szPackage ) + { + FIXME("Should create an empty database and package\n"); + return ERROR_FUNCTION_FAILED; + } + + if( dwOptions ) + FIXME("dwOptions %08x not supported\n", dwOptions); + + ret = MSI_OpenPackageW( szPackage, &package ); + if( ret == ERROR_SUCCESS ) + { + *phPackage = alloc_msihandle( &package->hdr ); + if (! *phPackage) + ret = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &package->hdr ); + } + + return ret; +} + +UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage) +{ + return MsiOpenPackageExW( szPackage, 0, phPackage ); +} + +UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) +{ + LPWSTR szwPack = NULL; + UINT ret; + + if( szPackage ) + { + szwPack = strdupAtoW( szPackage ); + if( !szwPack ) + return ERROR_OUTOFMEMORY; + } + + ret = MsiOpenPackageExW( szwPack, dwOptions, phPackage ); + + msi_free( szwPack ); + + return ret; +} + +UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage) +{ + return MsiOpenPackageExA( szPackage, 0, phPackage ); +} + +MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall) +{ + MSIPACKAGE *package; + MSIHANDLE handle = 0; + IUnknown *remote_unk; + IWineMsiRemotePackage *remote_package; + + TRACE("(%d)\n",hInstall); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( package) + { + handle = alloc_msihandle( &package->db->hdr ); + msiobj_release( &package->hdr ); + } + else if ((remote_unk = msi_get_remote(hInstall))) + { + if (IUnknown_QueryInterface(remote_unk, &IID_IWineMsiRemotePackage, + (LPVOID *)&remote_package) == S_OK) + { + IWineMsiRemotePackage_GetActiveDatabase(remote_package, &handle); + IWineMsiRemotePackage_Release(remote_package); + } + else + { + WARN("remote handle %d is not a package\n", hInstall); + } + IUnknown_Release(remote_unk); + } + + return handle; +} + +INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType, MSIRECORD *record ) +{ + static const WCHAR szActionData[] = {'A','c','t','i','o','n','D','a','t','a',0}; + static const WCHAR szSetProgress[] = {'S','e','t','P','r','o','g','r','e','s','s',0}; + static const WCHAR szActionText[] = {'A','c','t','i','o','n','T','e','x','t',0}; + MSIRECORD *uirow; + LPWSTR deformated, message; + DWORD i, len, total_len, log_type = 0; + INT rc = 0; + char *msg; + + TRACE("%x\n", eMessageType); + + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_FATALEXIT) + log_type |= INSTALLLOGMODE_FATALEXIT; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR) + log_type |= INSTALLLOGMODE_ERROR; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING) + log_type |= INSTALLLOGMODE_WARNING; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER) + log_type |= INSTALLLOGMODE_USER; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO) + log_type |= INSTALLLOGMODE_INFO; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_RESOLVESOURCE) + log_type |= INSTALLLOGMODE_RESOLVESOURCE; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_OUTOFDISKSPACE) + log_type |= INSTALLLOGMODE_OUTOFDISKSPACE; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA) + log_type |= INSTALLLOGMODE_COMMONDATA; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART) + log_type |= INSTALLLOGMODE_ACTIONSTART; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA) + log_type |= INSTALLLOGMODE_ACTIONDATA; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS) + log_type |= INSTALLLOGMODE_PROGRESS; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INITIALIZE) + log_type |= INSTALLLOGMODE_INITIALIZE; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_TERMINATE) + log_type |= INSTALLLOGMODE_TERMINATE; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_SHOWDIALOG) + log_type |= INSTALLLOGMODE_SHOWDIALOG; + + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART) + { + static const WCHAR template_s[]= + {'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ',0}; + static const WCHAR format[] = + {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0}; + WCHAR timet[0x100]; + LPCWSTR action_text, action; + LPWSTR deformatted = NULL; + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100); + + action = MSI_RecordGetString(record, 1); + action_text = MSI_RecordGetString(record, 2); + + if (!action || !action_text) + return IDOK; + + deformat_string(package, action_text, &deformatted); + + len = strlenW(timet) + strlenW(action) + strlenW(template_s); + if (deformatted) + len += strlenW(deformatted); + message = msi_alloc(len*sizeof(WCHAR)); + sprintfW(message, template_s, timet, action); + if (deformatted) + strcatW(message, deformatted); + msi_free(deformatted); + } + else + { + static const WCHAR format[] = {'%','u',':',' ',0}; + UINT count = MSI_RecordGetFieldCount( record ); + WCHAR *p; + + total_len = 1; + for (i = 1; i <= count; i++) + { + len = 0; + MSI_RecordGetStringW( record, i, NULL, &len ); + total_len += len + 13; + } + p = message = msi_alloc( total_len * sizeof(WCHAR) ); + if (!p) return ERROR_OUTOFMEMORY; + + for (i = 1; i <= count; i++) + { + if (count > 1) + { + len = sprintfW( p, format, i ); + total_len -= len; + p += len; + } + len = total_len; + MSI_RecordGetStringW( record, i, p, &len ); + total_len -= len; + p += len; + if (count > 1 && total_len) + { + *p++ = ' '; + total_len--; + } + } + p[0] = 0; + } + + TRACE("%p %p %p %x %x %s\n", gUIHandlerA, gUIHandlerW, gUIHandlerRecord, + gUIFilter, log_type, debugstr_w(message)); + + /* convert it to ASCII */ + len = WideCharToMultiByte( CP_ACP, 0, message, -1, NULL, 0, NULL, NULL ); + msg = msi_alloc( len ); + WideCharToMultiByte( CP_ACP, 0, message, -1, msg, len, NULL, NULL ); + + if (gUIHandlerW && (gUIFilter & log_type)) + { + rc = gUIHandlerW( gUIContext, eMessageType, message ); + } + else if (gUIHandlerA && (gUIFilter & log_type)) + { + rc = gUIHandlerA( gUIContext, eMessageType, msg ); + } + else if (gUIHandlerRecord && (gUIFilter & log_type)) + { + MSIHANDLE rec = MsiCreateRecord( 1 ); + MsiRecordSetStringW( rec, 0, message ); + rc = gUIHandlerRecord( gUIContext, eMessageType, rec ); + MsiCloseHandle( rec ); + } + + if (!rc && package->log_file != INVALID_HANDLE_VALUE && + (eMessageType & 0xff000000) != INSTALLMESSAGE_PROGRESS) + { + DWORD written; + WriteFile( package->log_file, msg, len - 1, &written, NULL ); + WriteFile( package->log_file, "\n", 1, &written, NULL ); + } + msi_free( msg ); + msi_free( message ); + + switch (eMessageType & 0xff000000) + { + case INSTALLMESSAGE_ACTIONDATA: + deformat_string(package, MSI_RecordGetString(record, 2), &deformated); + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW(uirow, 1, deformated); + msi_free(deformated); + + ControlEvent_FireSubscribedEvent(package, szActionData, uirow); + msiobj_release(&uirow->hdr); + + if (package->action_progress_increment) + { + uirow = MSI_CreateRecord(2); + MSI_RecordSetInteger(uirow, 1, 2); + MSI_RecordSetInteger(uirow, 2, package->action_progress_increment); + ControlEvent_FireSubscribedEvent(package, szSetProgress, uirow); + msiobj_release(&uirow->hdr); + } + break; + + case INSTALLMESSAGE_ACTIONSTART: + deformat_string(package, MSI_RecordGetString(record, 2), &deformated); + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW(uirow, 1, deformated); + msi_free(deformated); + + ControlEvent_FireSubscribedEvent(package, szActionText, uirow); + + msiobj_release(&uirow->hdr); + break; + + case INSTALLMESSAGE_PROGRESS: + ControlEvent_FireSubscribedEvent(package, szSetProgress, record); + break; + } + + return ERROR_SUCCESS; +} + +INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType, + MSIHANDLE hRecord) +{ + UINT ret = ERROR_INVALID_HANDLE; + MSIPACKAGE *package = NULL; + MSIRECORD *record = NULL; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if( !package ) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemotePackage_ProcessMessage( remote_package, eMessageType, hRecord ); + + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + if( !record ) + goto out; + + ret = MSI_ProcessMessage( package, eMessageType, record ); + +out: + msiobj_release( &package->hdr ); + if( record ) + msiobj_release( &record->hdr ); + + return ret; +} + +/* property code */ + +UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue ) +{ + LPWSTR szwName = NULL, szwValue = NULL; + UINT r = ERROR_OUTOFMEMORY; + + szwName = strdupAtoW( szName ); + if( szName && !szwName ) + goto end; + + szwValue = strdupAtoW( szValue ); + if( szValue && !szwValue ) + goto end; + + r = MsiSetPropertyW( hInstall, szwName, szwValue); + +end: + msi_free( szwName ); + msi_free( szwValue ); + + return r; +} + +void msi_reset_folders( MSIPACKAGE *package, BOOL source ) +{ + MSIFOLDER *folder; + + LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry ) + { + if ( source ) + { + msi_free( folder->ResolvedSource ); + folder->ResolvedSource = NULL; + } + else + { + msi_free( folder->ResolvedTarget ); + folder->ResolvedTarget = NULL; + } + } +} + +UINT msi_set_property( MSIDATABASE *db, LPCWSTR szName, LPCWSTR szValue ) +{ + static const WCHAR insert_query[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ', + '(','`','_','P','r','o','p','e','r','t','y','`',',','`','V','a','l','u','e','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; + static const WCHAR update_query[] = { + 'U','P','D','A','T','E',' ','`','_','P','r','o','p','e','r','t','y','`',' ', + 'S','E','T',' ','`','V','a','l','u','e','`',' ','=',' ','?',' ','W','H','E','R','E',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0}; + static const WCHAR delete_query[] = { + 'D','E','L','E','T','E',' ','F','R','O','M',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ','W','H','E','R','E',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0}; + MSIQUERY *view; + MSIRECORD *row = NULL; + DWORD sz = 0; + WCHAR query[1024]; + UINT rc; + + TRACE("%p %s %s\n", db, debugstr_w(szName), debugstr_w(szValue)); + + if (!szName) + return ERROR_INVALID_PARAMETER; + + /* this one is weird... */ + if (!szName[0]) + return szValue ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS; + + rc = msi_get_property(db, szName, 0, &sz); + if (!szValue || !*szValue) + { + sprintfW(query, delete_query, szName); + } + else if (rc == ERROR_MORE_DATA || rc == ERROR_SUCCESS) + { + sprintfW(query, update_query, szName); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row, 1, szValue); + } + else + { + strcpyW(query, insert_query); + + row = MSI_CreateRecord(2); + MSI_RecordSetStringW(row, 1, szName); + MSI_RecordSetStringW(row, 2, szValue); + } + + rc = MSI_DatabaseOpenViewW(db, query, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_ViewExecute(view, row); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + if (row) msiobj_release(&row->hdr); + return rc; +} + +UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package ) + { + HRESULT hr; + BSTR name = NULL, value = NULL; + IWineMsiRemotePackage *remote_package; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + name = SysAllocString( szName ); + value = SysAllocString( szValue ); + if ((!name && szName) || (!value && szValue)) + { + SysFreeString( name ); + SysFreeString( value ); + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + hr = IWineMsiRemotePackage_SetProperty( remote_package, name, value ); + + SysFreeString( name ); + SysFreeString( value ); + IWineMsiRemotePackage_Release( remote_package ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + + ret = msi_set_property( package->db, szName, szValue ); + if (ret == ERROR_SUCCESS && !strcmpW( szName, szSourceDir )) + msi_reset_folders( package, TRUE ); + + msiobj_release( &package->hdr ); + return ret; +} + +static MSIRECORD *msi_get_property_row( MSIDATABASE *db, LPCWSTR name ) +{ + static const WCHAR query[]= { + 'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ', + 'F','R','O','M',' ' ,'`','_','P','r','o','p','e','r','t','y','`',' ', + 'W','H','E','R','E',' ' ,'`','_','P','r','o','p','e','r','t','y','`','=','?',0}; + MSIRECORD *rec, *row = NULL; + MSIQUERY *view; + UINT r; + + if (!name || !*name) + return NULL; + + rec = MSI_CreateRecord(1); + if (!rec) + return NULL; + + MSI_RecordSetStringW(rec, 1, name); + + r = MSI_DatabaseOpenViewW(db, query, &view); + if (r == ERROR_SUCCESS) + { + MSI_ViewExecute(view, rec); + MSI_ViewFetch(view, &row); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + msiobj_release(&rec->hdr); + return row; +} + +/* internal function, not compatible with MsiGetPropertyW */ +UINT msi_get_property( MSIDATABASE *db, LPCWSTR szName, + LPWSTR szValueBuf, LPDWORD pchValueBuf ) +{ + MSIRECORD *row; + UINT rc = ERROR_FUNCTION_FAILED; + + row = msi_get_property_row( db, szName ); + + if (*pchValueBuf > 0) + szValueBuf[0] = 0; + + if (row) + { + rc = MSI_RecordGetStringW(row, 1, szValueBuf, pchValueBuf); + msiobj_release(&row->hdr); + } + + if (rc == ERROR_SUCCESS) + TRACE("returning %s for property %s\n", debugstr_w(szValueBuf), + debugstr_w(szName)); + else if (rc == ERROR_MORE_DATA) + TRACE("need %d sized buffer for %s\n", *pchValueBuf, + debugstr_w(szName)); + else + { + *pchValueBuf = 0; + TRACE("property %s not found\n", debugstr_w(szName)); + } + + return rc; +} + +LPWSTR msi_dup_property(MSIDATABASE *db, LPCWSTR prop) +{ + DWORD sz = 0; + LPWSTR str; + UINT r; + + r = msi_get_property(db, prop, NULL, &sz); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + return NULL; + + sz++; + str = msi_alloc(sz * sizeof(WCHAR)); + r = msi_get_property(db, prop, str, &sz); + if (r != ERROR_SUCCESS) + { + msi_free(str); + str = NULL; + } + + return str; +} + +int msi_get_property_int( MSIDATABASE *db, LPCWSTR prop, int def ) +{ + LPWSTR str = msi_dup_property( db, prop ); + int val = str ? atoiW(str) : def; + msi_free(str); + return val; +} + +static UINT MSI_GetProperty( MSIHANDLE handle, LPCWSTR name, + awstring *szValueBuf, LPDWORD pchValueBuf ) +{ + MSIPACKAGE *package; + MSIRECORD *row = NULL; + UINT r = ERROR_FUNCTION_FAILED; + LPCWSTR val = NULL; + + TRACE("%u %s %p %p\n", handle, debugstr_w(name), + szValueBuf->str.w, pchValueBuf ); + + if (!name) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE ); + if (!package) + { + HRESULT hr; + IWineMsiRemotePackage *remote_package; + LPWSTR value = NULL; + BSTR bname; + DWORD len; + + remote_package = (IWineMsiRemotePackage *)msi_get_remote( handle ); + if (!remote_package) + return ERROR_INVALID_HANDLE; + + bname = SysAllocString( name ); + if (!bname) + { + IWineMsiRemotePackage_Release( remote_package ); + return ERROR_OUTOFMEMORY; + } + + len = 0; + hr = IWineMsiRemotePackage_GetProperty( remote_package, bname, NULL, &len ); + if (FAILED(hr)) + goto done; + + len++; + value = msi_alloc(len * sizeof(WCHAR)); + if (!value) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + hr = IWineMsiRemotePackage_GetProperty( remote_package, bname, value, &len ); + if (FAILED(hr)) + goto done; + + r = msi_strcpy_to_awstring( value, szValueBuf, pchValueBuf ); + + /* Bug required by Adobe installers */ + if (!szValueBuf->unicode && !szValueBuf->str.a) + *pchValueBuf *= sizeof(WCHAR); + +done: + IWineMsiRemotePackage_Release(remote_package); + SysFreeString(bname); + msi_free(value); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return r; + } + + row = msi_get_property_row( package->db, name ); + if (row) + val = MSI_RecordGetString( row, 1 ); + + if (!val) + val = szEmpty; + + r = msi_strcpy_to_awstring( val, szValueBuf, pchValueBuf ); + + if (row) + msiobj_release( &row->hdr ); + msiobj_release( &package->hdr ); + + return r; +} + +UINT WINAPI MsiGetPropertyA( MSIHANDLE hInstall, LPCSTR szName, + LPSTR szValueBuf, LPDWORD pchValueBuf ) +{ + awstring val; + LPWSTR name; + UINT r; + + val.unicode = FALSE; + val.str.a = szValueBuf; + + name = strdupAtoW( szName ); + if (szName && !name) + return ERROR_OUTOFMEMORY; + + r = MSI_GetProperty( hInstall, name, &val, pchValueBuf ); + msi_free( name ); + return r; +} + +UINT WINAPI MsiGetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, + LPWSTR szValueBuf, LPDWORD pchValueBuf ) +{ + awstring val; + + val.unicode = TRUE; + val.str.w = szValueBuf; + + return MSI_GetProperty( hInstall, szName, &val, pchValueBuf ); +} + +typedef struct _msi_remote_package_impl { + IWineMsiRemotePackage IWineMsiRemotePackage_iface; + MSIHANDLE package; + LONG refs; +} msi_remote_package_impl; + +static inline msi_remote_package_impl *impl_from_IWineMsiRemotePackage( IWineMsiRemotePackage *iface ) +{ + return CONTAINING_RECORD(iface, msi_remote_package_impl, IWineMsiRemotePackage_iface); +} + +static HRESULT WINAPI mrp_QueryInterface( IWineMsiRemotePackage *iface, + REFIID riid,LPVOID *ppobj) +{ + if( IsEqualCLSID( riid, &IID_IUnknown ) || + IsEqualCLSID( riid, &IID_IWineMsiRemotePackage ) ) + { + IWineMsiRemotePackage_AddRef( iface ); + *ppobj = iface; + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI mrp_AddRef( IWineMsiRemotePackage *iface ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + + return InterlockedIncrement( &This->refs ); +} + +static ULONG WINAPI mrp_Release( IWineMsiRemotePackage *iface ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + ULONG r; + + r = InterlockedDecrement( &This->refs ); + if (r == 0) + { + MsiCloseHandle( This->package ); + msi_free( This ); + } + return r; +} + +static HRESULT WINAPI mrp_SetMsiHandle( IWineMsiRemotePackage *iface, MSIHANDLE handle ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + This->package = handle; + return S_OK; +} + +static HRESULT WINAPI mrp_GetActiveDatabase( IWineMsiRemotePackage *iface, MSIHANDLE *handle ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + IWineMsiRemoteDatabase *rdb = NULL; + HRESULT hr; + MSIHANDLE hdb; + + hr = create_msi_remote_database( NULL, (LPVOID *)&rdb ); + if (FAILED(hr) || !rdb) + { + ERR("Failed to create remote database\n"); + return hr; + } + + hdb = MsiGetActiveDatabase(This->package); + + hr = IWineMsiRemoteDatabase_SetMsiHandle( rdb, hdb ); + if (FAILED(hr)) + { + ERR("Failed to set the database handle\n"); + return hr; + } + + *handle = alloc_msi_remote_handle( (IUnknown *)rdb ); + return S_OK; +} + +static HRESULT WINAPI mrp_GetProperty( IWineMsiRemotePackage *iface, BSTR property, BSTR value, DWORD *size ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetPropertyW(This->package, property, value, size); + if (r != ERROR_SUCCESS) return HRESULT_FROM_WIN32(r); + return S_OK; +} + +static HRESULT WINAPI mrp_SetProperty( IWineMsiRemotePackage *iface, BSTR property, BSTR value ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetPropertyW(This->package, property, value); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_ProcessMessage( IWineMsiRemotePackage *iface, INSTALLMESSAGE message, MSIHANDLE record ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiProcessMessage(This->package, message, record); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_DoAction( IWineMsiRemotePackage *iface, BSTR action ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiDoActionW(This->package, action); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_Sequence( IWineMsiRemotePackage *iface, BSTR table, int sequence ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSequenceW(This->package, table, sequence); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetTargetPath( IWineMsiRemotePackage *iface, BSTR folder, BSTR value, DWORD *size ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetTargetPathW(This->package, folder, value, size); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_SetTargetPath( IWineMsiRemotePackage *iface, BSTR folder, BSTR value) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetTargetPathW(This->package, folder, value); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetSourcePath( IWineMsiRemotePackage *iface, BSTR folder, BSTR value, DWORD *size ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetSourcePathW(This->package, folder, value, size); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetMode( IWineMsiRemotePackage *iface, MSIRUNMODE mode, BOOL *ret ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + *ret = MsiGetMode(This->package, mode); + return S_OK; +} + +static HRESULT WINAPI mrp_SetMode( IWineMsiRemotePackage *iface, MSIRUNMODE mode, BOOL state ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetMode(This->package, mode, state); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetFeatureState( IWineMsiRemotePackage *iface, BSTR feature, + INSTALLSTATE *installed, INSTALLSTATE *action ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetFeatureStateW(This->package, feature, installed, action); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_SetFeatureState( IWineMsiRemotePackage *iface, BSTR feature, INSTALLSTATE state ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetFeatureStateW(This->package, feature, state); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetComponentState( IWineMsiRemotePackage *iface, BSTR component, + INSTALLSTATE *installed, INSTALLSTATE *action ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetComponentStateW(This->package, component, installed, action); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_SetComponentState( IWineMsiRemotePackage *iface, BSTR component, INSTALLSTATE state ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetComponentStateW(This->package, component, state); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetLanguage( IWineMsiRemotePackage *iface, LANGID *language ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + *language = MsiGetLanguage(This->package); + return S_OK; +} + +static HRESULT WINAPI mrp_SetInstallLevel( IWineMsiRemotePackage *iface, int level ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiSetInstallLevel(This->package, level); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_FormatRecord( IWineMsiRemotePackage *iface, MSIHANDLE record, + BSTR *value) +{ + DWORD size = 0; + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiFormatRecordW(This->package, record, NULL, &size); + if (r == ERROR_SUCCESS) + { + *value = SysAllocStringLen(NULL, size); + if (!*value) + return E_OUTOFMEMORY; + size++; + r = MsiFormatRecordW(This->package, record, *value, &size); + } + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_EvaluateCondition( IWineMsiRemotePackage *iface, BSTR condition ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiEvaluateConditionW(This->package, condition); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_GetFeatureCost( IWineMsiRemotePackage *iface, BSTR feature, + INT cost_tree, INSTALLSTATE state, INT *cost ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiGetFeatureCostW(This->package, feature, cost_tree, state, cost); + return HRESULT_FROM_WIN32(r); +} + +static HRESULT WINAPI mrp_EnumComponentCosts( IWineMsiRemotePackage *iface, BSTR component, + DWORD index, INSTALLSTATE state, BSTR drive, + DWORD *buflen, INT *cost, INT *temp ) +{ + msi_remote_package_impl* This = impl_from_IWineMsiRemotePackage( iface ); + UINT r = MsiEnumComponentCostsW(This->package, component, index, state, drive, buflen, cost, temp); + return HRESULT_FROM_WIN32(r); +} + +static const IWineMsiRemotePackageVtbl msi_remote_package_vtbl = +{ + mrp_QueryInterface, + mrp_AddRef, + mrp_Release, + mrp_SetMsiHandle, + mrp_GetActiveDatabase, + mrp_GetProperty, + mrp_SetProperty, + mrp_ProcessMessage, + mrp_DoAction, + mrp_Sequence, + mrp_GetTargetPath, + mrp_SetTargetPath, + mrp_GetSourcePath, + mrp_GetMode, + mrp_SetMode, + mrp_GetFeatureState, + mrp_SetFeatureState, + mrp_GetComponentState, + mrp_SetComponentState, + mrp_GetLanguage, + mrp_SetInstallLevel, + mrp_FormatRecord, + mrp_EvaluateCondition, + mrp_GetFeatureCost, + mrp_EnumComponentCosts +}; + +HRESULT create_msi_remote_package( IUnknown *pOuter, LPVOID *ppObj ) +{ + msi_remote_package_impl* This; + + This = msi_alloc( sizeof *This ); + if (!This) + return E_OUTOFMEMORY; + + This->IWineMsiRemotePackage_iface.lpVtbl = &msi_remote_package_vtbl; + This->package = 0; + This->refs = 1; + + *ppObj = This; + + return S_OK; +} + +UINT msi_package_add_info(MSIPACKAGE *package, DWORD context, DWORD options, + LPCWSTR property, LPWSTR value) +{ + MSISOURCELISTINFO *info; + + LIST_FOR_EACH_ENTRY( info, &package->sourcelist_info, MSISOURCELISTINFO, entry ) + { + if (!strcmpW( info->value, value )) return ERROR_SUCCESS; + } + + info = msi_alloc(sizeof(MSISOURCELISTINFO)); + if (!info) + return ERROR_OUTOFMEMORY; + + info->context = context; + info->options = options; + info->property = property; + info->value = strdupW(value); + list_add_head(&package->sourcelist_info, &info->entry); + + return ERROR_SUCCESS; +} + +UINT msi_package_add_media_disk(MSIPACKAGE *package, DWORD context, DWORD options, + DWORD disk_id, LPWSTR volume_label, LPWSTR disk_prompt) +{ + MSIMEDIADISK *disk; + + LIST_FOR_EACH_ENTRY( disk, &package->sourcelist_media, MSIMEDIADISK, entry ) + { + if (disk->disk_id == disk_id) return ERROR_SUCCESS; + } + + disk = msi_alloc(sizeof(MSIMEDIADISK)); + if (!disk) + return ERROR_OUTOFMEMORY; + + disk->context = context; + disk->options = options; + disk->disk_id = disk_id; + disk->volume_label = strdupW(volume_label); + disk->disk_prompt = strdupW(disk_prompt); + list_add_head(&package->sourcelist_media, &disk->entry); + + return ERROR_SUCCESS; +} diff --git a/libmsi/patch.c b/libmsi/patch.c new file mode 100644 index 0000000..4801872 --- /dev/null +++ b/libmsi/patch.c @@ -0,0 +1,783 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004,2005 Aric Stewart for CodeWeavers + * Copyright 2011 Hans Leidekker 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 "winreg.h" +#include "objbase.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static BOOL match_language( MSIPACKAGE *package, LANGID langid ) +{ + UINT i; + + if (!package->num_langids || !langid) return TRUE; + for (i = 0; i < package->num_langids; i++) + { + if (package->langids[i] == langid) return TRUE; + } + return FALSE; +} + +static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *patch ) +{ + LPWSTR prod_code, patch_product, template = NULL; + UINT ret = ERROR_FUNCTION_FAILED; + + prod_code = msi_dup_property( package->db, szProductCode ); + patch_product = msi_get_suminfo_product( patch ); + + TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product)); + + if (strstrW( patch_product, prod_code )) + { + MSISUMMARYINFO *si; + const WCHAR *p; + + si = MSI_GetSummaryInformationW( patch, 0 ); + if (!si) + { + ERR("no summary information!\n"); + goto end; + } + template = msi_suminfo_dup_string( si, PID_TEMPLATE ); + if (!template) + { + ERR("no template property!\n"); + msiobj_release( &si->hdr ); + goto end; + } + if (!template[0]) + { + ret = ERROR_SUCCESS; + msiobj_release( &si->hdr ); + goto end; + } + TRACE("template: %s\n", debugstr_w(template)); + p = strchrW( template, ';' ); + if (p && match_language( package, atoiW( p + 1 ) )) + { + TRACE("applicable transform\n"); + ret = ERROR_SUCCESS; + } + /* FIXME: check platform */ + msiobj_release( &si->hdr ); + } + +end: + msi_free( patch_product ); + msi_free( prod_code ); + msi_free( template ); + return ret; +} + +static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + IStorage *stg = NULL; + HRESULT r; + + TRACE("%p %s\n", package, debugstr_w(name)); + + if (*name++ != ':') + { + ERR("expected a colon in %s\n", debugstr_w(name)); + return ERROR_FUNCTION_FAILED; + } + r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg ); + if (SUCCEEDED(r)) + { + ret = check_transform_applicable( package, stg ); + if (ret == ERROR_SUCCESS) + msi_table_apply_transform( package->db, stg ); + else + TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name)); + IStorage_Release( stg ); + } + else + { + ERR("failed to open substorage %s\n", debugstr_w(name)); + } + return ERROR_SUCCESS; +} + +UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) +{ + LPWSTR guid_list, *guids, product_code; + UINT i, ret = ERROR_FUNCTION_FAILED; + + product_code = msi_dup_property( package->db, szProductCode ); + if (!product_code) + { + /* FIXME: the property ProductCode should be written into the DB somewhere */ + ERR("no product code to check\n"); + return ERROR_SUCCESS; + } + guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE ); + guids = msi_split_string( guid_list, ';' ); + for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++) + { + if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS; + } + msi_free( guids ); + msi_free( guid_list ); + msi_free( product_code ); + return ret; +} + +static UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch ) +{ + MSIPATCHINFO *pi; + UINT r = ERROR_SUCCESS; + WCHAR *p; + + if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) ))) + { + return ERROR_OUTOFMEMORY; + } + if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER ))) + { + msi_free( pi ); + return ERROR_OUTOFMEMORY; + } + p = pi->patchcode; + if (*p != '{') + { + msi_free( pi->patchcode ); + msi_free( pi ); + return ERROR_PATCH_PACKAGE_INVALID; + } + if (!(p = strchrW( p + 1, '}' ))) + { + msi_free( pi->patchcode ); + msi_free( pi ); + return ERROR_PATCH_PACKAGE_INVALID; + } + if (p[1]) + { + FIXME("patch obsoletes %s\n", debugstr_w(p + 1)); + p[1] = 0; + } + TRACE("patch code %s\n", debugstr_w(pi->patchcode)); + if (!(pi->products = msi_suminfo_dup_string( si, PID_TEMPLATE ))) + { + msi_free( pi->patchcode ); + msi_free( pi ); + return ERROR_OUTOFMEMORY; + } + if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR ))) + { + msi_free( pi->patchcode ); + msi_free( pi->products ); + msi_free( pi ); + return ERROR_OUTOFMEMORY; + } + *patch = pi; + return r; +} + +static UINT patch_set_media_source_prop( MSIPACKAGE *package ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ', + 'I','S',' ','N','O','T',' ','N','U','L','L',0}; + MSIQUERY *view; + MSIRECORD *rec; + const WCHAR *property; + WCHAR *patch; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewExecute( view, 0 ); + if (r != ERROR_SUCCESS) + goto done; + + if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + property = MSI_RecordGetString( rec, 1 ); + patch = msi_dup_property( package->db, szPatch ); + msi_set_property( package->db, property, patch ); + msi_free( patch ); + msiobj_release( &rec->hdr ); + } + +done: + msiobj_release( &view->hdr ); + return r; +} + +struct patch_offset +{ + struct list entry; + WCHAR *name; + UINT sequence; +}; + +struct patch_offset_list +{ + struct list files; + UINT count, min, max; + UINT offset_to_apply; +}; + +static struct patch_offset_list *patch_offset_list_create( void ) +{ + struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) ); + list_init( &pos->files ); + pos->count = pos->max = 0; + pos->min = 999999; + return pos; +} + +static void patch_offset_list_free( struct patch_offset_list *pos ) +{ + struct patch_offset *po, *po2; + + LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry ) + { + msi_free( po->name ); + msi_free( po ); + } + msi_free( pos ); +} + +static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ', + 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_DatabaseOpenViewW( db, query, &view ); + if (r != ERROR_SUCCESS) + return; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger( rec, 1, last_sequence ); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + return; + + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + UINT sequence = MSI_RecordGetInteger( rec, 2 ); + + /* FIXME: we only use the max/min sequence numbers for now */ + pos->min = min( pos->min, sequence ); + pos->max = max( pos->max, sequence ); + pos->count++; + msiobj_release( &rec->hdr ); + } + msiobj_release( &view->hdr ); +} + +static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ', + 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_DatabaseOpenViewW( db, query, &view ); + if (r != ERROR_SUCCESS) + return; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger( rec, 1, last_sequence ); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + return; + + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + UINT attributes = MSI_RecordGetInteger( rec, 7 ); + if (attributes & msidbFileAttributesPatchAdded) + { + struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) ); + + po->name = msi_dup_record_field( rec, 1 ); + po->sequence = MSI_RecordGetInteger( rec, 8 ); + pos->min = min( pos->min, po->sequence ); + pos->max = max( pos->max, po->sequence ); + list_add_tail( &pos->files, &po->entry ); + pos->count++; + } + msiobj_release( &rec->hdr ); + } + msiobj_release( &view->hdr ); +} + +static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ', + 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ', + 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; + struct patch_offset *po; + MSIRECORD *rec; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( db, query, &view ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rec = MSI_CreateRecord( 2 ); + MSI_RecordSetInteger( rec, 1, pos->min ); + MSI_RecordSetInteger( rec, 2, pos->max ); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + goto done; + + LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry ) + { + UINT r_fetch; + while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) + { + const WCHAR *file = MSI_RecordGetString( rec, 1 ); + UINT seq; + + if (!strcmpiW( file, po->name )) + { + /* update record */ + seq = MSI_RecordGetInteger( rec, 8 ); + MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply ); + r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); + if (r != ERROR_SUCCESS) + ERR("Failed to update offset for file %s\n", debugstr_w(file)); + msiobj_release( &rec->hdr ); + break; + } + msiobj_release( &rec->hdr ); + } + if (r_fetch != ERROR_SUCCESS) break; + } + +done: + msiobj_release( &view->hdr ); + return ERROR_SUCCESS; +} + +static const WCHAR patch_media_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', + 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ', + 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0}; + +struct patch_media +{ + struct list entry; + UINT disk_id; + UINT last_sequence; + WCHAR *prompt; + WCHAR *cabinet; + WCHAR *volume; + WCHAR *source; +}; + +static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch ) +{ + static const WCHAR delete_query[] = { + 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', + 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0}; + static const WCHAR insert_query[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ', + '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',', + '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',', + '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0}; + MSIQUERY *view; + MSIRECORD *rec; + UINT r, disk_id; + struct list media_list; + struct patch_media *media, *next; + + r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view ); + if (r != ERROR_SUCCESS) return r; + + r = MSI_ViewExecute( view, 0 ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + TRACE("query failed %u\n", r); + return r; + } + list_init( &media_list ); + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + disk_id = MSI_RecordGetInteger( rec, 1 ); + TRACE("disk_id %u\n", disk_id); + if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID) + { + msiobj_release( &rec->hdr ); + continue; + } + if (!(media = msi_alloc( sizeof( *media )))) goto done; + media->disk_id = disk_id; + media->last_sequence = MSI_RecordGetInteger( rec, 2 ); + media->prompt = msi_dup_record_field( rec, 3 ); + media->cabinet = msi_dup_record_field( rec, 4 ); + media->volume = msi_dup_record_field( rec, 5 ); + media->source = msi_dup_record_field( rec, 6 ); + + list_add_tail( &media_list, &media->entry ); + msiobj_release( &rec->hdr ); + } + LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry ) + { + MSIQUERY *delete_view, *insert_view; + + r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view ); + if (r != ERROR_SUCCESS) goto done; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger( rec, 1, media->disk_id ); + + r = MSI_ViewExecute( delete_view, rec ); + msiobj_release( &delete_view->hdr ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; + + r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view ); + if (r != ERROR_SUCCESS) goto done; + + disk_id = package->db->media_transform_disk_id; + TRACE("disk id %u\n", disk_id); + TRACE("last sequence %u\n", media->last_sequence); + TRACE("prompt %s\n", debugstr_w(media->prompt)); + TRACE("cabinet %s\n", debugstr_w(media->cabinet)); + TRACE("volume %s\n", debugstr_w(media->volume)); + TRACE("source %s\n", debugstr_w(media->source)); + + rec = MSI_CreateRecord( 6 ); + MSI_RecordSetInteger( rec, 1, disk_id ); + MSI_RecordSetInteger( rec, 2, media->last_sequence ); + MSI_RecordSetStringW( rec, 3, media->prompt ); + MSI_RecordSetStringW( rec, 4, media->cabinet ); + MSI_RecordSetStringW( rec, 5, media->volume ); + MSI_RecordSetStringW( rec, 6, media->source ); + + r = MSI_ViewExecute( insert_view, rec ); + msiobj_release( &insert_view->hdr ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; + + r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet ); + if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r); + package->db->media_transform_disk_id++; + } + +done: + msiobj_release( &view->hdr ); + LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry ) + { + list_remove( &media->entry ); + msi_free( media->prompt ); + msi_free( media->cabinet ); + msi_free( media->volume ); + msi_free( media->source ); + msi_free( media ); + } + return r; +} + +static UINT set_patch_offsets( MSIDATABASE *db ) +{ + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_DatabaseOpenViewW( db, patch_media_query, &view ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewExecute( view, 0 ); + if (r != ERROR_SUCCESS) + goto done; + + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + UINT last_sequence = MSI_RecordGetInteger( rec, 2 ); + struct patch_offset_list *pos; + + /* FIXME: set/check Source field instead? */ + if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET) + { + msiobj_release( &rec->hdr ); + continue; + } + pos = patch_offset_list_create(); + patch_offset_get_files( db, last_sequence, pos ); + patch_offset_get_patches( db, last_sequence, pos ); + if (pos->count) + { + UINT offset = db->media_transform_offset - pos->min; + last_sequence = offset + pos->max; + + /* FIXME: this is for the patch table, which is not yet properly transformed */ + last_sequence += pos->min; + pos->offset_to_apply = offset; + patch_offset_modify_db( db, pos ); + + MSI_RecordSetInteger( rec, 2, last_sequence ); + r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); + if (r != ERROR_SUCCESS) + ERR("Failed to update Media table entry, expect breakage (%u)\n", r); + db->media_transform_offset = last_sequence + 1; + } + patch_offset_list_free( pos ); + msiobj_release( &rec->hdr ); + } + +done: + msiobj_release( &view->hdr ); + return r; +} + +static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch ) +{ + UINT i, r = ERROR_SUCCESS; + WCHAR **substorage; + + /* apply substorage transforms */ + substorage = msi_split_string( patch->transforms, ';' ); + for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++) + { + r = apply_substorage_transform( package, patch_db, substorage[i] ); + if (r == ERROR_SUCCESS) + { + add_patch_media( package, patch_db->storage ); + set_patch_offsets( package->db ); + } + } + msi_free( substorage ); + if (r != ERROR_SUCCESS) + return r; + + patch_set_media_source_prop( package ); + + patch->state = MSIPATCHSTATE_APPLIED; + list_add_tail( &package->patches, &patch->entry ); + return ERROR_SUCCESS; +} + +void msi_free_patchinfo( MSIPATCHINFO *patch ) +{ + msi_free( patch->patchcode ); + msi_free( patch->products ); + msi_free( patch->transforms ); + msi_free( patch->filename ); + msi_free( patch->localfile ); + msi_free( patch ); +} + +static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file ) +{ + static const WCHAR dotmsp[] = {'.','m','s','p',0}; + MSIDATABASE *patch_db = NULL; + WCHAR localfile[MAX_PATH]; + MSISUMMARYINFO *si; + MSIPATCHINFO *patch = NULL; + UINT r = ERROR_SUCCESS; + + TRACE("%p %s\n", package, debugstr_w(file)); + + r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db ); + if (r != ERROR_SUCCESS) + { + ERR("failed to open patch collection %s\n", debugstr_w( file ) ); + return r; + } + if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 ))) + { + msiobj_release( &patch_db->hdr ); + return ERROR_FUNCTION_FAILED; + } + r = msi_check_patch_applicable( package, si ); + if (r != ERROR_SUCCESS) + { + TRACE("patch not applicable\n"); + r = ERROR_SUCCESS; + goto done; + } + r = msi_parse_patch_summary( si, &patch ); + if ( r != ERROR_SUCCESS ) + goto done; + + r = msi_create_empty_local_file( localfile, dotmsp ); + if ( r != ERROR_SUCCESS ) + goto done; + + r = ERROR_OUTOFMEMORY; + if (!(patch->filename = strdupW( file ))) goto done; + if (!(patch->localfile = strdupW( localfile ))) goto done; + + r = msi_apply_patch_db( package, patch_db, patch ); + if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r); + +done: + msiobj_release( &si->hdr ); + msiobj_release( &patch_db->hdr ); + if (patch && r != ERROR_SUCCESS) + { + DeleteFileW( patch->localfile ); + msi_free_patchinfo( patch ); + } + return r; +} + +/* get the PATCH property, and apply all the patches it specifies */ +UINT msi_apply_patches( MSIPACKAGE *package ) +{ + LPWSTR patch_list, *patches; + UINT i, r = ERROR_SUCCESS; + + patch_list = msi_dup_property( package->db, szPatch ); + + TRACE("patches to be applied: %s\n", debugstr_w(patch_list)); + + patches = msi_split_string( patch_list, ';' ); + for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++) + r = msi_apply_patch_package( package, patches[i] ); + + msi_free( patches ); + msi_free( patch_list ); + return r; +} + +UINT msi_apply_transforms( MSIPACKAGE *package ) +{ + static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0}; + LPWSTR xform_list, *xforms; + UINT i, r = ERROR_SUCCESS; + + xform_list = msi_dup_property( package->db, szTransforms ); + xforms = msi_split_string( xform_list, ';' ); + + for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++) + { + if (xforms[i][0] == ':') + r = apply_substorage_transform( package, package->db, xforms[i] ); + else + { + WCHAR *transform; + + if (!PathIsRelativeW( xforms[i] )) transform = xforms[i]; + else + { + WCHAR *p = strrchrW( package->PackagePath, '\\' ); + DWORD len = p - package->PackagePath + 1; + + if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) )) + { + msi_free( xforms ); + msi_free( xform_list ); + return ERROR_OUTOFMEMORY; + } + memcpy( transform, package->PackagePath, len * sizeof(WCHAR) ); + memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) ); + } + r = MSI_DatabaseApplyTransformW( package->db, transform, 0 ); + if (transform != xforms[i]) msi_free( transform ); + } + } + msi_free( xforms ); + msi_free( xform_list ); + return r; +} + +UINT msi_apply_registered_patch( MSIPACKAGE *package, LPCWSTR patch_code ) +{ + UINT r; + DWORD len; + WCHAR patch_file[MAX_PATH]; + MSIDATABASE *patch_db; + MSIPATCHINFO *patch_info; + MSISUMMARYINFO *si; + + len = sizeof(patch_file) / sizeof(WCHAR); + r = MsiGetPatchInfoExW( patch_code, package->ProductCode, NULL, package->Context, + INSTALLPROPERTY_LOCALPACKAGEW, patch_file, &len ); + if (r != ERROR_SUCCESS) + { + ERR("failed to get patch filename %u\n", r); + return r; + } + r = MSI_OpenDatabaseW( patch_file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db ); + if (r != ERROR_SUCCESS) + { + ERR("failed to open patch database %s\n", debugstr_w( patch_file )); + return r; + } + si = MSI_GetSummaryInformationW( patch_db->storage, 0 ); + if (!si) + { + msiobj_release( &patch_db->hdr ); + return ERROR_FUNCTION_FAILED; + } + r = msi_parse_patch_summary( si, &patch_info ); + msiobj_release( &si->hdr ); + if (r != ERROR_SUCCESS) + { + ERR("failed to parse patch summary %u\n", r); + msiobj_release( &patch_db->hdr ); + return r; + } + patch_info->localfile = strdupW( patch_file ); + if (!patch_info->localfile) + { + msiobj_release( &patch_db->hdr ); + msi_free_patchinfo( patch_info ); + return ERROR_OUTOFMEMORY; + } + r = msi_apply_patch_db( package, patch_db, patch_info ); + msiobj_release( &patch_db->hdr ); + if (r != ERROR_SUCCESS) + { + ERR("failed to apply patch %u\n", r); + msi_free_patchinfo( patch_info ); + } + return r; +} diff --git a/libmsi/query.h b/libmsi/query.h new file mode 100644 index 0000000..ca34b4c --- /dev/null +++ b/libmsi/query.h @@ -0,0 +1,141 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 + */ + +#ifndef __WINE_MSI_QUERY_H +#define __WINE_MSI_QUERY_H + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "objidl.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "wine/list.h" + + +#define OP_EQ 1 +#define OP_AND 2 +#define OP_OR 3 +#define OP_GT 4 +#define OP_LT 5 +#define OP_LE 6 +#define OP_GE 7 +#define OP_NE 8 +#define OP_ISNULL 9 +#define OP_NOTNULL 10 + +#define EXPR_COMPLEX 1 +#define EXPR_COLUMN 2 +#define EXPR_COL_NUMBER 3 +#define EXPR_IVAL 4 +#define EXPR_SVAL 5 +#define EXPR_UVAL 6 +#define EXPR_STRCMP 7 +#define EXPR_WILDCARD 9 +#define EXPR_COL_NUMBER_STRING 10 +#define EXPR_COL_NUMBER32 11 +#define EXPR_UNARY 12 + +struct sql_str { + LPCWSTR data; + INT len; +}; + +struct complex_expr +{ + UINT op; + struct expr *left; + struct expr *right; +}; + +struct tagJOINTABLE; +union ext_column +{ + struct + { + LPCWSTR column; + LPCWSTR table; + } unparsed; + struct + { + UINT column; + struct tagJOINTABLE *table; + } parsed; +}; + +struct expr +{ + int type; + union + { + struct complex_expr expr; + INT ival; + UINT uval; + LPCWSTR sval; + union ext_column column; + } u; +}; + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, + struct list *mem ) DECLSPEC_HIDDEN; + +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) DECLSPEC_HIDDEN; + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + const column_info *columns ) DECLSPEC_HIDDEN; + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) DECLSPEC_HIDDEN; + +UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + column_info *columns ) DECLSPEC_HIDDEN; + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR tables, + struct expr *cond ) DECLSPEC_HIDDEN; + +UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, + column_info *col_info, BOOL hold ) DECLSPEC_HIDDEN; + +UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, + column_info *columns, column_info *values, BOOL temp ) DECLSPEC_HIDDEN; + +UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *list, struct expr *expr ) DECLSPEC_HIDDEN; + +UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) DECLSPEC_HIDDEN; + +UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold ) DECLSPEC_HIDDEN; + +UINT STREAMS_CreateView( MSIDATABASE *db, MSIVIEW **view ) DECLSPEC_HIDDEN; + +UINT STORAGES_CreateView( MSIDATABASE *db, MSIVIEW **view ) DECLSPEC_HIDDEN; + +UINT DROP_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name ) DECLSPEC_HIDDEN; + +int sqliteGetToken(const WCHAR *z, int *tokenType, int *skip) DECLSPEC_HIDDEN; + +MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec ) DECLSPEC_HIDDEN; + +UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, + MSICONDITION persistent ) DECLSPEC_HIDDEN; + +#endif /* __WINE_MSI_QUERY_H */ diff --git a/libmsi/record.c b/libmsi/record.c new file mode 100644 index 0000000..1dace78 --- /dev/null +++ b/libmsi/record.c @@ -0,0 +1,1066 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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 "winuser.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" +#include "winnls.h" +#include "ole2.h" + +#include "winreg.h" +#include "shlwapi.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define MSIFIELD_NULL 0 +#define MSIFIELD_INT 1 +#define MSIFIELD_WSTR 3 +#define MSIFIELD_STREAM 4 +#define MSIFIELD_INTPTR 5 + +static void MSI_FreeField( MSIFIELD *field ) +{ + switch( field->type ) + { + case MSIFIELD_NULL: + case MSIFIELD_INT: + case MSIFIELD_INTPTR: + break; + case MSIFIELD_WSTR: + msi_free( field->u.szwVal); + break; + case MSIFIELD_STREAM: + IStream_Release( field->u.stream ); + break; + default: + ERR("Invalid field type %d\n", field->type); + } +} + +void MSI_CloseRecord( MSIOBJECTHDR *arg ) +{ + MSIRECORD *rec = (MSIRECORD *) arg; + UINT i; + + for( i=0; i<=rec->count; i++ ) + MSI_FreeField( &rec->fields[i] ); +} + +MSIRECORD *MSI_CreateRecord( UINT cParams ) +{ + MSIRECORD *rec; + UINT len; + + TRACE("%d\n", cParams); + + if( cParams>65535 ) + return NULL; + + len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams; + rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord ); + if( rec ) + rec->count = cParams; + return rec; +} + +MSIHANDLE WINAPI MsiCreateRecord( UINT cParams ) +{ + MSIRECORD *rec; + MSIHANDLE ret = 0; + + TRACE("%d\n", cParams); + + rec = MSI_CreateRecord( cParams ); + if( rec ) + { + ret = alloc_msihandle( &rec->hdr ); + msiobj_release( &rec->hdr ); + } + return ret; +} + +UINT MSI_RecordGetFieldCount( const MSIRECORD *rec ) +{ + return rec->count; +} + +UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return -1; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetFieldCount( rec ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ret; +} + +static BOOL string2intW( LPCWSTR str, int *out ) +{ + int x = 0; + LPCWSTR p = str; + + if( *p == '-' ) /* skip the minus sign */ + p++; + while ( *p ) + { + if( (*p < '0') || (*p > '9') ) + return FALSE; + x *= 10; + x += (*p - '0'); + p++; + } + + if( str[0] == '-' ) /* check if it's negative */ + x = -x; + *out = x; + + return TRUE; +} + +UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n, + MSIRECORD *out_rec, UINT out_n ) +{ + UINT r = ERROR_SUCCESS; + + msiobj_lock( &in_rec->hdr ); + + if ( in_n > in_rec->count || out_n > out_rec->count ) + r = ERROR_FUNCTION_FAILED; + else if ( in_rec != out_rec || in_n != out_n ) + { + LPWSTR str; + MSIFIELD *in, *out; + + in = &in_rec->fields[in_n]; + out = &out_rec->fields[out_n]; + + switch ( in->type ) + { + case MSIFIELD_NULL: + break; + case MSIFIELD_INT: + out->u.iVal = in->u.iVal; + break; + case MSIFIELD_INTPTR: + out->u.pVal = in->u.pVal; + break; + case MSIFIELD_WSTR: + str = strdupW( in->u.szwVal ); + if ( !str ) + r = ERROR_OUTOFMEMORY; + else + out->u.szwVal = str; + break; + case MSIFIELD_STREAM: + IStream_AddRef( in->u.stream ); + out->u.stream = in->u.stream; + break; + default: + ERR("invalid field type %d\n", in->type); + } + if (r == ERROR_SUCCESS) + out->type = in->type; + } + + msiobj_unlock( &in_rec->hdr ); + + return r; +} + +INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField ) +{ + int ret; + + TRACE( "%p %d\n", rec, iField ); + + if( iField > rec->count ) + return MININT_PTR; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return rec->fields[iField].u.iVal; + case MSIFIELD_INTPTR: + return rec->fields[iField].u.pVal; + case MSIFIELD_WSTR: + if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) + return ret; + return MININT_PTR; + default: + break; + } + + return MININT_PTR; +} + +int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField) +{ + int ret = 0; + + TRACE("%p %d\n", rec, iField ); + + if( iField > rec->count ) + return MSI_NULL_INTEGER; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return rec->fields[iField].u.iVal; + case MSIFIELD_INTPTR: + return rec->fields[iField].u.pVal; + case MSIFIELD_WSTR: + if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) + return ret; + return MSI_NULL_INTEGER; + default: + break; + } + + return MSI_NULL_INTEGER; +} + +int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d\n", handle, iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return MSI_NULL_INTEGER; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetInteger( rec, iField ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ret; +} + +UINT WINAPI MsiRecordClearData( MSIHANDLE handle ) +{ + MSIRECORD *rec; + UINT i; + + TRACE("%d\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + for( i=0; i<=rec->count; i++) + { + MSI_FreeField( &rec->fields[i] ); + rec->fields[i].type = MSIFIELD_NULL; + rec->fields[i].u.iVal = 0; + } + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal ) +{ + TRACE("%p %u %ld\n", rec, iField, pVal); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_INTPTR; + rec->fields[iField].u.pVal = pVal; + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal ) +{ + TRACE("%p %u %d\n", rec, iField, iVal); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_INT; + rec->fields[iField].u.iVal = iVal; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %u %d\n", handle, iField, iVal); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetInteger( rec, iField, iVal ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField ) +{ + BOOL r = TRUE; + + TRACE("%p %d\n", rec, iField ); + + r = ( iField > rec->count ) || + ( rec->fields[iField].type == MSIFIELD_NULL ); + + return r; +} + +BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d\n", handle, iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return 0; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordIsNull( rec, iField ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; + +} + +UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField, + LPSTR szValue, LPDWORD pcchValue) +{ + UINT len=0, ret; + CHAR buffer[16]; + + TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue); + + if( iField > rec->count ) + { + if ( szValue && *pcchValue > 0 ) + szValue[0] = 0; + + *pcchValue = 0; + return ERROR_SUCCESS; + } + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfA(buffer, "%d", rec->fields[iField].u.iVal); + len = lstrlenA( buffer ); + if (szValue) + lstrcpynA(szValue, buffer, *pcchValue); + break; + case MSIFIELD_WSTR: + len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + NULL, 0 , NULL, NULL); + if (szValue) + WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + szValue, *pcchValue, NULL, NULL); + if( szValue && *pcchValue && len>*pcchValue ) + szValue[*pcchValue-1] = 0; + if( len ) + len--; + break; + case MSIFIELD_NULL: + if( szValue && *pcchValue > 0 ) + szValue[0] = 0; + break; + default: + ret = ERROR_INVALID_PARAMETER; + break; + } + + if( szValue && *pcchValue <= len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField, + LPSTR szValue, LPDWORD pcchValue) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField ) +{ + if( iField > rec->count ) + return NULL; + + if( rec->fields[iField].type != MSIFIELD_WSTR ) + return NULL; + + return rec->fields[iField].u.szwVal; +} + +UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField, + LPWSTR szValue, LPDWORD pcchValue) +{ + UINT len=0, ret; + WCHAR buffer[16]; + static const WCHAR szFormat[] = { '%','d',0 }; + + TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue); + + if( iField > rec->count ) + { + if ( szValue && *pcchValue > 0 ) + szValue[0] = 0; + + *pcchValue = 0; + return ERROR_SUCCESS; + } + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal); + len = lstrlenW( buffer ); + if (szValue) + lstrcpynW(szValue, buffer, *pcchValue); + break; + case MSIFIELD_WSTR: + len = lstrlenW( rec->fields[iField].u.szwVal ); + if (szValue) + lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue); + break; + case MSIFIELD_NULL: + if( szValue && *pcchValue > 0 ) + szValue[0] = 0; + break; + default: + break; + } + + if( szValue && *pcchValue <= len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField, + LPWSTR szValue, LPDWORD pcchValue) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +static UINT msi_get_stream_size( IStream *stm ) +{ + STATSTG stat; + HRESULT r; + + r = IStream_Stat( stm, &stat, STATFLAG_NONAME ); + if( FAILED(r) ) + return 0; + return stat.cbSize.QuadPart; +} + +static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField) +{ + TRACE("%p %d\n", rec, iField); + + if( iField > rec->count ) + return 0; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return sizeof (INT); + case MSIFIELD_WSTR: + return lstrlenW( rec->fields[iField].u.szwVal ); + case MSIFIELD_NULL: + break; + case MSIFIELD_STREAM: + return msi_get_stream_size( rec->fields[iField].u.stream ); + } + return 0; +} + +UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d\n", handle, iField); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return 0; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordDataSize( rec, iField); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue ) +{ + LPWSTR str; + + TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue)); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + if( szValue && szValue[0] ) + { + str = strdupAtoW( szValue ); + rec->fields[iField].type = MSIFIELD_WSTR; + rec->fields[iField].u.szwVal = str; + } + else + { + rec->fields[iField].type = MSIFIELD_NULL; + rec->fields[iField].u.szwVal = NULL; + } + + return 0; +} + +UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStringA( rec, iField, szValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue ) +{ + LPWSTR str; + + TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue)); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + + if( szValue && szValue[0] ) + { + str = strdupW( szValue ); + rec->fields[iField].type = MSIFIELD_WSTR; + rec->fields[iField].u.szwVal = str; + } + else + { + rec->fields[iField].type = MSIFIELD_NULL; + rec->fields[iField].u.szwVal = NULL; + } + + return 0; +} + +UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStringW( rec, iField, szValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +/* read the data in a file into an IStream */ +static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm) +{ + DWORD sz, szHighWord = 0, read; + HANDLE handle; + HGLOBAL hGlob = 0; + HRESULT hr; + ULARGE_INTEGER ulSize; + + TRACE("reading %s\n", debugstr_w(szFile)); + + /* read the file into memory */ + handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if( handle == INVALID_HANDLE_VALUE ) + return GetLastError(); + sz = GetFileSize(handle, &szHighWord); + if( sz != INVALID_FILE_SIZE && szHighWord == 0 ) + { + hGlob = GlobalAlloc(GMEM_FIXED, sz); + if( hGlob ) + { + BOOL r = ReadFile(handle, hGlob, sz, &read, NULL); + if( !r ) + { + GlobalFree(hGlob); + hGlob = 0; + } + } + } + CloseHandle(handle); + if( !hGlob ) + return ERROR_FUNCTION_FAILED; + + /* make a stream out of it, and set the correct file size */ + hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm); + if( FAILED( hr ) ) + { + GlobalFree(hGlob); + return ERROR_FUNCTION_FAILED; + } + + /* set the correct size - CreateStreamOnHGlobal screws it up */ + ulSize.QuadPart = sz; + IStream_SetSize(*pstm, ulSize); + + TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream) +{ + if ( (iField == 0) || (iField > rec->count) ) + return ERROR_INVALID_PARAMETER; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_STREAM; + rec->fields[iField].u.stream = stream; + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename) +{ + IStream *stm = NULL; + HRESULT r; + + if( (iField == 0) || (iField > rec->count) ) + return ERROR_INVALID_PARAMETER; + + /* no filename means we should seek back to the start of the stream */ + if( !szFilename ) + { + LARGE_INTEGER ofs; + ULARGE_INTEGER cur; + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_FIELD; + + stm = rec->fields[iField].u.stream; + if( !stm ) + return ERROR_INVALID_FIELD; + + ofs.QuadPart = 0; + r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + } + else + { + /* read the file into a stream and save the stream in the record */ + r = RECORD_StreamFromFile(szFilename, &stm); + if( r != ERROR_SUCCESS ) + return r; + + /* if all's good, store it in the record */ + MSI_RecordSetStream(rec, iField, stm); + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename) +{ + LPWSTR wstr = NULL; + UINT ret; + + TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename)); + + if( szFilename ) + { + wstr = strdupAtoW( szFilename ); + if( !wstr ) + return ERROR_OUTOFMEMORY; + } + ret = MsiRecordSetStreamW(hRecord, iField, wstr); + msi_free(wstr); + + return ret; +} + +UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz) +{ + ULONG count; + HRESULT r; + IStream *stm; + + TRACE("%p %d %p %p\n", rec, iField, buf, sz); + + if( !sz ) + return ERROR_INVALID_PARAMETER; + + if( iField > rec->count) + return ERROR_INVALID_PARAMETER; + + if ( rec->fields[iField].type == MSIFIELD_NULL ) + { + *sz = 0; + return ERROR_INVALID_DATA; + } + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_DATATYPE; + + stm = rec->fields[iField].u.stream; + if( !stm ) + return ERROR_INVALID_PARAMETER; + + /* if there's no buffer pointer, calculate the length to the end */ + if( !buf ) + { + LARGE_INTEGER ofs; + ULARGE_INTEGER end, cur; + + ofs.QuadPart = cur.QuadPart = 0; + end.QuadPart = 0; + IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + IStream_Seek( stm, ofs, STREAM_SEEK_END, &end ); + ofs.QuadPart = cur.QuadPart; + IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + *sz = end.QuadPart - cur.QuadPart; + + return ERROR_SUCCESS; + } + + /* read the data */ + count = 0; + r = IStream_Read( stm, buf, *sz, &count ); + if( FAILED( r ) ) + { + *sz = 0; + return ERROR_FUNCTION_FAILED; + } + + *sz = count; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%d %d %p %p\n", handle, iField, buf, sz); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordReadStream( rec, iField, buf, sz ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm ) +{ + TRACE("%p %d %p\n", rec, iField, stm); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + + rec->fields[iField].type = MSIFIELD_STREAM; + rec->fields[iField].u.stream = stm; + IStream_AddRef( stm ); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm) +{ + TRACE("%p %d %p\n", rec, iField, pstm); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_FIELD; + + *pstm = rec->fields[iField].u.stream; + IStream_AddRef( *pstm ); + + return ERROR_SUCCESS; +} + +static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name ) +{ + ULARGE_INTEGER size; + LARGE_INTEGER pos; + IStream *out; + DWORD stgm; + HRESULT r; + + stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE; + r = SHCreateStreamOnFileW( name, stgm, &out ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size ); + if( FAILED( r ) ) + goto end; + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); + if( FAILED( r ) ) + goto end; + + r = IStream_CopyTo( stm, out, size, NULL, NULL ); + +end: + IStream_Release( out ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + return ERROR_SUCCESS; +} + +UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name ) +{ + IStream *stm = NULL; + UINT r; + + TRACE("%p %u %s\n", rec, iField, debugstr_w(name)); + + msiobj_lock( &rec->hdr ); + + r = MSI_RecordGetIStream( rec, iField, &stm ); + if( r == ERROR_SUCCESS ) + { + r = msi_dump_stream_to_file( stm, name ); + IStream_Release( stm ); + } + + msiobj_unlock( &rec->hdr ); + + return r; +} + +MSIRECORD *MSI_CloneRecord(MSIRECORD *rec) +{ + MSIRECORD *clone; + UINT r, i, count; + + count = MSI_RecordGetFieldCount(rec); + clone = MSI_CreateRecord(count); + if (!clone) + return NULL; + + for (i = 0; i <= count; i++) + { + if (rec->fields[i].type == MSIFIELD_STREAM) + { + if (FAILED(IStream_Clone(rec->fields[i].u.stream, + &clone->fields[i].u.stream))) + { + msiobj_release(&clone->hdr); + return NULL; + } + clone->fields[i].type = MSIFIELD_STREAM; + } + else + { + r = MSI_RecordCopyField(rec, i, clone, i); + if (r != ERROR_SUCCESS) + { + msiobj_release(&clone->hdr); + return NULL; + } + } + } + + return clone; +} + +BOOL MSI_RecordsAreFieldsEqual(MSIRECORD *a, MSIRECORD *b, UINT field) +{ + if (a->fields[field].type != b->fields[field].type) + return FALSE; + + switch (a->fields[field].type) + { + case MSIFIELD_NULL: + break; + + case MSIFIELD_INT: + if (a->fields[field].u.iVal != b->fields[field].u.iVal) + return FALSE; + break; + + case MSIFIELD_WSTR: + if (strcmpW(a->fields[field].u.szwVal, b->fields[field].u.szwVal)) + return FALSE; + break; + + case MSIFIELD_STREAM: + default: + return FALSE; + } + return TRUE; +} + + +BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b) +{ + UINT i; + + if (a->count != b->count) + return FALSE; + + for (i = 0; i <= a->count; i++) + { + if (!MSI_RecordsAreFieldsEqual( a, b, i )) + return FALSE; + } + + return TRUE; +} + +WCHAR *msi_dup_record_field( MSIRECORD *rec, INT field ) +{ + DWORD sz = 0; + WCHAR *str; + UINT r; + + if (MSI_RecordIsNull( rec, field )) return NULL; + + r = MSI_RecordGetStringW( rec, field, NULL, &sz ); + if (r != ERROR_SUCCESS) + return NULL; + + sz++; + str = msi_alloc( sz * sizeof(WCHAR) ); + if (!str) return NULL; + str[0] = 0; + r = MSI_RecordGetStringW( rec, field, str, &sz ); + if (r != ERROR_SUCCESS) + { + ERR("failed to get string!\n"); + msi_free( str ); + return NULL; + } + return str; +} diff --git a/libmsi/registry.c b/libmsi/registry.c new file mode 100644 index 0000000..4143a79 --- /dev/null +++ b/libmsi/registry.c @@ -0,0 +1,2478 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack for CodeWeavers + * Copyright 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 +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msipriv.h" +#include "wincrypt.h" +#include "wine/unicode.h" +#include "winver.h" +#include "winuser.h" +#include "sddl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * This module will be all the helper functions for registry access by the + * installer bits. + */ + +static const WCHAR szUserDataFeatures_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\','F','e','a','t','u','r','e','s',0}; + +static const WCHAR szUserDataComp_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','C','o','m','p','o','n','e','n','t','s','\\','%','s',0}; + +static const WCHAR szUserDataComponents_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','C','o','m','p','o','n','e','n','t','s',0}; + +static const WCHAR szUserDataProd_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s',0}; + +static const WCHAR szUserDataProducts_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','r','o','d','u','c','t','s',0}; + +static const WCHAR szUserDataPatch_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','a','t','c','h','e','s','\\','%','s',0}; + +static const WCHAR szUserDataPatches_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','a','t','c','h','e','s',0}; + +static const WCHAR szUserDataProductPatches_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\','P','a','t','c','h','e','s',0}; + +static const WCHAR szInstallProperties_fmt[] = { + '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','\\','U','s','e','r','D','a','t','a','\\', + '%','s','\\','P','r','o','d','u','c','t','s','\\','%','s','\\', + 'I','n','s','t','a','l','l','P','r','o','p','e','r','t','i','e','s',0}; + +static const WCHAR szInstaller_LocalManaged_fmt[] = { + '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','\\','M','a','n','a','g','e','d','\\','%','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0}; + +static const WCHAR szInstaller_LocalManagedProd_fmt[] = { + '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','\\','M','a','n','a','g','e','d','\\','%','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s','\\','%','s',0}; + +static const WCHAR szInstaller_LocalManagedFeat_fmt[] = { + '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','\\','M','a','n','a','g','e','d','\\','%','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\','%','s',0}; + +static const WCHAR szInstaller_Products[] = { + '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','\\','P','r','o','d','u','c','t','s',0}; + +static const WCHAR szInstaller_Patches[] = { + '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','\\','P','a','t','c','h','e','s',0}; + +static const WCHAR szInstaller_Components[] = { + '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','\\','C','o','m','p','o','n','e','n','t','s',0}; + +static const WCHAR szInstaller_LocalClassesProducts[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0}; + +static const WCHAR szInstaller_LocalClassesFeatures[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s',0}; + +static const WCHAR szInstaller_LocalClassesProd[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s','\\',0}; + +static const WCHAR szInstaller_LocalClassesFeat[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','F','e','a','t','u','r','e','s','\\',0}; + +static const WCHAR szInstaller_ClassesUpgradeCode[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0}; + +static const WCHAR szInstaller_ClassesUpgradeCodes[] = { + 'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','U','p','g','r','a','d','e','C','o','d','e','s',0}; + +static const WCHAR szInstaller_Features[] = { + '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','\\','F','e','a','t','u','r','e','s','\\',0}; + +static const WCHAR szInstaller_UpgradeCodes[] = { + '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','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0}; + +static const WCHAR szInstaller_UserUpgradeCodes[] = { + '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','\\','U','p','g','r','a','d','e','C','o','d','e','s','\\',0}; + +static const WCHAR szUninstall[] = { + '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','\\', + 'U','n','i','n','s','t','a','l','l','\\',0}; + +static const WCHAR szUninstall_32node[] = { + 'S','o','f','t','w','a','r','e','\\','W','o','w','6','4','3','2','N','o','d','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','\\','U','n','i','n','s','t','a','l','l','\\',0}; + +static const WCHAR szUserComponents[] = { + '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}; + +static const WCHAR szUserFeatures[] = { + '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','\\','F','e','a','t','u','r','e','s','\\',0}; + +static const WCHAR szUserProducts[] = { + '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','\\','P','r','o','d','u','c','t','s','\\',0}; + +static const WCHAR szUserPatches[] = { + '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','\\','P','a','t','c','h','e','s','\\',0}; + +BOOL unsquash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=0; + + if (lstrlenW(in) != 32) + return FALSE; + + out[n++]='{'; + for(i=0; i<8; i++) + out[n++] = in[7-i]; + out[n++]='-'; + for(i=0; i<4; i++) + out[n++] = in[11-i]; + out[n++]='-'; + for(i=0; i<4; i++) + out[n++] = in[15-i]; + out[n++]='-'; + for(i=0; i<2; i++) + { + out[n++] = in[17+i*2]; + out[n++] = in[16+i*2]; + } + out[n++]='-'; + for( ; i<8; i++) + { + out[n++] = in[17+i*2]; + out[n++] = in[16+i*2]; + } + out[n++]='}'; + out[n]=0; + return TRUE; +} + +BOOL squash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=1; + GUID guid; + + out[0] = 0; + + if (FAILED(CLSIDFromString((LPCOLESTR)in, &guid))) + return FALSE; + + for(i=0; i<8; i++) + out[7-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[11-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[15-i] = in[n++]; + n++; + for(i=0; i<2; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + n++; + for( ; i<8; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + out[32]=0; + return TRUE; +} + + +/* tables for encoding and decoding base85 */ +static const unsigned char table_dec85[0x80] = { +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff, +0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17, +0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, +0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36, +0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46, +0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff, +}; + +static const char table_enc85[] = +"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO" +"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx" +"yz{}~"; + +/* + * Converts a base85 encoded guid into a GUID pointer + * Base85 encoded GUIDs should be 20 characters long. + * + * returns TRUE if successful, FALSE if not + */ +BOOL decode_base85_guid( LPCWSTR str, GUID *guid ) +{ + DWORD i, val = 0, base = 1, *p; + + if (!str) + return FALSE; + + p = (DWORD*) guid; + for( i=0; i<20; i++ ) + { + if( (i%5) == 0 ) + { + val = 0; + base = 1; + } + val += table_dec85[str[i]] * base; + if( str[i] >= 0x80 ) + return FALSE; + if( table_dec85[str[i]] == 0xff ) + return FALSE; + if( (i%5) == 4 ) + p[i/5] = val; + base *= 85; + } + return TRUE; +} + +/* + * Encodes a base85 guid given a GUID pointer + * Caller should provide a 21 character buffer for the encoded string. + * + * returns TRUE if successful, FALSE if not + */ +BOOL encode_base85_guid( GUID *guid, LPWSTR str ) +{ + unsigned int x, *p, i; + + p = (unsigned int*) guid; + for( i=0; i<4; i++ ) + { + x = p[i]; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + } + *str = 0; + + return TRUE; +} + +DWORD msi_version_str_to_dword(LPCWSTR p) +{ + DWORD major, minor = 0, build = 0, version = 0; + + if (!p) + return version; + + major = atoiW(p); + + p = strchrW(p, '.'); + if (p) + { + minor = atoiW(p+1); + p = strchrW(p+1, '.'); + if (p) + build = atoiW(p+1); + } + + return MAKELONG(build, MAKEWORD(minor, major)); +} + +LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) +{ + DWORD len; + if (!value) value = szEmpty; + len = (lstrlenW(value) + 1) * sizeof (WCHAR); + return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len ); +} + +LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) +{ + LPCWSTR p = value; + while (*p) p += lstrlenW(p) + 1; + return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ, + (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) ); +} + +LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val ) +{ + return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) ); +} + +LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val ) +{ + HKEY hsubkey = 0; + LONG r; + + r = RegCreateKeyW( hkey, path, &hsubkey ); + if (r != ERROR_SUCCESS) + return r; + r = msi_reg_set_val_str( hsubkey, name, val ); + RegCloseKey( hsubkey ); + return r; +} + +LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name ) +{ + DWORD len = 0; + LPWSTR val; + LONG r; + + r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len); + if (r != ERROR_SUCCESS) + return NULL; + + len += sizeof (WCHAR); + val = msi_alloc( len ); + if (!val) + return NULL; + val[0] = 0; + RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len); + return val; +} + +BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val) +{ + DWORD type, len = sizeof (DWORD); + LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len); + return r == ERROR_SUCCESS && type == REG_DWORD; +} + +static WCHAR *get_user_sid(void) +{ + HANDLE token; + DWORD size = 256; + TOKEN_USER *user; + WCHAR *ret; + + if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token )) return NULL; + if (!(user = msi_alloc( size ))) + { + CloseHandle( token ); + return NULL; + } + if (!GetTokenInformation( token, TokenUser, user, size, &size )) + { + msi_free( user ); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(user = msi_alloc( size ))) + { + CloseHandle( token ); + return NULL; + } + GetTokenInformation( token, TokenUser, user, size, &size ); + } + CloseHandle( token ); + if (!ConvertSidToStringSidW( user->User.Sid, &ret )) + { + msi_free( user ); + return NULL; + } + msi_free( user ); + return ret; +} + +UINT MSIREG_OpenUninstallKey(const WCHAR *product, enum platform platform, HKEY *key, BOOL create) +{ + WCHAR keypath[0x200]; + + TRACE("%s\n", debugstr_w(product)); + + if (is_64bit && platform == PLATFORM_INTEL) + { + strcpyW(keypath, szUninstall_32node); + strcatW(keypath, product); + } + else + { + strcpyW(keypath, szUninstall); + strcatW(keypath, product); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, KEY_ALL_ACCESS, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, KEY_ALL_ACCESS, key); +} + +UINT MSIREG_DeleteUninstallKey(const WCHAR *product, enum platform platform) +{ + WCHAR keypath[0x200]; + + TRACE("%s\n", debugstr_w(product)); + + if (is_64bit && platform == PLATFORM_INTEL) + { + strcpyW(keypath, szUninstall_32node); + strcatW(keypath, product); + } + else + { + strcpyW(keypath, szUninstall); + strcatW(keypath, product); + } + return RegDeleteTreeW(HKEY_LOCAL_MACHINE, keypath); +} + +UINT MSIREG_OpenProductKey(LPCWSTR szProduct, LPCWSTR szUserSid, MSIINSTALLCONTEXT context, HKEY *key, BOOL create) +{ + LPWSTR usersid = NULL; + HKEY root = HKEY_LOCAL_MACHINE; + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[MAX_PATH]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + strcpyW(keypath, szInstaller_LocalClassesProd); + strcatW(keypath, squished_pc); + } + else if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + root = HKEY_CURRENT_USER; + strcpyW(keypath, szUserProducts); + strcatW(keypath, squished_pc); + } + else + { + if (!szUserSid) + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + szUserSid = usersid; + } + sprintfW(keypath, szInstaller_LocalManagedProd_fmt, szUserSid, squished_pc); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(root, keypath, 0, access, key); +} + +UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct) +{ + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + strcpyW(keypath, szUserProducts); + strcatW(keypath, squished_pc); + return RegDeleteTreeW(HKEY_CURRENT_USER, keypath); +} + +UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create) +{ + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szPatch, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szPatch), debugstr_w(squished_pc)); + + strcpyW(keypath, szUserPatches); + strcatW(keypath, squished_pc); + + if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key); + return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key); +} + +UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context, HKEY *key, BOOL create) +{ + LPWSTR usersid; + HKEY root = HKEY_LOCAL_MACHINE; + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[MAX_PATH]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + strcpyW(keypath, szInstaller_LocalClassesFeat); + strcatW(keypath, squished_pc); + } + else if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + root = HKEY_CURRENT_USER; + strcpyW(keypath, szUserFeatures); + strcatW(keypath, squished_pc); + } + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szInstaller_LocalManagedFeat_fmt, usersid, squished_pc); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(root, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(root, keypath, 0, access, key); +} + +UINT MSIREG_DeleteUserFeaturesKey(LPCWSTR szProduct) +{ + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + strcpyW(keypath, szUserFeatures); + strcatW(keypath, squished_pc); + return RegDeleteTreeW(HKEY_CURRENT_USER, keypath); +} + +static UINT MSIREG_OpenInstallerFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + strcpyW(keypath, szInstaller_Features); + strcatW(keypath, squished_pc); + + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, MSIINSTALLCONTEXT context, HKEY *key, BOOL create) +{ + LPWSTR usersid; + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + sprintfW(keypath, szUserDataFeatures_fmt, szLocalSid, squished_pc); + } + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataFeatures_fmt, usersid, squished_pc); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY *key, BOOL create) +{ + WCHAR squished_cc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szComponent, squished_cc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szComponent), debugstr_w(squished_cc)); + + strcpyW(keypath, szUserComponents); + strcatW(keypath, squished_cc); + + if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key); + return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key); +} + +UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR comp[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + + if (!squash_guid(szComponent, comp)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szComponent), debugstr_w(comp)); + + if (!szUserSid) + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataComp_fmt, usersid, comp); + LocalFree(usersid); + } + else + sprintfW(keypath, szUserDataComp_fmt, szUserSid, comp); + + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_DeleteUserDataComponentKey(LPCWSTR szComponent, LPCWSTR szUserSid) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR comp[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + HKEY hkey; + LONG r; + + if (!squash_guid(szComponent, comp)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szComponent), debugstr_w(comp)); + + if (!szUserSid) + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataComponents_fmt, usersid); + LocalFree(usersid); + } + else + sprintfW(keypath, szUserDataComponents_fmt, szUserSid); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, comp); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + sprintfW(keypath, szUserDataProd_fmt, szLocalSid, squished_pc); + else if (szUserSid) + sprintfW(keypath, szUserDataProd_fmt, szUserSid, squished_pc); + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataProd_fmt, usersid, squished_pc); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenUserDataPatchKey(LPCWSTR szPatch, MSIINSTALLCONTEXT dwContext, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_patch[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + + if (!squash_guid(szPatch, squished_patch)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szPatch), debugstr_w(squished_patch)); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + sprintfW(keypath, szUserDataPatch_fmt, szLocalSid, squished_patch); + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataPatch_fmt, usersid, squished_patch); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_DeleteUserDataPatchKey(LPCWSTR patch, MSIINSTALLCONTEXT context) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_patch[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + HKEY hkey; + LONG r; + + if (!squash_guid(patch, squished_patch)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(patch), debugstr_w(squished_patch)); + + if (context == MSIINSTALLCONTEXT_MACHINE) + sprintfW(keypath, szUserDataPatches_fmt, szLocalSid); + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataPatches_fmt, usersid); + LocalFree(usersid); + } + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_patch); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_OpenUserDataProductPatchesKey(LPCWSTR product, MSIINSTALLCONTEXT context, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_product[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + + if (!squash_guid(product, squished_product)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(product), debugstr_w(squished_product)); + + if (context == MSIINSTALLCONTEXT_MACHINE) + sprintfW(keypath, szUserDataProductPatches_fmt, szLocalSid, squished_product); + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataProductPatches_fmt, usersid, squished_product); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenInstallProps(LPCWSTR szProduct, MSIINSTALLCONTEXT dwContext, LPCWSTR szUserSid, HKEY *key, BOOL create) +{ + LPWSTR usersid; + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + sprintfW(keypath, szInstallProperties_fmt, szLocalSid, squished_pc); + else if (szUserSid) + sprintfW(keypath, szInstallProperties_fmt, szUserSid, squished_pc); + else + { + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szInstallProperties_fmt, usersid, squished_pc); + LocalFree(usersid); + } + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + LPWSTR usersid; + HKEY hkey; + LONG r; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (!(usersid = get_user_sid())) + { + ERR("Failed to retrieve user SID\n"); + return ERROR_FUNCTION_FAILED; + } + sprintfW(keypath, szUserDataProducts_fmt, usersid); + LocalFree(usersid); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_pc); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_DeleteProductKey(LPCWSTR szProduct) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE]; + HKEY hkey; + LONG r; + + if (!squash_guid(szProduct, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProduct), debugstr_w(squished_pc)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_Products, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_pc); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szPatch, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szPatch), debugstr_w(squished_pc)); + + sprintfW(keypath, szInstaller_Patches, squished_pc); + + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szUpgradeCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szUpgradeCode), debugstr_w(squished_pc)); + + strcpyW(keypath, szInstaller_UpgradeCodes); + strcatW(keypath, squished_pc); + + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create) +{ + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szUpgradeCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szUpgradeCode), debugstr_w(squished_pc)); + + strcpyW(keypath, szInstaller_UserUpgradeCodes); + strcatW(keypath, squished_pc); + + if (create) return RegCreateKeyW(HKEY_CURRENT_USER, keypath, key); + return RegOpenKeyW(HKEY_CURRENT_USER, keypath, key); +} + +UINT MSIREG_DeleteUserUpgradeCodesKey(LPCWSTR szUpgradeCode) +{ + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szUpgradeCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szUpgradeCode), debugstr_w(squished_pc)); + + strcpyW(keypath, szInstaller_UserUpgradeCodes); + strcatW(keypath, squished_pc); + return RegDeleteTreeW(HKEY_CURRENT_USER, keypath); +} + +UINT MSIREG_DeleteLocalClassesProductKey(LPCWSTR szProductCode) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE]; + HKEY hkey; + LONG r; + + if (!squash_guid(szProductCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProductCode), debugstr_w(squished_pc)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_LocalClassesProducts, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_pc); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_DeleteLocalClassesFeaturesKey(LPCWSTR szProductCode) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE]; + HKEY hkey; + LONG r; + + if (!squash_guid(szProductCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szProductCode), debugstr_w(squished_pc)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_LocalClassesFeatures, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_pc); + RegCloseKey(hkey); + return r; +} + +UINT MSIREG_OpenClassesUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY *key, BOOL create) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE], keypath[0x200]; + + if (!squash_guid(szUpgradeCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szUpgradeCode), debugstr_w(squished_pc)); + + strcpyW(keypath, szInstaller_ClassesUpgradeCode); + strcatW(keypath, squished_pc); + + if (create) return RegCreateKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, key, NULL); + return RegOpenKeyExW(HKEY_LOCAL_MACHINE, keypath, 0, access, key); +} + +UINT MSIREG_DeleteClassesUpgradeCodesKey(LPCWSTR szUpgradeCode) +{ + REGSAM access = KEY_WOW64_64KEY | KEY_ALL_ACCESS; + WCHAR squished_pc[GUID_SIZE]; + HKEY hkey; + LONG r; + + if (!squash_guid(szUpgradeCode, squished_pc)) return ERROR_FUNCTION_FAILED; + TRACE("%s squished %s\n", debugstr_w(szUpgradeCode), debugstr_w(squished_pc)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szInstaller_ClassesUpgradeCodes, 0, access, &hkey)) return ERROR_SUCCESS; + r = RegDeleteTreeW(hkey, squished_pc); + RegCloseKey(hkey); + return r; +} + +/************************************************************************* + * MsiDecomposeDescriptorW [MSI.@] + * + * Decomposes an MSI descriptor into product, feature and component parts. + * An MSI descriptor is a string of the form: + * [base 85 guid] [feature code] '>' [base 85 guid] + * + * PARAMS + * szDescriptor [I] the descriptor to decompose + * szProduct [O] buffer of MAX_FEATURE_CHARS+1 for the product guid + * szFeature [O] buffer of MAX_FEATURE_CHARS+1 for the feature code + * szComponent [O] buffer of MAX_FEATURE_CHARS+1 for the component guid + * pUsed [O] the length of the descriptor + * + * RETURNS + * ERROR_SUCCESS if everything worked correctly + * ERROR_INVALID_PARAMETER if the descriptor was invalid + * + */ +UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct, + LPWSTR szFeature, LPWSTR szComponent, LPDWORD pUsed ) +{ + UINT r, len; + LPWSTR p; + GUID product, component; + + TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct, + szFeature, szComponent, pUsed); + + r = decode_base85_guid( szDescriptor, &product ); + if( !r ) + return ERROR_INVALID_PARAMETER; + + TRACE("product %s\n", debugstr_guid( &product )); + + p = strchrW(&szDescriptor[20],'>'); + if( !p ) + return ERROR_INVALID_PARAMETER; + + len = (p - &szDescriptor[20]); + if( len > MAX_FEATURE_CHARS ) + return ERROR_INVALID_PARAMETER; + + TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len )); + + r = decode_base85_guid( p+1, &component ); + if( !r ) + return ERROR_INVALID_PARAMETER; + + TRACE("component %s\n", debugstr_guid( &component )); + + if (szProduct) + StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 ); + if (szComponent) + StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 ); + if (szFeature) + { + memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) ); + szFeature[len] = 0; + } + len = ( &p[21] - szDescriptor ); + + TRACE("length = %d\n", len); + if (pUsed) *pUsed = len; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct, + LPSTR szFeature, LPSTR szComponent, LPDWORD pUsed ) +{ + WCHAR product[MAX_FEATURE_CHARS+1]; + WCHAR feature[MAX_FEATURE_CHARS+1]; + WCHAR component[MAX_FEATURE_CHARS+1]; + LPWSTR str = NULL, p = NULL, f = NULL, c = NULL; + UINT r; + + TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct, + szFeature, szComponent, pUsed); + + str = strdupAtoW( szDescriptor ); + if( szDescriptor && !str ) + return ERROR_OUTOFMEMORY; + + if (szProduct) + p = product; + if (szFeature) + f = feature; + if (szComponent) + c = component; + + r = MsiDecomposeDescriptorW( str, p, f, c, pUsed ); + + if (r == ERROR_SUCCESS) + { + WideCharToMultiByte( CP_ACP, 0, p, -1, + szProduct, MAX_FEATURE_CHARS+1, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, f, -1, + szFeature, MAX_FEATURE_CHARS+1, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, c, -1, + szComponent, MAX_FEATURE_CHARS+1, NULL, NULL ); + } + + msi_free( str ); + + return r; +} + +UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%d %p\n", index, lpguid); + + if (NULL == lpguid) + return ERROR_INVALID_PARAMETER; + r = MsiEnumProductsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid) +{ + TRACE("%d %p\n", index, lpguid); + + if (NULL == lpguid) + return ERROR_INVALID_PARAMETER; + + return MsiEnumProductsExW( NULL, szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid, + NULL, NULL, NULL ); +} + +UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, + LPSTR szFeature, LPSTR szParent) +{ + DWORD r; + WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE]; + LPWSTR szwProduct = NULL; + + TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent); + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwFeature, -1, + szFeature, GUID_SIZE, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, szwParent, -1, + szParent, GUID_SIZE, NULL, NULL); + } + + msi_free( szwProduct); + + return r; +} + +UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, + LPWSTR szFeature, LPWSTR szParent) +{ + HKEY hkeyProduct = 0; + DWORD r, sz; + + TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent); + + if( !szProduct ) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenInstallerFeaturesKey(szProduct,&hkeyProduct,FALSE); + if( r != ERROR_SUCCESS ) + return ERROR_NO_MORE_ITEMS; + + sz = GUID_SIZE; + r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL); + RegCloseKey(hkeyProduct); + + return r; +} + +UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%u, %p\n", index, lpguid); + + if (!lpguid) return ERROR_INVALID_PARAMETER; + + r = MsiEnumComponentsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid) +{ + TRACE("%u, %p\n", index, lpguid); + + if (!lpguid) return ERROR_INVALID_PARAMETER; + + return MsiEnumComponentsExW( szAllSid, MSIINSTALLCONTEXT_ALL, index, lpguid, NULL, NULL, NULL ); +} + +UINT WINAPI MsiEnumComponentsExA( LPCSTR user_sid, DWORD ctx, DWORD index, CHAR guid[39], + MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len ) +{ + UINT r; + WCHAR *user_sidW = NULL, *sidW = NULL, guidW[GUID_SIZE]; + + TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_a(user_sid), ctx, index, guid, installed_ctx, + sid, sid_len); + + if (sid && !sid_len) return ERROR_INVALID_PARAMETER; + if (user_sid && !(user_sidW = strdupAtoW( user_sid ))) return ERROR_OUTOFMEMORY; + if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) ))) + { + msi_free( user_sidW ); + return ERROR_OUTOFMEMORY; + } + r = MsiEnumComponentsExW( user_sidW, ctx, index, guidW, installed_ctx, sidW, sid_len ); + if (r == ERROR_SUCCESS) + { + if (guid) WideCharToMultiByte( CP_ACP, 0, guidW, GUID_SIZE, guid, GUID_SIZE, NULL, NULL ); + if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL ); + } + msi_free( user_sidW ); + msi_free( sidW ); + return r; +} + +static UINT fetch_machine_component( DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39], + MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len ) +{ + static const WCHAR componentsW[] = + {'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','\\','U','s','e','r','D','a','t','a','\\', + 'S','-','1','-','5','-','1','8','\\','C','o','m','p','o','n','e','n','t','s',0}; + UINT r = ERROR_SUCCESS; + WCHAR component[GUID_SIZE]; + DWORD i = 0, len_component; + REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY; + HKEY key_components; + + if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, componentsW, 0, access, &key_components )) + return ERROR_NO_MORE_ITEMS; + + len_component = sizeof(component)/sizeof(component[0]); + while (!RegEnumKeyExW( key_components, i, component, &len_component, NULL, NULL, NULL, NULL )) + { + if (*idx == index) goto found; + (*idx)++; + len_component = sizeof(component)/sizeof(component[0]); + i++; + } + RegCloseKey( key_components ); + return ERROR_NO_MORE_ITEMS; + +found: + if (sid_len) + { + if (*sid_len < 1) + { + *sid_len = 1; + r = ERROR_MORE_DATA; + } + else if (sid) + { + *sid_len = 0; + sid[0] = 0; + } + } + if (guid) unsquash_guid( component, guid ); + if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE; + RegCloseKey( key_components ); + return r; +} + +static UINT fetch_user_component( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx, + WCHAR guid[39], MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, + LPDWORD sid_len ) +{ + static const WCHAR userdataW[] = + {'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','\\','U','s','e','r','D','a','t','a',0}; + static const WCHAR componentsW[] = {'\\','C','o','m','p','o','n','e','n','t','s',0}; + UINT r = ERROR_SUCCESS; + WCHAR path[MAX_PATH], component[GUID_SIZE], user[128]; + DWORD i = 0, j = 0, len_component, len_user; + REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY; + HKEY key_users, key_components; + + if (ctx == MSIINSTALLCONTEXT_USERMANAGED) /* FIXME: were to find these? */ + return ERROR_NO_MORE_ITEMS; + + if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, userdataW, 0, access, &key_users )) + return ERROR_NO_MORE_ITEMS; + + len_user = sizeof(user)/sizeof(user[0]); + while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL )) + { + if ((strcmpW( usersid, szAllSid ) && strcmpW( usersid, user )) || + !strcmpW( szLocalSid, user )) + { + i++; + len_user = sizeof(user)/sizeof(user[0]); + continue; + } + strcpyW( path, user ); + strcatW( path, componentsW ); + if (RegOpenKeyExW( key_users, path, 0, access, &key_components )) + { + i++; + len_user = sizeof(user)/sizeof(user[0]); + continue; + } + len_component = sizeof(component)/sizeof(component[0]); + while (!RegEnumKeyExW( key_components, j, component, &len_component, NULL, NULL, NULL, NULL )) + { + if (*idx == index) goto found; + (*idx)++; + len_component = sizeof(component)/sizeof(component[0]); + j++; + } + RegCloseKey( key_components ); + len_user = sizeof(user)/sizeof(user[0]); + i++; + } + RegCloseKey( key_users ); + return ERROR_NO_MORE_ITEMS; + +found: + if (sid_len) + { + if (*sid_len < len_user + 1) + { + *sid_len = len_user + 1; + r = ERROR_MORE_DATA; + } + else if (sid) + { + *sid_len = len_user; + strcpyW( sid, user ); + } + } + if (guid) unsquash_guid( component, guid ); + if (installed_ctx) *installed_ctx = ctx; + RegCloseKey( key_components ); + RegCloseKey( key_users ); + return r; +} + +static UINT enum_components( const WCHAR *usersid, DWORD ctx, DWORD index, DWORD *idx, WCHAR guid[39], + MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len ) +{ + UINT r = ERROR_NO_MORE_ITEMS; + WCHAR *user = NULL; + + if (!usersid) + { + usersid = user = get_user_sid(); + if (!user) return ERROR_FUNCTION_FAILED; + } + if (ctx & MSIINSTALLCONTEXT_USERMANAGED) + { + r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERMANAGED, index, idx, guid, + installed_ctx, sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED) + { + r = fetch_user_component( usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index, idx, guid, + installed_ctx, sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + if (ctx & MSIINSTALLCONTEXT_MACHINE) + { + r = fetch_machine_component( MSIINSTALLCONTEXT_MACHINE, index, idx, guid, installed_ctx, + sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + +done: + LocalFree( user ); + return r; +} + +UINT WINAPI MsiEnumComponentsExW( LPCWSTR user_sid, DWORD ctx, DWORD index, WCHAR guid[39], + MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len ) +{ + UINT r; + DWORD idx = 0; + static DWORD last_index; + + TRACE("%s, %u, %u, %p, %p, %p, %p\n", debugstr_w(user_sid), ctx, index, guid, installed_ctx, + sid, sid_len); + + if ((sid && !sid_len) || !ctx || (user_sid && ctx == MSIINSTALLCONTEXT_MACHINE)) + return ERROR_INVALID_PARAMETER; + + if (index && index - last_index != 1) + return ERROR_INVALID_PARAMETER; + + if (!index) last_index = 0; + + r = enum_components( user_sid, ctx, index, &idx, guid, installed_ctx, sid, sid_len ); + if (r == ERROR_SUCCESS) + last_index = index; + else + last_index = 0; + + return r; +} + +UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct) +{ + DWORD r; + WCHAR szwProduct[GUID_SIZE]; + LPWSTR szwComponent = NULL; + + TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct); + + if ( !szProduct ) + return ERROR_INVALID_PARAMETER; + + if( szComponent ) + { + szwComponent = strdupAtoW( szComponent ); + if( !szwComponent ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwProduct, -1, + szProduct, GUID_SIZE, NULL, NULL); + } + + msi_free( szwComponent); + + return r; +} + +UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct) +{ + HKEY hkeyComp = 0; + DWORD r, sz; + WCHAR szValName[SQUISH_GUID_SIZE]; + + TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct); + + if (!szComponent || !*szComponent || !szProduct) + return ERROR_INVALID_PARAMETER; + + if (MSIREG_OpenUserDataComponentKey(szComponent, NULL, &hkeyComp, FALSE) != ERROR_SUCCESS && + MSIREG_OpenUserDataComponentKey(szComponent, szLocalSid, &hkeyComp, FALSE) != ERROR_SUCCESS) + return ERROR_UNKNOWN_COMPONENT; + + /* see if there are any products at all */ + sz = SQUISH_GUID_SIZE; + r = RegEnumValueW(hkeyComp, 0, szValName, &sz, NULL, NULL, NULL, NULL); + if (r != ERROR_SUCCESS) + { + RegCloseKey(hkeyComp); + + if (index != 0) + return ERROR_INVALID_PARAMETER; + + return ERROR_UNKNOWN_COMPONENT; + } + + sz = SQUISH_GUID_SIZE; + r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL); + if( r == ERROR_SUCCESS ) + { + unsquash_guid(szValName, szProduct); + TRACE("-> %s\n", debugstr_w(szProduct)); + } + RegCloseKey(hkeyComp); + return r; +} + +static UINT MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex, + awstring *lpQualBuf, LPDWORD pcchQual, + awstring *lpAppBuf, LPDWORD pcchAppBuf ) +{ + DWORD name_sz, val_sz, name_max, val_max, type, ofs; + LPWSTR name = NULL, val = NULL; + UINT r, r2; + HKEY key; + + TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex, + lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf); + + if (!szComponent) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE ); + if (r != ERROR_SUCCESS) + return ERROR_UNKNOWN_COMPONENT; + + /* figure out how big the name is we want to return */ + name_max = 0x10; + r = ERROR_OUTOFMEMORY; + name = msi_alloc( name_max * sizeof(WCHAR) ); + if (!name) + goto end; + + val_max = 0x10; + r = ERROR_OUTOFMEMORY; + val = msi_alloc( val_max ); + if (!val) + goto end; + + /* loop until we allocate enough memory */ + while (1) + { + name_sz = name_max; + val_sz = val_max; + r = RegEnumValueW( key, iIndex, name, &name_sz, + NULL, &type, (LPBYTE)val, &val_sz ); + if (r == ERROR_SUCCESS) + break; + if (r != ERROR_MORE_DATA) + goto end; + + if (type != REG_MULTI_SZ) + { + ERR("component data has wrong type (%d)\n", type); + goto end; + } + + r = ERROR_OUTOFMEMORY; + if ((name_sz+1) >= name_max) + { + name_max *= 2; + msi_free( name ); + name = msi_alloc( name_max * sizeof (WCHAR) ); + if (!name) + goto end; + continue; + } + if (val_sz > val_max) + { + val_max = val_sz + sizeof (WCHAR); + msi_free( val ); + val = msi_alloc( val_max * sizeof (WCHAR) ); + if (!val) + goto end; + continue; + } + ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz ); + goto end; + } + + ofs = 0; + r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs ); + if (r != ERROR_SUCCESS) + goto end; + + TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs)); + + r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual ); + r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf ); + + if (r2 != ERROR_SUCCESS) + r = r2; + +end: + msi_free(val); + msi_free(name); + RegCloseKey(key); + + return r; +} + +/************************************************************************* + * MsiEnumComponentQualifiersA [MSI.@] + */ +UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex, + LPSTR lpQualifierBuf, LPDWORD pcchQualifierBuf, + LPSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf ) +{ + awstring qual, appdata; + LPWSTR comp; + UINT r; + + TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex, + lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, + pcchApplicationDataBuf); + + comp = strdupAtoW( szComponent ); + if (szComponent && !comp) + return ERROR_OUTOFMEMORY; + + qual.unicode = FALSE; + qual.str.a = lpQualifierBuf; + + appdata.unicode = FALSE; + appdata.str.a = lpApplicationDataBuf; + + r = MSI_EnumComponentQualifiers( comp, iIndex, + &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf ); + msi_free( comp ); + return r; +} + +/************************************************************************* + * MsiEnumComponentQualifiersW [MSI.@] + */ +UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex, + LPWSTR lpQualifierBuf, LPDWORD pcchQualifierBuf, + LPWSTR lpApplicationDataBuf, LPDWORD pcchApplicationDataBuf ) +{ + awstring qual, appdata; + + TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex, + lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, + pcchApplicationDataBuf); + + qual.unicode = TRUE; + qual.str.w = lpQualifierBuf; + + appdata.unicode = TRUE; + appdata.str.w = lpApplicationDataBuf; + + return MSI_EnumComponentQualifiers( szComponent, iIndex, + &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf ); +} + +/************************************************************************* + * MsiEnumRelatedProductsW [MSI.@] + * + */ +UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved, + DWORD iProductIndex, LPWSTR lpProductBuf) +{ + UINT r; + HKEY hkey; + DWORD dwSize = SQUISH_GUID_SIZE; + WCHAR szKeyName[SQUISH_GUID_SIZE]; + + TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved, + iProductIndex, lpProductBuf); + + if (NULL == szUpgradeCode) + return ERROR_INVALID_PARAMETER; + if (NULL == lpProductBuf) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_NO_MORE_ITEMS; + + r = RegEnumValueW(hkey, iProductIndex, szKeyName, &dwSize, NULL, NULL, NULL, NULL); + if( r == ERROR_SUCCESS ) + unsquash_guid(szKeyName, lpProductBuf); + RegCloseKey(hkey); + + return r; +} + +/************************************************************************* + * MsiEnumRelatedProductsA [MSI.@] + * + */ +UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved, + DWORD iProductIndex, LPSTR lpProductBuf) +{ + LPWSTR szwUpgradeCode = NULL; + WCHAR productW[GUID_SIZE]; + UINT r; + + TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved, + iProductIndex, lpProductBuf); + + if (szUpgradeCode) + { + szwUpgradeCode = strdupAtoW( szUpgradeCode ); + if( !szwUpgradeCode ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved, + iProductIndex, productW ); + if (r == ERROR_SUCCESS) + { + WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE, + lpProductBuf, GUID_SIZE, NULL, NULL ); + } + msi_free( szwUpgradeCode); + return r; +} + +/*********************************************************************** + * MsiEnumPatchesExA [MSI.@] + */ +UINT WINAPI MsiEnumPatchesExA(LPCSTR szProductCode, LPCSTR szUserSid, + DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPSTR szPatchCode, + LPSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext, + LPSTR szTargetUserSid, LPDWORD pcchTargetUserSid) +{ + LPWSTR prodcode = NULL; + LPWSTR usersid = NULL; + LPWSTR targsid = NULL; + WCHAR patch[GUID_SIZE]; + WCHAR targprod[GUID_SIZE]; + DWORD len; + UINT r; + + TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", + debugstr_a(szProductCode), debugstr_a(szUserSid), dwContext, dwFilter, + dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext, + szTargetUserSid, pcchTargetUserSid); + + if (szTargetUserSid && !pcchTargetUserSid) + return ERROR_INVALID_PARAMETER; + + if (szProductCode) prodcode = strdupAtoW(szProductCode); + if (szUserSid) usersid = strdupAtoW(szUserSid); + + r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex, + patch, targprod, pdwTargetProductContext, + NULL, &len); + if (r != ERROR_SUCCESS) + goto done; + + WideCharToMultiByte(CP_ACP, 0, patch, -1, szPatchCode, + GUID_SIZE, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, targprod, -1, szTargetProductCode, + GUID_SIZE, NULL, NULL); + + if (!szTargetUserSid) + { + if (pcchTargetUserSid) + *pcchTargetUserSid = len; + + goto done; + } + + targsid = msi_alloc(++len * sizeof(WCHAR)); + if (!targsid) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiEnumPatchesExW(prodcode, usersid, dwContext, dwFilter, dwIndex, + patch, targprod, pdwTargetProductContext, + targsid, &len); + if (r != ERROR_SUCCESS || !szTargetUserSid) + goto done; + + WideCharToMultiByte(CP_ACP, 0, targsid, -1, szTargetUserSid, + *pcchTargetUserSid, NULL, NULL); + + len = lstrlenW(targsid); + if (*pcchTargetUserSid < len + 1) + { + r = ERROR_MORE_DATA; + *pcchTargetUserSid = len * sizeof(WCHAR); + } + else + *pcchTargetUserSid = len; + +done: + msi_free(prodcode); + msi_free(usersid); + msi_free(targsid); + + return r; +} + +static UINT msi_get_patch_state(LPCWSTR prodcode, LPCWSTR usersid, + MSIINSTALLCONTEXT context, + LPWSTR patch, MSIPATCHSTATE *state) +{ + DWORD type, val, size; + HKEY prod, hkey = 0; + HKEY udpatch = 0; + LONG res; + UINT r = ERROR_NO_MORE_ITEMS; + + *state = MSIPATCHSTATE_INVALID; + + r = MSIREG_OpenUserDataProductKey(prodcode, context, + usersid, &prod, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_NO_MORE_ITEMS; + + res = RegOpenKeyExW(prod, szPatches, 0, KEY_READ, &hkey); + if (res != ERROR_SUCCESS) + goto done; + + res = RegOpenKeyExW(hkey, patch, 0, KEY_READ, &udpatch); + if (res != ERROR_SUCCESS) + goto done; + + size = sizeof(DWORD); + res = RegGetValueW(udpatch, NULL, szState, RRF_RT_DWORD, &type, &val, &size); + if (res != ERROR_SUCCESS || + val < MSIPATCHSTATE_APPLIED || val > MSIPATCHSTATE_REGISTERED) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + *state = val; + r = ERROR_SUCCESS; + +done: + RegCloseKey(udpatch); + RegCloseKey(hkey); + RegCloseKey(prod); + + return r; +} + +static UINT msi_check_product_patches(LPCWSTR prodcode, LPCWSTR usersid, + MSIINSTALLCONTEXT context, DWORD filter, DWORD index, DWORD *idx, + LPWSTR patch, LPWSTR targetprod, MSIINSTALLCONTEXT *targetctx, + LPWSTR targetsid, DWORD *sidsize, LPWSTR *transforms) +{ + MSIPATCHSTATE state = MSIPATCHSTATE_INVALID; + LPWSTR ptr, patches = NULL; + HKEY prod, patchkey = 0; + HKEY localprod = 0, localpatch = 0; + DWORD type, size; + LONG res; + UINT temp, r = ERROR_NO_MORE_ITEMS; + + if (MSIREG_OpenProductKey(prodcode, usersid, context, + &prod, FALSE) != ERROR_SUCCESS) + return ERROR_NO_MORE_ITEMS; + + size = 0; + res = RegGetValueW(prod, szPatches, szPatches, RRF_RT_ANY, &type, NULL, + &size); + if (res != ERROR_SUCCESS) + goto done; + + if (type != REG_MULTI_SZ) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + patches = msi_alloc(size); + if (!patches) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + res = RegGetValueW(prod, szPatches, szPatches, RRF_RT_ANY, &type, + patches, &size); + if (res != ERROR_SUCCESS) + goto done; + + for (ptr = patches; *ptr && r == ERROR_NO_MORE_ITEMS; ptr += lstrlenW(ptr) + 1) + { + if (!unsquash_guid(ptr, patch)) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + size = 0; + res = RegGetValueW(prod, szPatches, ptr, RRF_RT_REG_SZ, + &type, NULL, &size); + if (res != ERROR_SUCCESS) + continue; + + if (transforms) + { + *transforms = msi_alloc(size); + if (!*transforms) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + res = RegGetValueW(prod, szPatches, ptr, RRF_RT_REG_SZ, + &type, *transforms, &size); + if (res != ERROR_SUCCESS) + continue; + } + + if (context == MSIINSTALLCONTEXT_USERMANAGED) + { + if (!(filter & MSIPATCHSTATE_APPLIED)) + { + temp = msi_get_patch_state(prodcode, usersid, context, + ptr, &state); + if (temp == ERROR_BAD_CONFIGURATION) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + if (temp != ERROR_SUCCESS || !(filter & state)) + continue; + } + } + else if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + if (!(filter & MSIPATCHSTATE_APPLIED)) + { + temp = msi_get_patch_state(prodcode, usersid, context, + ptr, &state); + if (temp == ERROR_BAD_CONFIGURATION) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + if (temp != ERROR_SUCCESS || !(filter & state)) + continue; + } + else + { + temp = MSIREG_OpenUserDataPatchKey(patch, context, + &patchkey, FALSE); + RegCloseKey(patchkey); + if (temp != ERROR_SUCCESS) + continue; + } + } + else if (context == MSIINSTALLCONTEXT_MACHINE) + { + usersid = szEmpty; + + if (MSIREG_OpenUserDataProductKey(prodcode, context, NULL, &localprod, FALSE) == ERROR_SUCCESS && + RegOpenKeyExW(localprod, szPatches, 0, KEY_READ, &localpatch) == ERROR_SUCCESS && + RegOpenKeyExW(localpatch, ptr, 0, KEY_READ, &patchkey) == ERROR_SUCCESS) + { + res = RegGetValueW(patchkey, NULL, szState, RRF_RT_REG_DWORD, + &type, &state, &size); + + if (!(filter & state)) + res = ERROR_NO_MORE_ITEMS; + + RegCloseKey(patchkey); + } + + RegCloseKey(localpatch); + RegCloseKey(localprod); + + if (res != ERROR_SUCCESS) + continue; + } + + if (*idx < index) + { + (*idx)++; + continue; + } + + r = ERROR_SUCCESS; + if (targetprod) + lstrcpyW(targetprod, prodcode); + + if (targetctx) + *targetctx = context; + + if (targetsid) + { + lstrcpynW(targetsid, usersid, *sidsize); + if (lstrlenW(usersid) >= *sidsize) + r = ERROR_MORE_DATA; + } + + if (sidsize) + { + *sidsize = lstrlenW(usersid); + if (!targetsid) + *sidsize *= sizeof(WCHAR); + } + } + +done: + RegCloseKey(prod); + msi_free(patches); + + return r; +} + +static UINT msi_enum_patches(LPCWSTR szProductCode, LPCWSTR szUserSid, + DWORD dwContext, DWORD dwFilter, DWORD dwIndex, DWORD *idx, + LPWSTR szPatchCode, LPWSTR szTargetProductCode, + MSIINSTALLCONTEXT *pdwTargetProductContext, LPWSTR szTargetUserSid, + LPDWORD pcchTargetUserSid, LPWSTR *szTransforms) +{ + LPWSTR usersid = NULL; + UINT r = ERROR_INVALID_PARAMETER; + + if (!szUserSid) + { + szUserSid = usersid = get_user_sid(); + if (!usersid) return ERROR_FUNCTION_FAILED; + } + + if (dwContext & MSIINSTALLCONTEXT_USERMANAGED) + { + r = msi_check_product_patches(szProductCode, szUserSid, + MSIINSTALLCONTEXT_USERMANAGED, dwFilter, + dwIndex, idx, szPatchCode, + szTargetProductCode, + pdwTargetProductContext, szTargetUserSid, + pcchTargetUserSid, szTransforms); + if (r != ERROR_NO_MORE_ITEMS) + goto done; + } + + if (dwContext & MSIINSTALLCONTEXT_USERUNMANAGED) + { + r = msi_check_product_patches(szProductCode, szUserSid, + MSIINSTALLCONTEXT_USERUNMANAGED, dwFilter, + dwIndex, idx, szPatchCode, + szTargetProductCode, + pdwTargetProductContext, szTargetUserSid, + pcchTargetUserSid, szTransforms); + if (r != ERROR_NO_MORE_ITEMS) + goto done; + } + + if (dwContext & MSIINSTALLCONTEXT_MACHINE) + { + r = msi_check_product_patches(szProductCode, szUserSid, + MSIINSTALLCONTEXT_MACHINE, dwFilter, + dwIndex, idx, szPatchCode, + szTargetProductCode, + pdwTargetProductContext, szTargetUserSid, + pcchTargetUserSid, szTransforms); + if (r != ERROR_NO_MORE_ITEMS) + goto done; + } + +done: + LocalFree(usersid); + return r; +} + +/*********************************************************************** + * MsiEnumPatchesExW [MSI.@] + */ +UINT WINAPI MsiEnumPatchesExW(LPCWSTR szProductCode, LPCWSTR szUserSid, + DWORD dwContext, DWORD dwFilter, DWORD dwIndex, LPWSTR szPatchCode, + LPWSTR szTargetProductCode, MSIINSTALLCONTEXT *pdwTargetProductContext, + LPWSTR szTargetUserSid, LPDWORD pcchTargetUserSid) +{ + WCHAR squished_pc[GUID_SIZE]; + DWORD idx = 0; + UINT r; + + static DWORD last_index; + + TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", + debugstr_w(szProductCode), debugstr_w(szUserSid), dwContext, dwFilter, + dwIndex, szPatchCode, szTargetProductCode, pdwTargetProductContext, + szTargetUserSid, pcchTargetUserSid); + + if (!szProductCode || !squash_guid(szProductCode, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (szUserSid && !strcmpW( szUserSid, szLocalSid )) + return ERROR_INVALID_PARAMETER; + + if (dwContext & MSIINSTALLCONTEXT_MACHINE && szUserSid) + return ERROR_INVALID_PARAMETER; + + if (dwContext <= MSIINSTALLCONTEXT_NONE || + dwContext > MSIINSTALLCONTEXT_ALL) + return ERROR_INVALID_PARAMETER; + + if (dwFilter <= MSIPATCHSTATE_INVALID || dwFilter > MSIPATCHSTATE_ALL) + return ERROR_INVALID_PARAMETER; + + if (dwIndex && dwIndex - last_index != 1) + return ERROR_INVALID_PARAMETER; + + if (dwIndex == 0) + last_index = 0; + + r = msi_enum_patches(szProductCode, szUserSid, dwContext, dwFilter, + dwIndex, &idx, szPatchCode, szTargetProductCode, + pdwTargetProductContext, szTargetUserSid, + pcchTargetUserSid, NULL); + + if (r == ERROR_SUCCESS) + last_index = dwIndex; + else + last_index = 0; + + return r; +} + +/*********************************************************************** + * MsiEnumPatchesA [MSI.@] + */ +UINT WINAPI MsiEnumPatchesA(LPCSTR szProduct, DWORD iPatchIndex, + LPSTR lpPatchBuf, LPSTR lpTransformsBuf, LPDWORD pcchTransformsBuf) +{ + LPWSTR product, transforms; + WCHAR patch[GUID_SIZE]; + DWORD len; + UINT r; + + TRACE("(%s %d %p %p %p)\n", debugstr_a(szProduct), iPatchIndex, + lpPatchBuf, lpTransformsBuf, pcchTransformsBuf); + + if (!szProduct || !lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf) + return ERROR_INVALID_PARAMETER; + + product = strdupAtoW(szProduct); + if (!product) + return ERROR_OUTOFMEMORY; + + len = *pcchTransformsBuf; + transforms = msi_alloc( len * sizeof(WCHAR) ); + if (!transforms) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = MsiEnumPatchesW(product, iPatchIndex, patch, transforms, &len); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + goto done; + + WideCharToMultiByte(CP_ACP, 0, patch, -1, lpPatchBuf, + GUID_SIZE, NULL, NULL); + + if (!WideCharToMultiByte(CP_ACP, 0, transforms, -1, lpTransformsBuf, + *pcchTransformsBuf, NULL, NULL)) + r = ERROR_MORE_DATA; + + if (r == ERROR_MORE_DATA) + { + lpTransformsBuf[*pcchTransformsBuf - 1] = '\0'; + *pcchTransformsBuf = len * 2; + } + else + *pcchTransformsBuf = strlen( lpTransformsBuf ); + +done: + msi_free(transforms); + msi_free(product); + + return r; +} + +/*********************************************************************** + * MsiEnumPatchesW [MSI.@] + */ +UINT WINAPI MsiEnumPatchesW(LPCWSTR szProduct, DWORD iPatchIndex, + LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, LPDWORD pcchTransformsBuf) +{ + WCHAR squished_pc[GUID_SIZE]; + LPWSTR transforms = NULL; + HKEY prod; + DWORD idx = 0; + UINT r; + + TRACE("(%s %d %p %p %p)\n", debugstr_w(szProduct), iPatchIndex, + lpPatchBuf, lpTransformsBuf, pcchTransformsBuf); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!lpPatchBuf || !lpTransformsBuf || !pcchTransformsBuf) + return ERROR_INVALID_PARAMETER; + + if (MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + &prod, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + &prod, FALSE) != ERROR_SUCCESS && + MSIREG_OpenProductKey(szProduct, NULL, MSIINSTALLCONTEXT_MACHINE, + &prod, FALSE) != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + RegCloseKey(prod); + + r = msi_enum_patches(szProduct, NULL, MSIINSTALLCONTEXT_ALL, + MSIPATCHSTATE_ALL, iPatchIndex, &idx, lpPatchBuf, + NULL, NULL, NULL, NULL, &transforms); + if (r != ERROR_SUCCESS) + goto done; + + lstrcpynW(lpTransformsBuf, transforms, *pcchTransformsBuf); + if (*pcchTransformsBuf <= lstrlenW(transforms)) + { + r = ERROR_MORE_DATA; + *pcchTransformsBuf = lstrlenW(transforms); + } + else + *pcchTransformsBuf = lstrlenW(transforms); + +done: + msi_free(transforms); + return r; +} + +UINT WINAPI MsiEnumProductsExA( LPCSTR product, LPCSTR usersid, DWORD ctx, DWORD index, + CHAR installed_product[GUID_SIZE], + MSIINSTALLCONTEXT *installed_ctx, LPSTR sid, LPDWORD sid_len ) +{ + UINT r; + WCHAR installed_productW[GUID_SIZE], *productW = NULL, *usersidW = NULL, *sidW = NULL; + + TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_a(product), debugstr_a(usersid), + ctx, index, installed_product, installed_ctx, sid, sid_len); + + if (sid && !sid_len) return ERROR_INVALID_PARAMETER; + if (product && !(productW = strdupAtoW( product ))) return ERROR_OUTOFMEMORY; + if (usersid && !(usersidW = strdupAtoW( usersid ))) + { + msi_free( productW ); + return ERROR_OUTOFMEMORY; + } + if (sid && !(sidW = msi_alloc( *sid_len * sizeof(WCHAR) ))) + { + msi_free( usersidW ); + msi_free( productW ); + return ERROR_OUTOFMEMORY; + } + r = MsiEnumProductsExW( productW, usersidW, ctx, index, installed_productW, + installed_ctx, sidW, sid_len ); + if (r == ERROR_SUCCESS) + { + if (installed_product) WideCharToMultiByte( CP_ACP, 0, installed_productW, GUID_SIZE, + installed_product, GUID_SIZE, NULL, NULL ); + if (sid) WideCharToMultiByte( CP_ACP, 0, sidW, *sid_len + 1, sid, *sid_len + 1, NULL, NULL ); + } + msi_free( productW ); + msi_free( usersidW ); + msi_free( sidW ); + return r; +} + +static UINT fetch_machine_product( const WCHAR *match, DWORD index, DWORD *idx, + WCHAR installed_product[GUID_SIZE], + MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len ) +{ + static const WCHAR productsW[] = + {'S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s','\\', + 'I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0}; + UINT r; + WCHAR product[GUID_SIZE]; + DWORD i = 0, len; + REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY; + HKEY key; + + if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, productsW, 0, access, &key )) + return ERROR_NO_MORE_ITEMS; + + len = sizeof(product)/sizeof(product[0]); + while (!RegEnumKeyExW( key, i, product, &len, NULL, NULL, NULL, NULL )) + { + if (match && strcmpW( match, product )) + { + i++; + len = sizeof(product)/sizeof(product[0]); + continue; + } + if (*idx == index) goto found; + (*idx)++; + len = sizeof(product)/sizeof(product[0]); + i++; + } + RegCloseKey( key ); + return ERROR_NO_MORE_ITEMS; + +found: + if (sid_len && *sid_len < 1) + { + *sid_len = 1; + r = ERROR_MORE_DATA; + } + else + { + if (installed_product) unsquash_guid( product, installed_product ); + if (installed_ctx) *installed_ctx = MSIINSTALLCONTEXT_MACHINE; + if (sid) + { + sid[0] = 0; + *sid_len = 0; + } + r = ERROR_SUCCESS; + } + RegCloseKey( key ); + return r; +} + +static UINT fetch_user_product( const WCHAR *match, const WCHAR *usersid, DWORD ctx, DWORD index, + DWORD *idx, WCHAR installed_product[GUID_SIZE], + MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len ) +{ + static const WCHAR managedW[] = + {'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','\\','M','a','n','a','g','e','d',0}; + static const WCHAR managed_productsW[] = + {'\\','I','n','s','t','a','l','l','e','r','\\','P','r','o','d','u','c','t','s',0}; + static const WCHAR unmanaged_productsW[] = + {'\\','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','\\','P','r','o','d','u','c','t','s',0}; + UINT r; + const WCHAR *subkey; + WCHAR path[MAX_PATH], product[GUID_SIZE], user[128]; + DWORD i = 0, j = 0, len_product, len_user; + REGSAM access = KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY; + HKEY key_users, key_products; + + if (ctx == MSIINSTALLCONTEXT_USERMANAGED) + { + subkey = managed_productsW; + if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, managedW, 0, access, &key_users )) + return ERROR_NO_MORE_ITEMS; + } + else if (ctx == MSIINSTALLCONTEXT_USERUNMANAGED) + { + subkey = unmanaged_productsW; + if (RegOpenKeyExW( HKEY_USERS, NULL, 0, access, &key_users )) + return ERROR_NO_MORE_ITEMS; + } + else return ERROR_INVALID_PARAMETER; + + len_user = sizeof(user)/sizeof(user[0]); + while (!RegEnumKeyExW( key_users, i, user, &len_user, NULL, NULL, NULL, NULL )) + { + if (strcmpW( usersid, user ) && strcmpW( usersid, szAllSid )) + { + i++; + len_user = sizeof(user)/sizeof(user[0]); + continue; + } + strcpyW( path, user ); + strcatW( path, subkey ); + if (RegOpenKeyExW( key_users, path, 0, access, &key_products )) + { + i++; + len_user = sizeof(user)/sizeof(user[0]); + continue; + } + len_product = sizeof(product)/sizeof(product[0]); + while (!RegEnumKeyExW( key_products, j, product, &len_product, NULL, NULL, NULL, NULL )) + { + if (match && strcmpW( match, product )) + { + j++; + len_product = sizeof(product)/sizeof(product[0]); + continue; + } + if (*idx == index) goto found; + (*idx)++; + len_product = sizeof(product)/sizeof(product[0]); + j++; + } + RegCloseKey( key_products ); + len_user = sizeof(user)/sizeof(user[0]); + i++; + } + RegCloseKey( key_users ); + return ERROR_NO_MORE_ITEMS; + +found: + if (sid_len && *sid_len <= len_user) + { + *sid_len = len_user; + r = ERROR_MORE_DATA; + } + else + { + if (installed_product) unsquash_guid( product, installed_product ); + if (installed_ctx) *installed_ctx = ctx; + if (sid) + { + strcpyW( sid, user ); + *sid_len = len_user; + } + r = ERROR_SUCCESS; + } + RegCloseKey( key_products ); + RegCloseKey( key_users ); + return r; +} + +static UINT enum_products( const WCHAR *product, const WCHAR *usersid, DWORD ctx, DWORD index, + DWORD *idx, WCHAR installed_product[GUID_SIZE], + MSIINSTALLCONTEXT *installed_ctx, WCHAR *sid, DWORD *sid_len ) +{ + UINT r = ERROR_NO_MORE_ITEMS; + WCHAR *user = NULL; + + if (!usersid) + { + usersid = user = get_user_sid(); + if (!user) return ERROR_FUNCTION_FAILED; + } + if (ctx & MSIINSTALLCONTEXT_MACHINE) + { + r = fetch_machine_product( product, index, idx, installed_product, installed_ctx, + sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + if (ctx & MSIINSTALLCONTEXT_USERUNMANAGED) + { + r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, index, + idx, installed_product, installed_ctx, sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + if (ctx & MSIINSTALLCONTEXT_USERMANAGED) + { + r = fetch_user_product( product, usersid, MSIINSTALLCONTEXT_USERMANAGED, index, + idx, installed_product, installed_ctx, sid, sid_len ); + if (r != ERROR_NO_MORE_ITEMS) goto done; + } + +done: + LocalFree( user ); + return r; +} + +UINT WINAPI MsiEnumProductsExW( LPCWSTR product, LPCWSTR usersid, DWORD ctx, DWORD index, + WCHAR installed_product[GUID_SIZE], + MSIINSTALLCONTEXT *installed_ctx, LPWSTR sid, LPDWORD sid_len ) +{ + UINT r; + DWORD idx = 0; + static DWORD last_index; + + TRACE("%s, %s, %u, %u, %p, %p, %p, %p\n", debugstr_w(product), debugstr_w(usersid), + ctx, index, installed_product, installed_ctx, sid, sid_len); + + if ((sid && !sid_len) || !ctx || (usersid && ctx == MSIINSTALLCONTEXT_MACHINE)) + return ERROR_INVALID_PARAMETER; + + if (index && index - last_index != 1) + return ERROR_INVALID_PARAMETER; + + if (!index) last_index = 0; + + r = enum_products( product, usersid, ctx, index, &idx, installed_product, installed_ctx, + sid, sid_len ); + if (r == ERROR_SUCCESS) + last_index = index; + else + last_index = 0; + + return r; +} diff --git a/libmsi/script.c b/libmsi/script.c new file mode 100644 index 0000000..392c5ac --- /dev/null +++ b/libmsi/script.c @@ -0,0 +1,379 @@ +/* + * Implementation of scripting for Microsoft Installer (msi.dll) + * + * Copyright 2007 Misha Koshelev + * + * 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 + */ + +#define COBJMACROS + +#include <stdarg.h> +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "msidefs.h" +#include "msipriv.h" +#include "activscp.h" +#include "oleauto.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "msiserver.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#ifdef _WIN64 + +#define IActiveScriptParse_Release IActiveScriptParse64_Release +#define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew +#define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText + +#else + +#define IActiveScriptParse_Release IActiveScriptParse32_Release +#define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew +#define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText + +#endif + +static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0}; +static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0}; +static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0}; + +/* + * MsiActiveScriptSite - Our IActiveScriptSite implementation. + */ + +typedef struct { + IActiveScriptSite lpVtbl; + IDispatch *pInstaller; + IDispatch *pSession; + LONG ref; +} MsiActiveScriptSite; + +static const struct IActiveScriptSiteVtbl ASS_Vtbl; + +static HRESULT create_ActiveScriptSite(IUnknown *pUnkOuter, LPVOID *ppObj) +{ + MsiActiveScriptSite* object; + + TRACE("(%p,%p)\n", pUnkOuter, ppObj); + + if( pUnkOuter ) + return CLASS_E_NOAGGREGATION; + + object = msi_alloc_zero( sizeof(MsiActiveScriptSite) ); + + object->lpVtbl.lpVtbl = &ASS_Vtbl; + object->ref = 1; + object->pInstaller = NULL; + object->pSession = NULL; + + *ppObj = object; + + return S_OK; +} + +/* + * Call a script. + */ +DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action) +{ + HRESULT hr; + IActiveScript *pActiveScript = NULL; + IActiveScriptParse *pActiveScriptParse = NULL; + MsiActiveScriptSite *pActiveScriptSite = NULL; + IDispatch *pDispatch = NULL; + DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; + DISPID dispid; + CLSID clsid; + VARIANT var; + DWORD ret = ERROR_INSTALL_FAILURE; + + CoInitialize(NULL); + + /* Create MsiActiveScriptSite object */ + hr = create_ActiveScriptSite(NULL, (void **)&pActiveScriptSite); + if (hr != S_OK) goto done; + + /* Create an installer object */ + hr = create_msiserver(NULL, (LPVOID *)&pActiveScriptSite->pInstaller); + if (hr != S_OK) goto done; + + /* Create a session object */ + hr = create_session(hPackage, pActiveScriptSite->pInstaller, &pActiveScriptSite->pSession); + if (hr != S_OK) goto done; + + /* Create the scripting engine */ + if ((type & 7) == msidbCustomActionTypeJScript) + hr = CLSIDFromProgID(szJScript, &clsid); + else if ((type & 7) == msidbCustomActionTypeVBScript) + hr = CLSIDFromProgID(szVBScript, &clsid); + else { + ERR("Unknown script type %d\n", type); + goto done; + } + if (FAILED(hr)) { + ERR("Could not find CLSID for Windows Script\n"); + goto done; + } + hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript); + if (FAILED(hr)) { + ERR("Could not instantiate class for Windows Script\n"); + goto done; + } + + hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse); + if (FAILED(hr)) goto done; + + hr = IActiveScript_SetScriptSite(pActiveScript, (IActiveScriptSite *)pActiveScriptSite); + if (FAILED(hr)) goto done; + + hr = IActiveScriptParse_InitNew(pActiveScriptParse); + if (FAILED(hr)) goto done; + + hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_GLOBALMEMBERS|SCRIPTITEM_ISVISIBLE); + if (FAILED(hr)) goto done; + + hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL); + if (FAILED(hr)) goto done; + + hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED); + if (FAILED(hr)) goto done; + + /* Call a function if necessary through the IDispatch interface */ + if (function != NULL && strlenW(function) > 0) { + TRACE("Calling function %s\n", debugstr_w(function)); + + hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch); + if (FAILED(hr)) goto done; + + hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid); + if (FAILED(hr)) goto done; + + hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL); + if (FAILED(hr)) goto done; + + /* Check return value, if it's not IDOK we failed */ + hr = VariantChangeType(&var, &var, 0, VT_I4); + if (FAILED(hr)) goto done; + + if (V_I4(&var) == IDOK) + ret = ERROR_SUCCESS; + else ret = ERROR_INSTALL_FAILURE; + + VariantClear(&var); + } else { + /* If no function to be called, MSI behavior is to succeed */ + ret = ERROR_SUCCESS; + } + +done: + + if (pDispatch) IDispatch_Release(pDispatch); + if (pActiveScript) IActiveScript_Release(pActiveScript); + if (pActiveScriptParse) IActiveScriptParse_Release(pActiveScriptParse); + if (pActiveScriptSite) + { + if (pActiveScriptSite->pSession) IDispatch_Release(pActiveScriptSite->pSession); + if (pActiveScriptSite->pInstaller) IDispatch_Release(pActiveScriptSite->pInstaller); + IActiveScriptSite_Release((IActiveScriptSite *)pActiveScriptSite); + } + CoUninitialize(); /* must call even if CoInitialize failed */ + return ret; +} + +/* + * MsiActiveScriptSite + */ + +/*** IUnknown methods ***/ +static HRESULT WINAPI MsiActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** ppvObject) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + + TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IActiveScriptSite)) + { + IActiveScriptSite_AddRef(iface); + *ppvObject = This; + return S_OK; + } + + TRACE("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject); + + return E_NOINTERFACE; +} + +static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + + TRACE("(%p/%p)\n", iface, This); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)\n", iface, This); + + if (!ref) + msi_free(This); + + return ref; +} + +/*** IActiveScriptSite methods **/ +static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p)\n", This, iface, plcid); + return E_NOTIMPL; /* Script will use system-defined locale */ +} + +static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p,%d,%p,%p)\n", This, iface, pstrName, dwReturnMask, ppiunkItem, ppti); + + /* Determine the kind of pointer that is requested, and make sure placeholder is valid */ + if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { + if (!ppti) return E_INVALIDARG; + *ppti = NULL; + } + if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { + if (!ppiunkItem) return E_INVALIDARG; + *ppiunkItem = NULL; + } + + /* Are we looking for the session object? */ + if (!strcmpW(szSession, pstrName)) { + if (dwReturnMask & SCRIPTINFO_ITYPEINFO) + return load_type_info(This->pSession, ppti, &DIID_Session, 0); + else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { + IDispatch_QueryInterface(This->pSession, &IID_IUnknown, (void **)ppiunkItem); + return S_OK; + } + } + + return TYPE_E_ELEMENTNOTFOUND; +} + +static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p)\n", This, iface, pbstrVersion); + return E_NOTIMPL; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p,%p)\n", This, iface, pvarResult, pexcepinfo); + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState) +{ + switch (ssScriptState) { + case SCRIPTSTATE_UNINITIALIZED: + TRACE("State: Uninitialized.\n"); + break; + + case SCRIPTSTATE_INITIALIZED: + TRACE("State: Initialized.\n"); + break; + + case SCRIPTSTATE_STARTED: + TRACE("State: Started.\n"); + break; + + case SCRIPTSTATE_CONNECTED: + TRACE("State: Connected.\n"); + break; + + case SCRIPTSTATE_DISCONNECTED: + TRACE("State: Disconnected.\n"); + break; + + case SCRIPTSTATE_CLOSED: + TRACE("State: Closed.\n"); + break; + + default: + ERR("Unknown State: %d\n", ssScriptState); + break; + } + + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + EXCEPINFO exception; + HRESULT hr; + + TRACE("(%p/%p)->(%p)\n", This, iface, pscripterror); + + memset(&exception, 0, sizeof(EXCEPINFO)); + hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception); + if (SUCCEEDED(hr)) + { + ERR("script error: %s\n", debugstr_w(exception.bstrDescription)); + SysFreeString(exception.bstrSource); + SysFreeString(exception.bstrDescription); + SysFreeString(exception.bstrHelpFile); + } + + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)\n", This, iface); + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)\n", This, iface); + return S_OK; +} + +static const struct IActiveScriptSiteVtbl ASS_Vtbl = +{ + MsiActiveScriptSite_QueryInterface, + MsiActiveScriptSite_AddRef, + MsiActiveScriptSite_Release, + MsiActiveScriptSite_GetLCID, + MsiActiveScriptSite_GetItemInfo, + MsiActiveScriptSite_GetDocVersionString, + MsiActiveScriptSite_OnScriptTerminate, + MsiActiveScriptSite_OnStateChange, + MsiActiveScriptSite_OnScriptError, + MsiActiveScriptSite_OnEnterScript, + MsiActiveScriptSite_OnLeaveScript +}; diff --git a/libmsi/select.c b/libmsi/select.c new file mode 100644 index 0000000..982cf6a --- /dev/null +++ b/libmsi/select.c @@ -0,0 +1,458 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSISELECTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT num_cols; + UINT max_cols; + UINT cols[1]; +} MSISELECTVIEW; + +static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %p\n", sv, row, col, val ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( !col || col > sv->num_cols ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + if( !col ) + { + *val = 0; + return ERROR_SUCCESS; + } + return sv->table->ops->fetch_int( sv->table, row, col, val ); +} + +static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %p\n", sv, row, col, stm ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( !col || col > sv->num_cols ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + if( !col ) + { + *stm = NULL; + return ERROR_SUCCESS; + } + return sv->table->ops->fetch_stream( sv->table, row, col, stm ); +} + +static UINT SELECT_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW *)view; + + TRACE("%p %d %p\n", sv, row, rec ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return msi_view_get_row(sv->db, view, row, rec); +} + +static UINT SELECT_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + UINT i, expanded_mask = 0, r = ERROR_SUCCESS, col_count = 0; + MSIRECORD *expanded; + + TRACE("%p %d %p %08x\n", sv, row, rec, mask ); + + if ( !sv->table ) + return ERROR_FUNCTION_FAILED; + + /* test if any of the mask bits are invalid */ + if ( mask >= (1<<sv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + /* find the number of columns in the table below */ + r = sv->table->ops->get_dimensions( sv->table, NULL, &col_count ); + if( r ) + return r; + + /* expand the record to the right size for the underlying table */ + expanded = MSI_CreateRecord( col_count ); + if ( !expanded ) + return ERROR_FUNCTION_FAILED; + + /* move the right fields across */ + for ( i=0; i<sv->num_cols; i++ ) + { + r = MSI_RecordCopyField( rec, i+1, expanded, sv->cols[ i ] ); + if (r != ERROR_SUCCESS) + break; + expanded_mask |= (1<<(sv->cols[i]-1)); + } + + /* set the row in the underlying table */ + if (r == ERROR_SUCCESS) + r = sv->table->ops->set_row( sv->table, row, expanded, expanded_mask ); + + msiobj_release( &expanded->hdr ); + return r; +} + +static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, UINT row, BOOL temporary ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + UINT i, table_cols, r; + MSIRECORD *outrec; + + TRACE("%p %p\n", sv, record ); + + if ( !sv->table ) + return ERROR_FUNCTION_FAILED; + + /* rearrange the record to suit the table */ + r = sv->table->ops->get_dimensions( sv->table, NULL, &table_cols ); + if (r != ERROR_SUCCESS) + return r; + + outrec = MSI_CreateRecord( table_cols + 1 ); + + for (i=0; i<sv->num_cols; i++) + { + r = MSI_RecordCopyField( record, i+1, outrec, sv->cols[i] ); + if (r != ERROR_SUCCESS) + goto fail; + } + + r = sv->table->ops->insert_row( sv->table, outrec, row, temporary ); + +fail: + msiobj_release( &outrec->hdr ); + + return r; +} + +static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p\n", sv, record); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->execute( sv->table, record ); +} + +static UINT SELECT_close( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->close( sv->table ); +} + +static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p %p\n", sv, rows, cols ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( cols ) + *cols = sv->num_cols; + + return sv->table->ops->get_dimensions( sv->table, rows, NULL ); +} + +static UINT SELECT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %p %p %p %p\n", sv, n, name, type, temporary, table_name ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( !n || n > sv->num_cols ) + return ERROR_FUNCTION_FAILED; + + n = sv->cols[ n - 1 ]; + if( !n ) + { + if (name) *name = szEmpty; + if (type) *type = MSITYPE_UNKNOWN | MSITYPE_VALID; + if (temporary) *temporary = FALSE; + if (table_name) *table_name = szEmpty; + return ERROR_SUCCESS; + } + return sv->table->ops->get_column_info( sv->table, n, name, + type, temporary, table_name ); +} + +static UINT msi_select_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + UINT r, i, num_columns, col, type, val; + LPCWSTR str; + MSIRECORD *mod; + + r = SELECT_get_dimensions(view, NULL, &num_columns); + if (r != ERROR_SUCCESS) + return r; + + r = sv->table->ops->get_row(sv->table, row - 1, &mod); + if (r != ERROR_SUCCESS) + return r; + + for (i = 0; i < num_columns; i++) + { + col = sv->cols[i]; + + r = SELECT_get_column_info(view, i + 1, NULL, &type, NULL, NULL); + if (r != ERROR_SUCCESS) + { + ERR("Failed to get column information: %d\n", r); + goto done; + } + + if (MSITYPE_IS_BINARY(type)) + { + ERR("Cannot modify binary data!\n"); + r = ERROR_FUNCTION_FAILED; + goto done; + } + else if (type & MSITYPE_STRING) + { + str = MSI_RecordGetString(rec, i + 1); + r = MSI_RecordSetStringW(mod, col, str); + } + else + { + val = MSI_RecordGetInteger(rec, i + 1); + r = MSI_RecordSetInteger(mod, col, val); + } + + if (r != ERROR_SUCCESS) + { + ERR("Failed to modify record: %d\n", r); + goto done; + } + } + + r = sv->table->ops->modify(sv->table, MSIMODIFY_UPDATE, mod, row); + +done: + msiobj_release(&mod->hdr); + return r; +} + +static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %p %d\n", sv, eModifyMode, rec, row ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if (eModifyMode == MSIMODIFY_UPDATE) + return msi_select_update(view, rec, row); + + return sv->table->ops->modify( sv->table, eModifyMode, rec, row ); +} + +static UINT SELECT_delete( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( sv->table ) + sv->table->ops->delete( sv->table ); + sv->table = NULL; + + msi_free( sv ); + + return ERROR_SUCCESS; +} + +static UINT SELECT_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->find_matching_rows( sv->table, col, val, row, handle ); +} + + +static const MSIVIEWOPS select_ops = +{ + SELECT_fetch_int, + SELECT_fetch_stream, + SELECT_get_row, + SELECT_set_row, + SELECT_insert_row, + NULL, + SELECT_execute, + SELECT_close, + SELECT_get_dimensions, + SELECT_get_column_info, + SELECT_modify, + SELECT_delete, + SELECT_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name, + LPCWSTR table_name ) +{ + UINT r, n; + MSIVIEW *table; + + TRACE("%p adding %s.%s\n", sv, debugstr_w( table_name ), + debugstr_w( name )); + + if( sv->view.ops != &select_ops ) + return ERROR_FUNCTION_FAILED; + + table = sv->table; + if( !table ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_column_info ) + return ERROR_FUNCTION_FAILED; + + if( sv->num_cols >= sv->max_cols ) + return ERROR_FUNCTION_FAILED; + + if ( !name[0] ) n = 0; + else + { + r = VIEW_find_column( table, name, table_name, &n ); + if( r != ERROR_SUCCESS ) + return r; + } + + sv->cols[sv->num_cols] = n; + TRACE("Translating column %s from %d -> %d\n", + debugstr_w( name ), sv->num_cols, n); + + sv->num_cols++; + + return ERROR_SUCCESS; +} + +static int select_count_columns( const column_info *col ) +{ + int n; + for (n = 0; col; col = col->next) + n++; + return n; +} + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + const column_info *columns ) +{ + MSISELECTVIEW *sv = NULL; + UINT count = 0, r = ERROR_SUCCESS; + + TRACE("%p\n", sv ); + + count = select_count_columns( columns ); + + sv = msi_alloc_zero( sizeof *sv + count*sizeof (UINT) ); + if( !sv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + sv->view.ops = &select_ops; + sv->db = db; + sv->table = table; + sv->num_cols = 0; + sv->max_cols = count; + + while( columns ) + { + r = SELECT_AddColumn( sv, columns->column, columns->table ); + if( r ) + break; + columns = columns->next; + } + + if( r == ERROR_SUCCESS ) + *view = &sv->view; + else + msi_free( sv ); + + return r; +} diff --git a/libmsi/source.c b/libmsi/source.c new file mode 100644 index 0000000..881f3d2 --- /dev/null +++ b/libmsi/source.c @@ -0,0 +1,1363 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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 +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "wincrypt.h" +#include "winver.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "sddl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * These apis are defined in MSI 3.0 + */ + +typedef struct tagMediaInfo +{ + struct list entry; + LPWSTR path; + WCHAR szIndex[10]; + DWORD index; +} media_info; + +static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, DWORD dwOptions, + MSIINSTALLCONTEXT context, BOOL create) +{ + HKEY rootkey = 0; + UINT rc = ERROR_FUNCTION_FAILED; + + if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + if (dwOptions & MSICODE_PATCH) + rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create); + else + rc = MSIREG_OpenProductKey(szProduct, NULL, context, + &rootkey, create); + } + else if (context == MSIINSTALLCONTEXT_USERMANAGED) + { + if (dwOptions & MSICODE_PATCH) + rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create); + else + rc = MSIREG_OpenProductKey(szProduct, NULL, context, + &rootkey, create); + } + else if (context == MSIINSTALLCONTEXT_MACHINE) + { + if (dwOptions & MSICODE_PATCH) + rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create); + else + rc = MSIREG_OpenProductKey(szProduct, NULL, context, + &rootkey, create); + } + + if (rc != ERROR_SUCCESS) + { + if (dwOptions & MSICODE_PATCH) + return ERROR_UNKNOWN_PATCH; + else + return ERROR_UNKNOWN_PRODUCT; + } + + if (create) + rc = RegCreateKeyW(rootkey, szSourceList, key); + else + { + rc = RegOpenKeyW(rootkey,szSourceList, key); + if (rc != ERROR_SUCCESS) + rc = ERROR_BAD_CONFIGURATION; + } + + return rc; +} + +static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR media[] = {'M','e','d','i','a',0}; + + if (create) + rc = RegCreateKeyW(rootkey, media, key); + else + rc = RegOpenKeyW(rootkey,media, key); + + return rc; +} + +static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR net[] = {'N','e','t',0}; + + if (create) + rc = RegCreateKeyW(rootkey, net, key); + else + rc = RegOpenKeyW(rootkey, net, key); + + return rc; +} + +static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR URL[] = {'U','R','L',0}; + + if (create) + rc = RegCreateKeyW(rootkey, URL, key); + else + rc = RegOpenKeyW(rootkey, URL, key); + + return rc; +} + +/****************************************************************** + * MsiSourceListEnumMediaDisksA (MSI.@) + */ +UINT WINAPI MsiSourceListEnumMediaDisksA(LPCSTR szProductCodeOrPatchCode, + LPCSTR szUserSid, MSIINSTALLCONTEXT dwContext, + DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId, + LPSTR szVolumeLabel, LPDWORD pcchVolumeLabel, + LPSTR szDiskPrompt, LPDWORD pcchDiskPrompt) +{ + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR volume = NULL; + LPWSTR prompt = NULL; + UINT r = ERROR_INVALID_PARAMETER; + + TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p, %p)\n", debugstr_a(szProductCodeOrPatchCode), + debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, pdwDiskId, + szVolumeLabel, pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt); + + if (szDiskPrompt && !pcchDiskPrompt) + return ERROR_INVALID_PARAMETER; + + if (szProductCodeOrPatchCode) product = strdupAtoW(szProductCodeOrPatchCode); + if (szUserSid) usersid = strdupAtoW(szUserSid); + + /* FIXME: add tests for an invalid format */ + + if (pcchVolumeLabel) + volume = msi_alloc(*pcchVolumeLabel * sizeof(WCHAR)); + + if (pcchDiskPrompt) + prompt = msi_alloc(*pcchDiskPrompt * sizeof(WCHAR)); + + if (volume) *volume = '\0'; + if (prompt) *prompt = '\0'; + r = MsiSourceListEnumMediaDisksW(product, usersid, dwContext, dwOptions, + dwIndex, pdwDiskId, volume, pcchVolumeLabel, + prompt, pcchDiskPrompt); + if (r != ERROR_SUCCESS) + goto done; + + if (szVolumeLabel && pcchVolumeLabel) + WideCharToMultiByte(CP_ACP, 0, volume, -1, szVolumeLabel, + *pcchVolumeLabel + 1, NULL, NULL); + + if (szDiskPrompt) + WideCharToMultiByte(CP_ACP, 0, prompt, -1, szDiskPrompt, + *pcchDiskPrompt + 1, NULL, NULL); + +done: + msi_free(product); + msi_free(usersid); + msi_free(volume); + msi_free(prompt); + + return r; +} + +/****************************************************************** + * MsiSourceListEnumMediaDisksW (MSI.@) + */ +UINT WINAPI MsiSourceListEnumMediaDisksW(LPCWSTR szProductCodeOrPatchCode, + LPCWSTR szUserSid, MSIINSTALLCONTEXT dwContext, + DWORD dwOptions, DWORD dwIndex, LPDWORD pdwDiskId, + LPWSTR szVolumeLabel, LPDWORD pcchVolumeLabel, + LPWSTR szDiskPrompt, LPDWORD pcchDiskPrompt) +{ + WCHAR squished_pc[GUID_SIZE]; + WCHAR convert[11]; + LPWSTR value = NULL; + LPWSTR data = NULL; + LPWSTR ptr, ptr2; + HKEY source, media; + DWORD valuesz, datasz = 0; + DWORD type; + DWORD numvals, size; + LONG res; + UINT r; + static DWORD index = 0; + + static const WCHAR fmt[] = {'#','%','d',0}; + + TRACE("(%s, %s, %d, %d, %d, %p, %p, %p, %p)\n", debugstr_w(szProductCodeOrPatchCode), + debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szVolumeLabel, + pcchVolumeLabel, szDiskPrompt, pcchDiskPrompt); + + if (!szProductCodeOrPatchCode || + !squash_guid(szProductCodeOrPatchCode, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) + return ERROR_INVALID_PARAMETER; + + if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH) + return ERROR_INVALID_PARAMETER; + + if (szDiskPrompt && !pcchDiskPrompt) + return ERROR_INVALID_PARAMETER; + + if (dwIndex == 0) + index = 0; + + if (dwIndex != index) + return ERROR_INVALID_PARAMETER; + + r = OpenSourceKey(szProductCodeOrPatchCode, &source, + dwOptions, dwContext, FALSE); + if (r != ERROR_SUCCESS) + return r; + + r = OpenMediaSubkey(source, &media, FALSE); + if (r != ERROR_SUCCESS) + { + RegCloseKey(source); + return ERROR_NO_MORE_ITEMS; + } + + if (!pcchVolumeLabel && !pcchDiskPrompt) + { + r = RegEnumValueW(media, dwIndex, NULL, NULL, NULL, + &type, NULL, NULL); + goto done; + } + + res = RegQueryInfoKeyW(media, NULL, NULL, NULL, NULL, NULL, + NULL, &numvals, &valuesz, &datasz, NULL, NULL); + if (res != ERROR_SUCCESS) + { + r = ERROR_BAD_CONFIGURATION; + goto done; + } + + value = msi_alloc(++valuesz * sizeof(WCHAR)); + data = msi_alloc(++datasz * sizeof(WCHAR)); + if (!value || !data) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + r = RegEnumValueW(media, dwIndex, value, &valuesz, + NULL, &type, (LPBYTE)data, &datasz); + if (r != ERROR_SUCCESS) + goto done; + + if (pdwDiskId) + *pdwDiskId = atolW(value); + + ptr2 = data; + ptr = strchrW(data, ';'); + if (!ptr) + ptr = data; + else + *ptr = '\0'; + + if (pcchVolumeLabel) + { + if (type == REG_DWORD) + { + sprintfW(convert, fmt, *data); + size = lstrlenW(convert); + ptr2 = convert; + } + else + size = lstrlenW(data); + + if (size >= *pcchVolumeLabel) + r = ERROR_MORE_DATA; + else if (szVolumeLabel) + lstrcpyW(szVolumeLabel, ptr2); + + *pcchVolumeLabel = size; + } + + if (pcchDiskPrompt) + { + if (!*ptr) + ptr++; + + if (type == REG_DWORD) + { + sprintfW(convert, fmt, *ptr); + size = lstrlenW(convert); + ptr = convert; + } + else + size = lstrlenW(ptr); + + if (size >= *pcchDiskPrompt) + r = ERROR_MORE_DATA; + else if (szDiskPrompt) + lstrcpyW(szDiskPrompt, ptr); + + *pcchDiskPrompt = size; + } + + index++; + +done: + msi_free(value); + msi_free(data); + RegCloseKey(source); + + return r; +} + +/****************************************************************** + * MsiSourceListEnumSourcesA (MSI.@) + */ +UINT WINAPI MsiSourceListEnumSourcesA(LPCSTR szProductCodeOrPatch, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, + DWORD dwOptions, DWORD dwIndex, + LPSTR szSource, LPDWORD pcchSource) +{ + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR source = NULL; + DWORD len = 0; + UINT r = ERROR_INVALID_PARAMETER; + static DWORD index = 0; + + TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_a(szProductCodeOrPatch), + debugstr_a(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource); + + if (dwIndex == 0) + index = 0; + + if (szSource && !pcchSource) + goto done; + + if (dwIndex != index) + goto done; + + if (szProductCodeOrPatch) product = strdupAtoW(szProductCodeOrPatch); + if (szUserSid) usersid = strdupAtoW(szUserSid); + + r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions, + dwIndex, NULL, &len); + if (r != ERROR_SUCCESS) + goto done; + + source = msi_alloc(++len * sizeof(WCHAR)); + if (!source) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + *source = '\0'; + r = MsiSourceListEnumSourcesW(product, usersid, dwContext, dwOptions, + dwIndex, source, &len); + if (r != ERROR_SUCCESS) + goto done; + + len = WideCharToMultiByte(CP_ACP, 0, source, -1, NULL, 0, NULL, NULL); + if (pcchSource && *pcchSource >= len) + WideCharToMultiByte(CP_ACP, 0, source, -1, szSource, len, NULL, NULL); + else if (szSource) + r = ERROR_MORE_DATA; + + if (pcchSource) + *pcchSource = len - 1; + +done: + msi_free(product); + msi_free(usersid); + msi_free(source); + + if (r == ERROR_SUCCESS) + { + if (szSource || !pcchSource) index++; + } + else if (dwIndex > index) + index = 0; + + return r; +} + +/****************************************************************** + * MsiSourceListEnumSourcesW (MSI.@) + */ +UINT WINAPI MsiSourceListEnumSourcesW(LPCWSTR szProductCodeOrPatch, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, + DWORD dwOptions, DWORD dwIndex, + LPWSTR szSource, LPDWORD pcchSource) +{ + WCHAR squished_pc[GUID_SIZE]; + WCHAR name[32]; + HKEY source = NULL; + HKEY subkey = NULL; + LONG res; + UINT r = ERROR_INVALID_PARAMETER; + static DWORD index = 0; + + static const WCHAR format[] = {'%','d',0}; + + TRACE("(%s, %s, %d, %d, %d, %p, %p)\n", debugstr_w(szProductCodeOrPatch), + debugstr_w(szUserSid), dwContext, dwOptions, dwIndex, szSource, pcchSource); + + if (dwIndex == 0) + index = 0; + + if (!szProductCodeOrPatch || !squash_guid(szProductCodeOrPatch, squished_pc)) + goto done; + + if (szSource && !pcchSource) + goto done; + + if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))) + goto done; + + if ((dwOptions & MSISOURCETYPE_NETWORK) && (dwOptions & MSISOURCETYPE_URL)) + goto done; + + if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) + goto done; + + if (dwIndex != index) + goto done; + + r = OpenSourceKey(szProductCodeOrPatch, &source, + dwOptions, dwContext, FALSE); + if (r != ERROR_SUCCESS) + goto done; + + if (dwOptions & MSISOURCETYPE_NETWORK) + r = OpenNetworkSubkey(source, &subkey, FALSE); + else if (dwOptions & MSISOURCETYPE_URL) + r = OpenURLSubkey(source, &subkey, FALSE); + + if (r != ERROR_SUCCESS) + { + r = ERROR_NO_MORE_ITEMS; + goto done; + } + + sprintfW(name, format, dwIndex + 1); + + res = RegQueryValueExW(subkey, name, 0, 0, (LPBYTE)szSource, pcchSource); + if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) + r = ERROR_NO_MORE_ITEMS; + +done: + RegCloseKey(subkey); + RegCloseKey(source); + + if (r == ERROR_SUCCESS) + { + if (szSource || !pcchSource) index++; + } + else if (dwIndex > index) + index = 0; + + return r; +} + +/****************************************************************** + * MsiSourceListGetInfoA (MSI.@) + */ +UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCSTR szProperty, LPSTR szValue, + LPDWORD pcchValue) +{ + UINT ret; + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR property = NULL; + LPWSTR value = NULL; + DWORD len = 0; + + if (szValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (szProduct) product = strdupAtoW(szProduct); + if (szUserSid) usersid = strdupAtoW(szUserSid); + if (szProperty) property = strdupAtoW(szProperty); + + ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions, + property, NULL, &len); + if (ret != ERROR_SUCCESS) + goto done; + + value = msi_alloc(++len * sizeof(WCHAR)); + if (!value) + return ERROR_OUTOFMEMORY; + + *value = '\0'; + ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions, + property, value, &len); + if (ret != ERROR_SUCCESS) + goto done; + + len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL); + if (*pcchValue >= len) + WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL); + else if (szValue) + ret = ERROR_MORE_DATA; + + *pcchValue = len - 1; + +done: + msi_free(product); + msi_free(usersid); + msi_free(property); + msi_free(value); + return ret; +} + +/****************************************************************** + * MsiSourceListGetInfoW (MSI.@) + */ +UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCWSTR szProperty, LPWSTR szValue, + LPDWORD pcchValue) +{ + WCHAR squished_pc[GUID_SIZE]; + HKEY sourcekey, media; + LPWSTR source, ptr; + DWORD size; + UINT rc; + + static const WCHAR mediapack[] = { + 'M','e','d','i','a','P','a','c','k','a','g','e',0}; + + TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty)); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (szValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (dwContext != MSIINSTALLCONTEXT_USERMANAGED && + dwContext != MSIINSTALLCONTEXT_USERUNMANAGED && + dwContext != MSIINSTALLCONTEXT_MACHINE) + return ERROR_INVALID_PARAMETER; + + if (!szProperty) + return ERROR_INVALID_PARAMETER; + + if (szUserSid) + FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); + + rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, dwContext, FALSE); + if (rc != ERROR_SUCCESS) + return rc; + + if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) || + !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW )) + { + rc = OpenMediaSubkey(sourcekey, &media, FALSE); + if (rc != ERROR_SUCCESS) + { + RegCloseKey(sourcekey); + return ERROR_SUCCESS; + } + + if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW )) + szProperty = mediapack; + + RegQueryValueExW(media, szProperty, 0, 0, (LPBYTE)szValue, pcchValue); + RegCloseKey(media); + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) || + !strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW )) + { + rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, + 0, 0, NULL, &size); + if (rc != ERROR_SUCCESS) + { + RegCloseKey(sourcekey); + return ERROR_SUCCESS; + } + + source = msi_alloc(size); + RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, + 0, 0, (LPBYTE)source, &size); + + if (!*source) + { + msi_free(source); + RegCloseKey(sourcekey); + return ERROR_SUCCESS; + } + + if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDTYPEW )) + { + if (*source != 'n' && *source != 'u' && *source != 'm') + { + msi_free(source); + RegCloseKey(sourcekey); + return ERROR_SUCCESS; + } + + ptr = source; + source[1] = '\0'; + } + else + { + ptr = strrchrW(source, ';'); + if (!ptr) + ptr = source; + else + ptr++; + } + + if (szValue) + { + if (strlenW(ptr) < *pcchValue) + lstrcpyW(szValue, ptr); + else + rc = ERROR_MORE_DATA; + } + + *pcchValue = lstrlenW(ptr); + msi_free(source); + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW )) + { + *pcchValue = *pcchValue * sizeof(WCHAR); + rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, + (LPBYTE)szValue, pcchValue); + if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) + { + *pcchValue = 0; + rc = ERROR_SUCCESS; + } + else + { + if (*pcchValue) + *pcchValue = (*pcchValue - 1) / sizeof(WCHAR); + if (szValue) + szValue[*pcchValue] = '\0'; + } + } + else + { + FIXME("Unknown property %s\n",debugstr_w(szProperty)); + rc = ERROR_UNKNOWN_PROPERTY; + } + + RegCloseKey(sourcekey); + return rc; +} + +/****************************************************************** + * MsiSourceListSetInfoA (MSI.@) + */ +UINT WINAPI MsiSourceListSetInfoA(LPCSTR szProduct, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCSTR szProperty, LPCSTR szValue) +{ + UINT ret; + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR property = NULL; + LPWSTR value = NULL; + + if (szProduct) product = strdupAtoW(szProduct); + if (szUserSid) usersid = strdupAtoW(szUserSid); + if (szProperty) property = strdupAtoW(szProperty); + if (szValue) value = strdupAtoW(szValue); + + ret = MsiSourceListSetInfoW(product, usersid, dwContext, dwOptions, + property, value); + + msi_free(product); + msi_free(usersid); + msi_free(property); + msi_free(value); + + return ret; +} + +UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid, + MSIINSTALLCONTEXT context, DWORD options, + LPCWSTR value) +{ + HKEY source; + LPWSTR buffer; + WCHAR typechar; + DWORD size; + UINT r; + int index = 1; + + static const WCHAR format[] = {'%','c',';','%','i',';','%','s',0}; + + if (options & MSISOURCETYPE_NETWORK) + typechar = 'n'; + else if (options & MSISOURCETYPE_URL) + typechar = 'u'; + else if (options & MSISOURCETYPE_MEDIA) + typechar = 'm'; + else + return ERROR_INVALID_PARAMETER; + + if (!(options & MSISOURCETYPE_MEDIA)) + { + r = MsiSourceListAddSourceExW(product, usersid, context, + options, value, 0); + if (r != ERROR_SUCCESS) + return r; + + index = 0; + while ((r = MsiSourceListEnumSourcesW(product, usersid, context, options, + index, NULL, NULL)) == ERROR_SUCCESS) + index++; + + if (r != ERROR_NO_MORE_ITEMS) + return r; + } + + size = (lstrlenW(format) + lstrlenW(value) + 7) * sizeof(WCHAR); + buffer = msi_alloc(size); + if (!buffer) + return ERROR_OUTOFMEMORY; + + r = OpenSourceKey(product, &source, MSICODE_PRODUCT, context, FALSE); + if (r != ERROR_SUCCESS) + return r; + + sprintfW(buffer, format, typechar, index, value); + + size = (lstrlenW(buffer) + 1) * sizeof(WCHAR); + r = RegSetValueExW(source, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, + REG_SZ, (LPBYTE)buffer, size); + msi_free(buffer); + + RegCloseKey(source); + return r; +} + +/****************************************************************** + * MsiSourceListSetInfoW (MSI.@) + */ +UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCWSTR szProperty, LPCWSTR szValue) +{ + WCHAR squished_pc[GUID_SIZE]; + HKEY sourcekey, media; + LPCWSTR property; + UINT rc; + + static const WCHAR media_package[] = { + 'M','e','d','i','a','P','a','c','k','a','g','e',0 + }; + + TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid), + dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue)); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!szProperty) + return ERROR_INVALID_PARAMETER; + + if (!szValue) + return ERROR_UNKNOWN_PROPERTY; + + if (dwContext == MSIINSTALLCONTEXT_MACHINE && szUserSid) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_UNKNOWN_PATCH; + } + + property = szProperty; + if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW )) + property = media_package; + + rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE); + if (rc != ERROR_SUCCESS) + return rc; + + if (strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW ) && + dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL)) + { + RegCloseKey(sourcekey); + return ERROR_INVALID_PARAMETER; + } + + if (!strcmpW( szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW ) || + !strcmpW( szProperty, INSTALLPROPERTY_DISKPROMPTW )) + { + rc = OpenMediaSubkey(sourcekey, &media, TRUE); + if (rc == ERROR_SUCCESS) + { + rc = msi_reg_set_val_str(media, property, szValue); + RegCloseKey(media); + } + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_PACKAGENAMEW )) + { + DWORD size = (lstrlenW(szValue) + 1) * sizeof(WCHAR); + rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, + REG_SZ, (const BYTE *)szValue, size); + if (rc != ERROR_SUCCESS) + rc = ERROR_UNKNOWN_PROPERTY; + } + else if (!strcmpW( szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW )) + { + if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))) + rc = ERROR_INVALID_PARAMETER; + else + rc = msi_set_last_used_source(szProduct, szUserSid, dwContext, + dwOptions, szValue); + } + else + rc = ERROR_UNKNOWN_PROPERTY; + + RegCloseKey(sourcekey); + return rc; +} + +/****************************************************************** + * MsiSourceListAddSourceW (MSI.@) + */ +UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName, + DWORD dwReserved, LPCWSTR szSource) +{ + WCHAR squished_pc[GUID_SIZE]; + INT ret; + LPWSTR sidstr = NULL; + DWORD sidsize = 0; + DWORD domsize = 0; + DWORD context; + HKEY hkey = 0; + UINT r; + + TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource)); + + if (!szSource || !*szSource) + return ERROR_INVALID_PARAMETER; + + if (dwReserved != 0) + return ERROR_INVALID_PARAMETER; + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!szUserName || !*szUserName) + context = MSIINSTALLCONTEXT_MACHINE; + else + { + if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL)) + { + PSID psid = msi_alloc(sidsize); + + if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL)) + ConvertSidToStringSidW(psid, &sidstr); + + msi_free(psid); + } + + r = MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERMANAGED, &hkey, FALSE); + if (r == ERROR_SUCCESS) + context = MSIINSTALLCONTEXT_USERMANAGED; + else + { + r = MSIREG_OpenProductKey(szProduct, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + &hkey, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + context = MSIINSTALLCONTEXT_USERUNMANAGED; + } + + RegCloseKey(hkey); + } + + ret = MsiSourceListAddSourceExW(szProduct, sidstr, + context, MSISOURCETYPE_NETWORK, szSource, 0); + + if (sidstr) + LocalFree(sidstr); + + return ret; +} + +/****************************************************************** + * MsiSourceListAddSourceA (MSI.@) + */ +UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName, + DWORD dwReserved, LPCSTR szSource) +{ + INT ret; + LPWSTR szwproduct; + LPWSTR szwusername; + LPWSTR szwsource; + + szwproduct = strdupAtoW( szProduct ); + szwusername = strdupAtoW( szUserName ); + szwsource = strdupAtoW( szSource ); + + ret = MsiSourceListAddSourceW(szwproduct, szwusername, dwReserved, szwsource); + + msi_free(szwproduct); + msi_free(szwusername); + msi_free(szwsource); + + return ret; +} + +/****************************************************************** + * MsiSourceListAddSourceExA (MSI.@) + */ +UINT WINAPI MsiSourceListAddSourceExA(LPCSTR szProduct, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCSTR szSource, DWORD dwIndex) +{ + UINT ret; + LPWSTR product, usersid, source; + + product = strdupAtoW(szProduct); + usersid = strdupAtoW(szUserSid); + source = strdupAtoW(szSource); + + ret = MsiSourceListAddSourceExW(product, usersid, dwContext, + dwOptions, source, dwIndex); + + msi_free(product); + msi_free(usersid); + msi_free(source); + + return ret; +} + +static void free_source_list(struct list *sourcelist) +{ + while (!list_empty(sourcelist)) + { + media_info *info = LIST_ENTRY(list_head(sourcelist), media_info, entry); + list_remove(&info->entry); + msi_free(info->path); + msi_free(info); + } +} + +static void add_source_to_list(struct list *sourcelist, media_info *info, + DWORD *index) +{ + media_info *iter; + BOOL found = FALSE; + static const WCHAR fmt[] = {'%','i',0}; + + if (index) *index = 0; + + if (list_empty(sourcelist)) + { + list_add_head(sourcelist, &info->entry); + return; + } + + LIST_FOR_EACH_ENTRY(iter, sourcelist, media_info, entry) + { + if (!found && info->index < iter->index) + { + found = TRUE; + list_add_before(&iter->entry, &info->entry); + } + + /* update the rest of the list */ + if (found) + sprintfW(iter->szIndex, fmt, ++iter->index); + else if (index) + (*index)++; + } + + if (!found) + list_add_after(&iter->entry, &info->entry); +} + +static UINT fill_source_list(struct list *sourcelist, HKEY sourcekey, DWORD *count) +{ + UINT r = ERROR_SUCCESS; + DWORD index = 0; + WCHAR name[10]; + DWORD size, val_size; + media_info *entry; + + *count = 0; + + while (r == ERROR_SUCCESS) + { + size = sizeof(name) / sizeof(name[0]); + r = RegEnumValueW(sourcekey, index, name, &size, NULL, NULL, NULL, &val_size); + if (r != ERROR_SUCCESS) + return r; + + entry = msi_alloc(sizeof(media_info)); + if (!entry) + goto error; + + entry->path = msi_alloc(val_size); + if (!entry->path) + { + msi_free(entry); + goto error; + } + + lstrcpyW(entry->szIndex, name); + entry->index = atoiW(name); + + size++; + r = RegEnumValueW(sourcekey, index, name, &size, NULL, + NULL, (LPBYTE)entry->path, &val_size); + if (r != ERROR_SUCCESS) + { + msi_free(entry->path); + msi_free(entry); + goto error; + } + + index = ++(*count); + add_source_to_list(sourcelist, entry, NULL); + } + +error: + *count = -1; + free_source_list(sourcelist); + return ERROR_OUTOFMEMORY; +} + +/****************************************************************** + * MsiSourceListAddSourceExW (MSI.@) + */ +UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, + DWORD dwIndex) +{ + HKEY sourcekey; + HKEY typekey; + UINT rc; + struct list sourcelist; + media_info *info; + WCHAR squished_pc[GUID_SIZE]; + WCHAR name[10]; + LPWSTR source; + LPCWSTR postfix; + DWORD size, count; + DWORD index; + + static const WCHAR fmt[] = {'%','i',0}; + + TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid), + dwContext, dwOptions, debugstr_w(szSource), dwIndex); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (!szSource || !*szSource) + return ERROR_INVALID_PARAMETER; + + if (!(dwOptions & (MSISOURCETYPE_NETWORK | MSISOURCETYPE_URL))) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + if (szUserSid && (dwContext & MSIINSTALLCONTEXT_MACHINE)) + return ERROR_INVALID_PARAMETER; + + rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE); + if (rc != ERROR_SUCCESS) + return rc; + + if (dwOptions & MSISOURCETYPE_NETWORK) + rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE); + else if (dwOptions & MSISOURCETYPE_URL) + rc = OpenURLSubkey(sourcekey, &typekey, TRUE); + else if (dwOptions & MSISOURCETYPE_MEDIA) + rc = OpenMediaSubkey(sourcekey, &typekey, TRUE); + else + { + ERR("unknown media type: %08x\n", dwOptions); + RegCloseKey(sourcekey); + return ERROR_FUNCTION_FAILED; + } + if (rc != ERROR_SUCCESS) + { + ERR("can't open subkey %u\n", rc); + RegCloseKey(sourcekey); + return rc; + } + + postfix = (dwOptions & MSISOURCETYPE_NETWORK) ? szBackSlash : szForwardSlash; + if (szSource[lstrlenW(szSource) - 1] == *postfix) + source = strdupW(szSource); + else + { + size = lstrlenW(szSource) + 2; + source = msi_alloc(size * sizeof(WCHAR)); + lstrcpyW(source, szSource); + lstrcatW(source, postfix); + } + + list_init(&sourcelist); + rc = fill_source_list(&sourcelist, typekey, &count); + if (rc != ERROR_NO_MORE_ITEMS) + return rc; + + size = (lstrlenW(source) + 1) * sizeof(WCHAR); + + if (count == 0) + { + rc = RegSetValueExW(typekey, szOne, 0, REG_EXPAND_SZ, (LPBYTE)source, size); + goto done; + } + else if (dwIndex > count || dwIndex == 0) + { + sprintfW(name, fmt, count + 1); + rc = RegSetValueExW(typekey, name, 0, REG_EXPAND_SZ, (LPBYTE)source, size); + goto done; + } + else + { + sprintfW(name, fmt, dwIndex); + info = msi_alloc(sizeof(media_info)); + if (!info) + { + rc = ERROR_OUTOFMEMORY; + goto done; + } + + info->path = strdupW(source); + lstrcpyW(info->szIndex, name); + info->index = dwIndex; + add_source_to_list(&sourcelist, info, &index); + + LIST_FOR_EACH_ENTRY(info, &sourcelist, media_info, entry) + { + if (info->index < index) + continue; + + size = (lstrlenW(info->path) + 1) * sizeof(WCHAR); + rc = RegSetValueExW(typekey, info->szIndex, 0, + REG_EXPAND_SZ, (LPBYTE)info->path, size); + if (rc != ERROR_SUCCESS) + goto done; + } + } + +done: + free_source_list(&sourcelist); + msi_free(source); + RegCloseKey(typekey); + RegCloseKey(sourcekey); + return rc; +} + +/****************************************************************** + * MsiSourceListAddMediaDiskA (MSI.@) + */ +UINT WINAPI MsiSourceListAddMediaDiskA(LPCSTR szProduct, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, + LPCSTR szVolumeLabel, LPCSTR szDiskPrompt) +{ + UINT r; + LPWSTR product = NULL; + LPWSTR usersid = NULL; + LPWSTR volume = NULL; + LPWSTR prompt = NULL; + + if (szProduct) product = strdupAtoW(szProduct); + if (szUserSid) usersid = strdupAtoW(szUserSid); + if (szVolumeLabel) volume = strdupAtoW(szVolumeLabel); + if (szDiskPrompt) prompt = strdupAtoW(szDiskPrompt); + + r = MsiSourceListAddMediaDiskW(product, usersid, dwContext, dwOptions, + dwDiskId, volume, prompt); + + msi_free(product); + msi_free(usersid); + msi_free(volume); + msi_free(prompt); + + return r; +} + +/****************************************************************** + * MsiSourceListAddMediaDiskW (MSI.@) + */ +UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, + LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt) +{ + HKEY sourcekey; + HKEY mediakey; + UINT rc; + WCHAR szIndex[10]; + WCHAR squished_pc[GUID_SIZE]; + LPWSTR buffer; + DWORD size; + + static const WCHAR fmt[] = {'%','i',0}; + + TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct), + debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId, + debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt)); + + if (!szProduct || !squash_guid(szProduct, squished_pc)) + return ERROR_INVALID_PARAMETER; + + if (dwOptions != MSICODE_PRODUCT && dwOptions != MSICODE_PATCH) + return ERROR_INVALID_PARAMETER; + + if ((szVolumeLabel && !*szVolumeLabel) || (szDiskPrompt && !*szDiskPrompt)) + return ERROR_INVALID_PARAMETER; + + if ((dwContext & MSIINSTALLCONTEXT_MACHINE) && szUserSid) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, dwContext, FALSE); + if (rc != ERROR_SUCCESS) + return rc; + + OpenMediaSubkey(sourcekey, &mediakey, TRUE); + + sprintfW(szIndex, fmt, dwDiskId); + + size = 2; + if (szVolumeLabel) size += lstrlenW(szVolumeLabel); + if (szDiskPrompt) size += lstrlenW(szDiskPrompt); + + size *= sizeof(WCHAR); + buffer = msi_alloc(size); + *buffer = '\0'; + + if (szVolumeLabel) lstrcpyW(buffer, szVolumeLabel); + lstrcatW(buffer, szSemiColon); + if (szDiskPrompt) lstrcatW(buffer, szDiskPrompt); + + RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size); + msi_free(buffer); + + RegCloseKey(sourcekey); + RegCloseKey(mediakey); + + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearAllA (MSI.@) + */ +UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved ) +{ + FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved); + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearAllW (MSI.@) + */ +UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved ) +{ + FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved); + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearAllExA (MSI.@) + */ +UINT WINAPI MsiSourceListClearAllExA( LPCSTR szProduct, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions ) +{ + FIXME("(%s %s %d %08x)\n", debugstr_a(szProduct), debugstr_a(szUserSid), + dwContext, dwOptions); + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearAllExW (MSI.@) + */ +UINT WINAPI MsiSourceListClearAllExW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions ) +{ + FIXME("(%s %s %d %08x)\n", debugstr_w(szProduct), debugstr_w(szUserSid), + dwContext, dwOptions); + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearSourceA (MSI.@) + */ +UINT WINAPI MsiSourceListClearSourceA(LPCSTR szProductCodeOrPatchCode, LPCSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCSTR szSource) +{ + FIXME("(%s %s %x %x %s)\n", debugstr_a(szProductCodeOrPatchCode), debugstr_a(szUserSid), + dwContext, dwOptions, debugstr_a(szSource)); + return ERROR_SUCCESS; +} + +/****************************************************************** + * MsiSourceListClearSourceW (MSI.@) + */ +UINT WINAPI MsiSourceListClearSourceW(LPCWSTR szProductCodeOrPatchCode, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCWSTR szSource) +{ + FIXME("(%s %s %x %x %s)\n", debugstr_w(szProductCodeOrPatchCode), debugstr_w(szUserSid), + dwContext, dwOptions, debugstr_w(szSource)); + return ERROR_SUCCESS; +} diff --git a/libmsi/sql.y b/libmsi/sql.y new file mode 100644 index 0000000..ead7743 --- /dev/null +++ b/libmsi/sql.y @@ -0,0 +1,1031 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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 "config.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "windef.h" +#include "winbase.h" +#include "query.h" +#include "wine/list.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +static int sql_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_SQL_input +{ + MSIDATABASE *db; + LPCWSTR command; + DWORD n, len; + UINT r; + MSIVIEW **view; /* View structure for the resulting query. This value + * tracks the view currently being created so we can free + * this view on syntax error. + */ + struct list *mem; +} SQL_input; + +static UINT SQL_getstring( void *info, const struct sql_str *strdata, LPWSTR *str ); +static INT SQL_getint( void *info ); +static int sql_lex( void *SQL_lval, SQL_input *info ); + +static LPWSTR parser_add_table( void *info, LPCWSTR list, LPCWSTR table ); +static void *parser_alloc( void *info, unsigned int sz ); +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ); + +static BOOL SQL_MarkPrimaryKeys( column_info **cols, column_info *keys); + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ); +static struct expr * EXPR_unary( void *info, struct expr *l, UINT op ); +static struct expr * EXPR_column( void *info, const column_info *column ); +static struct expr * EXPR_ival( void *info, int val ); +static struct expr * EXPR_sval( void *info, const struct sql_str *str ); +static struct expr * EXPR_wildcard( void *info ); + +#define PARSER_BUBBLE_UP_VIEW( sql, result, current_view ) \ + *sql->view = current_view; \ + result = current_view + +%} + +%pure-parser + +%union +{ + struct sql_str str; + LPWSTR string; + column_info *column_list; + MSIVIEW *query; + struct expr *expr; + USHORT column_type; + int integer; +} + +%token TK_ALTER TK_AND TK_BY TK_CHAR TK_COMMA TK_CREATE TK_DELETE TK_DROP +%token TK_DISTINCT TK_DOT TK_EQ TK_FREE TK_FROM TK_GE TK_GT TK_HOLD TK_ADD +%token <str> TK_ID +%token TK_ILLEGAL TK_INSERT TK_INT +%token <str> TK_INTEGER +%token TK_INTO TK_IS TK_KEY TK_LE TK_LONG TK_LONGCHAR TK_LP TK_LT +%token TK_LOCALIZABLE TK_MINUS TK_NE TK_NOT TK_NULL +%token TK_OBJECT TK_OR TK_ORDER TK_PRIMARY TK_RP +%token TK_SELECT TK_SET TK_SHORT TK_SPACE TK_STAR +%token <str> TK_STRING +%token TK_TABLE TK_TEMPORARY TK_UPDATE TK_VALUES TK_WHERE TK_WILDCARD + +/* + * These are extra tokens used by the lexer but never seen by the + * parser. We put them in a rule so that the parser generator will + * add them to the parse.h output file. + * + */ +%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION + COLUMN AGG_FUNCTION. + +%type <string> table tablelist id string +%type <column_list> selcollist collist selcolumn column column_and_type column_def table_def +%type <column_list> column_assignment update_assign_list constlist +%type <query> query from selectfrom unorderdfrom +%type <query> oneupdate onedelete oneselect onequery onecreate oneinsert onealter onedrop +%type <expr> expr val column_val const_val +%type <column_type> column_type data_type data_type_l data_count +%type <integer> number alterop + +%left TK_OR +%left TK_AND +%left TK_NOT +%left TK_EQ TK_NE TK_LT TK_GT TK_LE TK_GE TK_LIKE +%right TK_NEGATION + +%% + +query: + onequery + { + SQL_input* sql = (SQL_input*) info; + *sql->view = $1; + } + ; + +onequery: + oneselect + | onecreate + | oneinsert + | oneupdate + | onedelete + | onealter + | onedrop + ; + +oneinsert: + TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); + if( !insert ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, insert ); + } + | TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); + if( !insert ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, insert ); + } + ; + +onecreate: + TK_CREATE TK_TABLE table TK_LP table_def TK_RP + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + UINT r; + + if( !$5 ) + YYABORT; + r = CREATE_CreateView( sql->db, &create, $3, $5, FALSE ); + if( !create ) + { + sql->r = r; + YYABORT; + } + + PARSER_BUBBLE_UP_VIEW( sql, $$, create ); + } + | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !$5 ) + YYABORT; + CREATE_CreateView( sql->db, &create, $3, $5, TRUE ); + if( !create ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, create ); + } + ; + +oneupdate: + TK_UPDATE table TK_SET update_assign_list TK_WHERE expr + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *update = NULL; + + UPDATE_CreateView( sql->db, &update, $2, $4, $6 ); + if( !update ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, update ); + } + | TK_UPDATE table TK_SET update_assign_list + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *update = NULL; + + UPDATE_CreateView( sql->db, &update, $2, $4, NULL ); + if( !update ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, update ); + } + ; + +onedelete: + TK_DELETE from + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *delete = NULL; + + DELETE_CreateView( sql->db, &delete, $2 ); + if( !delete ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, delete ); + } + ; + +onealter: + TK_ALTER TK_TABLE table alterop + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *alter = NULL; + + ALTER_CreateView( sql->db, &alter, $3, NULL, $4 ); + if( !alter ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); + } + | TK_ALTER TK_TABLE table TK_ADD column_and_type + { + SQL_input *sql = (SQL_input *)info; + MSIVIEW *alter = NULL; + + ALTER_CreateView( sql->db, &alter, $3, $5, 0 ); + if (!alter) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); + } + | TK_ALTER TK_TABLE table TK_ADD column_and_type TK_HOLD + { + SQL_input *sql = (SQL_input *)info; + MSIVIEW *alter = NULL; + + ALTER_CreateView( sql->db, &alter, $3, $5, 1 ); + if (!alter) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); + } + ; + +alterop: + TK_HOLD + { + $$ = 1; + } + | TK_FREE + { + $$ = -1; + } + ; + +onedrop: + TK_DROP TK_TABLE table + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* drop = NULL; + UINT r; + + r = DROP_CreateView( sql->db, &drop, $3 ); + if( r != ERROR_SUCCESS || !$$ ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, drop ); + } + ; + +table_def: + column_def TK_PRIMARY TK_KEY collist + { + if( SQL_MarkPrimaryKeys( &$1, $4 ) ) + $$ = $1; + else + $$ = NULL; + } + ; + +column_def: + column_def TK_COMMA column_and_type + { + column_info *ci; + + for( ci = $1; ci->next; ci = ci->next ) + ; + + ci->next = $3; + $$ = $1; + } + | column_and_type + { + $$ = $1; + } + ; + +column_and_type: + column column_type + { + $$ = $1; + $$->type = ($2 | MSITYPE_VALID); + $$->temporary = $2 & MSITYPE_TEMPORARY ? TRUE : FALSE; + } + ; + +column_type: + data_type_l + { + $$ = $1; + } + | data_type_l TK_LOCALIZABLE + { + $$ = $1 | MSITYPE_LOCALIZABLE; + } + | data_type_l TK_TEMPORARY + { + $$ = $1 | MSITYPE_TEMPORARY; + } + ; + +data_type_l: + data_type + { + $$ |= MSITYPE_NULLABLE; + } + | data_type TK_NOT TK_NULL + { + $$ = $1; + } + ; + +data_type: + TK_CHAR + { + $$ = MSITYPE_STRING | 1; + } + | TK_CHAR TK_LP data_count TK_RP + { + $$ = MSITYPE_STRING | 0x400 | $3; + } + | TK_LONGCHAR + { + $$ = MSITYPE_STRING | 0x400; + } + | TK_SHORT + { + $$ = 2 | 0x400; + } + | TK_INT + { + $$ = 2 | 0x400; + } + | TK_LONG + { + $$ = 4; + } + | TK_OBJECT + { + $$ = MSITYPE_STRING | MSITYPE_VALID; + } + ; + +data_count: + number + { + if( ( $1 > 255 ) || ( $1 < 0 ) ) + YYABORT; + $$ = $1; + } + ; + +oneselect: + TK_SELECT selectfrom + { + $$ = $2; + } + | TK_SELECT TK_DISTINCT selectfrom + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* distinct = NULL; + UINT r; + + r = DISTINCT_CreateView( sql->db, &distinct, $3 ); + if (r != ERROR_SUCCESS) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, distinct ); + } + ; + +selectfrom: + selcollist from + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* select = NULL; + UINT r; + + if( $1 ) + { + r = SELECT_CreateView( sql->db, &select, $2, $1 ); + if (r != ERROR_SUCCESS) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, select ); + } + else + $$ = $2; + } + ; + +selcollist: + selcolumn + | selcolumn TK_COMMA selcollist + { + $1->next = $3; + } + | TK_STAR + { + $$ = NULL; + } + ; + +collist: + column + | column TK_COMMA collist + { + $1->next = $3; + } + | TK_STAR + { + $$ = NULL; + } + ; + +from: + TK_FROM table + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* table = NULL; + UINT r; + + r = TABLE_CreateView( sql->db, $2, &table ); + if( r != ERROR_SUCCESS || !$$ ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, table ); + } + | unorderdfrom TK_ORDER TK_BY collist + { + UINT r; + + if( $4 ) + { + r = $1->ops->sort( $1, $4 ); + if ( r != ERROR_SUCCESS) + YYABORT; + } + + $$ = $1; + } + | unorderdfrom + ; + +unorderdfrom: + TK_FROM tablelist + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* where = NULL; + UINT r; + + r = WHERE_CreateView( sql->db, &where, $2, NULL ); + if( r != ERROR_SUCCESS ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, where ); + } + | TK_FROM tablelist TK_WHERE expr + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW* where = NULL; + UINT r; + + r = WHERE_CreateView( sql->db, &where, $2, $4 ); + if( r != ERROR_SUCCESS ) + YYABORT; + + PARSER_BUBBLE_UP_VIEW( sql, $$, where ); + } + ; + +tablelist: + table + { + $$ = $1; + } + | table TK_COMMA tablelist + { + $$ = parser_add_table( info, $3, $1 ); + if (!$$) + YYABORT; + } + ; + +expr: + TK_LP expr TK_RP + { + $$ = $2; + if( !$$ ) + YYABORT; + } + | expr TK_AND expr + { + $$ = EXPR_complex( info, $1, OP_AND, $3 ); + if( !$$ ) + YYABORT; + } + | expr TK_OR expr + { + $$ = EXPR_complex( info, $1, OP_OR, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_EQ val + { + $$ = EXPR_complex( info, $1, OP_EQ, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_GT val + { + $$ = EXPR_complex( info, $1, OP_GT, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_LT val + { + $$ = EXPR_complex( info, $1, OP_LT, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_LE val + { + $$ = EXPR_complex( info, $1, OP_LE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_GE val + { + $$ = EXPR_complex( info, $1, OP_GE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_NE val + { + $$ = EXPR_complex( info, $1, OP_NE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_IS TK_NULL + { + $$ = EXPR_unary( info, $1, OP_ISNULL ); + if( !$$ ) + YYABORT; + } + | column_val TK_IS TK_NOT TK_NULL + { + $$ = EXPR_unary( info, $1, OP_NOTNULL ); + if( !$$ ) + YYABORT; + } + ; + +val: + column_val + | const_val + ; + +constlist: + const_val + { + $$ = parser_alloc_column( info, NULL, NULL ); + if( !$$ ) + YYABORT; + $$->val = $1; + } + | const_val TK_COMMA constlist + { + $$ = parser_alloc_column( info, NULL, NULL ); + if( !$$ ) + YYABORT; + $$->val = $1; + $$->next = $3; + } + ; + +update_assign_list: + column_assignment + | column_assignment TK_COMMA update_assign_list + { + $$ = $1; + $$->next = $3; + } + ; + +column_assignment: + column TK_EQ const_val + { + $$ = $1; + $$->val = $3; + } + ; + +const_val: + number + { + $$ = EXPR_ival( info, $1 ); + if( !$$ ) + YYABORT; + } + | TK_MINUS number %prec TK_NEGATION + { + $$ = EXPR_ival( info, -$2 ); + if( !$$ ) + YYABORT; + } + | TK_STRING + { + $$ = EXPR_sval( info, &$1 ); + if( !$$ ) + YYABORT; + } + | TK_WILDCARD + { + $$ = EXPR_wildcard( info ); + if( !$$ ) + YYABORT; + } + ; + +column_val: + column + { + $$ = EXPR_column( info, $1 ); + if( !$$ ) + YYABORT; + } + ; + +column: + table TK_DOT id + { + $$ = parser_alloc_column( info, $1, $3 ); + if( !$$ ) + YYABORT; + } + | id + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + ; + +selcolumn: + table TK_DOT id + { + $$ = parser_alloc_column( info, $1, $3 ); + if( !$$ ) + YYABORT; + } + | id + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + | string + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + ; + +table: + id + { + $$ = $1; + } + ; + +id: + TK_ID + { + if ( SQL_getstring( info, &$1, &$$ ) != ERROR_SUCCESS || !$$ ) + YYABORT; + } + ; + +string: + TK_STRING + { + if ( SQL_getstring( info, &$1, &$$ ) != ERROR_SUCCESS || !$$ ) + YYABORT; + } + ; + +number: + TK_INTEGER + { + $$ = SQL_getint( info ); + } + ; + +%% + +static LPWSTR parser_add_table( void *info, LPCWSTR list, LPCWSTR table ) +{ + static const WCHAR space[] = {' ',0}; + DWORD len = strlenW( list ) + strlenW( table ) + 2; + LPWSTR ret; + + ret = parser_alloc( info, len * sizeof(WCHAR) ); + if( ret ) + { + strcpyW( ret, list ); + strcatW( ret, space ); + strcatW( ret, table ); + } + return ret; +} + +static void *parser_alloc( void *info, unsigned int sz ) +{ + SQL_input* sql = (SQL_input*) info; + struct list *mem; + + mem = msi_alloc( sizeof (struct list) + sz ); + list_add_tail( sql->mem, mem ); + return &mem[1]; +} + +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ) +{ + column_info *col; + + col = parser_alloc( info, sizeof (*col) ); + if( col ) + { + col->table = table; + col->column = column; + col->val = NULL; + col->type = 0; + col->next = NULL; + } + + return col; +} + +static int sql_lex( void *SQL_lval, SQL_input *sql ) +{ + int token, skip; + struct sql_str * str = SQL_lval; + + do + { + sql->n += sql->len; + if( ! sql->command[sql->n] ) + return 0; /* end of input */ + + /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */ + sql->len = sqliteGetToken( &sql->command[sql->n], &token, &skip ); + if( sql->len==0 ) + break; + str->data = &sql->command[sql->n]; + str->len = sql->len; + sql->n += skip; + } + while( token == TK_SPACE ); + + /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */ + + return token; +} + +UINT SQL_getstring( void *info, const struct sql_str *strdata, LPWSTR *str ) +{ + LPCWSTR p = strdata->data; + UINT len = strdata->len; + + /* match quotes */ + if( ( (p[0]=='`') && (p[len-1]!='`') ) || + ( (p[0]=='\'') && (p[len-1]!='\'') ) ) + return ERROR_FUNCTION_FAILED; + + /* if there's quotes, remove them */ + if( ( (p[0]=='`') && (p[len-1]=='`') ) || + ( (p[0]=='\'') && (p[len-1]=='\'') ) ) + { + p++; + len -= 2; + } + *str = parser_alloc( info, (len + 1)*sizeof(WCHAR) ); + if( !*str ) + return ERROR_OUTOFMEMORY; + memcpy( *str, p, len*sizeof(WCHAR) ); + (*str)[len]=0; + + return ERROR_SUCCESS; +} + +INT SQL_getint( void *info ) +{ + SQL_input* sql = (SQL_input*) info; + LPCWSTR p = &sql->command[sql->n]; + INT i, r = 0; + + for( i=0; i<sql->len; i++ ) + { + if( '0' > p[i] || '9' < p[i] ) + { + ERR("should only be numbers here!\n"); + break; + } + r = (p[i]-'0') + r*10; + } + + return r; +} + +static int sql_error( const char *str ) +{ + return 0; +} + +static struct expr * EXPR_wildcard( void *info ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_WILDCARD; + } + return e; +} + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COMPLEX; + e->u.expr.left = l; + e->u.expr.op = op; + e->u.expr.right = r; + } + return e; +} + +static struct expr * EXPR_unary( void *info, struct expr *l, UINT op ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_UNARY; + e->u.expr.left = l; + e->u.expr.op = op; + e->u.expr.right = NULL; + } + return e; +} + +static struct expr * EXPR_column( void *info, const column_info *column ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COLUMN; + e->u.column.unparsed.column = column->column; + e->u.column.unparsed.table = column->table; + } + return e; +} + +static struct expr * EXPR_ival( void *info, int val ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_IVAL; + e->u.ival = val; + } + return e; +} + +static struct expr * EXPR_sval( void *info, const struct sql_str *str ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_SVAL; + if( SQL_getstring( info, str, (LPWSTR *)&e->u.sval ) != ERROR_SUCCESS ) + return NULL; /* e will be freed by query destructor */ + } + return e; +} + +static void swap_columns( column_info **cols, column_info *A, int idx ) +{ + column_info *preA = NULL, *preB = NULL, *B, *ptr; + int i = 0; + + B = NULL; + ptr = *cols; + while( ptr ) + { + if( i++ == idx ) + B = ptr; + else if( !B ) + preB = ptr; + + if( ptr->next == A ) + preA = ptr; + + ptr = ptr->next; + } + + if( preB ) preB->next = A; + if( preA ) preA->next = B; + ptr = A->next; + A->next = B->next; + B->next = ptr; + if( idx == 0 ) + *cols = A; +} + +static BOOL SQL_MarkPrimaryKeys( column_info **cols, + column_info *keys ) +{ + column_info *k; + BOOL found = TRUE; + int count; + + for( k = keys, count = 0; k && found; k = k->next, count++ ) + { + column_info *c; + int idx; + + found = FALSE; + for( c = *cols, idx = 0; c && !found; c = c->next, idx++ ) + { + if( strcmpW( k->column, c->column ) ) + continue; + c->type |= MSITYPE_KEY; + found = TRUE; + if (idx != count) + swap_columns( cols, c, count ); + } + } + + return found; +} + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, + struct list *mem ) +{ + SQL_input sql; + int r; + + *phview = NULL; + + sql.db = db; + sql.command = command; + sql.n = 0; + sql.len = 0; + sql.r = ERROR_BAD_QUERY_SYNTAX; + sql.view = phview; + sql.mem = mem; + + r = sql_parse(&sql); + + TRACE("Parse returned %d\n", r); + if( r ) + { + if (*sql.view) + { + (*sql.view)->ops->delete(*sql.view); + *sql.view = NULL; + } + return sql.r; + } + + return ERROR_SUCCESS; +} diff --git a/libmsi/storages.c b/libmsi/storages.c new file mode 100644 index 0000000..c980df8 --- /dev/null +++ b/libmsi/storages.c @@ -0,0 +1,553 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2008 James Hawkins + * + * 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 "winuser.h" +#include "winerror.h" +#include "ole2.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "msipriv.h" +#include "query.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define NUM_STORAGES_COLS 2 +#define MAX_STORAGES_NAME_LEN 62 + +typedef struct tabSTORAGE +{ + UINT str_index; + IStorage *storage; +} STORAGE; + +typedef struct tagMSISTORAGESVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + STORAGE **storages; + UINT max_storages; + UINT num_rows; + UINT row_size; +} MSISTORAGESVIEW; + +static BOOL storages_set_table_size(MSISTORAGESVIEW *sv, UINT size) +{ + if (size >= sv->max_storages) + { + sv->max_storages *= 2; + sv->storages = msi_realloc(sv->storages, sv->max_storages * sizeof(STORAGE *)); + if (!sv->storages) + return FALSE; + } + + return TRUE; +} + +static STORAGE *create_storage(MSISTORAGESVIEW *sv, LPCWSTR name, IStorage *stg) +{ + STORAGE *storage; + + storage = msi_alloc(sizeof(STORAGE)); + if (!storage) + return NULL; + + storage->str_index = msi_addstringW(sv->db->strings, name, -1, 1, StringNonPersistent); + storage->storage = stg; + + if (storage->storage) + IStorage_AddRef(storage->storage); + + return storage; +} + +static UINT STORAGES_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, val); + + if (col != 1) + return ERROR_INVALID_PARAMETER; + + if (row >= sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + *val = sv->storages[row]->str_index; + + return ERROR_SUCCESS; +} + +static UINT STORAGES_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, stm); + + if (row >= sv->num_rows) + return ERROR_FUNCTION_FAILED; + + return ERROR_INVALID_DATA; +} + +static UINT STORAGES_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + + FIXME("%p %d %p\n", sv, row, rec); + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +static HRESULT stream_to_storage(IStream *stm, IStorage **stg) +{ + ILockBytes *lockbytes = NULL; + STATSTG stat; + LPVOID data; + HRESULT hr; + DWORD size, read; + ULARGE_INTEGER offset; + + hr = IStream_Stat(stm, &stat, STATFLAG_NONAME); + if (FAILED(hr)) + return hr; + + if (stat.cbSize.QuadPart >> 32) + { + ERR("Storage is too large\n"); + return E_FAIL; + } + + size = stat.cbSize.QuadPart; + data = msi_alloc(size); + if (!data) + return E_OUTOFMEMORY; + + hr = IStream_Read(stm, data, size, &read); + if (FAILED(hr) || read != size) + goto done; + + hr = CreateILockBytesOnHGlobal(NULL, TRUE, &lockbytes); + if (FAILED(hr)) + goto done; + + ZeroMemory(&offset, sizeof(ULARGE_INTEGER)); + hr = ILockBytes_WriteAt(lockbytes, offset, data, size, &read); + if (FAILED(hr) || read != size) + goto done; + + hr = StgOpenStorageOnILockBytes(lockbytes, NULL, + STGM_READWRITE | STGM_SHARE_DENY_NONE, + NULL, 0, stg); + if (FAILED(hr)) + goto done; + +done: + msi_free(data); + if (lockbytes) ILockBytes_Release(lockbytes); + return hr; +} + +static UINT STORAGES_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + IStorage *stg, *substg = NULL; + IStream *stm; + LPWSTR name = NULL; + HRESULT hr; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("(%p, %p)\n", view, rec); + + if (row > sv->num_rows) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordGetIStream(rec, 2, &stm); + if (r != ERROR_SUCCESS) + return r; + + r = stream_to_storage(stm, &stg); + if (r != ERROR_SUCCESS) + { + IStream_Release(stm); + return r; + } + + name = strdupW(MSI_RecordGetString(rec, 1)); + if (!name) + { + r = ERROR_OUTOFMEMORY; + goto done; + } + + hr = IStorage_CreateStorage(sv->db->storage, name, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &substg); + if (FAILED(hr)) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg); + if (FAILED(hr)) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + sv->storages[row] = create_storage(sv, name, stg); + if (!sv->storages[row]) + r = ERROR_FUNCTION_FAILED; + +done: + msi_free(name); + + if (substg) IStorage_Release(substg); + IStorage_Release(stg); + IStream_Release(stm); + + return r; +} + +static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + + if (!storages_set_table_size(sv, ++sv->num_rows)) + return ERROR_FUNCTION_FAILED; + + if (row == -1) + row = sv->num_rows - 1; + + /* FIXME have to readjust rows */ + + return STORAGES_set_row(view, row, rec, 0); +} + +static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row) +{ + FIXME("(%p %d): stub!\n", view, row); + return ERROR_SUCCESS; +} + +static UINT STORAGES_execute(struct tagMSIVIEW *view, MSIRECORD *record) +{ + TRACE("(%p, %p)\n", view, record); + return ERROR_SUCCESS; +} + +static UINT STORAGES_close(struct tagMSIVIEW *view) +{ + TRACE("(%p)\n", view); + return ERROR_SUCCESS; +} + +static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + + TRACE("(%p, %p, %p)\n", view, rows, cols); + + if (cols) *cols = NUM_STORAGES_COLS; + if (rows) *rows = sv->num_rows; + + return ERROR_SUCCESS; +} + +static UINT STORAGES_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, + table_name); + + if (n == 0 || n > NUM_STORAGES_COLS) + return ERROR_INVALID_PARAMETER; + + switch (n) + { + case 1: + if (name) *name = szName; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STORAGES_NAME_LEN; + break; + + case 2: + if (name) *name = szData; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE; + break; + } + if (table_name) *table_name = szStorages; + if (temporary) *temporary = FALSE; + return ERROR_SUCCESS; +} + +static UINT storages_find_row(MSISTORAGESVIEW *sv, MSIRECORD *rec, UINT *row) +{ + LPCWSTR str; + UINT r, i, id, data; + + str = MSI_RecordGetString(rec, 1); + r = msi_string2idW(sv->db->strings, str, &id); + if (r != ERROR_SUCCESS) + return r; + + for (i = 0; i < sv->num_rows; i++) + { + STORAGES_fetch_int(&sv->view, i, 1, &data); + + if (data == id) + { + *row = i; + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +static UINT storages_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + UINT r, row; + + r = storages_find_row(sv, rec, &row); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + return STORAGES_set_row(view, row, rec, 0); +} + +static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + UINT r, row; + + r = storages_find_row(sv, rec, &row); + if (r == ERROR_SUCCESS) + return storages_modify_update(view, rec); + + return STORAGES_insert_row(view, rec, -1, FALSE); +} + +static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) +{ + UINT r; + + TRACE("%p %d %p\n", view, eModifyMode, rec); + + switch (eModifyMode) + { + case MSIMODIFY_ASSIGN: + r = storages_modify_assign(view, rec); + break; + + case MSIMODIFY_INSERT: + r = STORAGES_insert_row(view, rec, -1, FALSE); + break; + + case MSIMODIFY_UPDATE: + r = storages_modify_update(view, rec); + break; + + case MSIMODIFY_VALIDATE_NEW: + case MSIMODIFY_INSERT_TEMPORARY: + case MSIMODIFY_REFRESH: + case MSIMODIFY_REPLACE: + case MSIMODIFY_MERGE: + case MSIMODIFY_DELETE: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_DELETE: + FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + r = ERROR_INVALID_DATA; + } + + return r; +} + +static UINT STORAGES_delete(struct tagMSIVIEW *view) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + UINT i; + + TRACE("(%p)\n", view); + + for (i = 0; i < sv->num_rows; i++) + { + if (sv->storages[i]->storage) + IStorage_Release(sv->storages[i]->storage); + msi_free(sv->storages[i]); + } + + msi_free(sv->storages); + sv->storages = NULL; + msi_free(sv); + + return ERROR_SUCCESS; +} + +static UINT STORAGES_find_matching_rows(struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle) +{ + MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; + UINT index = PtrToUlong(*handle); + + TRACE("(%d, %d): %d\n", *row, col, val); + + if (col == 0 || col > NUM_STORAGES_COLS) + return ERROR_INVALID_PARAMETER; + + while (index < sv->num_rows) + { + if (sv->storages[index]->str_index == val) + { + *row = index; + break; + } + + index++; + } + + *handle = UlongToPtr(++index); + if (index >= sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + return ERROR_SUCCESS; +} + +static const MSIVIEWOPS storages_ops = +{ + STORAGES_fetch_int, + STORAGES_fetch_stream, + STORAGES_get_row, + STORAGES_set_row, + STORAGES_insert_row, + STORAGES_delete_row, + STORAGES_execute, + STORAGES_close, + STORAGES_get_dimensions, + STORAGES_get_column_info, + STORAGES_modify, + STORAGES_delete, + STORAGES_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static INT add_storages_to_table(MSISTORAGESVIEW *sv) +{ + STORAGE *storage = NULL; + IEnumSTATSTG *stgenum = NULL; + STATSTG stat; + HRESULT hr; + UINT count = 0, size; + + hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum); + if (FAILED(hr)) + return -1; + + sv->max_storages = 1; + sv->storages = msi_alloc(sizeof(STORAGE *)); + if (!sv->storages) + return -1; + + while (TRUE) + { + size = 0; + hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size); + if (FAILED(hr) || !size) + break; + + if (stat.type != STGTY_STORAGE) + { + CoTaskMemFree(stat.pwcsName); + continue; + } + + TRACE("enumerated storage %s\n", debugstr_w(stat.pwcsName)); + + storage = create_storage(sv, stat.pwcsName, NULL); + if (!storage) + { + count = -1; + CoTaskMemFree(stat.pwcsName); + break; + } + + IStorage_OpenStorage(sv->db->storage, stat.pwcsName, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, + &storage->storage); + CoTaskMemFree(stat.pwcsName); + + if (!storages_set_table_size(sv, ++count)) + { + count = -1; + break; + } + + sv->storages[count - 1] = storage; + } + + IEnumSTATSTG_Release(stgenum); + return count; +} + +UINT STORAGES_CreateView(MSIDATABASE *db, MSIVIEW **view) +{ + MSISTORAGESVIEW *sv; + INT rows; + + TRACE("(%p, %p)\n", db, view); + + sv = msi_alloc_zero( sizeof(MSISTORAGESVIEW) ); + if (!sv) + return ERROR_FUNCTION_FAILED; + + sv->view.ops = &storages_ops; + sv->db = db; + + rows = add_storages_to_table(sv); + if (rows < 0) + { + msi_free( sv ); + return ERROR_FUNCTION_FAILED; + } + sv->num_rows = rows; + + *view = (MSIVIEW *)sv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/streams.c b/libmsi/streams.c new file mode 100644 index 0000000..71616e9 --- /dev/null +++ b/libmsi/streams.c @@ -0,0 +1,563 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2007 James Hawkins + * + * 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 "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "msipriv.h" +#include "query.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define NUM_STREAMS_COLS 2 + +typedef struct tabSTREAM +{ + UINT str_index; + IStream *stream; +} STREAM; + +typedef struct tagMSISTREAMSVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + STREAM **streams; + UINT max_streams; + UINT num_rows; + UINT row_size; +} MSISTREAMSVIEW; + +static BOOL streams_set_table_size(MSISTREAMSVIEW *sv, UINT size) +{ + if (size >= sv->max_streams) + { + sv->max_streams *= 2; + sv->streams = msi_realloc_zero(sv->streams, sv->max_streams * sizeof(STREAM *)); + if (!sv->streams) + return FALSE; + } + + return TRUE; +} + +static STREAM *create_stream(MSISTREAMSVIEW *sv, LPCWSTR name, BOOL encoded, IStream *stm) +{ + STREAM *stream; + WCHAR decoded[MAX_STREAM_NAME_LEN]; + + stream = msi_alloc(sizeof(STREAM)); + if (!stream) + return NULL; + + if (encoded) + { + decode_streamname(name, decoded); + TRACE("stream -> %s %s\n", debugstr_w(name), debugstr_w(decoded)); + name = decoded; + } + + stream->str_index = msi_addstringW(sv->db->strings, name, -1, 1, StringNonPersistent); + stream->stream = stm; + return stream; +} + +static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, val); + + if (col != 1) + return ERROR_INVALID_PARAMETER; + + if (row >= sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + *val = sv->streams[row]->str_index; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, stm); + + if (row >= sv->num_rows) + return ERROR_FUNCTION_FAILED; + + IStream_AddRef(sv->streams[row]->stream); + *stm = sv->streams[row]->stream; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("%p %d %p\n", sv, row, rec); + + return msi_view_get_row( sv->db, view, row, rec ); +} + +static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + STREAM *stream; + IStream *stm; + STATSTG stat; + LPWSTR encname = NULL, name = NULL; + USHORT *data = NULL; + HRESULT hr; + ULONG count; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask); + + if (row > sv->num_rows) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordGetIStream(rec, 2, &stm); + if (r != ERROR_SUCCESS) + return r; + + hr = IStream_Stat(stm, &stat, STATFLAG_NONAME); + if (FAILED(hr)) + { + WARN("failed to stat stream: %08x\n", hr); + goto done; + } + + if (stat.cbSize.QuadPart >> 32) + { + WARN("stream too large\n"); + goto done; + } + + data = msi_alloc(stat.cbSize.QuadPart); + if (!data) + goto done; + + hr = IStream_Read(stm, data, stat.cbSize.QuadPart, &count); + if (FAILED(hr) || count != stat.cbSize.QuadPart) + { + WARN("failed to read stream: %08x\n", hr); + goto done; + } + + name = strdupW(MSI_RecordGetString(rec, 1)); + if (!name) + { + WARN("failed to retrieve stream name\n"); + goto done; + } + + encname = encode_streamname(FALSE, name); + msi_destroy_stream(sv->db, encname); + + r = write_stream_data(sv->db->storage, name, data, count, FALSE); + if (r != ERROR_SUCCESS) + { + WARN("failed to write stream data: %d\n", r); + goto done; + } + + stream = create_stream(sv, name, FALSE, NULL); + if (!stream) + goto done; + + hr = IStorage_OpenStream(sv->db->storage, encname, 0, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream); + if (FAILED(hr)) + { + WARN("failed to open stream: %08x\n", hr); + goto done; + } + + sv->streams[row] = stream; + +done: + msi_free(name); + msi_free(data); + msi_free(encname); + + IStream_Release(stm); + + return r; +} + +static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT i; + + TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary); + + if (!streams_set_table_size(sv, ++sv->num_rows)) + return ERROR_FUNCTION_FAILED; + + if (row == -1) + row = sv->num_rows - 1; + + /* shift the rows to make room for the new row */ + for (i = sv->num_rows - 1; i > row; i--) + { + sv->streams[i] = sv->streams[i - 1]; + } + + return STREAMS_set_row(view, row, rec, 0); +} + +static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row) +{ + FIXME("(%p %d): stub!\n", view, row); + return ERROR_SUCCESS; +} + +static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record) +{ + TRACE("(%p, %p)\n", view, record); + return ERROR_SUCCESS; +} + +static UINT STREAMS_close(struct tagMSIVIEW *view) +{ + TRACE("(%p)\n", view); + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %p, %p)\n", view, rows, cols); + + if (cols) *cols = NUM_STREAMS_COLS; + if (rows) *rows = sv->num_rows; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, + table_name); + + if (n == 0 || n > NUM_STREAMS_COLS) + return ERROR_INVALID_PARAMETER; + + switch (n) + { + case 1: + if (name) *name = szName; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN; + break; + + case 2: + if (name) *name = szData; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE; + break; + } + if (table_name) *table_name = szStreams; + if (temporary) *temporary = FALSE; + return ERROR_SUCCESS; +} + +static UINT streams_find_row(MSISTREAMSVIEW *sv, MSIRECORD *rec, UINT *row) +{ + LPCWSTR str; + UINT r, i, id, data; + + str = MSI_RecordGetString(rec, 1); + r = msi_string2idW(sv->db->strings, str, &id); + if (r != ERROR_SUCCESS) + return r; + + for (i = 0; i < sv->num_rows; i++) + { + STREAMS_fetch_int(&sv->view, i, 1, &data); + + if (data == id) + { + *row = i; + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT r, row; + + r = streams_find_row(sv, rec, &row); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + return STREAMS_set_row(view, row, rec, 0); +} + +static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT r, row; + + r = streams_find_row(sv, rec, &row); + if (r == ERROR_SUCCESS) + return streams_modify_update(view, rec); + + return STREAMS_insert_row(view, rec, -1, FALSE); +} + +static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) +{ + UINT r; + + TRACE("%p %d %p\n", view, eModifyMode, rec); + + switch (eModifyMode) + { + case MSIMODIFY_ASSIGN: + r = streams_modify_assign(view, rec); + break; + + case MSIMODIFY_INSERT: + r = STREAMS_insert_row(view, rec, -1, FALSE); + break; + + case MSIMODIFY_UPDATE: + r = streams_modify_update(view, rec); + break; + + case MSIMODIFY_VALIDATE_NEW: + case MSIMODIFY_INSERT_TEMPORARY: + case MSIMODIFY_REFRESH: + case MSIMODIFY_REPLACE: + case MSIMODIFY_MERGE: + case MSIMODIFY_DELETE: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_DELETE: + FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + r = ERROR_INVALID_DATA; + } + + return r; +} + +static UINT STREAMS_delete(struct tagMSIVIEW *view) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT i; + + TRACE("(%p)\n", view); + + for (i = 0; i < sv->num_rows; i++) + { + if (sv->streams[i]) + { + if (sv->streams[i]->stream) + IStream_Release(sv->streams[i]->stream); + msi_free(sv->streams[i]); + } + } + + msi_free(sv->streams); + msi_free(sv); + + return ERROR_SUCCESS; +} + +static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT index = PtrToUlong(*handle); + + TRACE("(%p, %d, %d, %p, %p)\n", view, col, val, row, handle); + + if (col == 0 || col > NUM_STREAMS_COLS) + return ERROR_INVALID_PARAMETER; + + while (index < sv->num_rows) + { + if (sv->streams[index]->str_index == val) + { + *row = index; + break; + } + + index++; + } + + *handle = UlongToPtr(++index); + + if (index > sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + return ERROR_SUCCESS; +} + +static const MSIVIEWOPS streams_ops = +{ + STREAMS_fetch_int, + STREAMS_fetch_stream, + STREAMS_get_row, + STREAMS_set_row, + STREAMS_insert_row, + STREAMS_delete_row, + STREAMS_execute, + STREAMS_close, + STREAMS_get_dimensions, + STREAMS_get_column_info, + STREAMS_modify, + STREAMS_delete, + STREAMS_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static INT add_streams_to_table(MSISTREAMSVIEW *sv) +{ + IEnumSTATSTG *stgenum = NULL; + STATSTG stat; + STREAM *stream = NULL; + HRESULT hr; + UINT r, count = 0, size; + LPWSTR encname; + + hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum); + if (FAILED(hr)) + return -1; + + sv->max_streams = 1; + sv->streams = msi_alloc_zero(sizeof(STREAM *)); + if (!sv->streams) + return -1; + + while (TRUE) + { + size = 0; + hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size); + if (FAILED(hr) || !size) + break; + + if (stat.type != STGTY_STREAM) + { + CoTaskMemFree(stat.pwcsName); + continue; + } + + /* table streams are not in the _Streams table */ + if (*stat.pwcsName == 0x4840) + { + CoTaskMemFree(stat.pwcsName); + continue; + } + + stream = create_stream(sv, stat.pwcsName, TRUE, NULL); + if (!stream) + { + count = -1; + CoTaskMemFree(stat.pwcsName); + break; + } + + /* these streams appear to be unencoded */ + if (*stat.pwcsName == 0x0005) + { + r = msi_get_raw_stream(sv->db, stat.pwcsName, &stream->stream); + } + else + { + encname = encode_streamname(FALSE, stat.pwcsName); + r = msi_get_raw_stream(sv->db, encname, &stream->stream); + msi_free(encname); + } + CoTaskMemFree(stat.pwcsName); + + if (r != ERROR_SUCCESS) + { + WARN("unable to get stream %u\n", r); + count = -1; + break; + } + + if (!streams_set_table_size(sv, ++count)) + { + count = -1; + break; + } + + sv->streams[count - 1] = stream; + } + + IEnumSTATSTG_Release(stgenum); + return count; +} + +UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view) +{ + MSISTREAMSVIEW *sv; + INT rows; + + TRACE("(%p, %p)\n", db, view); + + sv = msi_alloc_zero( sizeof(MSISTREAMSVIEW) ); + if (!sv) + return ERROR_FUNCTION_FAILED; + + sv->view.ops = &streams_ops; + sv->db = db; + rows = add_streams_to_table(sv); + if (rows < 0) + { + msi_free( sv ); + return ERROR_FUNCTION_FAILED; + } + sv->num_rows = rows; + + *view = (MSIVIEW *)sv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/string.c b/libmsi/string.c new file mode 100644 index 0000000..065c04a --- /dev/null +++ b/libmsi/string.c @@ -0,0 +1,674 @@ +/* + * String Table Functions + * + * Copyright 2002-2004, Mike McCormack for CodeWeavers + * Copyright 2007 Robert Shearman for CodeWeavers + * Copyright 2010 Hans Leidekker 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 + */ + +#define COBJMACROS + +#include <stdarg.h> +#include <assert.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +struct msistring +{ + USHORT persistent_refcount; + USHORT nonpersistent_refcount; + LPWSTR str; +}; + +struct string_table +{ + UINT maxcount; /* the number of strings */ + UINT freeslot; + UINT codepage; + UINT sortcount; + struct msistring *strings; /* an array of strings */ + UINT *sorted; /* index */ +}; + +static BOOL validate_codepage( UINT codepage ) +{ + if (codepage != CP_ACP && !IsValidCodePage( codepage )) + { + WARN("invalid codepage %u\n", codepage); + return FALSE; + } + return TRUE; +} + +static string_table *init_stringtable( int entries, UINT codepage ) +{ + string_table *st; + + if (!validate_codepage( codepage )) + return NULL; + + st = msi_alloc( sizeof (string_table) ); + if( !st ) + return NULL; + if( entries < 1 ) + entries = 1; + + st->strings = msi_alloc_zero( sizeof(struct msistring) * entries ); + if( !st->strings ) + { + msi_free( st ); + return NULL; + } + + st->sorted = msi_alloc( sizeof (UINT) * entries ); + if( !st->sorted ) + { + msi_free( st->strings ); + msi_free( st ); + return NULL; + } + + st->maxcount = entries; + st->freeslot = 1; + st->codepage = codepage; + st->sortcount = 0; + + return st; +} + +VOID msi_destroy_stringtable( string_table *st ) +{ + UINT i; + + for( i=0; i<st->maxcount; i++ ) + { + if( st->strings[i].persistent_refcount || + st->strings[i].nonpersistent_refcount ) + msi_free( st->strings[i].str ); + } + msi_free( st->strings ); + msi_free( st->sorted ); + msi_free( st ); +} + +static int st_find_free_entry( string_table *st ) +{ + UINT i, sz, *s; + struct msistring *p; + + TRACE("%p\n", st); + + if( st->freeslot ) + { + for( i = st->freeslot; i < st->maxcount; i++ ) + if( !st->strings[i].persistent_refcount && + !st->strings[i].nonpersistent_refcount ) + return i; + } + for( i = 1; i < st->maxcount; i++ ) + if( !st->strings[i].persistent_refcount && + !st->strings[i].nonpersistent_refcount ) + return i; + + /* dynamically resize */ + sz = st->maxcount + 1 + st->maxcount/2; + p = msi_realloc_zero( st->strings, sz * sizeof(struct msistring) ); + if( !p ) + return -1; + + s = msi_realloc( st->sorted, sz*sizeof(UINT) ); + if( !s ) + { + msi_free( p ); + return -1; + } + + st->strings = p; + st->sorted = s; + + st->freeslot = st->maxcount; + st->maxcount = sz; + if( st->strings[st->freeslot].persistent_refcount || + st->strings[st->freeslot].nonpersistent_refcount ) + ERR("oops. expected freeslot to be free...\n"); + return st->freeslot; +} + +static int find_insert_index( const string_table *st, UINT string_id ) +{ + int i, c, low = 0, high = st->sortcount - 1; + + while (low <= high) + { + i = (low + high) / 2; + c = strcmpW( st->strings[string_id].str, st->strings[st->sorted[i]].str ); + + if (c < 0) + high = i - 1; + else if (c > 0) + low = i + 1; + else + return -1; /* already exists */ + } + return high + 1; +} + +static void insert_string_sorted( string_table *st, UINT string_id ) +{ + int i; + + i = find_insert_index( st, string_id ); + if (i == -1) + return; + + memmove( &st->sorted[i] + 1, &st->sorted[i], (st->sortcount - i) * sizeof(UINT) ); + st->sorted[i] = string_id; + st->sortcount++; +} + +static void set_st_entry( string_table *st, UINT n, LPWSTR str, USHORT refcount, enum StringPersistence persistence ) +{ + if (persistence == StringPersistent) + { + st->strings[n].persistent_refcount = refcount; + st->strings[n].nonpersistent_refcount = 0; + } + else + { + st->strings[n].persistent_refcount = 0; + st->strings[n].nonpersistent_refcount = refcount; + } + + st->strings[n].str = str; + + insert_string_sorted( st, n ); + + if( n < st->maxcount ) + st->freeslot = n + 1; +} + +static UINT msi_string2idA( const string_table *st, LPCSTR buffer, UINT *id ) +{ + DWORD sz; + UINT r = ERROR_INVALID_PARAMETER; + LPWSTR str; + + TRACE("Finding string %s in string table\n", debugstr_a(buffer) ); + + if( buffer[0] == 0 ) + { + *id = 0; + return ERROR_SUCCESS; + } + + sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 ); + if( sz <= 0 ) + return r; + str = msi_alloc( sz*sizeof(WCHAR) ); + if( !str ) + return ERROR_NOT_ENOUGH_MEMORY; + MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz ); + + r = msi_string2idW( st, str, id ); + msi_free( str ); + + return r; +} + +static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, USHORT refcount, enum StringPersistence persistence ) +{ + LPWSTR str; + int sz; + + if( !data ) + return 0; + if( !data[0] ) + return 0; + if( n > 0 ) + { + if( st->strings[n].persistent_refcount || + st->strings[n].nonpersistent_refcount ) + return -1; + } + else + { + if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) ) + { + if (persistence == StringPersistent) + st->strings[n].persistent_refcount += refcount; + else + st->strings[n].nonpersistent_refcount += refcount; + return n; + } + n = st_find_free_entry( st ); + if( n == -1 ) + return -1; + } + + if( n < 1 ) + { + ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n ); + return -1; + } + + /* allocate a new string */ + if( len < 0 ) + len = strlen(data); + sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 ); + str = msi_alloc( (sz+1)*sizeof(WCHAR) ); + if( !str ) + return -1; + MultiByteToWideChar( st->codepage, 0, data, len, str, sz ); + str[sz] = 0; + + set_st_entry( st, n, str, refcount, persistence ); + + return n; +} + +int msi_addstringW( string_table *st, const WCHAR *data, int len, USHORT refcount, enum StringPersistence persistence ) +{ + UINT n; + LPWSTR str; + + if( !data ) + return 0; + if( !data[0] ) + return 0; + + if( msi_string2idW( st, data, &n ) == ERROR_SUCCESS ) + { + if (persistence == StringPersistent) + st->strings[n].persistent_refcount += refcount; + else + st->strings[n].nonpersistent_refcount += refcount; + return n; + } + + n = st_find_free_entry( st ); + if( n == -1 ) + return -1; + + /* allocate a new string */ + if(len<0) + len = strlenW(data); + TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len ); + + str = msi_alloc( (len+1)*sizeof(WCHAR) ); + if( !str ) + return -1; + memcpy( str, data, len*sizeof(WCHAR) ); + str[len] = 0; + + set_st_entry( st, n, str, refcount, persistence ); + + return n; +} + +/* find the string identified by an id - return null if there's none */ +const WCHAR *msi_string_lookup_id( const string_table *st, UINT id ) +{ + if( id == 0 ) + return szEmpty; + + if( id >= st->maxcount ) + return NULL; + + if( id && !st->strings[id].persistent_refcount && !st->strings[id].nonpersistent_refcount) + return NULL; + + return st->strings[id].str; +} + +/* + * msi_id2stringA + * + * [in] st - pointer to the string table + * [in] id - id of the string to retrieve + * [out] buffer - destination of the UTF8 string + * [in/out] sz - number of bytes available in the buffer on input + * number of bytes used on output + * + * Returned string is not nul terminated. + */ +static UINT msi_id2stringA( const string_table *st, UINT id, LPSTR buffer, UINT *sz ) +{ + UINT len, lenW; + const WCHAR *str; + + TRACE("Finding string %d of %d\n", id, st->maxcount); + + str = msi_string_lookup_id( st, id ); + if( !str ) + return ERROR_FUNCTION_FAILED; + + lenW = strlenW( str ); + len = WideCharToMultiByte( st->codepage, 0, str, lenW, NULL, 0, NULL, NULL ); + if( *sz < len ) + { + *sz = len; + return ERROR_MORE_DATA; + } + *sz = WideCharToMultiByte( st->codepage, 0, str, lenW, buffer, *sz, NULL, NULL ); + return ERROR_SUCCESS; +} + +/* + * msi_string2idW + * + * [in] st - pointer to the string table + * [in] str - string to find in the string table + * [out] id - id of the string, if found + */ +UINT msi_string2idW( const string_table *st, LPCWSTR str, UINT *id ) +{ + int i, c, low = 0, high = st->sortcount - 1; + + while (low <= high) + { + i = (low + high) / 2; + c = strcmpW( str, st->strings[st->sorted[i]].str ); + + if (c < 0) + high = i - 1; + else if (c > 0) + low = i + 1; + else + { + *id = st->sorted[i]; + return ERROR_SUCCESS; + } + } + + return ERROR_INVALID_PARAMETER; +} + +static void string_totalsize( const string_table *st, UINT *datasize, UINT *poolsize ) +{ + UINT i, len, holesize; + + if( st->strings[0].str || st->strings[0].persistent_refcount || st->strings[0].nonpersistent_refcount) + ERR("oops. element 0 has a string\n"); + + *poolsize = 4; + *datasize = 0; + holesize = 0; + for( i=1; i<st->maxcount; i++ ) + { + if( !st->strings[i].persistent_refcount ) + { + TRACE("[%u] nonpersistent = %s\n", i, debugstr_w(st->strings[i].str)); + (*poolsize) += 4; + } + else if( st->strings[i].str ) + { + TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str)); + len = WideCharToMultiByte( st->codepage, 0, + st->strings[i].str, -1, NULL, 0, NULL, NULL); + if( len ) + len--; + (*datasize) += len; + if (len>0xffff) + (*poolsize) += 4; + (*poolsize) += holesize + 4; + holesize = 0; + } + else + holesize += 4; + } + TRACE("data %u pool %u codepage %x\n", *datasize, *poolsize, st->codepage ); +} + +HRESULT msi_init_string_table( IStorage *stg ) +{ + USHORT zero[2] = { 0, 0 }; + UINT ret; + + /* create the StringPool stream... add the zero string to it*/ + ret = write_stream_data(stg, szStringPool, zero, sizeof zero, TRUE); + if (ret != ERROR_SUCCESS) + return E_FAIL; + + /* create the StringData stream... make it zero length */ + ret = write_stream_data(stg, szStringData, NULL, 0, TRUE); + if (ret != ERROR_SUCCESS) + return E_FAIL; + + return S_OK; +} + +string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref ) +{ + string_table *st = NULL; + CHAR *data = NULL; + USHORT *pool = NULL; + UINT r, datasize = 0, poolsize = 0, codepage; + DWORD i, count, offset, len, n, refs; + + r = read_stream_data( stg, szStringPool, TRUE, (BYTE **)&pool, &poolsize ); + if( r != ERROR_SUCCESS) + goto end; + r = read_stream_data( stg, szStringData, TRUE, (BYTE **)&data, &datasize ); + if( r != ERROR_SUCCESS) + goto end; + + if ( (poolsize > 4) && (pool[1] & 0x8000) ) + *bytes_per_strref = LONG_STR_BYTES; + else + *bytes_per_strref = sizeof(USHORT); + + count = poolsize/4; + if( poolsize > 4 ) + codepage = pool[0] | ( (pool[1] & ~0x8000) << 16 ); + else + codepage = CP_ACP; + st = init_stringtable( count, codepage ); + if (!st) + goto end; + + offset = 0; + n = 1; + i = 1; + while( i<count ) + { + /* the string reference count is always the second word */ + refs = pool[i*2+1]; + + /* empty entries have two zeros, still have a string id */ + if (pool[i*2] == 0 && refs == 0) + { + i++; + n++; + continue; + } + + /* + * If a string is over 64k, the previous string entry is made null + * and its the high word of the length is inserted in the null string's + * reference count field. + */ + if( pool[i*2] == 0) + { + len = (pool[i*2+3] << 16) + pool[i*2+2]; + i += 2; + } + else + { + len = pool[i*2]; + i += 1; + } + + if ( (offset + len) > datasize ) + { + ERR("string table corrupt?\n"); + break; + } + + r = msi_addstring( st, n, data+offset, len, refs, StringPersistent ); + if( r != n ) + ERR("Failed to add string %d\n", n ); + n++; + offset += len; + } + + if ( datasize != offset ) + ERR("string table load failed! (%08x != %08x), please report\n", datasize, offset ); + + TRACE("Loaded %d strings\n", count); + +end: + msi_free( pool ); + msi_free( data ); + + return st; +} + +UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref ) +{ + UINT i, datasize = 0, poolsize = 0, sz, used, r, codepage, n; + UINT ret = ERROR_FUNCTION_FAILED; + CHAR *data = NULL; + USHORT *pool = NULL; + + TRACE("\n"); + + /* construct the new table in memory first */ + string_totalsize( st, &datasize, &poolsize ); + + TRACE("%u %u %u\n", st->maxcount, datasize, poolsize ); + + pool = msi_alloc( poolsize ); + if( ! pool ) + { + WARN("Failed to alloc pool %d bytes\n", poolsize ); + goto err; + } + data = msi_alloc( datasize ); + if( ! data ) + { + WARN("Failed to alloc data %d bytes\n", datasize ); + goto err; + } + + used = 0; + codepage = st->codepage; + pool[0] = codepage & 0xffff; + pool[1] = codepage >> 16; + if (st->maxcount > 0xffff) + { + pool[1] |= 0x8000; + *bytes_per_strref = LONG_STR_BYTES; + } + else + *bytes_per_strref = sizeof(USHORT); + + n = 1; + for( i=1; i<st->maxcount; i++ ) + { + if( !st->strings[i].persistent_refcount ) + { + pool[ n*2 ] = 0; + pool[ n*2 + 1] = 0; + n++; + continue; + } + + sz = datasize - used; + r = msi_id2stringA( st, i, data+used, &sz ); + if( r != ERROR_SUCCESS ) + { + ERR("failed to fetch string\n"); + sz = 0; + } + + if (sz) + pool[ n*2 + 1 ] = st->strings[i].persistent_refcount; + else + pool[ n*2 + 1 ] = 0; + if (sz < 0x10000) + { + pool[ n*2 ] = sz; + n++; + } + else + { + pool[ n*2 ] = 0; + pool[ n*2 + 2 ] = sz&0xffff; + pool[ n*2 + 3 ] = (sz>>16); + n += 2; + } + used += sz; + if( used > datasize ) + { + ERR("oops overran %d >= %d\n", used, datasize); + goto err; + } + } + + if( used != datasize ) + { + ERR("oops used %d != datasize %d\n", used, datasize); + goto err; + } + + /* write the streams */ + r = write_stream_data( storage, szStringData, data, datasize, TRUE ); + TRACE("Wrote StringData r=%08x\n", r); + if( r ) + goto err; + r = write_stream_data( storage, szStringPool, pool, poolsize, TRUE ); + TRACE("Wrote StringPool r=%08x\n", r); + if( r ) + goto err; + + ret = ERROR_SUCCESS; + +err: + msi_free( data ); + msi_free( pool ); + + return ret; +} + +UINT msi_get_string_table_codepage( const string_table *st ) +{ + return st->codepage; +} + +UINT msi_set_string_table_codepage( string_table *st, UINT codepage ) +{ + if (validate_codepage( codepage )) + { + st->codepage = codepage; + return ERROR_SUCCESS; + } + return ERROR_FUNCTION_FAILED; +} diff --git a/libmsi/suminfo.c b/libmsi/suminfo.c new file mode 100644 index 0000000..e0c99b8 --- /dev/null +++ b/libmsi/suminfo.c @@ -0,0 +1,998 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002, 2005 Mike McCormack 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 +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "msidefs.h" +#include "msipriv.h" +#include "objidl.h" +#include "propvarutil.h" +#include "msiserver.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#include "pshpack1.h" + +typedef struct { + WORD wByteOrder; + WORD wFormat; + DWORD dwOSVer; + CLSID clsID; + DWORD reserved; +} PROPERTYSETHEADER; + +typedef struct { + FMTID fmtid; + DWORD dwOffset; +} FORMATIDOFFSET; + +typedef struct { + DWORD cbSection; + DWORD cProperties; +} PROPERTYSECTIONHEADER; + +typedef struct { + DWORD propid; + DWORD dwOffset; +} PROPERTYIDOFFSET; + +typedef struct { + DWORD type; + union { + INT i4; + SHORT i2; + FILETIME ft; + struct { + DWORD len; + BYTE str[1]; + } str; + } u; +} PROPERTY_DATA; + +#include "poppack.h" + +static HRESULT (WINAPI *pPropVariantChangeType) + (PROPVARIANT *ppropvarDest, REFPROPVARIANT propvarSrc, + PROPVAR_CHANGE_FLAGS flags, VARTYPE vt); + +#define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER)) + +static void free_prop( PROPVARIANT *prop ) +{ + if (prop->vt == VT_LPSTR ) + msi_free( prop->u.pszVal ); + prop->vt = VT_EMPTY; +} + +static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg ) +{ + MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg; + DWORD i; + + for( i = 0; i < MSI_MAX_PROPS; i++ ) + free_prop( &si->property[i] ); + IStorage_Release( si->storage ); +} + +static UINT get_type( UINT uiProperty ) +{ + switch( uiProperty ) + { + case PID_CODEPAGE: + return VT_I2; + + case PID_SUBJECT: + case PID_AUTHOR: + case PID_KEYWORDS: + case PID_COMMENTS: + case PID_TEMPLATE: + case PID_LASTAUTHOR: + case PID_REVNUMBER: + case PID_APPNAME: + case PID_TITLE: + return VT_LPSTR; + + case PID_LASTPRINTED: + case PID_CREATE_DTM: + case PID_LASTSAVE_DTM: + return VT_FILETIME; + + case PID_WORDCOUNT: + case PID_CHARCOUNT: + case PID_SECURITY: + case PID_PAGECOUNT: + return VT_I4; + } + return VT_EMPTY; +} + +static UINT get_property_count( const PROPVARIANT *property ) +{ + UINT i, n = 0; + + if( !property ) + return n; + for( i = 0; i < MSI_MAX_PROPS; i++ ) + if( property[i].vt != VT_EMPTY ) + n++; + return n; +} + +static UINT propvar_changetype(PROPVARIANT *changed, PROPVARIANT *property, VARTYPE vt) +{ + HRESULT hr; + HMODULE propsys = LoadLibraryA("propsys.dll"); + pPropVariantChangeType = (void *)GetProcAddress(propsys, "PropVariantChangeType"); + + if (!pPropVariantChangeType) + { + ERR("PropVariantChangeType function missing!\n"); + return ERROR_FUNCTION_FAILED; + } + + hr = pPropVariantChangeType(changed, property, 0, vt); + return (hr == S_OK) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; +} + +/* FIXME: doesn't deal with endian conversion */ +static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz ) +{ + UINT type; + DWORD i, size; + PROPERTY_DATA *propdata; + PROPVARIANT property, *ptr; + PROPVARIANT changed; + PROPERTYIDOFFSET *idofs; + PROPERTYSECTIONHEADER *section_hdr; + + section_hdr = (PROPERTYSECTIONHEADER*) &data[0]; + idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE]; + + /* now set all the properties */ + for( i = 0; i < section_hdr->cProperties; i++ ) + { + if( idofs[i].propid >= MSI_MAX_PROPS ) + { + ERR("Unknown property ID %d\n", idofs[i].propid ); + break; + } + + type = get_type( idofs[i].propid ); + if( type == VT_EMPTY ) + { + ERR("propid %d has unknown type\n", idofs[i].propid); + break; + } + + propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ]; + + /* check we don't run off the end of the data */ + size = sz - idofs[i].dwOffset - sizeof(DWORD); + if( sizeof(DWORD) > size || + ( propdata->type == VT_FILETIME && sizeof(FILETIME) > size ) || + ( propdata->type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) ) + { + ERR("not enough data\n"); + break; + } + + property.vt = propdata->type; + if( propdata->type == VT_LPSTR ) + { + LPSTR str = msi_alloc( propdata->u.str.len ); + memcpy( str, propdata->u.str.str, propdata->u.str.len ); + str[ propdata->u.str.len - 1 ] = 0; + property.u.pszVal = str; + } + else if( propdata->type == VT_FILETIME ) + property.u.filetime = propdata->u.ft; + else if( propdata->type == VT_I2 ) + property.u.iVal = propdata->u.i2; + else if( propdata->type == VT_I4 ) + property.u.lVal = propdata->u.i4; + + /* check the type is the same as we expect */ + if( type != propdata->type ) + { + propvar_changetype(&changed, &property, type); + ptr = &changed; + } + else + ptr = &property; + + prop[ idofs[i].propid ] = *ptr; + } +} + +static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + PROPERTYSETHEADER set_hdr; + FORMATIDOFFSET format_hdr; + PROPERTYSECTIONHEADER section_hdr; + LPBYTE data = NULL; + LARGE_INTEGER ofs; + ULONG count, sz; + HRESULT r; + + TRACE("%p %p\n", si, stm); + + /* read the header */ + sz = sizeof set_hdr; + r = IStream_Read( stm, &set_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + if( set_hdr.wByteOrder != 0xfffe ) + { + ERR("property set not big-endian %04X\n", set_hdr.wByteOrder); + return ret; + } + + sz = sizeof format_hdr; + r = IStream_Read( stm, &format_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* check the format id is correct */ + if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) ) + return ret; + + /* seek to the location of the section */ + ofs.QuadPart = format_hdr.dwOffset; + r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL ); + if( FAILED(r) ) + return ret; + + /* read the section itself */ + sz = SECT_HDR_SIZE; + r = IStream_Read( stm, §ion_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + if( section_hdr.cProperties > MSI_MAX_PROPS ) + { + ERR("too many properties %d\n", section_hdr.cProperties); + return ret; + } + + data = msi_alloc( section_hdr.cbSection); + if( !data ) + return ret; + + memcpy( data, §ion_hdr, SECT_HDR_SIZE ); + + /* read all the data in one go */ + sz = section_hdr.cbSection - SECT_HDR_SIZE; + r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count ); + if( SUCCEEDED(r) && count == sz ) + read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE ); + else + ERR("failed to read properties %d %d\n", count, sz); + + msi_free( data ); + return ret; +} + +static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val ) +{ + if( data ) + { + data[ofs++] = val&0xff; + data[ofs++] = (val>>8)&0xff; + data[ofs++] = (val>>16)&0xff; + data[ofs++] = (val>>24)&0xff; + } + return 4; +} + +static DWORD write_filetime( LPBYTE data, DWORD ofs, const FILETIME *ft ) +{ + write_dword( data, ofs, ft->dwLowDateTime ); + write_dword( data, ofs + 4, ft->dwHighDateTime ); + return 8; +} + +static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str ) +{ + DWORD len = lstrlenA( str ) + 1; + write_dword( data, ofs, len ); + if( data ) + memcpy( &data[ofs + 4], str, len ); + return (7 + len) & ~3; +} + +static UINT write_property_to_data( const PROPVARIANT *prop, LPBYTE data ) +{ + DWORD sz = 0; + + if( prop->vt == VT_EMPTY ) + return sz; + + /* add the type */ + sz += write_dword( data, sz, prop->vt ); + switch( prop->vt ) + { + case VT_I2: + sz += write_dword( data, sz, prop->u.iVal ); + break; + case VT_I4: + sz += write_dword( data, sz, prop->u.lVal ); + break; + case VT_FILETIME: + sz += write_filetime( data, sz, &prop->u.filetime ); + break; + case VT_LPSTR: + sz += write_string( data, sz, prop->u.pszVal ); + break; + } + return sz; +} + +static UINT save_summary_info( const MSISUMMARYINFO * si, IStream *stm ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + PROPERTYSETHEADER set_hdr; + FORMATIDOFFSET format_hdr; + PROPERTYSECTIONHEADER section_hdr; + PROPERTYIDOFFSET idofs[MSI_MAX_PROPS]; + LPBYTE data = NULL; + ULONG count, sz; + HRESULT r; + int i; + + /* write the header */ + sz = sizeof set_hdr; + memset( &set_hdr, 0, sz ); + set_hdr.wByteOrder = 0xfffe; + set_hdr.wFormat = 0; + set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */ + /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */ + set_hdr.reserved = 1; + r = IStream_Write( stm, &set_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* write the format header */ + sz = sizeof format_hdr; + format_hdr.fmtid = FMTID_SummaryInformation; + format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr; + r = IStream_Write( stm, &format_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* add up how much space the data will take and calculate the offsets */ + section_hdr.cbSection = sizeof section_hdr; + section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]); + section_hdr.cProperties = 0; + for( i = 0; i < MSI_MAX_PROPS; i++ ) + { + sz = write_property_to_data( &si->property[i], NULL ); + if( !sz ) + continue; + idofs[ section_hdr.cProperties ].propid = i; + idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection; + section_hdr.cProperties++; + section_hdr.cbSection += sz; + } + + data = msi_alloc_zero( section_hdr.cbSection ); + + sz = 0; + memcpy( &data[sz], §ion_hdr, sizeof section_hdr ); + sz += sizeof section_hdr; + + memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] ); + sz += section_hdr.cProperties * sizeof idofs[0]; + + /* write out the data */ + for( i = 0; i < MSI_MAX_PROPS; i++ ) + sz += write_property_to_data( &si->property[i], &data[sz] ); + + r = IStream_Write( stm, data, sz, &count ); + msi_free( data ); + if( FAILED(r) || count != sz ) + return ret; + + return ERROR_SUCCESS; +} + +MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount ) +{ + IStream *stm = NULL; + MSISUMMARYINFO *si; + DWORD grfMode; + HRESULT r; + + TRACE("%p %d\n", stg, uiUpdateCount ); + + si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, + sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo ); + if( !si ) + return si; + + si->update_count = uiUpdateCount; + IStorage_AddRef( stg ); + si->storage = stg; + + /* read the stream... if we fail, we'll start with an empty property set */ + grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE; + r = IStorage_OpenStream( si->storage, szSumInfo, 0, grfMode, 0, &stm ); + if( SUCCEEDED(r) ) + { + load_summary_info( si, stm ); + IStream_Release( stm ); + } + + return si; +} + +UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, + LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle ) +{ + MSISUMMARYINFO *si; + MSIDATABASE *db; + UINT ret = ERROR_FUNCTION_FAILED; + + TRACE("%d %s %d %p\n", hDatabase, debugstr_w(szDatabase), + uiUpdateCount, pHandle); + + if( !pHandle ) + return ERROR_INVALID_PARAMETER; + + if( szDatabase && szDatabase[0] ) + { + LPCWSTR persist = uiUpdateCount ? MSIDBOPEN_TRANSACT : MSIDBOPEN_READONLY; + + ret = MSI_OpenDatabaseW( szDatabase, persist, &db ); + if( ret != ERROR_SUCCESS ) + return ret; + } + else + { + db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + HRESULT hr; + IWineMsiRemoteDatabase *remote_database; + + remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase ); + if ( !remote_database ) + return ERROR_INVALID_HANDLE; + + hr = IWineMsiRemoteDatabase_GetSummaryInformation( remote_database, + uiUpdateCount, pHandle ); + IWineMsiRemoteDatabase_Release( remote_database ); + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + return HRESULT_CODE(hr); + + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; + } + } + + si = MSI_GetSummaryInformationW( db->storage, uiUpdateCount ); + if (si) + { + *pHandle = alloc_msihandle( &si->hdr ); + if( *pHandle ) + ret = ERROR_SUCCESS; + else + ret = ERROR_NOT_ENOUGH_MEMORY; + msiobj_release( &si->hdr ); + } + + msiobj_release( &db->hdr ); + return ret; +} + +UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, + LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle) +{ + LPWSTR szwDatabase = NULL; + UINT ret; + + TRACE("%d %s %d %p\n", hDatabase, debugstr_a(szDatabase), + uiUpdateCount, pHandle); + + if( szDatabase ) + { + szwDatabase = strdupAtoW( szDatabase ); + if( !szwDatabase ) + return ERROR_FUNCTION_FAILED; + } + + ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle); + + msi_free( szwDatabase ); + + return ret; +} + +UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, PUINT pCount) +{ + MSISUMMARYINFO *si; + + TRACE("%d %p\n", hSummaryInfo, pCount); + + si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + if( pCount ) + *pCount = get_property_count( si->property ); + msiobj_release( &si->hdr ); + + return ERROR_SUCCESS; +} + +static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, + INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf) +{ + MSISUMMARYINFO *si; + PROPVARIANT *prop; + UINT ret = ERROR_SUCCESS; + + TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, str, pcchValueBuf); + + if ( uiProperty >= MSI_MAX_PROPS ) + { + if (puiDataType) *puiDataType = VT_EMPTY; + return ERROR_UNKNOWN_PROPERTY; + } + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + prop = &si->property[uiProperty]; + + if( puiDataType ) + *puiDataType = prop->vt; + + switch( prop->vt ) + { + case VT_I2: + if( piValue ) + *piValue = prop->u.iVal; + break; + case VT_I4: + if( piValue ) + *piValue = prop->u.lVal; + break; + case VT_LPSTR: + if( pcchValueBuf ) + { + DWORD len = 0; + + if( str->unicode ) + { + len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, NULL, 0 ) - 1; + MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, str->str.w, *pcchValueBuf ); + } + else + { + len = lstrlenA( prop->u.pszVal ); + if( str->str.a ) + lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf ); + } + if (len >= *pcchValueBuf) + ret = ERROR_MORE_DATA; + *pcchValueBuf = len; + } + break; + case VT_FILETIME: + if( pftValue ) + *pftValue = prop->u.filetime; + break; + case VT_EMPTY: + break; + default: + FIXME("Unknown property variant type\n"); + break; + } + msiobj_release( &si->hdr ); + return ret; +} + +LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) +{ + PROPVARIANT *prop; + + if ( uiProperty >= MSI_MAX_PROPS ) + return NULL; + prop = &si->property[uiProperty]; + if( prop->vt != VT_LPSTR ) + return NULL; + return strdupAtoW( prop->u.pszVal ); +} + +INT msi_suminfo_get_int32( MSISUMMARYINFO *si, UINT uiProperty ) +{ + PROPVARIANT *prop; + + if ( uiProperty >= MSI_MAX_PROPS ) + return -1; + prop = &si->property[uiProperty]; + if( prop->vt != VT_I4 ) + return -1; + return prop->u.lVal; +} + +LPWSTR msi_get_suminfo_product( IStorage *stg ) +{ + MSISUMMARYINFO *si; + LPWSTR prod; + + si = MSI_GetSummaryInformationW( stg, 0 ); + if (!si) + { + ERR("no summary information!\n"); + return NULL; + } + prod = msi_suminfo_dup_string( si, PID_REVNUMBER ); + msiobj_release( &si->hdr ); + return prod; +} + +UINT WINAPI MsiSummaryInfoGetPropertyA( + MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue, + FILETIME *pftValue, LPSTR szValueBuf, LPDWORD pcchValueBuf) +{ + awstring str; + + TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, szValueBuf, pcchValueBuf ); + + str.unicode = FALSE; + str.str.a = szValueBuf; + + return get_prop( handle, uiProperty, puiDataType, piValue, + pftValue, &str, pcchValueBuf ); +} + +UINT WINAPI MsiSummaryInfoGetPropertyW( + MSIHANDLE handle, UINT uiProperty, PUINT puiDataType, LPINT piValue, + FILETIME *pftValue, LPWSTR szValueBuf, LPDWORD pcchValueBuf) +{ + awstring str; + + TRACE("%d %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, szValueBuf, pcchValueBuf ); + + str.unicode = TRUE; + str.str.w = szValueBuf; + + return get_prop( handle, uiProperty, puiDataType, piValue, + pftValue, &str, pcchValueBuf ); +} + +static UINT set_prop( MSISUMMARYINFO *si, UINT uiProperty, UINT type, + INT iValue, FILETIME* pftValue, awcstring *str ) +{ + PROPVARIANT *prop; + UINT len; + + TRACE("%p %u %u %i %p %p\n", si, uiProperty, type, iValue, + pftValue, str ); + + prop = &si->property[uiProperty]; + + if( prop->vt == VT_EMPTY ) + { + if( !si->update_count ) + return ERROR_FUNCTION_FAILED; + + si->update_count--; + } + else if( prop->vt != type ) + return ERROR_SUCCESS; + + free_prop( prop ); + prop->vt = type; + switch( type ) + { + case VT_I4: + prop->u.lVal = iValue; + break; + case VT_I2: + prop->u.iVal = iValue; + break; + case VT_FILETIME: + prop->u.filetime = *pftValue; + break; + case VT_LPSTR: + if( str->unicode ) + { + len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, + NULL, 0, NULL, NULL ); + prop->u.pszVal = msi_alloc( len ); + WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, + prop->u.pszVal, len, NULL, NULL ); + } + else + { + len = lstrlenA( str->str.a ) + 1; + prop->u.pszVal = msi_alloc( len ); + lstrcpyA( prop->u.pszVal, str->str.a ); + } + break; + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty, + UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue ) +{ + awcstring str; + MSISUMMARYINFO *si; + UINT type, ret; + + TRACE("%d %u %u %i %p %s\n", handle, uiProperty, uiDataType, + iValue, pftValue, debugstr_w(szValue) ); + + type = get_type( uiProperty ); + if( type == VT_EMPTY || type != uiDataType ) + return ERROR_DATATYPE_MISMATCH; + + if( uiDataType == VT_LPSTR && !szValue ) + return ERROR_INVALID_PARAMETER; + + if( uiDataType == VT_FILETIME && !pftValue ) + return ERROR_INVALID_PARAMETER; + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + str.unicode = TRUE; + str.str.w = szValue; + ret = set_prop( si, uiProperty, type, iValue, pftValue, &str ); + + msiobj_release( &si->hdr ); + return ret; +} + +UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty, + UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue ) +{ + awcstring str; + MSISUMMARYINFO *si; + UINT type, ret; + + TRACE("%d %u %u %i %p %s\n", handle, uiProperty, uiDataType, + iValue, pftValue, debugstr_a(szValue) ); + + type = get_type( uiProperty ); + if( type == VT_EMPTY || type != uiDataType ) + return ERROR_DATATYPE_MISMATCH; + + if( uiDataType == VT_LPSTR && !szValue ) + return ERROR_INVALID_PARAMETER; + + if( uiDataType == VT_FILETIME && !pftValue ) + return ERROR_INVALID_PARAMETER; + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + str.unicode = FALSE; + str.str.a = szValue; + ret = set_prop( si, uiProperty, uiDataType, iValue, pftValue, &str ); + + msiobj_release( &si->hdr ); + return ret; +} + +static UINT suminfo_persist( MSISUMMARYINFO *si ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + IStream *stm = NULL; + DWORD grfMode; + HRESULT r; + + grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; + r = IStorage_CreateStream( si->storage, szSumInfo, grfMode, 0, 0, &stm ); + if( SUCCEEDED(r) ) + { + ret = save_summary_info( si, stm ); + IStream_Release( stm ); + } + return ret; +} + +static void parse_filetime( LPCWSTR str, FILETIME *ft ) +{ + SYSTEMTIME lt, utc; + const WCHAR *p = str; + WCHAR *end; + + memset( <, 0, sizeof(lt) ); + + /* YYYY/MM/DD hh:mm:ss */ + + while (isspaceW( *p )) p++; + + lt.wYear = strtolW( p, &end, 10 ); + if (*end != '/') return; + p = end + 1; + + lt.wMonth = strtolW( p, &end, 10 ); + if (*end != '/') return; + p = end + 1; + + lt.wDay = strtolW( p, &end, 10 ); + if (*end != ' ') return; + p = end + 1; + + while (isspaceW( *p )) p++; + + lt.wHour = strtolW( p, &end, 10 ); + if (*end != ':') return; + p = end + 1; + + lt.wMinute = strtolW( p, &end, 10 ); + if (*end != ':') return; + p = end + 1; + + lt.wSecond = strtolW( p, &end, 10 ); + + TzSpecificLocalTimeToSystemTime( NULL, <, &utc ); + SystemTimeToFileTime( &utc, ft ); +} + +static UINT parse_prop( LPCWSTR prop, LPCWSTR value, UINT *pid, INT *int_value, + FILETIME *ft_value, awcstring *str_value ) +{ + *pid = atoiW( prop ); + switch (*pid) + { + case PID_CODEPAGE: + case PID_WORDCOUNT: + case PID_CHARCOUNT: + case PID_SECURITY: + case PID_PAGECOUNT: + *int_value = atoiW( value ); + break; + + case PID_LASTPRINTED: + case PID_CREATE_DTM: + case PID_LASTSAVE_DTM: + parse_filetime( value, ft_value ); + break; + + case PID_SUBJECT: + case PID_AUTHOR: + case PID_KEYWORDS: + case PID_COMMENTS: + case PID_TEMPLATE: + case PID_LASTAUTHOR: + case PID_REVNUMBER: + case PID_APPNAME: + case PID_TITLE: + str_value->str.w = value; + str_value->unicode = TRUE; + break; + + default: + WARN("unhandled prop id %u\n", *pid); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +UINT msi_add_suminfo( MSIDATABASE *db, LPWSTR **records, int num_records, int num_columns ) +{ + UINT r = ERROR_FUNCTION_FAILED; + DWORD i, j; + MSISUMMARYINFO *si; + + si = MSI_GetSummaryInformationW( db->storage, num_records * (num_columns / 2) ); + if (!si) + { + ERR("no summary information!\n"); + return ERROR_FUNCTION_FAILED; + } + + for (i = 0; i < num_records; i++) + { + for (j = 0; j < num_columns; j += 2) + { + UINT pid; + INT int_value = 0; + FILETIME ft_value; + awcstring str_value; + + r = parse_prop( records[i][j], records[i][j + 1], &pid, &int_value, &ft_value, &str_value ); + if (r != ERROR_SUCCESS) + goto end; + + r = set_prop( si, pid, get_type(pid), int_value, &ft_value, &str_value ); + if (r != ERROR_SUCCESS) + goto end; + } + } + +end: + if (r == ERROR_SUCCESS) + r = suminfo_persist( si ); + + msiobj_release( &si->hdr ); + return r; +} + +UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle ) +{ + MSISUMMARYINFO *si; + UINT ret; + + TRACE("%d\n", handle ); + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + ret = suminfo_persist( si ); + + msiobj_release( &si->hdr ); + return ret; +} + +UINT WINAPI MsiCreateTransformSummaryInfoA( MSIHANDLE db, MSIHANDLE db_ref, LPCSTR transform, int error, int validation ) +{ + UINT r; + WCHAR *transformW = NULL; + + TRACE("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_a(transform), error, validation); + + if (transform && !(transformW = strdupAtoW( transform ))) + return ERROR_OUTOFMEMORY; + + r = MsiCreateTransformSummaryInfoW( db, db_ref, transformW, error, validation ); + msi_free( transformW ); + return r; +} + +UINT WINAPI MsiCreateTransformSummaryInfoW( MSIHANDLE db, MSIHANDLE db_ref, LPCWSTR transform, int error, int validation ) +{ + FIXME("%u, %u, %s, %d, %d\n", db, db_ref, debugstr_w(transform), error, validation); + return ERROR_FUNCTION_FAILED; +} diff --git a/libmsi/table.c b/libmsi/table.c new file mode 100644 index 0000000..93b0268 --- /dev/null +++ b/libmsi/table.c @@ -0,0 +1,2805 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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> +#include <assert.h> + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "winnls.h" +#include "msipriv.h" +#include "query.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define MSITABLE_HASH_TABLE_SIZE 37 + +typedef struct tagMSICOLUMNHASHENTRY +{ + struct tagMSICOLUMNHASHENTRY *next; + UINT value; + UINT row; +} MSICOLUMNHASHENTRY; + +typedef struct tagMSICOLUMNINFO +{ + LPCWSTR tablename; + UINT number; + LPCWSTR colname; + UINT type; + UINT offset; + INT ref_count; + BOOL temporary; + MSICOLUMNHASHENTRY **hash_table; +} MSICOLUMNINFO; + +struct tagMSITABLE +{ + BYTE **data; + BOOL *data_persistent; + UINT row_count; + struct list entry; + MSICOLUMNINFO *colinfo; + UINT col_count; + MSICONDITION persistent; + INT ref_count; + WCHAR name[1]; +}; + +/* information for default tables */ +static const WCHAR szTables[] = {'_','T','a','b','l','e','s',0}; +static const WCHAR szTable[] = {'T','a','b','l','e',0}; +static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0}; +static const WCHAR szNumber[] = {'N','u','m','b','e','r',0}; +static const WCHAR szType[] = {'T','y','p','e',0}; + +static const MSICOLUMNINFO _Columns_cols[4] = { + { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL }, + { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2, 2, 0, 0, NULL }, + { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, 0, NULL }, + { szColumns, 4, szType, MSITYPE_VALID | 2, 6, 0, 0, NULL }, +}; + +static const MSICOLUMNINFO _Tables_cols[1] = { + { szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL }, +}; + +#define MAX_STREAM_NAME 0x1f + +static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref ) +{ + if( MSITYPE_IS_BINARY(col->type) ) + return 2; + + if( col->type & MSITYPE_STRING ) + return bytes_per_strref; + + if( (col->type & 0xff) <= 2) + return 2; + + if( (col->type & 0xff) != 4 ) + ERR("Invalid column size!\n"); + + return 4; +} + +static int utf2mime(int x) +{ + if( (x>='0') && (x<='9') ) + return x-'0'; + if( (x>='A') && (x<='Z') ) + return x-'A'+10; + if( (x>='a') && (x<='z') ) + return x-'a'+10+26; + if( x=='.' ) + return 10+26+26; + if( x=='_' ) + return 10+26+26+1; + return -1; +} + +LPWSTR encode_streamname(BOOL bTable, LPCWSTR in) +{ + DWORD count = MAX_STREAM_NAME; + DWORD ch, next; + LPWSTR out, p; + + if( !bTable ) + count = lstrlenW( in )+2; + if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL; + p = out; + + if( bTable ) + { + *p++ = 0x4840; + count --; + } + while( count -- ) + { + ch = *in++; + if( !ch ) + { + *p = ch; + return out; + } + if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) ) + { + ch = utf2mime(ch) + 0x4800; + next = *in; + if( next && (next<0x80) ) + { + next = utf2mime(next); + if( next != -1 ) + { + next += 0x3ffffc0; + ch += (next<<6); + in++; + } + } + } + *p++ = ch; + } + ERR("Failed to encode stream name (%s)\n",debugstr_w(in)); + msi_free( out ); + return NULL; +} + +static int mime2utf(int x) +{ + if( x<10 ) + return x + '0'; + if( x<(10+26)) + return x - 10 + 'A'; + if( x<(10+26+26)) + return x - 10 - 26 + 'a'; + if( x == (10+26+26) ) + return '.'; + return '_'; +} + +BOOL decode_streamname(LPCWSTR in, LPWSTR out) +{ + WCHAR ch; + DWORD count = 0; + + while ( (ch = *in++) ) + { + if( (ch >= 0x3800 ) && (ch < 0x4840 ) ) + { + if( ch >= 0x4800 ) + ch = mime2utf(ch-0x4800); + else + { + ch -= 0x3800; + *out++ = mime2utf(ch&0x3f); + count++; + ch = mime2utf((ch>>6)&0x3f); + } + } + *out++ = ch; + count++; + } + *out = 0; + return count; +} + +void enum_stream_names( IStorage *stg ) +{ + IEnumSTATSTG *stgenum = NULL; + HRESULT r; + STATSTG stat; + ULONG n, count; + WCHAR name[0x40]; + + r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); + if( FAILED( r ) ) + return; + + n = 0; + while( 1 ) + { + count = 0; + r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); + if( FAILED( r ) || !count ) + break; + decode_streamname( stat.pwcsName, name ); + TRACE("stream %2d -> %s %s\n", n, + debugstr_w(stat.pwcsName), debugstr_w(name) ); + CoTaskMemFree( stat.pwcsName ); + n++; + } + + IEnumSTATSTG_Release( stgenum ); +} + +UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table, + BYTE **pdata, UINT *psz ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + VOID *data; + ULONG sz, count; + IStream *stm = NULL; + STATSTG stat; + LPWSTR encname; + + encname = encode_streamname(table, stname); + + TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname)); + + r = IStorage_OpenStream(stg, encname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + msi_free( encname ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08x - empty table?\n", r); + return ret; + } + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08x!\n", r); + goto end; + } + + if( stat.cbSize.QuadPart >> 32 ) + { + WARN("Too big!\n"); + goto end; + } + + sz = stat.cbSize.QuadPart; + data = msi_alloc( sz ); + if( !data ) + { + WARN("couldn't allocate memory r=%08x!\n", r); + ret = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + + r = IStream_Read(stm, data, sz, &count ); + if( FAILED( r ) || ( count != sz ) ) + { + msi_free( data ); + WARN("read stream failed r = %08x!\n", r); + goto end; + } + + *pdata = data; + *psz = sz; + ret = ERROR_SUCCESS; + +end: + IStream_Release( stm ); + + return ret; +} + +UINT write_stream_data( IStorage *stg, LPCWSTR stname, + LPCVOID data, UINT sz, BOOL bTable ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + ULONG count; + IStream *stm = NULL; + ULARGE_INTEGER size; + LARGE_INTEGER pos; + LPWSTR encname; + + encname = encode_streamname(bTable, stname ); + r = IStorage_OpenStream( stg, encname, NULL, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + if( FAILED(r) ) + { + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + } + msi_free( encname ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08x\n", r); + return ret; + } + + size.QuadPart = sz; + r = IStream_SetSize( stm, size ); + if( FAILED( r ) ) + { + WARN("Failed to SetSize\n"); + goto end; + } + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); + if( FAILED( r ) ) + { + WARN("Failed to Seek\n"); + goto end; + } + + if (sz) + { + r = IStream_Write(stm, data, sz, &count ); + if( FAILED( r ) || ( count != sz ) ) + { + WARN("Failed to Write\n"); + goto end; + } + } + + ret = ERROR_SUCCESS; + +end: + IStream_Release( stm ); + + return ret; +} + +static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ) +{ + UINT i; + for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table ); +} + +static void free_table( MSITABLE *table ) +{ + UINT i; + for( i=0; i<table->row_count; i++ ) + msi_free( table->data[i] ); + msi_free( table->data ); + msi_free( table->data_persistent ); + msi_free_colinfo( table->colinfo, table->col_count ); + msi_free( table->colinfo ); + msi_free( table ); +} + +static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref ) +{ + const MSICOLUMNINFO *last_col; + + if (!count) + return 0; + + if (bytes_per_strref != LONG_STR_BYTES) + { + UINT i, size = 0; + for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref ); + return size; + } + last_col = &cols[count - 1]; + return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref ); +} + +/* add this table to the list of cached tables in the database */ +static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg ) +{ + BYTE *rawdata = NULL; + UINT rawsize = 0, i, j, row_size, row_size_mem; + + TRACE("%s\n",debugstr_w(t->name)); + + row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref ); + row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES ); + + /* if we can't read the table, just assume that it's empty */ + read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize ); + if( !rawdata ) + return ERROR_SUCCESS; + + TRACE("Read %d bytes\n", rawsize ); + + if( rawsize % row_size ) + { + WARN("Table size is invalid %d/%d\n", rawsize, row_size ); + goto err; + } + + t->row_count = rawsize / row_size; + t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) ); + if( !t->data ) + goto err; + t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL)); + if ( !t->data_persistent ) + goto err; + + /* transpose all the data */ + TRACE("Transposing data from %d rows\n", t->row_count ); + for (i = 0; i < t->row_count; i++) + { + UINT ofs = 0, ofs_mem = 0; + + t->data[i] = msi_alloc( row_size_mem ); + if( !t->data[i] ) + goto err; + t->data_persistent[i] = TRUE; + + for (j = 0; j < t->col_count; j++) + { + UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); + UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref ); + UINT k; + + if ( n != 2 && n != 3 && n != 4 ) + { + ERR("oops - unknown column width %d\n", n); + goto err; + } + if (t->colinfo[j].type & MSITYPE_STRING && n < m) + { + for (k = 0; k < m; k++) + { + if (k < n) + t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; + else + t->data[i][ofs_mem + k] = 0; + } + } + else + { + for (k = 0; k < n; k++) + t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; + } + ofs_mem += m; + ofs += n; + } + } + + msi_free( rawdata ); + return ERROR_SUCCESS; +err: + msi_free( rawdata ); + return ERROR_FUNCTION_FAILED; +} + +void free_cached_tables( MSIDATABASE *db ) +{ + while( !list_empty( &db->tables ) ) + { + MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry ); + + list_remove( &t->entry ); + free_table( t ); + } +} + +static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name ) +{ + MSITABLE *t; + + LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry ) + if( !strcmpW( name, t->name ) ) + return t; + + return NULL; +} + +static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count ) +{ + DWORD i; + + for (i = 0; colinfo && i < count; i++) + { + assert( i + 1 == colinfo[i].number ); + if (i) colinfo[i].offset = colinfo[i - 1].offset + + bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES ); + else colinfo[i].offset = 0; + + TRACE("column %d is [%s] with type %08x ofs %d\n", + colinfo[i].number, debugstr_w(colinfo[i].colname), + colinfo[i].type, colinfo[i].offset); + } +} + +static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz ) +{ + const MSICOLUMNINFO *p; + DWORD i, n; + + TRACE("%s\n", debugstr_w(name)); + + if (!strcmpW( name, szTables )) + { + p = _Tables_cols; + n = 1; + } + else if (!strcmpW( name, szColumns )) + { + p = _Columns_cols; + n = 4; + } + else return ERROR_FUNCTION_FAILED; + + for (i = 0; i < n; i++) + { + if (colinfo && i < *sz) colinfo[i] = p[i]; + if (colinfo && i >= *sz) break; + } + table_calc_column_offsets( db, colinfo, n ); + *sz = n; + return ERROR_SUCCESS; +} + +static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ); + +static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount ) +{ + UINT r, column_count = 0; + MSICOLUMNINFO *columns; + + /* get the number of columns in this table */ + column_count = 0; + r = get_tablecolumns( db, name, NULL, &column_count ); + if (r != ERROR_SUCCESS) + return r; + + *pcount = column_count; + + /* if there's no columns, there's no table */ + if (!column_count) + return ERROR_INVALID_PARAMETER; + + TRACE("table %s found\n", debugstr_w(name)); + + columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) ); + if (!columns) + return ERROR_FUNCTION_FAILED; + + r = get_tablecolumns( db, name, columns, &column_count ); + if (r != ERROR_SUCCESS) + { + msi_free( columns ); + return ERROR_FUNCTION_FAILED; + } + *pcols = columns; + return r; +} + +static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret ) +{ + MSITABLE *table; + UINT r; + + /* first, see if the table is cached */ + table = find_cached_table( db, name ); + if (table) + { + *table_ret = table; + return ERROR_SUCCESS; + } + + /* nonexistent tables should be interpreted as empty tables */ + table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) ); + if (!table) + return ERROR_FUNCTION_FAILED; + + table->row_count = 0; + table->data = NULL; + table->data_persistent = NULL; + table->colinfo = NULL; + table->col_count = 0; + table->persistent = MSICONDITION_TRUE; + lstrcpyW( table->name, name ); + + if (!strcmpW( name, szTables ) || !strcmpW( name, szColumns )) + table->persistent = MSICONDITION_NONE; + + r = table_get_column_info( db, name, &table->colinfo, &table->col_count ); + if (r != ERROR_SUCCESS) + { + free_table( table ); + return r; + } + r = read_table_from_storage( db, table, db->storage ); + if (r != ERROR_SUCCESS) + { + free_table( table ); + return r; + } + list_add_head( &db->tables, &table->entry ); + *table_ret = table; + return ERROR_SUCCESS; +} + +static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes ) +{ + UINT ret = 0, i; + + for (i = 0; i < bytes; i++) + ret += data[row][col + i] << i * 8; + + return ret; +} + +static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ) +{ + UINT r, i, n = 0, table_id, count, maxcount = *sz; + MSITABLE *table = NULL; + + TRACE("%s\n", debugstr_w(szTableName)); + + /* first check if there is a default table with that name */ + r = get_defaulttablecolumns( db, szTableName, colinfo, sz ); + if (r == ERROR_SUCCESS && *sz) + return r; + + r = get_table( db, szColumns, &table ); + if (r != ERROR_SUCCESS) + { + ERR("couldn't load _Columns table\n"); + return ERROR_FUNCTION_FAILED; + } + + /* convert table and column names to IDs from the string table */ + r = msi_string2idW( db->strings, szTableName, &table_id ); + if (r != ERROR_SUCCESS) + { + WARN("Couldn't find id for %s\n", debugstr_w(szTableName)); + return r; + } + TRACE("Table id is %d, row count is %d\n", table_id, table->row_count); + + /* Note: _Columns table doesn't have non-persistent data */ + + /* if maxcount is non-zero, assume it's exactly right for this table */ + memset( colinfo, 0, maxcount * sizeof(*colinfo) ); + count = table->row_count; + for (i = 0; i < count; i++) + { + if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue; + if (colinfo) + { + UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES ); + UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15); + + /* check the column number is in range */ + if (col < 1 || col > maxcount) + { + ERR("column %d out of range\n", col); + continue; + } + /* check if this column was already set */ + if (colinfo[col - 1].number) + { + ERR("duplicate column %d\n", col); + continue; + } + colinfo[col - 1].tablename = msi_string_lookup_id( db->strings, table_id ); + colinfo[col - 1].number = col; + colinfo[col - 1].colname = msi_string_lookup_id( db->strings, id ); + colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset, + sizeof(USHORT) ) - (1 << 15); + colinfo[col - 1].offset = 0; + colinfo[col - 1].ref_count = 0; + colinfo[col - 1].hash_table = NULL; + } + n++; + } + TRACE("%s has %d columns\n", debugstr_w(szTableName), n); + + if (colinfo && n != maxcount) + { + ERR("missing column in table %s\n", debugstr_w(szTableName)); + msi_free_colinfo( colinfo, maxcount ); + return ERROR_FUNCTION_FAILED; + } + table_calc_column_offsets( db, colinfo, n ); + *sz = n; + return ERROR_SUCCESS; +} + +UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, + MSICONDITION persistent ) +{ + enum StringPersistence string_persistence = (persistent) ? StringPersistent : StringNonPersistent; + UINT r, nField; + MSIVIEW *tv = NULL; + MSIRECORD *rec = NULL; + column_info *col; + MSITABLE *table; + UINT i; + + /* only add tables that don't exist already */ + if( TABLE_Exists(db, name ) ) + { + WARN("table %s exists\n", debugstr_w(name)); + return ERROR_BAD_QUERY_SYNTAX; + } + + table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) ); + if( !table ) + return ERROR_FUNCTION_FAILED; + + table->ref_count = 1; + table->row_count = 0; + table->data = NULL; + table->data_persistent = NULL; + table->colinfo = NULL; + table->col_count = 0; + table->persistent = persistent; + lstrcpyW( table->name, name ); + + for( col = col_info; col; col = col->next ) + table->col_count++; + + table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) ); + if (!table->colinfo) + { + free_table( table ); + return ERROR_FUNCTION_FAILED; + } + + for( i = 0, col = col_info; col; i++, col = col->next ) + { + UINT table_id = msi_addstringW( db->strings, col->table, -1, 1, string_persistence ); + UINT col_id = msi_addstringW( db->strings, col->column, -1, 1, string_persistence ); + + table->colinfo[ i ].tablename = msi_string_lookup_id( db->strings, table_id ); + table->colinfo[ i ].number = i + 1; + table->colinfo[ i ].colname = msi_string_lookup_id( db->strings, col_id ); + table->colinfo[ i ].type = col->type; + table->colinfo[ i ].offset = 0; + table->colinfo[ i ].ref_count = 0; + table->colinfo[ i ].hash_table = NULL; + table->colinfo[ i ].temporary = col->temporary; + } + table_calc_column_offsets( db, table->colinfo, table->col_count); + + r = TABLE_CreateView( db, szTables, &tv ); + TRACE("CreateView returned %x\n", r); + if( r ) + { + free_table( table ); + return r; + } + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + goto err; + + rec = MSI_CreateRecord( 1 ); + if( !rec ) + goto err; + + r = MSI_RecordSetStringW( rec, 1, name ); + if( r ) + goto err; + + r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE ); + TRACE("insert_row returned %x\n", r); + if( r ) + goto err; + + tv->ops->delete( tv ); + tv = NULL; + + msiobj_release( &rec->hdr ); + rec = NULL; + + if( persistent != MSICONDITION_FALSE ) + { + /* add each column to the _Columns table */ + r = TABLE_CreateView( db, szColumns, &tv ); + if( r ) + return r; + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + goto err; + + rec = MSI_CreateRecord( 4 ); + if( !rec ) + goto err; + + r = MSI_RecordSetStringW( rec, 1, name ); + if( r ) + goto err; + + /* + * need to set the table, column number, col name and type + * for each column we enter in the table + */ + nField = 1; + for( col = col_info; col; col = col->next ) + { + r = MSI_RecordSetInteger( rec, 2, nField ); + if( r ) + goto err; + + r = MSI_RecordSetStringW( rec, 3, col->column ); + if( r ) + goto err; + + r = MSI_RecordSetInteger( rec, 4, col->type ); + if( r ) + goto err; + + r = tv->ops->insert_row( tv, rec, -1, FALSE ); + if( r ) + goto err; + + nField++; + } + if( !col ) + r = ERROR_SUCCESS; + } + +err: + if (rec) + msiobj_release( &rec->hdr ); + /* FIXME: remove values from the string table on error */ + if( tv ) + tv->ops->delete( tv ); + + if (r == ERROR_SUCCESS) + list_add_head( &db->tables, &table->entry ); + else + free_table( table ); + + return r; +} + +static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref ) +{ + BYTE *rawdata = NULL; + UINT rawsize, i, j, row_size, row_count; + UINT r = ERROR_FUNCTION_FAILED; + + /* Nothing to do for non-persistent tables */ + if( t->persistent == MSICONDITION_FALSE ) + return ERROR_SUCCESS; + + TRACE("Saving %s\n", debugstr_w( t->name ) ); + + row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref ); + row_count = t->row_count; + for (i = 0; i < t->row_count; i++) + { + if (!t->data_persistent[i]) + { + row_count = 1; /* yes, this is bizarre */ + break; + } + } + rawsize = row_count * row_size; + rawdata = msi_alloc_zero( rawsize ); + if( !rawdata ) + { + r = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + + rawsize = 0; + for (i = 0; i < t->row_count; i++) + { + UINT ofs = 0, ofs_mem = 0; + + if (!t->data_persistent[i]) break; + + for (j = 0; j < t->col_count; j++) + { + UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); + UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref ); + UINT k; + + if (n != 2 && n != 3 && n != 4) + { + ERR("oops - unknown column width %d\n", n); + goto err; + } + if (t->colinfo[j].type & MSITYPE_STRING && n < m) + { + UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES ); + if (id > 1 << bytes_per_strref * 8) + { + ERR("string id %u out of range\n", id); + goto err; + } + } + for (k = 0; k < n; k++) + { + rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k]; + } + ofs_mem += m; + ofs += n; + } + rawsize += row_size; + } + + TRACE("writing %d bytes\n", rawsize); + r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE ); + +err: + msi_free( rawdata ); + return r; +} + +static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name ) +{ + MSITABLE *table; + UINT size, offset, old_count; + UINT n; + + table = find_cached_table( db, name ); + old_count = table->col_count; + msi_free_colinfo( table->colinfo, table->col_count ); + msi_free( table->colinfo ); + table->colinfo = NULL; + + table_get_column_info( db, name, &table->colinfo, &table->col_count ); + if (!table->col_count) return; + + size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES ); + offset = table->colinfo[table->col_count - 1].offset; + + for ( n = 0; n < table->row_count; n++ ) + { + table->data[n] = msi_realloc( table->data[n], size ); + if (old_count < table->col_count) + memset( &table->data[n][offset], 0, size - offset ); + } +} + +/* try to find the table name in the _Tables table */ +BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) +{ + UINT r, table_id, i; + MSITABLE *table; + + if( !strcmpW( name, szTables ) || !strcmpW( name, szColumns ) || + !strcmpW( name, szStreams ) || !strcmpW( name, szStorages ) ) + return TRUE; + + r = msi_string2idW( db->strings, name, &table_id ); + if( r != ERROR_SUCCESS ) + { + TRACE("Couldn't find id for %s\n", debugstr_w(name)); + return FALSE; + } + + r = get_table( db, szTables, &table ); + if( r != ERROR_SUCCESS ) + { + ERR("table %s not available\n", debugstr_w(szTables)); + return FALSE; + } + + for( i = 0; i < table->row_count; i++ ) + { + if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id ) + return TRUE; + } + + return FALSE; +} + +/* below is the query interface to a table */ + +typedef struct tagMSITABLEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSITABLE *table; + MSICOLUMNINFO *columns; + UINT num_cols; + UINT row_size; + WCHAR name[1]; +} MSITABLEVIEW; + +static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT offset, n; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + /* how many rows are there ? */ + if( row >= tv->table->row_count ) + return ERROR_NO_MORE_ITEMS; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); + if (n != 2 && n != 3 && n != 4) + { + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + + offset = tv->columns[col-1].offset; + *val = read_table_int(tv->table->data, row, offset, n); + + /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */ + + return ERROR_SUCCESS; +} + +static UINT msi_stream_name( const MSITABLEVIEW *tv, UINT row, LPWSTR *pstname ) +{ + LPWSTR p, stname = NULL; + UINT i, r, type, ival; + DWORD len; + LPCWSTR sval; + MSIVIEW *view = (MSIVIEW *) tv; + + TRACE("%p %d\n", tv, row); + + len = lstrlenW( tv->name ) + 1; + stname = msi_alloc( len*sizeof(WCHAR) ); + if ( !stname ) + { + r = ERROR_OUTOFMEMORY; + goto err; + } + + lstrcpyW( stname, tv->name ); + + for ( i = 0; i < tv->num_cols; i++ ) + { + type = tv->columns[i].type; + if ( type & MSITYPE_KEY ) + { + WCHAR number[0x20]; + + r = TABLE_fetch_int( view, row, i+1, &ival ); + if ( r != ERROR_SUCCESS ) + goto err; + + if ( tv->columns[i].type & MSITYPE_STRING ) + { + sval = msi_string_lookup_id( tv->db->strings, ival ); + if ( !sval ) + { + r = ERROR_INVALID_PARAMETER; + goto err; + } + } + else + { + static const WCHAR fmt[] = { '%','d',0 }; + UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES ); + + switch( n ) + { + case 2: + sprintfW( number, fmt, ival-0x8000 ); + break; + case 4: + sprintfW( number, fmt, ival^0x80000000 ); + break; + default: + ERR( "oops - unknown column width %d\n", n ); + r = ERROR_FUNCTION_FAILED; + goto err; + } + sval = number; + } + + len += lstrlenW( szDot ) + lstrlenW( sval ); + p = msi_realloc ( stname, len*sizeof(WCHAR) ); + if ( !p ) + { + r = ERROR_OUTOFMEMORY; + goto err; + } + stname = p; + + lstrcatW( stname, szDot ); + lstrcatW( stname, sval ); + } + else + continue; + } + + *pstname = stname; + return ERROR_SUCCESS; + +err: + msi_free( stname ); + *pstname = NULL; + return r; +} + +/* + * We need a special case for streams, as we need to reference column with + * the name of the stream in the same table, and the table name + * which may not be available at higher levels of the query + */ +static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r; + LPWSTR encname, full_name = NULL; + + if( !view->ops->fetch_int ) + return ERROR_INVALID_PARAMETER; + + r = msi_stream_name( tv, row, &full_name ); + if ( r != ERROR_SUCCESS ) + { + ERR("fetching stream, error = %d\n", r); + return r; + } + + encname = encode_streamname( FALSE, full_name ); + r = msi_get_raw_stream( tv->db, encname, stm ); + if( r ) + ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r); + + msi_free( full_name ); + msi_free( encname ); + return r; +} + +static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val ) +{ + UINT offset, n, i; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + if( row >= tv->table->row_count ) + return ERROR_INVALID_PARAMETER; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + msi_free( tv->columns[col-1].hash_table ); + tv->columns[col-1].hash_table = NULL; + + n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); + if ( n != 2 && n != 3 && n != 4 ) + { + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + + offset = tv->columns[col-1].offset; + for ( i = 0; i < n; i++ ) + tv->table->data[row][offset + i] = (val >> i * 8) & 0xff; + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW *)view; + + if (!tv->table) + return ERROR_INVALID_PARAMETER; + + return msi_view_get_row(tv->db, view, row, rec); +} + +static UINT msi_addstreamW( MSIDATABASE *db, LPCWSTR name, IStream *data ) +{ + static const WCHAR insert[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ', + '`','_','S','t','r','e','a','m','s','`',' ', + '(','`','N','a','m','e','`',',','`','D','a','t','a','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; + MSIQUERY *query = NULL; + MSIRECORD *rec; + UINT r; + + TRACE("%p %s %p\n", db, debugstr_w(name), data); + + rec = MSI_CreateRecord( 2 ); + if ( !rec ) + return ERROR_OUTOFMEMORY; + + r = MSI_RecordSetStringW( rec, 1, name ); + if ( r != ERROR_SUCCESS ) + goto err; + + r = MSI_RecordSetIStream( rec, 2, data ); + if ( r != ERROR_SUCCESS ) + goto err; + + r = MSI_DatabaseOpenViewW( db, insert, &query ); + if ( r != ERROR_SUCCESS ) + goto err; + + r = MSI_ViewExecute( query, rec ); + +err: + msiobj_release( &query->hdr ); + msiobj_release( &rec->hdr ); + return r; +} + +static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue ) +{ + MSICOLUMNINFO columninfo; + UINT r; + + if ( (iField <= 0) || + (iField > tv->num_cols) || + MSI_RecordIsNull( rec, iField ) ) + return ERROR_FUNCTION_FAILED; + + columninfo = tv->columns[ iField - 1 ]; + + if ( MSITYPE_IS_BINARY(columninfo.type) ) + { + *pvalue = 1; /* refers to the first key column */ + } + else if ( columninfo.type & MSITYPE_STRING ) + { + LPCWSTR sval = MSI_RecordGetString( rec, iField ); + if (sval) + { + r = msi_string2idW(tv->db->strings, sval, pvalue); + if (r != ERROR_SUCCESS) + return ERROR_NOT_FOUND; + } + else *pvalue = 0; + } + else if ( bytes_per_column( tv->db, &columninfo, LONG_STR_BYTES ) == 2 ) + { + *pvalue = 0x8000 + MSI_RecordGetInteger( rec, iField ); + if ( *pvalue & 0xffff0000 ) + { + ERR("field %u value %d out of range\n", iField, *pvalue - 0x8000); + return ERROR_FUNCTION_FAILED; + } + } + else + { + INT ival = MSI_RecordGetInteger( rec, iField ); + *pvalue = ival ^ 0x80000000; + } + + return ERROR_SUCCESS; +} + +static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT i, val, r = ERROR_SUCCESS; + + if ( !tv->table ) + return ERROR_INVALID_PARAMETER; + + /* test if any of the mask bits are invalid */ + if ( mask >= (1<<tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + for ( i = 0; i < tv->num_cols; i++ ) + { + BOOL persistent; + + /* only update the fields specified in the mask */ + if ( !(mask&(1<<i)) ) + continue; + + persistent = (tv->table->persistent != MSICONDITION_FALSE) && + (tv->table->data_persistent[row]); + /* FIXME: should we allow updating keys? */ + + val = 0; + if ( !MSI_RecordIsNull( rec, i + 1 ) ) + { + r = get_table_value_from_record (tv, rec, i + 1, &val); + if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) ) + { + IStream *stm; + LPWSTR stname; + + if ( r != ERROR_SUCCESS ) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordGetIStream( rec, i + 1, &stm ); + if ( r != ERROR_SUCCESS ) + return r; + + r = msi_stream_name( tv, row, &stname ); + if ( r != ERROR_SUCCESS ) + { + IStream_Release( stm ); + return r; + } + + r = msi_addstreamW( tv->db, stname, stm ); + IStream_Release( stm ); + msi_free ( stname ); + + if ( r != ERROR_SUCCESS ) + return r; + } + else if ( tv->columns[i].type & MSITYPE_STRING ) + { + UINT x; + + if ( r != ERROR_SUCCESS ) + { + LPCWSTR sval = MSI_RecordGetString( rec, i + 1 ); + val = msi_addstringW( tv->db->strings, sval, -1, 1, + persistent ? StringPersistent : StringNonPersistent ); + } + else + { + TABLE_fetch_int(&tv->view, row, i + 1, &x); + if (val == x) + continue; + } + } + else + { + if ( r != ERROR_SUCCESS ) + return ERROR_FUNCTION_FAILED; + } + } + + r = TABLE_set_int( tv, row, i+1, val ); + if ( r != ERROR_SUCCESS ) + break; + } + return r; +} + +static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + BYTE **p, *row; + BOOL *b; + UINT sz; + BYTE ***data_ptr; + BOOL **data_persist_ptr; + UINT *row_count; + + TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE"); + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + row = msi_alloc_zero( tv->row_size ); + if( !row ) + return ERROR_NOT_ENOUGH_MEMORY; + + row_count = &tv->table->row_count; + data_ptr = &tv->table->data; + data_persist_ptr = &tv->table->data_persistent; + if (*num == -1) + *num = tv->table->row_count; + + sz = (*row_count + 1) * sizeof (BYTE*); + if( *data_ptr ) + p = msi_realloc( *data_ptr, sz ); + else + p = msi_alloc( sz ); + if( !p ) + { + msi_free( row ); + return ERROR_NOT_ENOUGH_MEMORY; + } + + sz = (*row_count + 1) * sizeof (BOOL); + if( *data_persist_ptr ) + b = msi_realloc( *data_persist_ptr, sz ); + else + b = msi_alloc( sz ); + if( !b ) + { + msi_free( row ); + msi_free( p ); + return ERROR_NOT_ENOUGH_MEMORY; + } + + *data_ptr = p; + (*data_ptr)[*row_count] = row; + + *data_persist_ptr = b; + (*data_persist_ptr)[*row_count] = !temporary; + + (*row_count)++; + + return ERROR_SUCCESS; +} + +static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %p\n", tv, record); + + TRACE("There are %d columns\n", tv->num_cols ); + + return ERROR_SUCCESS; +} + +static UINT TABLE_close( struct tagMSIVIEW *view ) +{ + TRACE("%p\n", view ); + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %p %p\n", view, rows, cols ); + + if( cols ) + *cols = tv->num_cols; + if( rows ) + { + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + *rows = tv->table->row_count; + } + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, + LPCWSTR *table_name ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %d %p %p\n", tv, n, name, type ); + + if( ( n == 0 ) || ( n > tv->num_cols ) ) + return ERROR_INVALID_PARAMETER; + + if( name ) + { + *name = tv->columns[n-1].colname; + if( !*name ) + return ERROR_FUNCTION_FAILED; + } + + if( table_name ) + { + *table_name = tv->columns[n-1].tablename; + if( !*table_name ) + return ERROR_FUNCTION_FAILED; + } + + if( type ) + *type = tv->columns[n-1].type; + + if( temporary ) + *temporary = tv->columns[n-1].temporary; + + return ERROR_SUCCESS; +} + +static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ); + +static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column ) +{ + UINT r, row, i; + + /* check there's no null values where they're not allowed */ + for( i = 0; i < tv->num_cols; i++ ) + { + if ( tv->columns[i].type & MSITYPE_NULLABLE ) + continue; + + if ( MSITYPE_IS_BINARY(tv->columns[i].type) ) + TRACE("skipping binary column\n"); + else if ( tv->columns[i].type & MSITYPE_STRING ) + { + LPCWSTR str; + + str = MSI_RecordGetString( rec, i+1 ); + if (str == NULL || str[0] == 0) + { + if (column) *column = i; + return ERROR_INVALID_DATA; + } + } + else + { + UINT n; + + n = MSI_RecordGetInteger( rec, i+1 ); + if (n == MSI_NULL_INTEGER) + { + if (column) *column = i; + return ERROR_INVALID_DATA; + } + } + } + + /* check there's no duplicate keys */ + r = msi_table_find_row( tv, rec, &row, column ); + if (r == ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + return ERROR_SUCCESS; +} + +static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec ) +{ + UINT r, i, ivalue, x; + + for (i = 0; i < tv->num_cols; i++ ) + { + if (!(tv->columns[i].type & MSITYPE_KEY)) continue; + + r = get_table_value_from_record( tv, rec, i + 1, &ivalue ); + if (r != ERROR_SUCCESS) + return 1; + + r = TABLE_fetch_int( &tv->view, row, i + 1, &x ); + if (r != ERROR_SUCCESS) + { + WARN("TABLE_fetch_int should not fail here %u\n", r); + return -1; + } + if (ivalue > x) + { + return 1; + } + else if (ivalue == x) + { + if (i < tv->num_cols - 1) continue; + return 0; + } + else + return -1; + } + return 1; +} + +static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec ) +{ + int idx, c, low = 0, high = tv->table->row_count - 1; + + TRACE("%p %p\n", tv, rec); + + while (low <= high) + { + idx = (low + high) / 2; + c = compare_record( tv, idx, rec ); + + if (c < 0) + high = idx - 1; + else if (c > 0) + low = idx + 1; + else + { + TRACE("found %u\n", idx); + return idx; + } + } + TRACE("found %u\n", high + 1); + return high + 1; +} + +static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT i, r; + + TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" ); + + /* check that the key is unique - can we find a matching row? */ + r = table_validate_new( tv, rec, NULL ); + if( r != ERROR_SUCCESS ) + return ERROR_FUNCTION_FAILED; + + if (row == -1) + row = find_insert_index( tv, rec ); + + r = table_create_new_row( view, &row, temporary ); + TRACE("insert_row returned %08x\n", r); + if( r != ERROR_SUCCESS ) + return r; + + /* shift the rows to make room for the new row */ + for (i = tv->table->row_count - 1; i > row; i--) + { + memmove(&(tv->table->data[i][0]), + &(tv->table->data[i - 1][0]), tv->row_size); + tv->table->data_persistent[i] = tv->table->data_persistent[i - 1]; + } + + /* Re-set the persistence flag */ + tv->table->data_persistent[row] = !temporary; + return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 ); +} + +static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r, num_rows, num_cols, i; + + TRACE("%p %d\n", tv, row); + + if ( !tv->table ) + return ERROR_INVALID_PARAMETER; + + r = TABLE_get_dimensions( view, &num_rows, &num_cols ); + if ( r != ERROR_SUCCESS ) + return r; + + if ( row >= num_rows ) + return ERROR_FUNCTION_FAILED; + + num_rows = tv->table->row_count; + tv->table->row_count--; + + /* reset the hash tables */ + for (i = 0; i < tv->num_cols; i++) + { + msi_free( tv->columns[i].hash_table ); + tv->columns[i].hash_table = NULL; + } + + for (i = row + 1; i < num_rows; i++) + { + memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size); + tv->table->data_persistent[i - 1] = tv->table->data_persistent[i]; + } + + msi_free(tv->table->data[num_rows - 1]); + + return ERROR_SUCCESS; +} + +static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW *)view; + UINT r, new_row; + + /* FIXME: MsiViewFetch should set rec index 0 to some ID that + * sets the fetched record apart from other records + */ + + if (!tv->table) + return ERROR_INVALID_PARAMETER; + + r = msi_table_find_row(tv, rec, &new_row, NULL); + if (r != ERROR_SUCCESS) + { + ERR("can't find row to modify\n"); + return ERROR_FUNCTION_FAILED; + } + + /* the row cannot be changed */ + if (row != new_row + 1) + return ERROR_FUNCTION_FAILED; + + return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1); +} + +static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW *)view; + UINT r, row; + + if (!tv->table) + return ERROR_INVALID_PARAMETER; + + r = msi_table_find_row(tv, rec, &row, NULL); + if (r == ERROR_SUCCESS) + return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1); + else + return TABLE_insert_row( view, rec, -1, FALSE ); +} + +static UINT modify_delete_row( struct tagMSIVIEW *view, MSIRECORD *rec ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW *)view; + UINT row, r; + + r = msi_table_find_row(tv, rec, &row, NULL); + if (r != ERROR_SUCCESS) + return r; + + return TABLE_delete_row(view, row); +} + +static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row ) +{ + MSIRECORD *curr; + UINT r, i, count; + + r = TABLE_get_row(view, row - 1, &curr); + if (r != ERROR_SUCCESS) + return r; + + /* Close the original record */ + MSI_CloseRecord(&rec->hdr); + + count = MSI_RecordGetFieldCount(rec); + for (i = 0; i < count; i++) + MSI_RecordCopyField(curr, i + 1, rec, i + 1); + + msiobj_release(&curr->hdr); + return ERROR_SUCCESS; +} + +static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r, frow, column; + + TRACE("%p %d %p\n", view, eModifyMode, rec ); + + switch (eModifyMode) + { + case MSIMODIFY_DELETE: + r = modify_delete_row( view, rec ); + break; + case MSIMODIFY_VALIDATE_NEW: + r = table_validate_new( tv, rec, &column ); + if (r != ERROR_SUCCESS) + { + tv->view.error = MSIDBERROR_DUPLICATEKEY; + tv->view.error_column = tv->columns[column].colname; + r = ERROR_INVALID_DATA; + } + break; + + case MSIMODIFY_INSERT: + r = table_validate_new( tv, rec, NULL ); + if (r != ERROR_SUCCESS) + break; + r = TABLE_insert_row( view, rec, -1, FALSE ); + break; + + case MSIMODIFY_INSERT_TEMPORARY: + r = table_validate_new( tv, rec, NULL ); + if (r != ERROR_SUCCESS) + break; + r = TABLE_insert_row( view, rec, -1, TRUE ); + break; + + case MSIMODIFY_REFRESH: + r = msi_refresh_record( view, rec, row ); + break; + + case MSIMODIFY_UPDATE: + r = msi_table_update( view, rec, row ); + break; + + case MSIMODIFY_ASSIGN: + r = msi_table_assign( view, rec ); + break; + + case MSIMODIFY_MERGE: + /* check row that matches this record */ + r = msi_table_find_row( tv, rec, &frow, &column ); + if (r != ERROR_SUCCESS) + { + r = table_validate_new( tv, rec, NULL ); + if (r == ERROR_SUCCESS) + r = TABLE_insert_row( view, rec, -1, FALSE ); + } + break; + + case MSIMODIFY_REPLACE: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_DELETE: + FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + r = ERROR_INVALID_DATA; + } + + return r; +} + +static UINT TABLE_delete( struct tagMSIVIEW *view ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p\n", view ); + + tv->table = NULL; + tv->columns = NULL; + + msi_free( tv ); + + return ERROR_SUCCESS; +} + +static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + const MSICOLUMNHASHENTRY *entry; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col > tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + if( !tv->columns[col-1].hash_table ) + { + UINT i; + UINT num_rows = tv->table->row_count; + MSICOLUMNHASHENTRY **hash_table; + MSICOLUMNHASHENTRY *new_entry; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + /* allocate contiguous memory for the table and its entries so we + * don't have to do an expensive cleanup */ + hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) + + num_rows * sizeof(MSICOLUMNHASHENTRY)); + if (!hash_table) + return ERROR_OUTOFMEMORY; + + memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*)); + tv->columns[col-1].hash_table = hash_table; + + new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE); + + for (i = 0; i < num_rows; i++, new_entry++) + { + UINT row_value; + + if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS) + continue; + + new_entry->next = NULL; + new_entry->value = row_value; + new_entry->row = i; + if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE]) + { + MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE]; + while (prev_entry->next) + prev_entry = prev_entry->next; + prev_entry->next = new_entry; + } + else + hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry; + } + } + + if( !*handle ) + entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE]; + else + entry = (*handle)->next; + + while (entry && entry->value != val) + entry = entry->next; + + *handle = entry; + if (!entry) + return ERROR_NO_MORE_ITEMS; + + *row = entry->row; + + return ERROR_SUCCESS; +} + +static UINT TABLE_add_ref(struct tagMSIVIEW *view) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT i; + + TRACE("%p %d\n", view, tv->table->ref_count); + + for (i = 0; i < tv->table->col_count; i++) + { + if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY) + InterlockedIncrement(&tv->table->colinfo[i].ref_count); + } + + return InterlockedIncrement(&tv->table->ref_count); +} + +static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + MSIRECORD *rec = NULL; + MSIVIEW *columns = NULL; + UINT row, r; + + rec = MSI_CreateRecord(2); + if (!rec) + return ERROR_OUTOFMEMORY; + + MSI_RecordSetStringW(rec, 1, table); + MSI_RecordSetInteger(rec, 2, number); + + r = TABLE_CreateView(tv->db, szColumns, &columns); + if (r != ERROR_SUCCESS) + return r; + + r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = TABLE_delete_row(columns, row); + if (r != ERROR_SUCCESS) + goto done; + + msi_update_table_columns(tv->db, table); + +done: + msiobj_release(&rec->hdr); + columns->ops->delete(columns); + return r; +} + +static UINT TABLE_release(struct tagMSIVIEW *view) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + INT ref = tv->table->ref_count; + UINT i, r; + + TRACE("%p %d\n", view, ref); + + for (i = 0; i < tv->table->col_count; i++) + { + if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY) + { + ref = InterlockedDecrement(&tv->table->colinfo[i].ref_count); + if (ref == 0) + { + r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, + tv->table->colinfo[i].number); + if (r != ERROR_SUCCESS) + break; + } + } + } + + ref = InterlockedDecrement(&tv->table->ref_count); + if (ref == 0) + { + if (!tv->table->row_count) + { + list_remove(&tv->table->entry); + free_table(tv->table); + TABLE_delete(view); + } + } + + return ref; +} + +static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number, + LPCWSTR column, UINT type, BOOL hold) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + MSITABLE *msitable; + MSIRECORD *rec; + UINT r, i; + + rec = MSI_CreateRecord(4); + if (!rec) + return ERROR_OUTOFMEMORY; + + MSI_RecordSetStringW(rec, 1, table); + MSI_RecordSetInteger(rec, 2, number); + MSI_RecordSetStringW(rec, 3, column); + MSI_RecordSetInteger(rec, 4, type); + + r = TABLE_insert_row(&tv->view, rec, -1, FALSE); + if (r != ERROR_SUCCESS) + goto done; + + msi_update_table_columns(tv->db, table); + + if (!hold) + goto done; + + msitable = find_cached_table(tv->db, table); + for (i = 0; i < msitable->col_count; i++) + { + if (!strcmpW( msitable->colinfo[i].colname, column )) + { + InterlockedIncrement(&msitable->colinfo[i].ref_count); + break; + } + } + +done: + msiobj_release(&rec->hdr); + return r; +} + +static UINT TABLE_drop(struct tagMSIVIEW *view) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + MSIVIEW *tables = NULL; + MSIRECORD *rec = NULL; + UINT r, row; + INT i; + + TRACE("dropping table %s\n", debugstr_w(tv->name)); + + for (i = tv->table->col_count - 1; i >= 0; i--) + { + r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, + tv->table->colinfo[i].number); + if (r != ERROR_SUCCESS) + return r; + } + + rec = MSI_CreateRecord(1); + if (!rec) + return ERROR_OUTOFMEMORY; + + MSI_RecordSetStringW(rec, 1, tv->name); + + r = TABLE_CreateView(tv->db, szTables, &tables); + if (r != ERROR_SUCCESS) + return r; + + r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = TABLE_delete_row(tables, row); + if (r != ERROR_SUCCESS) + goto done; + + list_remove(&tv->table->entry); + free_table(tv->table); + +done: + msiobj_release(&rec->hdr); + tables->ops->delete(tables); + + return r; +} + +static const MSIVIEWOPS table_ops = +{ + TABLE_fetch_int, + TABLE_fetch_stream, + TABLE_get_row, + TABLE_set_row, + TABLE_insert_row, + TABLE_delete_row, + TABLE_execute, + TABLE_close, + TABLE_get_dimensions, + TABLE_get_column_info, + TABLE_modify, + TABLE_delete, + TABLE_find_matching_rows, + TABLE_add_ref, + TABLE_release, + TABLE_add_column, + TABLE_remove_column, + NULL, + TABLE_drop, +}; + +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) +{ + MSITABLEVIEW *tv ; + UINT r, sz; + + TRACE("%p %s %p\n", db, debugstr_w(name), view ); + + if ( !strcmpW( name, szStreams ) ) + return STREAMS_CreateView( db, view ); + else if ( !strcmpW( name, szStorages ) ) + return STORAGES_CreateView( db, view ); + + sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ; + tv = msi_alloc_zero( sz ); + if( !tv ) + return ERROR_FUNCTION_FAILED; + + r = get_table( db, name, &tv->table ); + if( r != ERROR_SUCCESS ) + { + msi_free( tv ); + WARN("table not found\n"); + return r; + } + + TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count); + + /* fill the structure */ + tv->view.ops = &table_ops; + tv->db = db; + tv->columns = tv->table->colinfo; + tv->num_cols = tv->table->col_count; + tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES ); + + TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size ); + + *view = (MSIVIEW*) tv; + lstrcpyW( tv->name, name ); + + return ERROR_SUCCESS; +} + +UINT MSI_CommitTables( MSIDATABASE *db ) +{ + UINT r, bytes_per_strref; + HRESULT hr; + MSITABLE *table = NULL; + + TRACE("%p\n",db); + + r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref ); + if( r != ERROR_SUCCESS ) + { + WARN("failed to save string table r=%08x\n",r); + return r; + } + + LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry ) + { + r = save_table( db, table, bytes_per_strref ); + if( r != ERROR_SUCCESS ) + { + WARN("failed to save table %s (r=%08x)\n", + debugstr_w(table->name), r); + return r; + } + } + + hr = IStorage_Commit( db->storage, 0 ); + if (FAILED( hr )) + { + WARN("failed to commit changes 0x%08x\n", hr); + r = ERROR_FUNCTION_FAILED; + } + return r; +} + +MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ) +{ + MSITABLE *t; + UINT r; + + TRACE("%p %s\n", db, debugstr_w(table)); + + if (!table) + return MSICONDITION_ERROR; + + r = get_table( db, table, &t ); + if (r != ERROR_SUCCESS) + return MSICONDITION_NONE; + + return t->persistent; +} + +static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes) +{ + UINT ret = 0, i; + + for (i = 0; i < bytes; i++) + ret += (data[col + i] << i * 8); + + return ret; +} + +static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname ) +{ + LPWSTR stname = NULL, sval, p; + DWORD len; + UINT i, r; + + TRACE("%p %p\n", tv, rec); + + len = lstrlenW( tv->name ) + 1; + stname = msi_alloc( len*sizeof(WCHAR) ); + if ( !stname ) + { + r = ERROR_OUTOFMEMORY; + goto err; + } + + lstrcpyW( stname, tv->name ); + + for ( i = 0; i < tv->num_cols; i++ ) + { + if ( tv->columns[i].type & MSITYPE_KEY ) + { + sval = msi_dup_record_field( rec, i + 1 ); + if ( !sval ) + { + r = ERROR_OUTOFMEMORY; + goto err; + } + + len += lstrlenW( szDot ) + lstrlenW ( sval ); + p = msi_realloc ( stname, len*sizeof(WCHAR) ); + if ( !p ) + { + r = ERROR_OUTOFMEMORY; + goto err; + } + stname = p; + + lstrcatW( stname, szDot ); + lstrcatW( stname, sval ); + + msi_free( sval ); + } + else + continue; + } + + *pstname = encode_streamname( FALSE, stname ); + msi_free( stname ); + + return ERROR_SUCCESS; + +err: + msi_free ( stname ); + *pstname = NULL; + return r; +} + +static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st, + IStorage *stg, + const BYTE *rawdata, UINT bytes_per_strref ) +{ + UINT i, val, ofs = 0; + USHORT mask; + MSICOLUMNINFO *columns = tv->columns; + MSIRECORD *rec; + + mask = rawdata[0] | (rawdata[1] << 8); + rawdata += 2; + + rec = MSI_CreateRecord( tv->num_cols ); + if( !rec ) + return rec; + + TRACE("row ->\n"); + for( i=0; i<tv->num_cols; i++ ) + { + if ( (mask&1) && (i>=(mask>>8)) ) + break; + /* all keys must be present */ + if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) ) + continue; + + if( MSITYPE_IS_BINARY(tv->columns[i].type) ) + { + LPWSTR encname; + IStream *stm = NULL; + UINT r; + + ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref ); + + r = msi_record_encoded_stream_name( tv, rec, &encname ); + if ( r != ERROR_SUCCESS ) + return NULL; + + r = IStorage_OpenStream( stg, encname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); + if ( r != ERROR_SUCCESS ) + { + msi_free( encname ); + return NULL; + } + + MSI_RecordSetStream( rec, i+1, stm ); + TRACE(" field %d [%s]\n", i+1, debugstr_w(encname)); + msi_free( encname ); + } + else if( columns[i].type & MSITYPE_STRING ) + { + LPCWSTR sval; + + val = read_raw_int(rawdata, ofs, bytes_per_strref); + sval = msi_string_lookup_id( st, val ); + MSI_RecordSetStringW( rec, i+1, sval ); + TRACE(" field %d [%s]\n", i+1, debugstr_w(sval)); + ofs += bytes_per_strref; + } + else + { + UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref ); + switch( n ) + { + case 2: + val = read_raw_int(rawdata, ofs, n); + if (val) + MSI_RecordSetInteger( rec, i+1, val-0x8000 ); + TRACE(" field %d [0x%04x]\n", i+1, val ); + break; + case 4: + val = read_raw_int(rawdata, ofs, n); + if (val) + MSI_RecordSetInteger( rec, i+1, val^0x80000000 ); + TRACE(" field %d [0x%08x]\n", i+1, val ); + break; + default: + ERR("oops - unknown column width %d\n", n); + break; + } + ofs += n; + } + } + return rec; +} + +static void dump_record( MSIRECORD *rec ) +{ + UINT i, n; + + n = MSI_RecordGetFieldCount( rec ); + for( i=1; i<=n; i++ ) + { + LPCWSTR sval; + + if( MSI_RecordIsNull( rec, i ) ) + TRACE("row -> []\n"); + else if( (sval = MSI_RecordGetString( rec, i )) ) + TRACE("row -> [%s]\n", debugstr_w(sval)); + else + TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) ); + } +} + +static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize ) +{ + LPCWSTR sval; + UINT i; + + for( i=0; i<(rawsize/2); i++ ) + { + sval = msi_string_lookup_id( st, rawdata[i] ); + MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) ); + } +} + +static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec ) +{ + LPCWSTR str; + UINT i, r, *data; + + data = msi_alloc( tv->num_cols *sizeof (UINT) ); + for( i=0; i<tv->num_cols; i++ ) + { + data[i] = 0; + + if ( ~tv->columns[i].type & MSITYPE_KEY ) + continue; + + /* turn the transform column value into a row value */ + if ( ( tv->columns[i].type & MSITYPE_STRING ) && + ! MSITYPE_IS_BINARY(tv->columns[i].type) ) + { + str = MSI_RecordGetString( rec, i+1 ); + if (str) + { + r = msi_string2idW( tv->db->strings, str, &data[i] ); + + /* if there's no matching string in the string table, + these keys can't match any record, so fail now. */ + if (r != ERROR_SUCCESS) + { + msi_free( data ); + return NULL; + } + } + else data[i] = 0; + } + else + { + data[i] = MSI_RecordGetInteger( rec, i+1 ); + + if (data[i] == MSI_NULL_INTEGER) + data[i] = 0; + else if ((tv->columns[i].type&0xff) == 2) + data[i] += 0x8000; + else + data[i] += 0x80000000; + } + } + return data; +} + +static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column ) +{ + UINT i, r, x, ret = ERROR_FUNCTION_FAILED; + + for( i=0; i<tv->num_cols; i++ ) + { + if ( ~tv->columns[i].type & MSITYPE_KEY ) + continue; + + /* turn the transform column value into a row value */ + r = TABLE_fetch_int( &tv->view, row, i+1, &x ); + if ( r != ERROR_SUCCESS ) + { + ERR("TABLE_fetch_int shouldn't fail here\n"); + break; + } + + /* if this key matches, move to the next column */ + if ( x != data[i] ) + { + ret = ERROR_FUNCTION_FAILED; + break; + } + if (column) *column = i; + ret = ERROR_SUCCESS; + } + return ret; +} + +static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ) +{ + UINT i, r = ERROR_FUNCTION_FAILED, *data; + + data = msi_record_to_row( tv, rec ); + if( !data ) + return r; + for( i = 0; i < tv->table->row_count; i++ ) + { + r = msi_row_matches( tv, i, data, column ); + if( r == ERROR_SUCCESS ) + { + *row = i; + break; + } + } + msi_free( data ); + return r; +} + +typedef struct +{ + struct list entry; + LPWSTR name; +} TRANSFORMDATA; + +static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, + string_table *st, TRANSFORMDATA *transform, + UINT bytes_per_strref ) +{ + BYTE *rawdata = NULL; + MSITABLEVIEW *tv = NULL; + UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0; + MSIRECORD *rec = NULL; + WCHAR coltable[32]; + const WCHAR *name; + + if (!transform) + return ERROR_SUCCESS; + + name = transform->name; + + coltable[0] = 0; + TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) ); + + /* read the transform data */ + read_stream_data( stg, name, TRUE, &rawdata, &rawsize ); + if ( !rawdata ) + { + TRACE("table %s empty\n", debugstr_w(name) ); + return ERROR_INVALID_TABLE; + } + + /* create a table view */ + r = TABLE_CreateView( db, name, (MSIVIEW**) &tv ); + if( r != ERROR_SUCCESS ) + goto err; + + r = tv->view.ops->execute( &tv->view, NULL ); + if( r != ERROR_SUCCESS ) + goto err; + + TRACE("name = %s columns = %u row_size = %u raw size = %u\n", + debugstr_w(name), tv->num_cols, tv->row_size, rawsize ); + + /* interpret the data */ + for (n = 0; n < rawsize;) + { + mask = rawdata[n] | (rawdata[n + 1] << 8); + if (mask & 1) + { + /* + * if the low bit is set, columns are continuous and + * the number of columns is specified in the high byte + */ + sz = 2; + num_cols = mask >> 8; + for (i = 0; i < num_cols; i++) + { + if( (tv->columns[i].type & MSITYPE_STRING) && + ! MSITYPE_IS_BINARY(tv->columns[i].type) ) + sz += bytes_per_strref; + else + sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); + } + } + else + { + /* + * If the low bit is not set, mask is a bitmask. + * Excepting for key fields, which are always present, + * each bit indicates that a field is present in the transform record. + * + * mask == 0 is a special case ... only the keys will be present + * and it means that this row should be deleted. + */ + sz = 2; + num_cols = tv->num_cols; + for (i = 0; i < num_cols; i++) + { + if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask)) + { + if ((tv->columns[i].type & MSITYPE_STRING) && + !MSITYPE_IS_BINARY(tv->columns[i].type)) + sz += bytes_per_strref; + else + sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); + } + } + } + + /* check we didn't run of the end of the table */ + if (n + sz > rawsize) + { + ERR("borked.\n"); + dump_table( st, (USHORT *)rawdata, rawsize ); + break; + } + + rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref ); + if (rec) + { + WCHAR table[32]; + DWORD sz = 32; + UINT number = MSI_NULL_INTEGER; + UINT row = 0; + + if (!strcmpW( name, szColumns )) + { + MSI_RecordGetStringW( rec, 1, table, &sz ); + number = MSI_RecordGetInteger( rec, 2 ); + + /* + * Native msi seems writes nul into the Number (2nd) column of + * the _Columns table, only when the columns are from a new table + */ + if ( number == MSI_NULL_INTEGER ) + { + /* reset the column number on a new table */ + if (strcmpW( coltable, table )) + { + colcol = 0; + lstrcpyW( coltable, table ); + } + + /* fix nul column numbers */ + MSI_RecordSetInteger( rec, 2, ++colcol ); + } + } + + if (TRACE_ON(msidb)) dump_record( rec ); + + r = msi_table_find_row( tv, rec, &row, NULL ); + if (r == ERROR_SUCCESS) + { + if (!mask) + { + TRACE("deleting row [%d]:\n", row); + r = TABLE_delete_row( &tv->view, row ); + if (r != ERROR_SUCCESS) + WARN("failed to delete row %u\n", r); + } + else if (mask & 1) + { + TRACE("modifying full row [%d]:\n", row); + r = TABLE_set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 ); + if (r != ERROR_SUCCESS) + WARN("failed to modify row %u\n", r); + } + else + { + TRACE("modifying masked row [%d]:\n", row); + r = TABLE_set_row( &tv->view, row, rec, mask ); + if (r != ERROR_SUCCESS) + WARN("failed to modify row %u\n", r); + } + } + else + { + TRACE("inserting row\n"); + r = TABLE_insert_row( &tv->view, rec, -1, FALSE ); + if (r != ERROR_SUCCESS) + WARN("failed to insert row %u\n", r); + } + + if (number != MSI_NULL_INTEGER && !strcmpW( name, szColumns )) + msi_update_table_columns( db, table ); + + msiobj_release( &rec->hdr ); + } + + n += sz; + } + +err: + /* no need to free the table, it's associated with the database */ + msi_free( rawdata ); + if( tv ) + tv->view.ops->delete( &tv->view ); + + return ERROR_SUCCESS; +} + +/* + * msi_table_apply_transform + * + * Enumerate the table transforms in a transform storage and apply each one. + */ +UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) +{ + struct list transforms; + IEnumSTATSTG *stgenum = NULL; + TRANSFORMDATA *transform; + TRANSFORMDATA *tables = NULL, *columns = NULL; + HRESULT r; + STATSTG stat; + string_table *strings; + UINT ret = ERROR_FUNCTION_FAILED; + UINT bytes_per_strref; + + TRACE("%p %p\n", db, stg ); + + strings = msi_load_string_table( stg, &bytes_per_strref ); + if( !strings ) + goto end; + + r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); + if( FAILED( r ) ) + goto end; + + list_init(&transforms); + + while ( TRUE ) + { + MSITABLEVIEW *tv = NULL; + WCHAR name[0x40]; + ULONG count = 0; + + r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); + if ( FAILED( r ) || !count ) + break; + + decode_streamname( stat.pwcsName, name ); + CoTaskMemFree( stat.pwcsName ); + if ( name[0] != 0x4840 ) + continue; + + if ( !strcmpW( name+1, szStringPool ) || + !strcmpW( name+1, szStringData ) ) + continue; + + transform = msi_alloc_zero( sizeof(TRANSFORMDATA) ); + if ( !transform ) + break; + + list_add_tail( &transforms, &transform->entry ); + + transform->name = strdupW( name + 1 ); + + if ( !strcmpW( transform->name, szTables ) ) + tables = transform; + else if (!strcmpW( transform->name, szColumns ) ) + columns = transform; + + TRACE("transform contains stream %s\n", debugstr_w(name)); + + /* load the table */ + r = TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ); + if( r != ERROR_SUCCESS ) + continue; + + r = tv->view.ops->execute( &tv->view, NULL ); + if( r != ERROR_SUCCESS ) + { + tv->view.ops->delete( &tv->view ); + continue; + } + + tv->view.ops->delete( &tv->view ); + } + + /* + * Apply _Tables and _Columns transforms first so that + * the table metadata is correct, and empty tables exist. + */ + ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref ); + if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) + goto end; + + ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref ); + if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) + goto end; + + ret = ERROR_SUCCESS; + + while ( !list_empty( &transforms ) ) + { + transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry ); + + if ( strcmpW( transform->name, szColumns ) && + strcmpW( transform->name, szTables ) && + ret == ERROR_SUCCESS ) + { + ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref ); + } + + list_remove( &transform->entry ); + msi_free( transform->name ); + msi_free( transform ); + } + + if ( ret == ERROR_SUCCESS ) + append_storage_to_db( db, stg ); + +end: + if ( stgenum ) + IEnumSTATSTG_Release( stgenum ); + if ( strings ) + msi_destroy_stringtable( strings ); + + return ret; +} diff --git a/libmsi/tokenize.c b/libmsi/tokenize.c new file mode 100644 index 0000000..4798917 --- /dev/null +++ b/libmsi/tokenize.c @@ -0,0 +1,290 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** A tokenizer for SQL +** +** This file contains C code that splits an SQL input string up into +** individual tokens and sends those tokens one-by-one over to the +** parser for analysis. +*/ + +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "windef.h" +#include "winbase.h" +#include "wine/unicode.h" +#include "query.h" +#include "sql.tab.h" + +/* +** All the keywords of the SQL language are stored as in a hash +** table composed of instances of the following structure. +*/ +typedef struct Keyword Keyword; +struct Keyword { + const WCHAR *zName; /* The keyword name */ + int tokenType; /* The token value for this keyword */ +}; + +#define MAX_TOKEN_LEN 11 + +static const WCHAR ADD_W[] = { 'A','D','D',0 }; +static const WCHAR ALTER_W[] = { 'A','L','T','E','R',0 }; +static const WCHAR AND_W[] = { 'A','N','D',0 }; +static const WCHAR BY_W[] = { 'B','Y',0 }; +static const WCHAR CHAR_W[] = { 'C','H','A','R',0 }; +static const WCHAR CHARACTER_W[] = { 'C','H','A','R','A','C','T','E','R',0 }; +static const WCHAR CREATE_W[] = { 'C','R','E','A','T','E',0 }; +static const WCHAR DELETE_W[] = { 'D','E','L','E','T','E',0 }; +static const WCHAR DISTINCT_W[] = { 'D','I','S','T','I','N','C','T',0 }; +static const WCHAR DROP_W[] = { 'D','R','O','P',0 }; +static const WCHAR FREE_W[] = { 'F','R','E','E',0 }; +static const WCHAR FROM_W[] = { 'F','R','O','M',0 }; +static const WCHAR HOLD_W[] = { 'H','O','L','D',0 }; +static const WCHAR INSERT_W[] = { 'I','N','S','E','R','T',0 }; +static const WCHAR INT_W[] = { 'I','N','T',0 }; +static const WCHAR INTEGER_W[] = { 'I','N','T','E','G','E','R',0 }; +static const WCHAR INTO_W[] = { 'I','N','T','O',0 }; +static const WCHAR IS_W[] = { 'I','S',0 }; +static const WCHAR KEY_W[] = { 'K','E','Y',0 }; +static const WCHAR LIKE_W[] = { 'L','I','K','E',0 }; +static const WCHAR LOCALIZABLE_W[] = { 'L','O','C','A','L','I','Z','A','B','L','E',0 }; +static const WCHAR LONG_W[] = { 'L','O','N','G',0 }; +static const WCHAR LONGCHAR_W[] = { 'L','O','N','G','C','H','A','R',0 }; +static const WCHAR NOT_W[] = { 'N','O','T',0 }; +static const WCHAR NULL_W[] = { 'N','U','L','L',0 }; +static const WCHAR OBJECT_W[] = { 'O','B','J','E','C','T',0 }; +static const WCHAR OR_W[] = { 'O','R',0 }; +static const WCHAR ORDER_W[] = { 'O','R','D','E','R',0 }; +static const WCHAR PRIMARY_W[] = { 'P','R','I','M','A','R','Y',0 }; +static const WCHAR SELECT_W[] = { 'S','E','L','E','C','T',0 }; +static const WCHAR SET_W[] = { 'S','E','T',0 }; +static const WCHAR SHORT_W[] = { 'S','H','O','R','T',0 }; +static const WCHAR TABLE_W[] = { 'T','A','B','L','E',0 }; +static const WCHAR TEMPORARY_W[] = { 'T','E','M','P','O','R','A','R','Y',0 }; +static const WCHAR UPDATE_W[] = { 'U','P','D','A','T','E',0 }; +static const WCHAR VALUES_W[] = { 'V','A','L','U','E','S',0 }; +static const WCHAR WHERE_W[] = { 'W','H','E','R','E',0 }; + +/* +** These are the keywords +** They MUST be in alphabetical order +*/ +static const Keyword aKeywordTable[] = { + { ADD_W, TK_ADD }, + { ALTER_W, TK_ALTER }, + { AND_W, TK_AND }, + { BY_W, TK_BY }, + { CHAR_W, TK_CHAR }, + { CHARACTER_W, TK_CHAR }, + { CREATE_W, TK_CREATE }, + { DELETE_W, TK_DELETE }, + { DISTINCT_W, TK_DISTINCT }, + { DROP_W, TK_DROP }, + { FREE_W, TK_FREE }, + { FROM_W, TK_FROM }, + { HOLD_W, TK_HOLD }, + { INSERT_W, TK_INSERT }, + { INT_W, TK_INT }, + { INTEGER_W, TK_INT }, + { INTO_W, TK_INTO }, + { IS_W, TK_IS }, + { KEY_W, TK_KEY }, + { LIKE_W, TK_LIKE }, + { LOCALIZABLE_W, TK_LOCALIZABLE }, + { LONG_W, TK_LONG }, + { LONGCHAR_W, TK_LONGCHAR }, + { NOT_W, TK_NOT }, + { NULL_W, TK_NULL }, + { OBJECT_W, TK_OBJECT }, + { OR_W, TK_OR }, + { ORDER_W, TK_ORDER }, + { PRIMARY_W, TK_PRIMARY }, + { SELECT_W, TK_SELECT }, + { SET_W, TK_SET }, + { SHORT_W, TK_SHORT }, + { TABLE_W, TK_TABLE }, + { TEMPORARY_W, TK_TEMPORARY }, + { UPDATE_W, TK_UPDATE }, + { VALUES_W, TK_VALUES }, + { WHERE_W, TK_WHERE }, +}; + +#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) ) + +/* +** Comparison function for binary search. +*/ +static int compKeyword(const void *m1, const void *m2){ + const Keyword *k1 = m1, *k2 = m2; + + return strcmpiW( k1->zName, k2->zName ); +} + +/* +** This function looks up an identifier to determine if it is a +** keyword. If it is a keyword, the token code of that keyword is +** returned. If the input is not a keyword, TK_ID is returned. +*/ +static int sqliteKeywordCode(const WCHAR *z, int n){ + WCHAR str[MAX_TOKEN_LEN+1]; + Keyword key, *r; + + if( n>MAX_TOKEN_LEN ) + return TK_ID; + + memcpy( str, z, n*sizeof (WCHAR) ); + str[n] = 0; + key.tokenType = 0; + key.zName = str; + r = bsearch( &key, aKeywordTable, KEYWORD_COUNT, sizeof (Keyword), compKeyword ); + if( r ) + return r->tokenType; + return TK_ID; +} + + +/* +** If X is a character that can be used in an identifier then +** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0. +** +** In this implementation, an identifier can be a string of +** alphabetic characters, digits, and "_" plus any character +** with the high-order bit set. The latter rule means that +** any sequence of UTF-8 characters or characters taken from +** an extended ISO8859 character set can form an identifier. +*/ +static const char isIdChar[] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 2x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */ +}; + + +/* +** Return the length of the token that begins at z[0]. Return +** -1 if the token is (or might be) incomplete. Store the token +** type in *tokenType before returning. +*/ +int sqliteGetToken(const WCHAR *z, int *tokenType, int *skip){ + int i; + + *skip = 0; + switch( *z ){ + case ' ': case '\t': case '\n': case '\f': + for(i=1; isspace(z[i]) && z[i] != '\r'; i++){} + *tokenType = TK_SPACE; + return i; + case '-': + if( z[1]==0 ) return -1; + *tokenType = TK_MINUS; + return 1; + case '(': + *tokenType = TK_LP; + return 1; + case ')': + *tokenType = TK_RP; + return 1; + case '*': + *tokenType = TK_STAR; + return 1; + case '=': + *tokenType = TK_EQ; + return 1; + case '<': + if( z[1]=='=' ){ + *tokenType = TK_LE; + return 2; + }else if( z[1]=='>' ){ + *tokenType = TK_NE; + return 2; + }else{ + *tokenType = TK_LT; + return 1; + } + case '>': + if( z[1]=='=' ){ + *tokenType = TK_GE; + return 2; + }else{ + *tokenType = TK_GT; + return 1; + } + case '!': + if( z[1]!='=' ){ + *tokenType = TK_ILLEGAL; + return 2; + }else{ + *tokenType = TK_NE; + return 2; + } + case '?': + *tokenType = TK_WILDCARD; + return 1; + case ',': + *tokenType = TK_COMMA; + return 1; + case '`': case '\'': { + int delim = z[0]; + for(i=1; z[i]; i++){ + if( z[i]==delim ) + break; + } + if( z[i] ) i++; + if( delim == '`' ) + *tokenType = TK_ID; + else + *tokenType = TK_STRING; + return i; + } + case '.': + if( !isdigit(z[1]) ){ + *tokenType = TK_DOT; + return 1; + } + /* Fall through */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *tokenType = TK_INTEGER; + for(i=1; isdigit(z[i]); i++){} + return i; + case '[': + for(i=1; z[i] && z[i-1]!=']'; i++){} + *tokenType = TK_ID; + return i; + default: + if( !isIdChar[*z] ){ + break; + } + for(i=1; isIdChar[z[i]]; i++){} + *tokenType = sqliteKeywordCode(z, i); + if( *tokenType == TK_ID && z[i] == '`' ) *skip = 1; + return i; + } + *tokenType = TK_ILLEGAL; + return 1; +} diff --git a/libmsi/update.c b/libmsi/update.c new file mode 100644 index 0000000..d0e3c28 --- /dev/null +++ b/libmsi/update.c @@ -0,0 +1,269 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 Mike McCormack 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> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIUPDATEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *wv; + column_info *vals; +} MSIUPDATEVIEW; + +static UINT UPDATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + + TRACE("%p %d %d %p\n", uv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT UPDATE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + UINT i, r, col_count = 0, row_count = 0; + MSIRECORD *values = NULL; + MSIRECORD *where = NULL; + MSIVIEW *wv; + UINT cols_count, where_count; + column_info *col = uv->vals; + + TRACE("%p %p\n", uv, record ); + + /* extract the where markers from the record */ + if (record) + { + r = MSI_RecordGetFieldCount(record); + + for (i = 0; col; col = col->next) + i++; + + cols_count = i; + where_count = r - i; + + if (where_count > 0) + { + where = MSI_CreateRecord(where_count); + + if (where) + for (i = 1; i <= where_count; i++) + MSI_RecordCopyField(record, cols_count + i, where, i); + } + } + + wv = uv->wv; + if( !wv ) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + r = wv->ops->execute( wv, where ); + TRACE("tv execute returned %x\n", r); + if( r ) + goto done; + + r = wv->ops->get_dimensions( wv, &row_count, &col_count ); + if( r ) + goto done; + + values = msi_query_merge_record( col_count, uv->vals, record ); + if (!values) + { + r = ERROR_FUNCTION_FAILED; + goto done; + } + + for ( i=0; i<row_count; i++ ) + { + r = wv->ops->set_row( wv, i, values, (1 << col_count) - 1 ); + if (r != ERROR_SUCCESS) + break; + } + +done: + if ( where ) msiobj_release( &where->hdr ); + if ( values ) msiobj_release( &values->hdr ); + + return r; +} + + +static UINT UPDATE_close( struct tagMSIVIEW *view ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p\n", uv); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->close( wv ); +} + +static UINT UPDATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p %p %p\n", uv, rows, cols ); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->get_dimensions( wv, rows, cols ); +} + +static UINT UPDATE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p %d %p %p %p %p\n", uv, n, name, type, temporary, table_name ); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->get_column_info( wv, n, name, type, temporary, table_name ); +} + +static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + + TRACE("%p %d %p\n", uv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT UPDATE_delete( struct tagMSIVIEW *view ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p\n", uv ); + + wv = uv->wv; + if( wv ) + wv->ops->delete( wv ); + msiobj_release( &uv->db->hdr ); + msi_free( uv ); + + return ERROR_SUCCESS; +} + +static UINT UPDATE_find_matching_rows( struct tagMSIVIEW *view, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + TRACE("%p %d %d %p\n", view, col, val, *handle ); + + return ERROR_FUNCTION_FAILED; +} + + +static const MSIVIEWOPS update_ops = +{ + UPDATE_fetch_int, + NULL, + NULL, + NULL, + NULL, + NULL, + UPDATE_execute, + UPDATE_close, + UPDATE_get_dimensions, + UPDATE_get_column_info, + UPDATE_modify, + UPDATE_delete, + UPDATE_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *columns, struct expr *expr ) +{ + MSIUPDATEVIEW *uv = NULL; + UINT r; + MSIVIEW *sv = NULL, *wv = NULL; + + TRACE("%p\n", uv ); + + if (expr) + r = WHERE_CreateView( db, &wv, table, expr ); + else + r = TABLE_CreateView( db, table, &wv ); + + if( r != ERROR_SUCCESS ) + return r; + + /* then select the columns we want */ + r = SELECT_CreateView( db, &sv, wv, columns ); + if( r != ERROR_SUCCESS ) + { + wv->ops->delete( wv ); + return r; + } + + uv = msi_alloc_zero( sizeof *uv ); + if( !uv ) + { + wv->ops->delete( wv ); + return ERROR_FUNCTION_FAILED; + } + + /* fill the structure */ + uv->view.ops = &update_ops; + msiobj_addref( &db->hdr ); + uv->db = db; + uv->vals = columns; + uv->wv = sv; + *view = (MSIVIEW*) uv; + + return ERROR_SUCCESS; +} diff --git a/libmsi/upgrade.c b/libmsi/upgrade.c new file mode 100644 index 0000000..9480661 --- /dev/null +++ b/libmsi/upgrade.c @@ -0,0 +1,234 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 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 + */ + +/* + * Actions focused on in this module + * + * FindRelatedProducts + * MigrateFeatureStates (TODO) + * RemoveExistingProducts (TODO) + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msidefs.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes) +{ + DWORD langdword; + + if (!lang2 || lang2[0]==0) + return TRUE; + + langdword = atoiW(lang2); + + if (attributes & msidbUpgradeAttributesLanguagesExclusive) + return (lang1 != langdword); + else + return (lang1 == langdword); +} + +static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property, + LPCWSTR productid) +{ + LPWSTR prop; + LPWSTR newprop; + DWORD len; + UINT r; + + prop = msi_dup_property(package->db, action_property ); + if (prop) + len = strlenW(prop); + else + len = 0; + + /*separator*/ + len ++; + + len += strlenW(productid); + + /*null*/ + len++; + + newprop = msi_alloc( len*sizeof(WCHAR) ); + + if (prop) + { + strcpyW(newprop,prop); + strcatW(newprop,szSemiColon); + } + else + newprop[0] = 0; + strcatW(newprop,productid); + + r = msi_set_property( package->db, action_property, newprop ); + if (r == ERROR_SUCCESS && !strcmpW( action_property, szSourceDir )) + msi_reset_folders( package, TRUE ); + + TRACE("Found Related Product... %s now %s\n", + debugstr_w(action_property), debugstr_w(newprop)); + + msi_free( prop ); + msi_free( newprop ); +} + +static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param) +{ + MSIPACKAGE *package = param; + WCHAR product[GUID_SIZE]; + DWORD index = 0; + DWORD attributes = 0; + DWORD sz = GUID_SIZE; + LPCWSTR upgrade_code; + HKEY hkey = 0; + UINT rc = ERROR_SUCCESS; + MSIRECORD *uirow; + + upgrade_code = MSI_RecordGetString(rec,1); + + rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + uirow = MSI_CreateRecord(1); + attributes = MSI_RecordGetInteger(rec,5); + + while (rc == ERROR_SUCCESS) + { + rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL); + if (rc == ERROR_SUCCESS) + { + WCHAR productid[GUID_SIZE]; + LPCWSTR ver, language, action_property; + DWORD check = 0, comp_ver, sz = 0x100; + HKEY hukey; + INT r; + + TRACE("Looking at index %u product %s\n", index, debugstr_w(product)); + + unsquash_guid(product, productid); + if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) && + MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) && + MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE)) + { + TRACE("product key not found\n"); + rc = ERROR_SUCCESS; + index ++; + continue; + } + + sz = sizeof(DWORD); + RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz); + + /* check version minimum */ + ver = MSI_RecordGetString(rec,2); + if (ver) + { + comp_ver = msi_version_str_to_dword(ver); + r = check - comp_ver; + if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive))) + { + TRACE("version below minimum\n"); + RegCloseKey(hukey); + index ++; + continue; + } + } + + /* check version maximum */ + ver = MSI_RecordGetString(rec,3); + if (ver) + { + comp_ver = msi_version_str_to_dword(ver); + r = check - comp_ver; + if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive))) + { + RegCloseKey(hukey); + index ++; + continue; + } + TRACE("version above maximum\n"); + } + + /* check language */ + sz = sizeof(DWORD); + RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz); + RegCloseKey(hukey); + language = MSI_RecordGetString(rec,4); + if (!check_language(check, language, attributes)) + { + index ++; + TRACE("language doesn't match\n"); + continue; + } + TRACE("found related product\n"); + + action_property = MSI_RecordGetString(rec, 7); + append_productcode(package, action_property, productid); + MSI_RecordSetStringW(uirow, 1, productid); + msi_ui_actiondata(package, szFindRelatedProducts, uirow); + } + index ++; + } + RegCloseKey(hkey); + msiobj_release( &uirow->hdr); + + return ERROR_SUCCESS; +} + +UINT ACTION_FindRelatedProducts(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 rc; + + if (msi_get_property_int(package->db, szInstalled, 0)) + { + TRACE("Skipping FindRelatedProducts action: product already installed\n"); + return ERROR_SUCCESS; + } + if (msi_action_is_unique(package, szFindRelatedProducts)) + { + TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n"); + return ERROR_SUCCESS; + } + else + msi_register_unique_action(package, szFindRelatedProducts); + + rc = MSI_DatabaseOpenViewW(package->db, query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package); + msiobj_release(&view->hdr); + return rc; +} diff --git a/libmsi/where.c b/libmsi/where.c new file mode 100644 index 0000000..36c8ec8 --- /dev/null +++ b/libmsi/where.c @@ -0,0 +1,1289 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2011 Bernhard Loos + * + * 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> +#include <assert.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +/* below is the query interface to a table */ +typedef struct tagMSIROWENTRY +{ + struct tagMSIWHEREVIEW *wv; /* used during sorting */ + UINT values[1]; +} MSIROWENTRY; + +typedef struct tagJOINTABLE +{ + struct tagJOINTABLE *next; + MSIVIEW *view; + UINT col_count; + UINT row_count; + UINT table_index; +} JOINTABLE; + +typedef struct tagMSIORDERINFO +{ + UINT col_count; + UINT error; + union ext_column columns[1]; +} MSIORDERINFO; + +typedef struct tagMSIWHEREVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + JOINTABLE *tables; + UINT row_count; + UINT col_count; + UINT table_count; + MSIROWENTRY **reorder; + UINT reorder_size; /* number of entries available in reorder */ + struct expr *cond; + UINT rec_index; + MSIORDERINFO *order_info; +} MSIWHEREVIEW; + +static UINT WHERE_evaluate( MSIWHEREVIEW *wv, const UINT rows[], + struct expr *cond, INT *val, MSIRECORD *record ); + +#define INITIAL_REORDER_SIZE 16 + +#define INVALID_ROW_INDEX (-1) + +static void free_reorder(MSIWHEREVIEW *wv) +{ + UINT i; + + if (!wv->reorder) + return; + + for (i = 0; i < wv->row_count; i++) + msi_free(wv->reorder[i]); + + msi_free( wv->reorder ); + wv->reorder = NULL; + wv->reorder_size = 0; + wv->row_count = 0; +} + +static UINT init_reorder(MSIWHEREVIEW *wv) +{ + MSIROWENTRY **new = msi_alloc_zero(sizeof(MSIROWENTRY *) * INITIAL_REORDER_SIZE); + if (!new) + return ERROR_OUTOFMEMORY; + + free_reorder(wv); + + wv->reorder = new; + wv->reorder_size = INITIAL_REORDER_SIZE; + + return ERROR_SUCCESS; +} + +static inline UINT find_row(MSIWHEREVIEW *wv, UINT row, UINT *(values[])) +{ + if (row >= wv->row_count) + return ERROR_NO_MORE_ITEMS; + + *values = wv->reorder[row]->values; + + return ERROR_SUCCESS; +} + +static UINT add_row(MSIWHEREVIEW *wv, UINT vals[]) +{ + MSIROWENTRY *new; + + if (wv->reorder_size <= wv->row_count) + { + MSIROWENTRY **new_reorder; + UINT newsize = wv->reorder_size * 2; + + new_reorder = msi_realloc_zero(wv->reorder, sizeof(MSIROWENTRY *) * newsize); + if (!new_reorder) + return ERROR_OUTOFMEMORY; + + wv->reorder = new_reorder; + wv->reorder_size = newsize; + } + + new = msi_alloc(FIELD_OFFSET( MSIROWENTRY, values[wv->table_count] )); + + if (!new) + return ERROR_OUTOFMEMORY; + + wv->reorder[wv->row_count++] = new; + + memcpy(new->values, vals, wv->table_count * sizeof(UINT)); + new->wv = wv; + + return ERROR_SUCCESS; +} + +static JOINTABLE *find_table(MSIWHEREVIEW *wv, UINT col, UINT *table_col) +{ + JOINTABLE *table = wv->tables; + + if(col == 0 || col > wv->col_count) + return NULL; + + while (col > table->col_count) + { + col -= table->col_count; + table = table->next; + assert(table); + } + + *table_col = col; + return table; +} + +static UINT parse_column(MSIWHEREVIEW *wv, union ext_column *column, + UINT *column_type) +{ + JOINTABLE *table = wv->tables; + UINT i, r; + + do + { + LPCWSTR table_name; + + if (column->unparsed.table) + { + r = table->view->ops->get_column_info(table->view, 1, NULL, NULL, + NULL, &table_name); + if (r != ERROR_SUCCESS) + return r; + if (strcmpW(table_name, column->unparsed.table) != 0) + continue; + } + + for(i = 1; i <= table->col_count; i++) + { + LPCWSTR col_name; + + r = table->view->ops->get_column_info(table->view, i, &col_name, column_type, + NULL, NULL); + if(r != ERROR_SUCCESS ) + return r; + + if(strcmpW(col_name, column->unparsed.column)) + continue; + column->parsed.column = i; + column->parsed.table = table; + return ERROR_SUCCESS; + } + } + while ((table = table->next)); + + WARN("Couldn't find column %s.%s\n", debugstr_w( column->unparsed.table ), debugstr_w( column->unparsed.column ) ); + return ERROR_BAD_QUERY_SYNTAX; +} + +static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table; + UINT *rows; + UINT r; + + TRACE("%p %d %d %p\n", wv, row, col, val ); + + if( !wv->tables ) + return ERROR_FUNCTION_FAILED; + + r = find_row(wv, row, &rows); + if (r != ERROR_SUCCESS) + return r; + + table = find_table(wv, col, &col); + if (!table) + return ERROR_FUNCTION_FAILED; + + return table->view->ops->fetch_int(table->view, rows[table->table_index], col, val); +} + +static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table; + UINT *rows; + UINT r; + + TRACE("%p %d %d %p\n", wv, row, col, stm ); + + if( !wv->tables ) + return ERROR_FUNCTION_FAILED; + + r = find_row(wv, row, &rows); + if (r != ERROR_SUCCESS) + return r; + + table = find_table(wv, col, &col); + if (!table) + return ERROR_FUNCTION_FAILED; + + return table->view->ops->fetch_stream( table->view, rows[table->table_index], col, stm ); +} + +static UINT WHERE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view; + + TRACE("%p %d %p\n", wv, row, rec ); + + if (!wv->tables) + return ERROR_FUNCTION_FAILED; + + return msi_view_get_row( wv->db, view, row, rec ); +} + +static UINT WHERE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + UINT i, r, offset = 0; + JOINTABLE *table = wv->tables; + UINT *rows; + UINT mask_copy = mask; + + TRACE("%p %d %p %08x\n", wv, row, rec, mask ); + + if( !wv->tables ) + return ERROR_FUNCTION_FAILED; + + r = find_row(wv, row, &rows); + if (r != ERROR_SUCCESS) + return r; + + if (mask >= 1 << wv->col_count) + return ERROR_INVALID_PARAMETER; + + do + { + for (i = 0; i < table->col_count; i++) { + UINT type; + + if (!(mask_copy & (1 << i))) + continue; + r = table->view->ops->get_column_info(table->view, i + 1, NULL, + &type, NULL, NULL ); + if (r != ERROR_SUCCESS) + return r; + if (type & MSITYPE_KEY) + return ERROR_FUNCTION_FAILED; + } + mask_copy >>= table->col_count; + } + while (mask_copy && (table = table->next)); + + table = wv->tables; + + do + { + const UINT col_count = table->col_count; + UINT i; + MSIRECORD *reduced; + UINT reduced_mask = (mask >> offset) & ((1 << col_count) - 1); + + if (!reduced_mask) + { + offset += col_count; + continue; + } + + reduced = MSI_CreateRecord(col_count); + if (!reduced) + return ERROR_FUNCTION_FAILED; + + for (i = 1; i <= col_count; i++) + { + r = MSI_RecordCopyField(rec, i + offset, reduced, i); + if (r != ERROR_SUCCESS) + break; + } + + offset += col_count; + + if (r == ERROR_SUCCESS) + r = table->view->ops->set_row(table->view, rows[table->table_index], reduced, reduced_mask); + + msiobj_release(&reduced->hdr); + } + while ((table = table->next)); + return r; +} + +static UINT WHERE_delete_row(struct tagMSIVIEW *view, UINT row) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view; + UINT r; + UINT *rows; + + TRACE("(%p %d)\n", view, row); + + if (!wv->tables) + return ERROR_FUNCTION_FAILED; + + r = find_row(wv, row, &rows); + if ( r != ERROR_SUCCESS ) + return r; + + if (wv->table_count > 1) + return ERROR_CALL_NOT_IMPLEMENTED; + + return wv->tables->view->ops->delete_row(wv->tables->view, rows[0]); +} + +static INT INT_evaluate_binary( MSIWHEREVIEW *wv, const UINT rows[], + const struct complex_expr *expr, INT *val, MSIRECORD *record ) +{ + UINT rl, rr; + INT lval, rval; + + rl = WHERE_evaluate(wv, rows, expr->left, &lval, record); + if (rl != ERROR_SUCCESS && rl != ERROR_CONTINUE) + return rl; + rr = WHERE_evaluate(wv, rows, expr->right, &rval, record); + if (rr != ERROR_SUCCESS && rr != ERROR_CONTINUE) + return rr; + + if (rl == ERROR_CONTINUE || rr == ERROR_CONTINUE) + { + if (rl == rr) + { + *val = TRUE; + return ERROR_CONTINUE; + } + + if (expr->op == OP_AND) + { + if ((rl == ERROR_CONTINUE && !rval) || (rr == ERROR_CONTINUE && !lval)) + { + *val = FALSE; + return ERROR_SUCCESS; + } + } + else if (expr->op == OP_OR) + { + if ((rl == ERROR_CONTINUE && rval) || (rr == ERROR_CONTINUE && lval)) + { + *val = TRUE; + return ERROR_SUCCESS; + } + } + + *val = TRUE; + return ERROR_CONTINUE; + } + + switch( expr->op ) + { + case OP_EQ: + *val = ( lval == rval ); + break; + case OP_AND: + *val = ( lval && rval ); + break; + case OP_OR: + *val = ( lval || rval ); + break; + case OP_GT: + *val = ( lval > rval ); + break; + case OP_LT: + *val = ( lval < rval ); + break; + case OP_LE: + *val = ( lval <= rval ); + break; + case OP_GE: + *val = ( lval >= rval ); + break; + case OP_NE: + *val = ( lval != rval ); + break; + default: + ERR("Unknown operator %d\n", expr->op ); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static inline UINT expr_fetch_value(const union ext_column *expr, const UINT rows[], UINT *val) +{ + JOINTABLE *table = expr->parsed.table; + + if( rows[table->table_index] == INVALID_ROW_INDEX ) + { + *val = 1; + return ERROR_CONTINUE; + } + return table->view->ops->fetch_int(table->view, rows[table->table_index], + expr->parsed.column, val); +} + + +static UINT INT_evaluate_unary( MSIWHEREVIEW *wv, const UINT rows[], + const struct complex_expr *expr, INT *val, MSIRECORD *record ) +{ + UINT r; + UINT lval; + + r = expr_fetch_value(&expr->left->u.column, rows, &lval); + if(r != ERROR_SUCCESS) + return r; + + switch( expr->op ) + { + case OP_ISNULL: + *val = !lval; + break; + case OP_NOTNULL: + *val = lval; + break; + default: + ERR("Unknown operator %d\n", expr->op ); + return ERROR_FUNCTION_FAILED; + } + return ERROR_SUCCESS; +} + +static UINT STRING_evaluate( MSIWHEREVIEW *wv, const UINT rows[], + const struct expr *expr, + const MSIRECORD *record, + const WCHAR **str ) +{ + UINT val = 0, r = ERROR_SUCCESS; + + switch( expr->type ) + { + case EXPR_COL_NUMBER_STRING: + r = expr_fetch_value(&expr->u.column, rows, &val); + if (r == ERROR_SUCCESS) + *str = msi_string_lookup_id(wv->db->strings, val); + else + *str = NULL; + break; + + case EXPR_SVAL: + *str = expr->u.sval; + break; + + case EXPR_WILDCARD: + *str = MSI_RecordGetString(record, ++wv->rec_index); + break; + + default: + ERR("Invalid expression type\n"); + r = ERROR_FUNCTION_FAILED; + *str = NULL; + break; + } + return r; +} + +static UINT STRCMP_Evaluate( MSIWHEREVIEW *wv, const UINT rows[], const struct complex_expr *expr, + INT *val, const MSIRECORD *record ) +{ + int sr; + const WCHAR *l_str, *r_str; + UINT r; + + *val = TRUE; + r = STRING_evaluate(wv, rows, expr->left, record, &l_str); + if (r == ERROR_CONTINUE) + return r; + r = STRING_evaluate(wv, rows, expr->right, record, &r_str); + if (r == ERROR_CONTINUE) + return r; + + if( l_str == r_str || + ((!l_str || !*l_str) && (!r_str || !*r_str)) ) + sr = 0; + else if( l_str && ! r_str ) + sr = 1; + else if( r_str && ! l_str ) + sr = -1; + else + sr = strcmpW( l_str, r_str ); + + *val = ( expr->op == OP_EQ && ( sr == 0 ) ) || + ( expr->op == OP_NE && ( sr != 0 ) ); + + return ERROR_SUCCESS; +} + +static UINT WHERE_evaluate( MSIWHEREVIEW *wv, const UINT rows[], + struct expr *cond, INT *val, MSIRECORD *record ) +{ + UINT r, tval; + + if( !cond ) + { + *val = TRUE; + return ERROR_SUCCESS; + } + + switch( cond->type ) + { + case EXPR_COL_NUMBER: + r = expr_fetch_value(&cond->u.column, rows, &tval); + if( r != ERROR_SUCCESS ) + return r; + *val = tval - 0x8000; + return ERROR_SUCCESS; + + case EXPR_COL_NUMBER32: + r = expr_fetch_value(&cond->u.column, rows, &tval); + if( r != ERROR_SUCCESS ) + return r; + *val = tval - 0x80000000; + return r; + + case EXPR_UVAL: + *val = cond->u.uval; + return ERROR_SUCCESS; + + case EXPR_COMPLEX: + return INT_evaluate_binary(wv, rows, &cond->u.expr, val, record); + + case EXPR_UNARY: + return INT_evaluate_unary( wv, rows, &cond->u.expr, val, record ); + + case EXPR_STRCMP: + return STRCMP_Evaluate( wv, rows, &cond->u.expr, val, record ); + + case EXPR_WILDCARD: + *val = MSI_RecordGetInteger( record, ++wv->rec_index ); + return ERROR_SUCCESS; + + default: + ERR("Invalid expression type\n"); + break; + } + + return ERROR_SUCCESS; +} + +static UINT check_condition( MSIWHEREVIEW *wv, MSIRECORD *record, JOINTABLE **tables, + UINT table_rows[] ) +{ + UINT r = ERROR_FUNCTION_FAILED; + INT val; + + for (table_rows[(*tables)->table_index] = 0; + table_rows[(*tables)->table_index] < (*tables)->row_count; + table_rows[(*tables)->table_index]++) + { + val = 0; + wv->rec_index = 0; + r = WHERE_evaluate( wv, table_rows, wv->cond, &val, record ); + if (r != ERROR_SUCCESS && r != ERROR_CONTINUE) + break; + if (val) + { + if (*(tables + 1)) + { + r = check_condition(wv, record, tables + 1, table_rows); + if (r != ERROR_SUCCESS) + break; + } + else + { + if (r != ERROR_SUCCESS) + break; + add_row (wv, table_rows); + } + } + } + table_rows[(*tables)->table_index] = INVALID_ROW_INDEX; + return r; +} + +static int compare_entry( const void *left, const void *right ) +{ + const MSIROWENTRY *le = *(const MSIROWENTRY**)left; + const MSIROWENTRY *re = *(const MSIROWENTRY**)right; + const MSIWHEREVIEW *wv = le->wv; + MSIORDERINFO *order = wv->order_info; + UINT i, j, r, l_val, r_val; + + assert(le->wv == re->wv); + + if (order) + { + for (i = 0; i < order->col_count; i++) + { + const union ext_column *column = &order->columns[i]; + + r = column->parsed.table->view->ops->fetch_int(column->parsed.table->view, + le->values[column->parsed.table->table_index], + column->parsed.column, &l_val); + if (r != ERROR_SUCCESS) + { + order->error = r; + return 0; + } + + r = column->parsed.table->view->ops->fetch_int(column->parsed.table->view, + re->values[column->parsed.table->table_index], + column->parsed.column, &r_val); + if (r != ERROR_SUCCESS) + { + order->error = r; + return 0; + } + + if (l_val != r_val) + return l_val < r_val ? -1 : 1; + } + } + + for (j = 0; j < wv->table_count; j++) + { + if (le->values[j] != re->values[j]) + return le->values[j] < re->values[j] ? -1 : 1; + } + return 0; +} + +static void add_to_array( JOINTABLE **array, JOINTABLE *elem ) +{ + while (*array && *array != elem) + array++; + if (!*array) + *array = elem; +} + +static BOOL in_array( JOINTABLE **array, JOINTABLE *elem ) +{ + while (*array && *array != elem) + array++; + return *array != NULL; +} + +#define CONST_EXPR 1 /* comparison to a constant value */ +#define JOIN_TO_CONST_EXPR 0x10000 /* comparison to a table involved with + a CONST_EXPR comaprison */ + +static UINT reorder_check( const struct expr *expr, JOINTABLE **ordered_tables, + BOOL process_joins, JOINTABLE **lastused ) +{ + UINT res = 0; + + switch (expr->type) + { + case EXPR_WILDCARD: + case EXPR_SVAL: + case EXPR_UVAL: + return 0; + case EXPR_COL_NUMBER: + case EXPR_COL_NUMBER32: + case EXPR_COL_NUMBER_STRING: + if (in_array(ordered_tables, expr->u.column.parsed.table)) + return JOIN_TO_CONST_EXPR; + *lastused = expr->u.column.parsed.table; + return CONST_EXPR; + case EXPR_STRCMP: + case EXPR_COMPLEX: + res = reorder_check(expr->u.expr.right, ordered_tables, process_joins, lastused); + /* fall through */ + case EXPR_UNARY: + res += reorder_check(expr->u.expr.left, ordered_tables, process_joins, lastused); + if (res == 0) + return 0; + if (res == CONST_EXPR) + add_to_array(ordered_tables, *lastused); + if (process_joins && res == JOIN_TO_CONST_EXPR + CONST_EXPR) + add_to_array(ordered_tables, *lastused); + return res; + default: + ERR("Unknown expr type: %i\n", expr->type); + assert(0); + return 0x1000000; + } +} + +/* reorders the tablelist in a way to evaluate the condition as fast as possible */ +static JOINTABLE **ordertables( MSIWHEREVIEW *wv ) +{ + JOINTABLE *table; + JOINTABLE **tables; + + tables = msi_alloc_zero( (wv->table_count + 1) * sizeof(*tables) ); + + if (wv->cond) + { + table = NULL; + reorder_check(wv->cond, tables, FALSE, &table); + table = NULL; + reorder_check(wv->cond, tables, TRUE, &table); + } + + table = wv->tables; + while (table) + { + add_to_array(tables, table); + table = table->next; + } + return tables; +} + +static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + UINT r; + JOINTABLE *table = wv->tables; + UINT *rows; + JOINTABLE **ordered_tables; + int i = 0; + + TRACE("%p %p\n", wv, record); + + if( !table ) + return ERROR_FUNCTION_FAILED; + + r = init_reorder(wv); + if (r != ERROR_SUCCESS) + return r; + + do + { + table->view->ops->execute(table->view, NULL); + + r = table->view->ops->get_dimensions(table->view, &table->row_count, NULL); + if (r != ERROR_SUCCESS) + { + ERR("failed to get table dimensions\n"); + return r; + } + + /* each table must have at least one row */ + if (table->row_count == 0) + return ERROR_SUCCESS; + } + while ((table = table->next)); + + ordered_tables = ordertables( wv ); + + rows = msi_alloc( wv->table_count * sizeof(*rows) ); + for (i = 0; i < wv->table_count; i++) + rows[i] = INVALID_ROW_INDEX; + + r = check_condition(wv, record, ordered_tables, rows); + + if (wv->order_info) + wv->order_info->error = ERROR_SUCCESS; + + qsort(wv->reorder, wv->row_count, sizeof(MSIROWENTRY *), compare_entry); + + if (wv->order_info) + r = wv->order_info->error; + + msi_free( rows ); + msi_free( ordered_tables ); + return r; +} + +static UINT WHERE_close( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table = wv->tables; + + TRACE("%p\n", wv ); + + if (!table) + return ERROR_FUNCTION_FAILED; + + do + table->view->ops->close(table->view); + while ((table = table->next)); + + return ERROR_SUCCESS; +} + +static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %p %p\n", wv, rows, cols ); + + if(!wv->tables) + return ERROR_FUNCTION_FAILED; + + if (rows) + { + if (!wv->reorder) + return ERROR_FUNCTION_FAILED; + *rows = wv->row_count; + } + + if (cols) + *cols = wv->col_count; + + return ERROR_SUCCESS; +} + +static UINT WHERE_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table; + + TRACE("%p %d %p %p %p %p\n", wv, n, name, type, temporary, table_name ); + + if(!wv->tables) + return ERROR_FUNCTION_FAILED; + + table = find_table(wv, n, &n); + if (!table) + return ERROR_FUNCTION_FAILED; + + return table->view->ops->get_column_info(table->view, n, name, + type, temporary, table_name); +} + +static UINT join_find_row( MSIWHEREVIEW *wv, MSIRECORD *rec, UINT *row ) +{ + LPCWSTR str; + UINT r, i, id, data; + + str = MSI_RecordGetString( rec, 1 ); + r = msi_string2idW( wv->db->strings, str, &id ); + if (r != ERROR_SUCCESS) + return r; + + for (i = 0; i < wv->row_count; i++) + { + WHERE_fetch_int( &wv->view, i, 1, &data ); + + if (data == id) + { + *row = i; + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +static UINT join_modify_update( struct tagMSIVIEW *view, MSIRECORD *rec ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view; + UINT r, row, i, mask = 0; + MSIRECORD *current; + + + r = join_find_row( wv, rec, &row ); + if (r != ERROR_SUCCESS) + return r; + + r = msi_view_get_row( wv->db, view, row, ¤t ); + if (r != ERROR_SUCCESS) + return r; + + assert(MSI_RecordGetFieldCount(rec) == MSI_RecordGetFieldCount(current)); + + for (i = MSI_RecordGetFieldCount(rec); i > 0; i--) + { + if (!MSI_RecordsAreFieldsEqual(rec, current, i)) + mask |= 1 << (i - 1); + } + msiobj_release(¤t->hdr); + + return WHERE_set_row( view, row, rec, mask ); +} + +static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec, UINT row ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table = wv->tables; + UINT r; + + TRACE("%p %d %p\n", wv, eModifyMode, rec); + + if (!table) + return ERROR_FUNCTION_FAILED; + + if (!table->next) + { + UINT *rows; + + if (find_row(wv, row - 1, &rows) == ERROR_SUCCESS) + row = rows[0] + 1; + else + row = -1; + + return table->view->ops->modify(table->view, eModifyMode, rec, row); + } + + switch (eModifyMode) + { + case MSIMODIFY_UPDATE: + return join_modify_update( view, rec ); + + case MSIMODIFY_ASSIGN: + case MSIMODIFY_DELETE: + case MSIMODIFY_INSERT: + case MSIMODIFY_INSERT_TEMPORARY: + case MSIMODIFY_MERGE: + case MSIMODIFY_REPLACE: + case MSIMODIFY_SEEK: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_DELETE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_NEW: + r = ERROR_FUNCTION_FAILED; + break; + + case MSIMODIFY_REFRESH: + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + WARN("%p %d %p %u - unknown mode\n", view, eModifyMode, rec, row ); + r = ERROR_INVALID_PARAMETER; + break; + } + + return r; +} + +static UINT WHERE_delete( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + JOINTABLE *table = wv->tables; + + TRACE("%p\n", wv ); + + while(table) + { + JOINTABLE *next; + + table->view->ops->delete(table->view); + table->view = NULL; + next = table->next; + msi_free(table); + table = next; + } + wv->tables = NULL; + wv->table_count = 0; + + free_reorder(wv); + + msi_free(wv->order_info); + wv->order_info = NULL; + + msiobj_release( &wv->db->hdr ); + msi_free( wv ); + + return ERROR_SUCCESS; +} + +static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + UINT i, row_value; + + TRACE("%p, %d, %u, %p\n", view, col, val, *handle); + + if (!wv->tables) + return ERROR_FUNCTION_FAILED; + + if (col == 0 || col > wv->col_count) + return ERROR_INVALID_PARAMETER; + + for (i = PtrToUlong(*handle); i < wv->row_count; i++) + { + if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS) + continue; + + if (row_value == val) + { + *row = i; + *handle = UlongToPtr(i + 1); + return ERROR_SUCCESS; + } + } + + return ERROR_NO_MORE_ITEMS; +} + +static UINT WHERE_sort(struct tagMSIVIEW *view, column_info *columns) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW *)view; + JOINTABLE *table = wv->tables; + column_info *column = columns; + MSIORDERINFO *orderinfo; + UINT r, count = 0; + int i; + + TRACE("%p %p\n", view, columns); + + if (!table) + return ERROR_FUNCTION_FAILED; + + while (column) + { + count++; + column = column->next; + } + + if (count == 0) + return ERROR_SUCCESS; + + orderinfo = msi_alloc(sizeof(MSIORDERINFO) + (count - 1) * sizeof(union ext_column)); + if (!orderinfo) + return ERROR_OUTOFMEMORY; + + orderinfo->col_count = count; + + column = columns; + + for (i = 0; i < count; i++) + { + orderinfo->columns[i].unparsed.column = column->column; + orderinfo->columns[i].unparsed.table = column->table; + + r = parse_column(wv, &orderinfo->columns[i], NULL); + if (r != ERROR_SUCCESS) + goto error; + } + + wv->order_info = orderinfo; + + return ERROR_SUCCESS; +error: + msi_free(orderinfo); + return r; +} + +static const MSIVIEWOPS where_ops = +{ + WHERE_fetch_int, + WHERE_fetch_stream, + WHERE_get_row, + WHERE_set_row, + NULL, + WHERE_delete_row, + WHERE_execute, + WHERE_close, + WHERE_get_dimensions, + WHERE_get_column_info, + WHERE_modify, + WHERE_delete, + WHERE_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + WHERE_sort, + NULL, +}; + +static UINT WHERE_VerifyCondition( MSIWHEREVIEW *wv, struct expr *cond, + UINT *valid ) +{ + UINT r; + + switch( cond->type ) + { + case EXPR_COLUMN: + { + UINT type; + + *valid = FALSE; + + r = parse_column(wv, &cond->u.column, &type); + if (r != ERROR_SUCCESS) + break; + + if (type&MSITYPE_STRING) + cond->type = EXPR_COL_NUMBER_STRING; + else if ((type&0xff) == 4) + cond->type = EXPR_COL_NUMBER32; + else + cond->type = EXPR_COL_NUMBER; + + *valid = TRUE; + break; + } + case EXPR_COMPLEX: + r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid ); + if( r != ERROR_SUCCESS ) + return r; + if( !*valid ) + return ERROR_SUCCESS; + r = WHERE_VerifyCondition( wv, cond->u.expr.right, valid ); + if( r != ERROR_SUCCESS ) + return r; + + /* check the type of the comparison */ + if( ( cond->u.expr.left->type == EXPR_SVAL ) || + ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) || + ( cond->u.expr.right->type == EXPR_SVAL ) || + ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) ) + { + switch( cond->u.expr.op ) + { + case OP_EQ: + case OP_NE: + break; + default: + *valid = FALSE; + return ERROR_INVALID_PARAMETER; + } + + /* FIXME: check we're comparing a string to a column */ + + cond->type = EXPR_STRCMP; + } + + break; + case EXPR_UNARY: + if ( cond->u.expr.left->type != EXPR_COLUMN ) + { + *valid = FALSE; + return ERROR_INVALID_PARAMETER; + } + r = WHERE_VerifyCondition( wv, cond->u.expr.left, valid ); + if( r != ERROR_SUCCESS ) + return r; + break; + case EXPR_IVAL: + *valid = 1; + cond->type = EXPR_UVAL; + cond->u.uval = cond->u.ival; + break; + case EXPR_WILDCARD: + *valid = 1; + break; + case EXPR_SVAL: + *valid = 1; + break; + default: + ERR("Invalid expression type\n"); + *valid = 0; + break; + } + + return ERROR_SUCCESS; +} + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR tables, + struct expr *cond ) +{ + MSIWHEREVIEW *wv = NULL; + UINT r, valid = 0; + WCHAR *ptr; + + TRACE("(%s)\n", debugstr_w(tables) ); + + wv = msi_alloc_zero( sizeof *wv ); + if( !wv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + wv->view.ops = &where_ops; + msiobj_addref( &db->hdr ); + wv->db = db; + wv->cond = cond; + + while (*tables) + { + JOINTABLE *table; + + if ((ptr = strchrW(tables, ' '))) + *ptr = '\0'; + + table = msi_alloc(sizeof(JOINTABLE)); + if (!table) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + r = TABLE_CreateView(db, tables, &table->view); + if (r != ERROR_SUCCESS) + { + WARN("can't create table: %s\n", debugstr_w(tables)); + msi_free(table); + r = ERROR_BAD_QUERY_SYNTAX; + goto end; + } + + r = table->view->ops->get_dimensions(table->view, NULL, + &table->col_count); + if (r != ERROR_SUCCESS) + { + ERR("can't get table dimensions\n"); + goto end; + } + + wv->col_count += table->col_count; + table->table_index = wv->table_count++; + + table->next = wv->tables; + wv->tables = table; + + if (!ptr) + break; + + tables = ptr + 1; + } + + if( cond ) + { + r = WHERE_VerifyCondition( wv, cond, &valid ); + if( r != ERROR_SUCCESS ) + goto end; + if( !valid ) { + r = ERROR_FUNCTION_FAILED; + goto end; + } + } + + *view = (MSIVIEW*) wv; + + return ERROR_SUCCESS; +end: + WHERE_delete(&wv->view); + + return r; +} diff --git a/libmsi/wine/debug.h b/libmsi/wine/debug.h new file mode 100644 index 0000000..ba6fabe --- /dev/null +++ b/libmsi/wine/debug.h @@ -0,0 +1,288 @@ +/* + * Wine debugging interface + * + * Copyright 1999 Patrik Stridvall + * + * 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 + */ + +#ifndef __WINE_WINE_DEBUG_H +#define __WINE_WINE_DEBUG_H + +#include <stdarg.h> +#include <windef.h> +#ifndef GUID_DEFINED +#include <guiddef.h> +#endif + +#ifdef __WINE_WINE_TEST_H +#error This file should not be used in Wine tests +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct _GUID; + +/* + * Internal definitions (do not use these directly) + */ + +enum __wine_debug_class +{ + __WINE_DBCL_FIXME, + __WINE_DBCL_ERR, + __WINE_DBCL_WARN, + __WINE_DBCL_TRACE, + + __WINE_DBCL_INIT = 7 /* lazy init flag */ +}; + +struct __wine_debug_channel +{ + unsigned char flags; + char name[15]; +}; + +#ifndef WINE_NO_TRACE_MSGS +# define __WINE_GET_DEBUGGING_TRACE(dbch) ((dbch)->flags & (1 << __WINE_DBCL_TRACE)) +#else +# define __WINE_GET_DEBUGGING_TRACE(dbch) 0 +#endif + +#ifndef WINE_NO_DEBUG_MSGS +# define __WINE_GET_DEBUGGING_WARN(dbch) ((dbch)->flags & (1 << __WINE_DBCL_WARN)) +# define __WINE_GET_DEBUGGING_FIXME(dbch) ((dbch)->flags & (1 << __WINE_DBCL_FIXME)) +#else +# define __WINE_GET_DEBUGGING_WARN(dbch) 0 +# define __WINE_GET_DEBUGGING_FIXME(dbch) 0 +#endif + +/* define error macro regardless of what is configured */ +#define __WINE_GET_DEBUGGING_ERR(dbch) ((dbch)->flags & (1 << __WINE_DBCL_ERR)) + +#define __WINE_GET_DEBUGGING(dbcl,dbch) __WINE_GET_DEBUGGING##dbcl(dbch) + +#define __WINE_IS_DEBUG_ON(dbcl,dbch) \ + (__WINE_GET_DEBUGGING##dbcl(dbch) && (__wine_dbg_get_channel_flags(dbch) & (1 << __WINE_DBCL##dbcl))) + +#ifdef __GNUC__ + +#define __WINE_DPRINTF(dbcl,dbch) \ + do { if(__WINE_GET_DEBUGGING(dbcl,(dbch))) { \ + struct __wine_debug_channel * const __dbch = (dbch); \ + const enum __wine_debug_class __dbcl = __WINE_DBCL##dbcl; \ + __WINE_DBG_LOG + +#define __WINE_DBG_LOG(args...) \ + wine_dbg_log( __dbcl, __dbch, __FUNCTION__, args); } } while(0) + +#define __WINE_PRINTF_ATTR(fmt,args) __attribute__((format (printf,fmt,args))) + + +#ifdef WINE_NO_TRACE_MSGS +#define WINE_TRACE(args...) do { } while(0) +#define WINE_TRACE_(ch) WINE_TRACE +#endif + +#ifdef WINE_NO_DEBUG_MSGS +#define WINE_WARN(args...) do { } while(0) +#define WINE_WARN_(ch) WINE_WARN +#define WINE_FIXME(args...) do { } while(0) +#define WINE_FIXME_(ch) WINE_FIXME +#endif + +#elif defined(__SUNPRO_C) + +#define __WINE_DPRINTF(dbcl,dbch) \ + do { if(__WINE_GET_DEBUGGING(dbcl,(dbch))) { \ + struct __wine_debug_channel * const __dbch = (dbch); \ + const enum __WINE_DEBUG_CLASS __dbcl = __WINE_DBCL##dbcl; \ + __WINE_DBG_LOG + +#define __WINE_DBG_LOG(...) \ + wine_dbg_log( __dbcl, __dbch, __func__, __VA_ARGS__); } } while(0) + +#define __WINE_PRINTF_ATTR(fmt,args) + +#ifdef WINE_NO_TRACE_MSGS +#define WINE_TRACE(...) do { } while(0) +#define WINE_TRACE_(ch) WINE_TRACE +#endif + +#ifdef WINE_NO_DEBUG_MSGS +#define WINE_WARN(...) do { } while(0) +#define WINE_WARN_(ch) WINE_WARN +#define WINE_FIXME(...) do { } while(0) +#define WINE_FIXME_(ch) WINE_FIXME +#endif + +#else /* !__GNUC__ && !__SUNPRO_C */ + +#define __WINE_DPRINTF(dbcl,dbch) \ + (!__WINE_GET_DEBUGGING(dbcl,(dbch)) || \ + (wine_dbg_log(__WINE_DBCL##dbcl,(dbch),__FILE__,"%d: ",__LINE__) == -1)) ? \ + (void)0 : (void)wine_dbg_printf + +#define __WINE_PRINTF_ATTR(fmt, args) + +#endif /* !__GNUC__ && !__SUNPRO_C */ + +struct __wine_debug_functions +{ + char * (*get_temp_buffer)( size_t n ); + void (*release_temp_buffer)( char *buffer, size_t n ); + const char * (*dbgstr_an)( const char * s, int n ); + const char * (*dbgstr_wn)( const WCHAR *s, int n ); + int (*dbg_vprintf)( const char *format, va_list args ); + int (*dbg_vlog)( enum __wine_debug_class cls, struct __wine_debug_channel *channel, + const char *function, const char *format, va_list args ); +}; + +extern unsigned char __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel ); +extern int __wine_dbg_set_channel_flags( struct __wine_debug_channel *channel, + unsigned char set, unsigned char clear ); +extern void __wine_dbg_set_functions( const struct __wine_debug_functions *new_funcs, + struct __wine_debug_functions *old_funcs, size_t size ); + +/* + * Exported definitions and macros + */ + +/* These functions return a printable version of a string, including + quotes. The string will be valid for some time, but not indefinitely + as strings are re-used. */ +extern const char *wine_dbgstr_an( const char * s, int n ); +extern const char *wine_dbgstr_wn( const WCHAR *s, int n ); +extern const char *wine_dbg_sprintf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); + +extern int wine_dbg_printf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); +extern int wine_dbg_log( enum __wine_debug_class cls, struct __wine_debug_channel *ch, const char *func, + const char *format, ... ) __WINE_PRINTF_ATTR(4,5); + +static inline const char *wine_dbgstr_a( const char *s ) +{ + return wine_dbgstr_an( s, -1 ); +} + +static inline const char *wine_dbgstr_w( const WCHAR *s ) +{ + return wine_dbgstr_wn( s, -1 ); +} + +static inline const char *wine_dbgstr_guid( const GUID *id ) +{ + if (!id) return "(null)"; + if (!((ULONG_PTR)id >> 16)) return wine_dbg_sprintf( "<guid-0x%04hx>", (WORD)(ULONG_PTR)id ); + return wine_dbg_sprintf( "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + id->Data1, id->Data2, id->Data3, + id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3], + id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] ); +} + +static inline const char *wine_dbgstr_point( const POINT *pt ) +{ + if (!pt) return "(null)"; + return wine_dbg_sprintf( "(%d,%d)", pt->x, pt->y ); +} + +static inline const char *wine_dbgstr_size( const SIZE *size ) +{ + if (!size) return "(null)"; + return wine_dbg_sprintf( "(%d,%d)", size->cx, size->cy ); +} + +static inline const char *wine_dbgstr_rect( const RECT *rect ) +{ + if (!rect) return "(null)"; + return wine_dbg_sprintf( "(%d,%d)-(%d,%d)", rect->left, rect->top, + rect->right, rect->bottom ); +} + +static inline const char *wine_dbgstr_longlong( ULONGLONG ll ) +{ + if (sizeof(ll) > sizeof(unsigned long) && ll >> 32) + return wine_dbg_sprintf( "%lx%08lx", (unsigned long)(ll >> 32), (unsigned long)ll ); + else return wine_dbg_sprintf( "%lx", (unsigned long)ll ); +} + +#ifndef WINE_TRACE +#define WINE_TRACE __WINE_DPRINTF(_TRACE,__wine_dbch___default) +#define WINE_TRACE_(ch) __WINE_DPRINTF(_TRACE,&__wine_dbch_##ch) +#endif +#define WINE_TRACE_ON(ch) __WINE_IS_DEBUG_ON(_TRACE,&__wine_dbch_##ch) + +#ifndef WINE_WARN +#define WINE_WARN __WINE_DPRINTF(_WARN,__wine_dbch___default) +#define WINE_WARN_(ch) __WINE_DPRINTF(_WARN,&__wine_dbch_##ch) +#endif +#define WINE_WARN_ON(ch) __WINE_IS_DEBUG_ON(_WARN,&__wine_dbch_##ch) + +#ifndef WINE_FIXME +#define WINE_FIXME __WINE_DPRINTF(_FIXME,__wine_dbch___default) +#define WINE_FIXME_(ch) __WINE_DPRINTF(_FIXME,&__wine_dbch_##ch) +#endif +#define WINE_FIXME_ON(ch) __WINE_IS_DEBUG_ON(_FIXME,&__wine_dbch_##ch) + +#define WINE_ERR __WINE_DPRINTF(_ERR,__wine_dbch___default) +#define WINE_ERR_(ch) __WINE_DPRINTF(_ERR,&__wine_dbch_##ch) +#define WINE_ERR_ON(ch) __WINE_IS_DEBUG_ON(_ERR,&__wine_dbch_##ch) + +#define WINE_DECLARE_DEBUG_CHANNEL(ch) \ + static struct __wine_debug_channel __wine_dbch_##ch = { ~0, #ch } +#define WINE_DEFAULT_DEBUG_CHANNEL(ch) \ + static struct __wine_debug_channel __wine_dbch_##ch = { ~0, #ch }; \ + static struct __wine_debug_channel * const __wine_dbch___default = &__wine_dbch_##ch + +#define WINE_DPRINTF wine_dbg_printf +#define WINE_MESSAGE wine_dbg_printf + +#ifdef __WINESRC__ +/* Wine uses shorter names that are very likely to conflict with other software */ + +static inline const char *debugstr_an( const char * s, int n ) { return wine_dbgstr_an( s, n ); } +static inline const char *debugstr_wn( const WCHAR *s, int n ) { return wine_dbgstr_wn( s, n ); } +static inline const char *debugstr_guid( const struct _GUID *id ) { return wine_dbgstr_guid(id); } +static inline const char *debugstr_a( const char *s ) { return wine_dbgstr_an( s, -1 ); } +static inline const char *debugstr_w( const WCHAR *s ) { return wine_dbgstr_wn( s, -1 ); } + +#define TRACE WINE_TRACE +#define TRACE_(ch) WINE_TRACE_(ch) +#define TRACE_ON(ch) WINE_TRACE_ON(ch) + +#define WARN WINE_WARN +#define WARN_(ch) WINE_WARN_(ch) +#define WARN_ON(ch) WINE_WARN_ON(ch) + +#define FIXME WINE_FIXME +#define FIXME_(ch) WINE_FIXME_(ch) +#define FIXME_ON(ch) WINE_FIXME_ON(ch) + +#undef ERR /* Solaris got an 'ERR' define in <sys/reg.h> */ +#define ERR WINE_ERR +#define ERR_(ch) WINE_ERR_(ch) +#define ERR_ON(ch) WINE_ERR_ON(ch) + +#define DPRINTF WINE_DPRINTF +#define MESSAGE WINE_MESSAGE + +#endif /* __WINESRC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* __WINE_WINE_DEBUG_H */ diff --git a/libmsi/wine/list.h b/libmsi/wine/list.h new file mode 100644 index 0000000..9712603 --- /dev/null +++ b/libmsi/wine/list.h @@ -0,0 +1,232 @@ +/* + * Linked lists support + * + * Copyright (C) 2002 Alexandre Julliard + * + * 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 + */ + +#ifndef __WINE_SERVER_LIST_H +#define __WINE_SERVER_LIST_H + +struct list +{ + struct list *next; + struct list *prev; +}; + +/* Define a list like so: + * + * struct gadget + * { + * struct list entry; <-- doesn't have to be the first item in the struct + * int a, b; + * }; + * + * static struct list global_gadgets = LIST_INIT( global_gadgets ); + * + * or + * + * struct some_global_thing + * { + * struct list gadgets; + * }; + * + * list_init( &some_global_thing->gadgets ); + * + * Manipulate it like this: + * + * list_add_head( &global_gadgets, &new_gadget->entry ); + * list_remove( &new_gadget->entry ); + * list_add_after( &some_random_gadget->entry, &new_gadget->entry ); + * + * And to iterate over it: + * + * struct gadget *gadget; + * LIST_FOR_EACH_ENTRY( gadget, &global_gadgets, struct gadget, entry ) + * { + * ... + * } + * + */ + +/* add an element after the specified one */ +static inline void list_add_after( struct list *elem, struct list *to_add ) +{ + to_add->next = elem->next; + to_add->prev = elem; + elem->next->prev = to_add; + elem->next = to_add; +} + +/* add an element before the specified one */ +static inline void list_add_before( struct list *elem, struct list *to_add ) +{ + to_add->next = elem; + to_add->prev = elem->prev; + elem->prev->next = to_add; + elem->prev = to_add; +} + +/* add element at the head of the list */ +static inline void list_add_head( struct list *list, struct list *elem ) +{ + list_add_after( list, elem ); +} + +/* add element at the tail of the list */ +static inline void list_add_tail( struct list *list, struct list *elem ) +{ + list_add_before( list, elem ); +} + +/* remove an element from its list */ +static inline void list_remove( struct list *elem ) +{ + elem->next->prev = elem->prev; + elem->prev->next = elem->next; +} + +/* get the next element */ +static inline struct list *list_next( const struct list *list, const struct list *elem ) +{ + struct list *ret = elem->next; + if (elem->next == list) ret = NULL; + return ret; +} + +/* get the previous element */ +static inline struct list *list_prev( const struct list *list, const struct list *elem ) +{ + struct list *ret = elem->prev; + if (elem->prev == list) ret = NULL; + return ret; +} + +/* get the first element */ +static inline struct list *list_head( const struct list *list ) +{ + return list_next( list, list ); +} + +/* get the last element */ +static inline struct list *list_tail( const struct list *list ) +{ + return list_prev( list, list ); +} + +/* check if a list is empty */ +static inline int list_empty( const struct list *list ) +{ + return list->next == list; +} + +/* initialize a list */ +static inline void list_init( struct list *list ) +{ + list->next = list->prev = list; +} + +/* count the elements of a list */ +static inline unsigned int list_count( const struct list *list ) +{ + unsigned count = 0; + const struct list *ptr; + for (ptr = list->next; ptr != list; ptr = ptr->next) count++; + return count; +} + +/* move all elements from src to the tail of dst */ +static inline void list_move_tail( struct list *dst, struct list *src ) +{ + if (list_empty(src)) return; + + dst->prev->next = src->next; + src->next->prev = dst->prev; + dst->prev = src->prev; + src->prev->next = dst; + list_init(src); +} + +/* move all elements from src to the head of dst */ +static inline void list_move_head( struct list *dst, struct list *src ) +{ + if (list_empty(src)) return; + + dst->next->prev = src->prev; + src->prev->next = dst->next; + dst->next = src->next; + src->next->prev = dst; + list_init(src); +} + +/* iterate through the list */ +#define LIST_FOR_EACH(cursor,list) \ + for ((cursor) = (list)->next; (cursor) != (list); (cursor) = (cursor)->next) + +/* iterate through the list, with safety against removal */ +#define LIST_FOR_EACH_SAFE(cursor, cursor2, list) \ + for ((cursor) = (list)->next, (cursor2) = (cursor)->next; \ + (cursor) != (list); \ + (cursor) = (cursor2), (cursor2) = (cursor)->next) + +/* iterate through the list using a list entry */ +#define LIST_FOR_EACH_ENTRY(elem, list, type, field) \ + for ((elem) = LIST_ENTRY((list)->next, type, field); \ + &(elem)->field != (list); \ + (elem) = LIST_ENTRY((elem)->field.next, type, field)) + +/* iterate through the list using a list entry, with safety against removal */ +#define LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, list, type, field) \ + for ((cursor) = LIST_ENTRY((list)->next, type, field), \ + (cursor2) = LIST_ENTRY((cursor)->field.next, type, field); \ + &(cursor)->field != (list); \ + (cursor) = (cursor2), \ + (cursor2) = LIST_ENTRY((cursor)->field.next, type, field)) + +/* iterate through the list in reverse order */ +#define LIST_FOR_EACH_REV(cursor,list) \ + for ((cursor) = (list)->prev; (cursor) != (list); (cursor) = (cursor)->prev) + +/* iterate through the list in reverse order, with safety against removal */ +#define LIST_FOR_EACH_SAFE_REV(cursor, cursor2, list) \ + for ((cursor) = (list)->prev, (cursor2) = (cursor)->prev; \ + (cursor) != (list); \ + (cursor) = (cursor2), (cursor2) = (cursor)->prev) + +/* iterate through the list in reverse order using a list entry */ +#define LIST_FOR_EACH_ENTRY_REV(elem, list, type, field) \ + for ((elem) = LIST_ENTRY((list)->prev, type, field); \ + &(elem)->field != (list); \ + (elem) = LIST_ENTRY((elem)->field.prev, type, field)) + +/* iterate through the list in reverse order using a list entry, with safety against removal */ +#define LIST_FOR_EACH_ENTRY_SAFE_REV(cursor, cursor2, list, type, field) \ + for ((cursor) = LIST_ENTRY((list)->prev, type, field), \ + (cursor2) = LIST_ENTRY((cursor)->field.prev, type, field); \ + &(cursor)->field != (list); \ + (cursor) = (cursor2), \ + (cursor2) = LIST_ENTRY((cursor)->field.prev, type, field)) + +/* macros for statically initialized lists */ +#undef LIST_INIT +#define LIST_INIT(list) { &(list), &(list) } + +/* get pointer to object containing list element */ +#undef LIST_ENTRY +#define LIST_ENTRY(elem, type, field) \ + ((type *)((char *)(elem) - (size_t)(&((type *)0)->field))) + +#endif /* __WINE_SERVER_LIST_H */ diff --git a/libmsi/wine/unicode.h b/libmsi/wine/unicode.h new file mode 100644 index 0000000..35c6166 --- /dev/null +++ b/libmsi/wine/unicode.h @@ -0,0 +1,311 @@ +/* + * Wine internal Unicode definitions + * + * Copyright 2000 Alexandre Julliard + * + * 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 + */ + +#ifndef __WINE_WINE_UNICODE_H +#define __WINE_WINE_UNICODE_H + +#include <stdarg.h> + +#include <windef.h> +#include <winbase.h> +#include <winnls.h> + +#ifdef __WINE_WINE_TEST_H +#error This file should not be used in Wine tests +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WINE_UNICODE_API +# if defined(_MSC_VER) || defined(__MINGW32__) +# define WINE_UNICODE_API DECLSPEC_IMPORT +# else +# define WINE_UNICODE_API +# endif +#endif + +#ifndef WINE_UNICODE_INLINE +#define WINE_UNICODE_INLINE static inline +#endif + +/* code page info common to SBCS and DBCS */ +struct cp_info +{ + unsigned int codepage; /* codepage id */ + unsigned int char_size; /* char size (1 or 2 bytes) */ + WCHAR def_char; /* default char value (can be double-byte) */ + WCHAR def_unicode_char; /* default Unicode char value */ + const char *name; /* code page name */ +}; + +struct sbcs_table +{ + struct cp_info info; + const WCHAR *cp2uni; /* code page -> Unicode map */ + const WCHAR *cp2uni_glyphs; /* code page -> Unicode map with glyph chars */ + const unsigned char *uni2cp_low; /* Unicode -> code page map */ + const unsigned short *uni2cp_high; +}; + +struct dbcs_table +{ + struct cp_info info; + const WCHAR *cp2uni; /* code page -> Unicode map */ + const unsigned char *cp2uni_leadbytes; + const unsigned short *uni2cp_low; /* Unicode -> code page map */ + const unsigned short *uni2cp_high; + unsigned char lead_bytes[12]; /* lead bytes ranges */ +}; + +union cptable +{ + struct cp_info info; + struct sbcs_table sbcs; + struct dbcs_table dbcs; +}; + +extern const union cptable *wine_cp_get_table( unsigned int codepage ); +extern const union cptable *wine_cp_enum_table( unsigned int index ); + +extern int wine_cp_mbstowcs( const union cptable *table, int flags, + const char *src, int srclen, + WCHAR *dst, int dstlen ); +extern int wine_cp_wcstombs( const union cptable *table, int flags, + const WCHAR *src, int srclen, + char *dst, int dstlen, const char *defchar, int *used ); +extern int wine_cpsymbol_mbstowcs( const char *src, int srclen, WCHAR *dst, int dstlen ); +extern int wine_cpsymbol_wcstombs( const WCHAR *src, int srclen, char *dst, int dstlen ); +extern int wine_utf8_mbstowcs( int flags, const char *src, int srclen, WCHAR *dst, int dstlen ); +extern int wine_utf8_wcstombs( int flags, const WCHAR *src, int srclen, char *dst, int dstlen ); + +extern int wine_compare_string( int flags, const WCHAR *str1, int len1, const WCHAR *str2, int len2 ); +extern int wine_get_sortkey( int flags, const WCHAR *src, int srclen, char *dst, int dstlen ); +extern int wine_fold_string( int flags, const WCHAR *src, int srclen , WCHAR *dst, int dstlen ); + +extern int strcmpiW( const WCHAR *str1, const WCHAR *str2 ); +extern int strncmpiW( const WCHAR *str1, const WCHAR *str2, int n ); +extern int memicmpW( const WCHAR *str1, const WCHAR *str2, int n ); +extern WCHAR *strstrW( const WCHAR *str, const WCHAR *sub ); +extern long int strtolW( const WCHAR *nptr, WCHAR **endptr, int base ); +extern unsigned long int strtoulW( const WCHAR *nptr, WCHAR **endptr, int base ); +extern int sprintfW( WCHAR *str, const WCHAR *format, ... ); +extern int snprintfW( WCHAR *str, size_t len, const WCHAR *format, ... ); +extern int vsprintfW( WCHAR *str, const WCHAR *format, va_list valist ); +extern int vsnprintfW( WCHAR *str, size_t len, const WCHAR *format, va_list valist ); + +WINE_UNICODE_INLINE int wine_is_dbcs_leadbyte( const union cptable *table, unsigned char ch ) +{ + return (table->info.char_size == 2) && (table->dbcs.cp2uni_leadbytes[ch]); +} + +WINE_UNICODE_INLINE WCHAR tolowerW( WCHAR ch ) +{ + extern WINE_UNICODE_API const WCHAR wine_casemap_lower[]; + return ch + wine_casemap_lower[wine_casemap_lower[ch >> 8] + (ch & 0xff)]; +} + +WINE_UNICODE_INLINE WCHAR toupperW( WCHAR ch ) +{ + extern WINE_UNICODE_API const WCHAR wine_casemap_upper[]; + return ch + wine_casemap_upper[wine_casemap_upper[ch >> 8] + (ch & 0xff)]; +} + +/* the character type contains the C1_* flags in the low 12 bits */ +/* and the C2_* type in the high 4 bits */ +WINE_UNICODE_INLINE unsigned short get_char_typeW( WCHAR ch ) +{ + extern WINE_UNICODE_API const unsigned short wine_wctype_table[]; + return wine_wctype_table[wine_wctype_table[ch >> 8] + (ch & 0xff)]; +} + +WINE_UNICODE_INLINE int iscntrlW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_CNTRL; +} + +WINE_UNICODE_INLINE int ispunctW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_PUNCT; +} + +WINE_UNICODE_INLINE int isspaceW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_SPACE; +} + +WINE_UNICODE_INLINE int isdigitW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_DIGIT; +} + +WINE_UNICODE_INLINE int isxdigitW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_XDIGIT; +} + +WINE_UNICODE_INLINE int islowerW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_LOWER; +} + +WINE_UNICODE_INLINE int isupperW( WCHAR wc ) +{ + return get_char_typeW(wc) & C1_UPPER; +} + +WINE_UNICODE_INLINE int isalnumW( WCHAR wc ) +{ + return get_char_typeW(wc) & (C1_ALPHA|C1_DIGIT|C1_LOWER|C1_UPPER); +} + +WINE_UNICODE_INLINE int isalphaW( WCHAR wc ) +{ + return get_char_typeW(wc) & (C1_ALPHA|C1_LOWER|C1_UPPER); +} + +WINE_UNICODE_INLINE int isgraphW( WCHAR wc ) +{ + return get_char_typeW(wc) & (C1_ALPHA|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER); +} + +WINE_UNICODE_INLINE int isprintW( WCHAR wc ) +{ + return get_char_typeW(wc) & (C1_ALPHA|C1_BLANK|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER); +} + +/* some useful string manipulation routines */ + +WINE_UNICODE_INLINE unsigned int strlenW( const WCHAR *str ) +{ + const WCHAR *s = str; + while (*s) s++; + return s - str; +} + +WINE_UNICODE_INLINE WCHAR *strcpyW( WCHAR *dst, const WCHAR *src ) +{ + WCHAR *p = dst; + while ((*p++ = *src++)); + return dst; +} + +/* strncpy doesn't do what you think, don't use it */ +#define strncpyW(d,s,n) error do_not_use_strncpyW_use_lstrcpynW_or_memcpy_instead + +WINE_UNICODE_INLINE int strcmpW( const WCHAR *str1, const WCHAR *str2 ) +{ + while (*str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +WINE_UNICODE_INLINE int strncmpW( const WCHAR *str1, const WCHAR *str2, int n ) +{ + if (n <= 0) return 0; + while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +WINE_UNICODE_INLINE WCHAR *strcatW( WCHAR *dst, const WCHAR *src ) +{ + strcpyW( dst + strlenW(dst), src ); + return dst; +} + +WINE_UNICODE_INLINE WCHAR *strchrW( const WCHAR *str, WCHAR ch ) +{ + do { if (*str == ch) return (WCHAR *)(ULONG_PTR)str; } while (*str++); + return NULL; +} + +WINE_UNICODE_INLINE WCHAR *strrchrW( const WCHAR *str, WCHAR ch ) +{ + WCHAR *ret = NULL; + do { if (*str == ch) ret = (WCHAR *)(ULONG_PTR)str; } while (*str++); + return ret; +} + +WINE_UNICODE_INLINE WCHAR *strpbrkW( const WCHAR *str, const WCHAR *accept ) +{ + for ( ; *str; str++) if (strchrW( accept, *str )) return (WCHAR *)(ULONG_PTR)str; + return NULL; +} + +WINE_UNICODE_INLINE size_t strspnW( const WCHAR *str, const WCHAR *accept ) +{ + const WCHAR *ptr; + for (ptr = str; *ptr; ptr++) if (!strchrW( accept, *ptr )) break; + return ptr - str; +} + +WINE_UNICODE_INLINE size_t strcspnW( const WCHAR *str, const WCHAR *reject ) +{ + const WCHAR *ptr; + for (ptr = str; *ptr; ptr++) if (strchrW( reject, *ptr )) break; + return ptr - str; +} + +WINE_UNICODE_INLINE WCHAR *strlwrW( WCHAR *str ) +{ + WCHAR *ret; + for (ret = str; *str; str++) *str = tolowerW(*str); + return ret; +} + +WINE_UNICODE_INLINE WCHAR *struprW( WCHAR *str ) +{ + WCHAR *ret; + for (ret = str; *str; str++) *str = toupperW(*str); + return ret; +} + +WINE_UNICODE_INLINE WCHAR *memchrW( const WCHAR *ptr, WCHAR ch, size_t n ) +{ + const WCHAR *end; + for (end = ptr + n; ptr < end; ptr++) if (*ptr == ch) return (WCHAR *)(ULONG_PTR)ptr; + return NULL; +} + +WINE_UNICODE_INLINE WCHAR *memrchrW( const WCHAR *ptr, WCHAR ch, size_t n ) +{ + const WCHAR *end; + WCHAR *ret = NULL; + for (end = ptr + n; ptr < end; ptr++) if (*ptr == ch) ret = (WCHAR *)(ULONG_PTR)ptr; + return ret; +} + +WINE_UNICODE_INLINE long int atolW( const WCHAR *str ) +{ + return strtolW( str, (WCHAR **)0, 10 ); +} + +WINE_UNICODE_INLINE int atoiW( const WCHAR *str ) +{ + return (int)atolW( str ); +} + +#undef WINE_UNICODE_INLINE + +#ifdef __cplusplus +} +#endif + +#endif /* __WINE_WINE_UNICODE_H */ diff --git a/libmsi/wine/wine_common_ver.rc b/libmsi/wine/wine_common_ver.rc new file mode 100644 index 0000000..0e755e6 --- /dev/null +++ b/libmsi/wine/wine_common_ver.rc @@ -0,0 +1,120 @@ +/* + * Copyright 2001 Dmitry Timoshkov + * Copyright 2004 Ivan Leo Puoti + * + * 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 "winresrc.h" + +/* +Assign WINE_FILEVERSION and WINE_FILEVERSION_STR high enough number +to make sure that programs, relying on the version numbers, will +never complain. +*/ + +#ifndef WINE_FILEVERSION_MAJOR +#define WINE_FILEVERSION_MAJOR 10 +#endif + +#ifndef WINE_FILEVERSION_MINOR +#define WINE_FILEVERSION_MINOR 0 +#endif + +#ifndef WINE_FILEVERSION_BUILD +#define WINE_FILEVERSION_BUILD 0 +#endif + +#ifndef WINE_FILEVERSION_PLATFORMID +#define WINE_FILEVERSION_PLATFORMID 0 +#endif + +#ifndef WINE_FILEVERSION +#define WINE_FILEVERSION WINE_FILEVERSION_MAJOR,WINE_FILEVERSION_MINOR,\ + WINE_FILEVERSION_BUILD,WINE_FILEVERSION_PLATFORMID +#endif + +#define WINE_VER_STRINGIZE2(x) #x +#define WINE_VER_STRINGIZE(x) WINE_VER_STRINGIZE2(x) + +#ifndef WINE_FILEVERSION_STR +#define WINE_FILEVERSION_STR WINE_VER_STRINGIZE(WINE_FILEVERSION_MAJOR.WINE_FILEVERSION_MINOR.WINE_FILEVERSION_BUILD.WINE_FILEVERSION_PLATFORMID) +#endif + +#ifndef WINE_FILEDESCRIPTION_STR +#define WINE_FILEDESCRIPTION_STR "Wine core dll" +#endif + +#ifndef WINE_FILENAME +#define WINE_FILENAME "" +#endif + +#ifndef WINE_FILENAME_STR +#define WINE_FILENAME_STR "" +#endif + +#ifndef WINE_FILETYPE +#define WINE_FILETYPE VFT_DLL +#endif + +#ifndef WINE_FILESUBTYPE +#define WINE_FILESUBTYPE VFT2_UNKNOWN +#endif + +#ifndef WINE_PRODUCTVERSION +#define WINE_PRODUCTVERSION 1,0,0,0 +#endif + +#ifndef WINE_PRODUCTVERSION_STR +#define WINE_PRODUCTVERSION_STR "1.0" +#endif + +#ifndef WINE_PRODUCTNAME_STR +#define WINE_PRODUCTNAME_STR "Wine" +#endif + +#ifndef WINE_EXTRAVALUES +#define WINE_EXTRAVALUES +#endif + +VS_VERSION_INFO VERSIONINFO +FILEVERSION WINE_FILEVERSION +PRODUCTVERSION WINE_PRODUCTVERSION +FILEFLAGSMASK 63 +FILEFLAGS 0 +FILEOS VOS_UNKNOWN +FILETYPE WINE_FILETYPE +FILESUBTYPE WINE_FILESUBTYPE +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" /* LANG_ENGLISH/SUBLANG_DEFAULT, CP 1252 */ + { + VALUE "CompanyName", "Microsoft Corporation" /* GameGuard depends on this */ + VALUE "FileDescription", WINE_FILEDESCRIPTION_STR + VALUE "FileVersion", WINE_FILEVERSION_STR + VALUE "InternalName", WINE_FILENAME + VALUE "LegalCopyright", "Copyright (c) 1993-2012 the Wine project authors (see the file AUTHORS for a complete list)" + VALUE "OriginalFilename", WINE_FILENAME_STR + VALUE "ProductName", WINE_PRODUCTNAME_STR + VALUE "ProductVersion", WINE_PRODUCTVERSION_STR + WINE_EXTRAVALUES + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04E4 /* LANG_ENGLISH/SUBLANG_DEFAULT, CP 1252 */ + } +} diff --git a/libmsi/winnt.h b/libmsi/winnt.h new file mode 100644 index 0000000..0be1db5 --- /dev/null +++ b/libmsi/winnt.h @@ -0,0 +1,151 @@ +/* + * Win32 definitions for Windows NT + * + * Copyright 1996 Alexandre Julliard + * + * 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 + */ + +#ifndef _WINE_WINNT_ +#define _WINE_WINNT_ + +#ifndef MIDL_PASS +# if defined(_MSC_VER) +# define DECLSPEC_IMPORT __declspec(dllimport) +# elif defined(__MINGW32__) || defined(__CYGWIN__) +# define DECLSPEC_IMPORT __attribute__((dllimport)) +# else +# define DECLSPEC_IMPORT DECLSPEC_HIDDEN +# endif +#else +# define DECLSPEC_IMPORT +#endif + +#ifndef DECLSPEC_NORETURN +# if defined(_MSC_VER) && (_MSC_VER >= 1200) && !defined(MIDL_PASS) +# define DECLSPEC_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define DECLSPEC_NORETURN __attribute__((noreturn)) +# else +# define DECLSPEC_NORETURN +# endif +#endif + +#ifndef DECLSPEC_ALIGN +# if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(MIDL_PASS) +# define DECLSPEC_ALIGN(x) __declspec(align(x)) +# elif defined(__GNUC__) +# define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) +# else +# define DECLSPEC_ALIGN(x) +# endif +#endif + +#ifndef DECLSPEC_CACHEALIGN +# define DECLSPEC_CACHEALIGN DECLSPEC_ALIGN(128) +#endif + +#ifndef DECLSPEC_UUID +# if defined(_MSC_VER) && (_MSC_VER >= 1100) && defined (__cplusplus) +# define DECLSPEC_UUID(x) __declspec(uuid(x)) +# else +# define DECLSPEC_UUID(x) +# endif +#endif + +#ifndef DECLSPEC_NOVTABLE +# if defined(_MSC_VER) && (_MSC_VER >= 1100) && defined(__cplusplus) +# define DECLSPEC_NOVTABLE __declspec(novtable) +# else +# define DECLSPEC_NOVTABLE +# endif +#endif + +#ifndef DECLSPEC_SELECTANY +#if defined(_MSC_VER) && (_MSC_VER >= 1100) +#define DECLSPEC_SELECTANY __declspec(selectany) +#else +#define DECLSPEC_SELECTANY +#endif +#endif + +#ifndef NOP_FUNCTION +# if defined(_MSC_VER) && (_MSC_VER >= 1210) +# define NOP_FUNCTION __noop +# else +# define NOP_FUNCTION (void)0 +# endif +#endif + +#ifndef DECLSPEC_ADDRSAFE +# if defined(_MSC_VER) && (_MSC_VER >= 1200) && (defined(_M_ALPHA) || defined(_M_AXP64)) +# define DECLSPEC_ADDRSAFE __declspec(address_safe) +# else +# define DECLSPEC_ADDRSAFE +# endif +#endif + +#ifndef FORCEINLINE +# if defined(_MSC_VER) && (_MSC_VER >= 1200) +# define FORCEINLINE __forceinline +# elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2))) +# define FORCEINLINE inline __attribute__((always_inline)) +# else +# define FORCEINLINE inline +# endif +#endif + +#ifndef DECLSPEC_DEPRECATED +# if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(MIDL_PASS) +# define DECLSPEC_DEPRECATED __declspec(deprecated) +# define DEPRECATE_SUPPORTED +# elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2))) +# define DECLSPEC_DEPRECATED __attribute__((deprecated)) +# define DEPRECATE_SUPPORTED +# else +# define DECLSPEC_DEPRECATED +# undef DEPRECATE_SUPPORTED +# endif +#endif + +/* a couple of useful Wine extensions */ + +#ifdef _MSC_VER +# define DECLSPEC_EXPORT __declspec(dllexport) +#elif defined(__MINGW32__) +# define DECLSPEC_EXPORT __attribute__((dllexport)) +#elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) +# define DECLSPEC_EXPORT __attribute__((visibility ("default"))) +#else +# define DECLSPEC_EXPORT +#endif + +#if defined(__MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__) +# define DECLSPEC_HIDDEN +#elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) +# define DECLSPEC_HIDDEN __attribute__((visibility ("hidden"))) +#else +# define DECLSPEC_HIDDEN +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))) +#define __WINE_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +#else +#define __WINE_ALLOC_SIZE(x) +#endif + +#include_next <winnt.h> + +#endif /* _WINNT_ */ diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..94e6f26 --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,5 @@ +/libtool.m4 +/ltoptions.m4 +/ltsugar.m4 +/ltversion.m4 +/lt~obsolete.m4 |