summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--Makefile.am2
-rw-r--r--build-aux/.gitignore7
-rw-r--r--configure.ac21
-rw-r--r--libmsi/Makefile.am69
-rw-r--r--libmsi/action.c7682
-rw-r--r--libmsi/alter.c285
-rw-r--r--libmsi/appsearch.c1189
-rw-r--r--libmsi/assembly.c694
-rw-r--r--libmsi/automation.c2461
-rw-r--r--libmsi/classes.c1564
-rw-r--r--libmsi/cond.y898
-rw-r--r--libmsi/create.c208
-rw-r--r--libmsi/custom.c1508
-rw-r--r--libmsi/database.c2180
-rw-r--r--libmsi/delete.c217
-rw-r--r--libmsi/dialog.c4164
-rw-r--r--libmsi/distinct.c322
-rw-r--r--libmsi/drop.c142
-rw-r--r--libmsi/events.c449
-rw-r--r--libmsi/files.c1329
-rw-r--r--libmsi/font.c385
-rw-r--r--libmsi/format.c1045
-rw-r--r--libmsi/handle.c346
-rw-r--r--libmsi/insert.c401
-rw-r--r--libmsi/instabsent.bmpbin0 -> 374 bytes
-rw-r--r--libmsi/instadvert.bmpbin0 -> 374 bytes
-rw-r--r--libmsi/install.c1740
-rw-r--r--libmsi/instlocal.bmpbin0 -> 374 bytes
-rw-r--r--libmsi/media.c936
-rw-r--r--libmsi/msi.c4215
-rw-r--r--libmsi/msi.h704
-rw-r--r--libmsi/msi.rc85
-rw-r--r--libmsi/msi_main.c245
-rw-r--r--libmsi/msipriv.h1249
-rw-r--r--libmsi/msiquery.c1053
-rw-r--r--libmsi/msiquery.h326
-rw-r--r--libmsi/msiserver.idl558
-rw-r--r--libmsi/msiserver.rgs8
-rw-r--r--libmsi/msiserver_dispids.h68
-rw-r--r--libmsi/package.c2674
-rw-r--r--libmsi/patch.c783
-rw-r--r--libmsi/query.h141
-rw-r--r--libmsi/record.c1066
-rw-r--r--libmsi/registry.c2478
-rw-r--r--libmsi/script.c379
-rw-r--r--libmsi/select.c458
-rw-r--r--libmsi/source.c1363
-rw-r--r--libmsi/sql.y1031
-rw-r--r--libmsi/storages.c553
-rw-r--r--libmsi/streams.c563
-rw-r--r--libmsi/string.c674
-rw-r--r--libmsi/suminfo.c998
-rw-r--r--libmsi/table.c2805
-rw-r--r--libmsi/tokenize.c290
-rw-r--r--libmsi/update.c269
-rw-r--r--libmsi/upgrade.c234
-rw-r--r--libmsi/where.c1289
-rw-r--r--libmsi/wine/debug.h288
-rw-r--r--libmsi/wine/list.h232
-rw-r--r--libmsi/wine/unicode.h311
-rw-r--r--libmsi/wine/wine_common_ver.rc120
-rw-r--r--libmsi/winnt.h151
-rw-r--r--m4/.gitignore5
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, &regType, 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
new file mode 100644
index 0000000..9347961
--- /dev/null
+++ b/libmsi/instabsent.bmp
Binary files differ
diff --git a/libmsi/instadvert.bmp b/libmsi/instadvert.bmp
new file mode 100644
index 0000000..77b5309
--- /dev/null
+++ b/libmsi/instadvert.bmp
Binary files differ
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
new file mode 100644
index 0000000..c9591ad
--- /dev/null
+++ b/libmsi/instlocal.bmp
Binary files differ
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, &section_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, &section_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], &section_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( &lt, 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, &lt, &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, &current );
+ 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(&current->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