--- /dev/null
+*.coff
+*.dll
+*.d
+*.a
+*.o
+*.sym
+*.map
+*.tmp
--- /dev/null
+# $Id: Makefile,v 1.1 2004/11/30 19:10:50 jimtabor Exp $
+
+PATH_TO_TOP = ../..
+
+TARGET_TYPE = winedll
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
--- /dev/null
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = msi.dll
+IMPORTS = shell32 cabinet oleaut32 ole32 version user32 advapi32 kernel32
+EXTRALIBS = -luuid $(LIBUNICODE)
+
+C_SRCS = \
+ action.c \
+ create.c \
+ distinct.c \
+ handle.c \
+ insert.c \
+ msi.c \
+ msiquery.c \
+ order.c \
+ package.c \
+ record.c \
+ regsvr.c \
+ select.c \
+ string.c \
+ suminfo.c \
+ table.c \
+ tokenize.c \
+ update.c \
+ where.c
+
+RC_SRCS = version.rc
+
+EXTRA_SRCS = sql.y cond.y
+EXTRA_OBJS = sql.tab.o cond.tab.o
+
+@MAKE_DLL_RULES@
+
+sql.tab.c sql.tab.h: sql.y
+ $(BISON) -p SQL_ -d $(SRCDIR)/sql.y -o sql.tab.c
+
+cond.tab.c cond.tab.h: cond.y
+ $(BISON) -p COND_ -d $(SRCDIR)/cond.y -o cond.tab.c
+
+# hack to allow parallel make
+sql.tab.h: sql.tab.c
+sql.tab.o: sql.tab.h
+cond.tab.h: cond.tab.c
+cond.tab.o: cond.tab.h
+
+tokenize.o: sql.tab.h
+
+### Dependencies:
--- /dev/null
+# $Id: Makefile.ros-template
+
+TARGET_NAME = msi
+
+TARGET_OBJECTS = @C_SRCS@ @EXTRA_OBJS@
+
+TARGET_CFLAGS = -D__REACTOS__ @EXTRADEFS@
+
+TARGET_SDKLIBS = @IMPORTS@ libwine.a wine_uuid.a ntdll.a
+
+TARGET_BASE = $(TARGET_BASE_LIB_WINMM)
+
+TARGET_RC_SRCS = @RC_SRCS@
+TARGET_RC_BINSRC = @RC_BINSRC@
+TARGET_RC_BINARIES = @RC_BINARIES@
+
+TARGET_CLEAN = *.tab.c *.tab.h
+
+default: all
+
+DEP_OBJECTS = $(TARGET_OBJECTS)
+
+include $(TOOLS_PATH)/depend.mk
+
+#
+# Bison is requiered for building msi.dll. If MingW32 for windows,
+# download bison from http://gnuwin32.sourceforge.net/
+# Make sure bison.exe is placed in your command path for execution.
+#
+#
+sql.tab.c sql.tab.h: sql.y
+ bison -p SQL_ -d ./sql.y -o sql.tab.c
+
+cond.tab.c cond.tab.h: cond.y
+ bison -p COND_ -d ./cond.y -o cond.tab.c
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Pages I need
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
+
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "fdi.h"
+#include "msi.h"
+#include "msiquery.h"
+//#include "msvcrt/fcntl.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "ver.h"
+
+#define CUSTOM_ACTION_TYPE_MASK 0x3F
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSIFEATURE
+{
+ WCHAR Feature[96];
+ WCHAR Feature_Parent[96];
+ WCHAR Title[0x100];
+ WCHAR Description[0x100];
+ INT Display;
+ INT Level;
+ WCHAR Directory[96];
+ INT Attributes;
+
+ INSTALLSTATE State;
+ BOOL Enabled;
+ INT ComponentCount;
+ INT Components[1024]; /* yes hardcoded limit.... I am bad */
+ INT Cost;
+} MSIFEATURE;
+
+typedef struct tagMSICOMPONENT
+{
+ WCHAR Component[96];
+ WCHAR ComponentId[96];
+ WCHAR Directory[96];
+ INT Attributes;
+ WCHAR Condition[0x100];
+ WCHAR KeyPath[96];
+
+ INSTALLSTATE State;
+ BOOL FeatureState;
+ BOOL Enabled;
+ INT Cost;
+} MSICOMPONENT;
+
+typedef struct tagMSIFOLDER
+{
+ WCHAR Directory[96];
+ WCHAR TargetDefault[96];
+ WCHAR SourceDefault[96];
+
+ WCHAR ResolvedTarget[MAX_PATH];
+ WCHAR ResolvedSource[MAX_PATH];
+ WCHAR Property[MAX_PATH]; /* initially set property */
+ INT ParentIndex;
+ INT State;
+ /* 0 = uninitialized */
+ /* 1 = existing */
+ /* 2 = created remove if empty */
+ /* 3 = created persist if empty */
+ INT Cost;
+ INT Space;
+}MSIFOLDER;
+
+typedef struct tagMSIFILE
+{
+ WCHAR File[72];
+ INT ComponentIndex;
+ WCHAR FileName[MAX_PATH];
+ INT FileSize;
+ WCHAR Version[72];
+ WCHAR Language[20];
+ INT Attributes;
+ INT Sequence;
+
+ INT State;
+ /* 0 = uninitialize */
+ /* 1 = not present */
+ /* 2 = present but replace */
+ /* 3 = present do not replace */
+ /* 4 = Installed */
+ WCHAR SourcePath[MAX_PATH];
+ WCHAR TargetPath[MAX_PATH];
+ BOOL Temporary;
+}MSIFILE;
+
+/*
+ * Prototypes
+ */
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
+
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action);
+
+static UINT ACTION_LaunchConditions(MSIPACKAGE *package);
+static UINT ACTION_CostInitialize(MSIPACKAGE *package);
+static UINT ACTION_CreateFolders(MSIPACKAGE *package);
+static UINT ACTION_CostFinalize(MSIPACKAGE *package);
+static UINT ACTION_FileCost(MSIPACKAGE *package);
+static UINT ACTION_InstallFiles(MSIPACKAGE *package);
+static UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
+static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package);
+static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action);
+static UINT ACTION_InstallInitialize(MSIPACKAGE *package);
+static UINT ACTION_InstallValidate(MSIPACKAGE *package);
+static UINT ACTION_ProcessComponents(MSIPACKAGE *package);
+static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package);
+static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package);
+static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package);
+static UINT ACTION_CreateShortcuts(MSIPACKAGE *package);
+static UINT ACTION_PublishProduct(MSIPACKAGE *package);
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source,
+ const LPWSTR target, const INT type);
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source,
+ const LPWSTR target, const INT type);
+
+static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data);
+static UINT resolve_folder(MSIPACKAGE *package, LPCWSTR name, LPWSTR path,
+ BOOL source, BOOL set_prop, MSIFOLDER **folder);
+
+static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
+
+/*
+ * consts and values used
+ */
+static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
+static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
+static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
+static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
+static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
+static const WCHAR c_collen[] = {'C',':','\\',0};
+
+static const WCHAR cszlsb[]={'[',0};
+static const WCHAR cszrsb[]={']',0};
+static const WCHAR cszbs[]={'\\',0};
+
+const static WCHAR szCreateFolders[] =
+ {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
+const static WCHAR szCostFinalize[] =
+ {'C','o','s','t','F','i','n','a','l','i','z','e',0};
+const static WCHAR szInstallFiles[] =
+ {'I','n','s','t','a','l','l','F','i','l','e','s',0};
+const static WCHAR szDuplicateFiles[] =
+ {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
+const static WCHAR szWriteRegistryValues[] =
+{'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
+const static WCHAR szCostInitialize[] =
+ {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
+const static WCHAR szFileCost[] = {'F','i','l','e','C','o','s','t',0};
+const static WCHAR szInstallInitialize[] =
+ {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
+const static WCHAR szInstallValidate[] =
+ {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
+const static WCHAR szLaunchConditions[] =
+ {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
+const static WCHAR szProcessComponents[] =
+ {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
+const static WCHAR szRegisterTypeLibraries[] =
+{'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r',
+'i','e','s',0};
+const static WCHAR szRegisterClassInfo[] =
+{'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
+const static WCHAR szRegisterProgIdInfo[] =
+{'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
+const static WCHAR szCreateShortcuts[] =
+{'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
+const static WCHAR szPublishProduct[] =
+{'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
+
+/********************************************************
+ * helper functions to get around current HACKS and such
+ ********************************************************/
+inline static void reduce_to_longfilename(WCHAR* filename)
+{
+ if (strchrW(filename,'|'))
+ {
+ WCHAR newname[MAX_PATH];
+ strcpyW(newname,strchrW(filename,'|')+1);
+ strcpyW(filename,newname);
+ }
+}
+
+inline static char *strdupWtoA( const WCHAR *str )
+{
+ char *ret = NULL;
+ if (str)
+ {
+ DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
+);
+ if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
+ WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+ }
+ return ret;
+}
+
+inline static WCHAR *strdupAtoW( const char *str )
+{
+ WCHAR *ret = NULL;
+ if (str)
+ {
+ DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+ if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+ MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+ }
+ return ret;
+}
+
+inline static WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
+{
+ UINT rc;
+ DWORD sz;
+ LPWSTR ret;
+
+ sz = 0;
+ rc = MSI_RecordGetStringW(row,index,NULL,&sz);
+ if (sz <= 0)
+ return NULL;
+
+ sz ++;
+ ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
+ rc = MSI_RecordGetStringW(row,index,ret,&sz);
+ return ret;
+}
+
+inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
+{
+ int rc = -1;
+ DWORD i;
+
+ for (i = 0; i < package->loaded_components; i++)
+ {
+ if (strcmpW(Component,package->components[i].Component)==0)
+ {
+ rc = i;
+ break;
+ }
+ }
+ return rc;
+}
+
+inline static int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
+{
+ int rc = -1;
+ DWORD i;
+
+ for (i = 0; i < package->loaded_features; i++)
+ {
+ if (strcmpW(Feature,package->features[i].Feature)==0)
+ {
+ rc = i;
+ break;
+ }
+ }
+ return rc;
+}
+
+inline static int get_loaded_file(MSIPACKAGE* package, LPCWSTR file)
+{
+ int rc = -1;
+ DWORD i;
+
+ for (i = 0; i < package->loaded_files; i++)
+ {
+ if (strcmpW(file,package->files[i].File)==0)
+ {
+ rc = i;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
+{
+ DWORD i;
+ DWORD index;
+
+ if (!package)
+ return -2;
+
+ for (i=0; i < package->loaded_files; i++)
+ if (strcmpW(package->files[i].File,name)==0)
+ return -1;
+
+ index = package->loaded_files;
+ package->loaded_files++;
+ if (package->loaded_files== 1)
+ package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
+ else
+ package->files = HeapReAlloc(GetProcessHeap(),0,
+ package->files , package->loaded_files * sizeof(MSIFILE));
+
+ memset(&package->files[index],0,sizeof(MSIFILE));
+
+ strcpyW(package->files[index].File,name);
+ strcpyW(package->files[index].TargetPath,path);
+ package->files[index].Temporary = TRUE;
+
+ TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));
+
+ return 0;
+}
+
+void ACTION_remove_tracked_tempfiles(MSIPACKAGE* package)
+{
+ DWORD i;
+
+ if (!package)
+ return;
+
+ for (i = 0; i < package->loaded_files; i++)
+ {
+ if (package->files[i].Temporary)
+ DeleteFileW(package->files[i].TargetPath);
+
+ }
+}
+
+static void 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);
+}
+
+static void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
+{
+ 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};
+ WCHAR message[1024];
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static WCHAR *ActionFormat=NULL;
+ static WCHAR LastAction[0x100] = {0};
+ WCHAR Query[1024];
+ LPWSTR ptr;
+
+ if (strcmpW(LastAction,action)!=0)
+ {
+ sprintfW(Query,Query_t,action);
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return;
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ return;
+ }
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ return;
+ }
+
+ if (MSI_RecordIsNull(row,3))
+ {
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return;
+ }
+
+ if (ActionFormat)
+ HeapFree(GetProcessHeap(),0,ActionFormat);
+
+ ActionFormat = load_dynamic_stringW(row,3);
+ strcpyW(LastAction,action);
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+ message[0]=0;
+ ptr = ActionFormat;
+ while (*ptr)
+ {
+ LPWSTR ptr2;
+ LPWSTR data=NULL;
+ WCHAR tmp[1023];
+ INT field;
+
+ ptr2 = strchrW(ptr,'[');
+ if (ptr2)
+ {
+ strncpyW(tmp,ptr,ptr2-ptr);
+ tmp[ptr2-ptr]=0;
+ strcatW(message,tmp);
+ ptr2++;
+ field = atoiW(ptr2);
+ data = load_dynamic_stringW(record,field);
+ if (data)
+ {
+ strcatW(message,data);
+ HeapFree(GetProcessHeap(),0,data);
+ }
+ ptr=strchrW(ptr2,']');
+ ptr++;
+ }
+ else
+ {
+ strcatW(message,ptr);
+ break;
+ }
+ }
+
+ row = MSI_CreateRecord(1);
+ MSI_RecordSetStringW(row,1,message);
+
+ MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
+ msiobj_release(&row->hdr);
+}
+
+
+static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
+{
+ static const WCHAR template_s[]=
+{'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ','%','s','.',0};
+ static const WCHAR format[] =
+{'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
+ 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};
+ WCHAR message[1024];
+ WCHAR timet[0x100];
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ WCHAR *ActionText=NULL;
+ WCHAR Query[1024];
+
+ GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
+
+ sprintfW(Query,Query_t,action);
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return;
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return;
+ }
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return;
+ }
+
+ ActionText = load_dynamic_stringW(row,2);
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ sprintfW(message,template_s,timet,action,ActionText);
+
+ row = MSI_CreateRecord(1);
+ MSI_RecordSetStringW(row,1,message);
+
+ MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
+ msiobj_release(&row->hdr);
+ HeapFree(GetProcessHeap(),0,ActionText);
+}
+
+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);
+}
+
+/****************************************************
+ * TOP level entry points
+ *****************************************************/
+
+UINT ACTION_DoTopLevelINSTALL(MSIPACKAGE *package, LPCWSTR szPackagePath,
+ LPCWSTR szCommandLine)
+{
+ DWORD sz;
+ WCHAR buffer[10];
+ UINT rc;
+ static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
+
+ if (szPackagePath)
+ {
+ LPWSTR p;
+ WCHAR check[MAX_PATH];
+ WCHAR pth[MAX_PATH];
+ DWORD size;
+
+ strcpyW(pth,szPackagePath);
+ p = strrchrW(pth,'\\');
+ if (p)
+ {
+ p++;
+ *p=0;
+ }
+
+ size = MAX_PATH;
+ if (MSI_GetPropertyW(package,cszSourceDir,check,&size)
+ != ERROR_SUCCESS )
+ MSI_SetPropertyW(package, cszSourceDir, pth);
+ }
+
+ if (szCommandLine)
+ {
+ LPWSTR ptr,ptr2;
+ ptr = (LPWSTR)szCommandLine;
+
+ while (*ptr)
+ {
+ WCHAR prop[0x100];
+ WCHAR val[0x100];
+
+ TRACE("Looking at %s\n",debugstr_w(ptr));
+
+ ptr2 = strchrW(ptr,'=');
+ if (ptr2)
+ {
+ BOOL quote=FALSE;
+ DWORD len = 0;
+
+ while (*ptr == ' ') ptr++;
+ strncpyW(prop,ptr,ptr2-ptr);
+ prop[ptr2-ptr]=0;
+ ptr2++;
+
+ ptr = ptr2;
+ while (*ptr && (quote || (!quote && *ptr!=' ')))
+ {
+ if (*ptr == '"')
+ quote = !quote;
+ ptr++;
+ len++;
+ }
+
+ if (*ptr2=='"')
+ {
+ ptr2++;
+ len -= 2;
+ }
+ strncpyW(val,ptr2,len);
+ val[len]=0;
+
+ if (strlenW(prop) > 0)
+ {
+ TRACE("Found commandline property (%s) = (%s)\n", debugstr_w(prop), debugstr_w(val));
+ MSI_SetPropertyW(package,prop,val);
+ }
+ }
+ ptr++;
+ }
+ }
+
+ sz = 10;
+ if (MSI_GetPropertyW(package,szUILevel,buffer,&sz) == ERROR_SUCCESS)
+ {
+ if (atoiW(buffer) >= INSTALLUILEVEL_REDUCED)
+ {
+ rc = ACTION_ProcessUISequence(package);
+ if (rc == ERROR_SUCCESS)
+ rc = ACTION_ProcessExecSequence(package,TRUE);
+ }
+ else
+ rc = ACTION_ProcessExecSequence(package,FALSE);
+ }
+ else
+ rc = ACTION_ProcessExecSequence(package,FALSE);
+
+ return rc;
+}
+
+
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
+{
+ MSIQUERY * view;
+ UINT rc;
+ static const WCHAR ExecSeqQuery[] = {
+ '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 };
+ WCHAR Query[1024];
+ MSIRECORD * row = 0;
+ static const WCHAR IVQuery[] = {
+ '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};
+
+ if (UIran)
+ {
+ INT seq = 0;
+
+ rc = MSI_DatabaseOpenViewW(package->db, IVQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ seq = MSI_RecordGetInteger(row,1);
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ sprintfW(Query,ExecSeqQuery,seq);
+ }
+ else
+ sprintfW(Query,ExecSeqQuery,0);
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_ViewExecute(view, 0);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ goto end;
+ }
+
+ TRACE("Running the actions \n");
+
+ while (1)
+ {
+ WCHAR buffer[0x100];
+ DWORD sz = 0x100;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ /* check conditions */
+ if (!MSI_RecordIsNull(row,2))
+ {
+ LPWSTR cond = NULL;
+ cond = load_dynamic_stringW(row,2);
+
+ if (cond)
+ {
+ /* this is a hack to skip errors in the condition code */
+ if (MSI_EvaluateConditionW(package, cond) ==
+ MSICONDITION_FALSE)
+ {
+ HeapFree(GetProcessHeap(),0,cond);
+ msiobj_release(&row->hdr);
+ continue;
+ }
+ else
+ HeapFree(GetProcessHeap(),0,cond);
+ }
+ }
+
+ sz=0x100;
+ rc = MSI_RecordGetStringW(row,1,buffer,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Error is %x\n",rc);
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ rc = ACTION_PerformAction(package,buffer);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Execution halted due to error (%i)\n",rc);
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ msiobj_release(&row->hdr);
+ }
+
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+end:
+ return rc;
+}
+
+
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
+{
+ MSIQUERY * view;
+ UINT rc;
+ static const WCHAR ExecSeqQuery [] = {
+ '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};
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_ViewExecute(view, 0);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ goto end;
+ }
+
+ TRACE("Running the actions \n");
+
+ while (1)
+ {
+ WCHAR buffer[0x100];
+ DWORD sz = 0x100;
+ MSIRECORD * row = 0;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ /* check conditions */
+ if (!MSI_RecordIsNull(row,2))
+ {
+ LPWSTR cond = NULL;
+ cond = load_dynamic_stringW(row,2);
+
+ if (cond)
+ {
+ /* this is a hack to skip errors in the condition code */
+ if (MSI_EvaluateConditionW(package, cond) ==
+ MSICONDITION_FALSE)
+ {
+ HeapFree(GetProcessHeap(),0,cond);
+ msiobj_release(&row->hdr);
+ continue;
+ }
+ else
+ HeapFree(GetProcessHeap(),0,cond);
+ }
+ }
+
+ sz=0x100;
+ rc = MSI_RecordGetStringW(row,1,buffer,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Error is %x\n",rc);
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ rc = ACTION_PerformAction(package,buffer);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Execution halted due to error (%i)\n",rc);
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ msiobj_release(&row->hdr);
+ }
+
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+end:
+ return rc;
+}
+
+/********************************************************
+ * ACTION helper functions and functions that perform the actions
+ *******************************************************/
+
+/*
+ * Alot of actions are really important even if they don't do anything
+ * explicit.. Lots of properties are set at the beginning of the installation
+ * CostFinalize does a bunch of work to translated the directories and such
+ *
+ * But until I get write access to the database that is hard, so I am going to
+ * hack it to see if I can get something to run.
+ */
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
+{
+ UINT rc = ERROR_SUCCESS;
+
+ TRACE("Performing action (%s)\n",debugstr_w(action));
+ ui_actioninfo(package, action, TRUE, 0);
+ ui_actionstart(package, action);
+ ui_progress(package,2,1,0,0);
+
+ /* pre install, setup and configuration block */
+ if (strcmpW(action,szLaunchConditions)==0)
+ rc = ACTION_LaunchConditions(package);
+ else if (strcmpW(action,szCostInitialize)==0)
+ rc = ACTION_CostInitialize(package);
+ else if (strcmpW(action,szFileCost)==0)
+ rc = ACTION_FileCost(package);
+ else if (strcmpW(action,szCostFinalize)==0)
+ rc = ACTION_CostFinalize(package);
+ else if (strcmpW(action,szInstallValidate)==0)
+ rc = ACTION_InstallValidate(package);
+
+ /* install block */
+ else if (strcmpW(action,szProcessComponents)==0)
+ rc = ACTION_ProcessComponents(package);
+ else if (strcmpW(action,szInstallInitialize)==0)
+ rc = ACTION_InstallInitialize(package);
+ else if (strcmpW(action,szCreateFolders)==0)
+ rc = ACTION_CreateFolders(package);
+ else if (strcmpW(action,szInstallFiles)==0)
+ rc = ACTION_InstallFiles(package);
+ else if (strcmpW(action,szDuplicateFiles)==0)
+ rc = ACTION_DuplicateFiles(package);
+ else if (strcmpW(action,szWriteRegistryValues)==0)
+ rc = ACTION_WriteRegistryValues(package);
+ else if (strcmpW(action,szRegisterTypeLibraries)==0)
+ rc = ACTION_RegisterTypeLibraries(package);
+ else if (strcmpW(action,szRegisterClassInfo)==0)
+ rc = ACTION_RegisterClassInfo(package);
+ else if (strcmpW(action,szRegisterProgIdInfo)==0)
+ rc = ACTION_RegisterProgIdInfo(package);
+ else if (strcmpW(action,szCreateShortcuts)==0)
+ rc = ACTION_CreateShortcuts(package);
+ else if (strcmpW(action,szPublishProduct)==0)
+ rc = ACTION_PublishProduct(package);
+
+ /*
+ Called during iTunes but unimplemented and seem important
+
+ ResolveSource (sets SourceDir)
+ RegisterProduct
+ InstallFinalize
+ */
+ else if ((rc = ACTION_CustomAction(package,action)) != ERROR_SUCCESS)
+ {
+ FIXME("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
+ rc = ERROR_SUCCESS;
+ }
+
+ ui_actioninfo(package, action, FALSE, rc);
+ return rc;
+}
+
+
+static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action)
+{
+ UINT rc = ERROR_SUCCESS;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ WCHAR ExecSeqQuery[1024] =
+ {'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','`',' ','=',' ','`',0};
+ static const WCHAR end[]={'`',0};
+ UINT type;
+ LPWSTR source;
+ LPWSTR target;
+ WCHAR *deformated=NULL;
+
+ strcatW(ExecSeqQuery,action);
+ strcatW(ExecSeqQuery,end);
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ type = MSI_RecordGetInteger(row,2);
+
+ source = load_dynamic_stringW(row,3);
+ target = load_dynamic_stringW(row,4);
+
+ TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
+ debugstr_w(source), debugstr_w(target));
+
+ /* we are ignoring ALOT of flags and important synchronization stuff */
+ switch (type & CUSTOM_ACTION_TYPE_MASK)
+ {
+ case 1: /* DLL file stored in a Binary table stream */
+ rc = HANDLE_CustomType1(package,source,target,type);
+ break;
+ case 2: /* EXE file stored in a Binary table strem */
+ rc = HANDLE_CustomType2(package,source,target,type);
+ break;
+ case 35: /* Directory set with formatted text. */
+ case 51: /* Property set with formatted text. */
+ deformat_string(package,target,&deformated);
+ rc = MSI_SetPropertyW(package,source,deformated);
+ HeapFree(GetProcessHeap(),0,deformated);
+ break;
+ default:
+ FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
+ type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
+ debugstr_w(target));
+ }
+
+ HeapFree(GetProcessHeap(),0,source);
+ HeapFree(GetProcessHeap(),0,target);
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+static UINT store_binary_to_temp(MSIPACKAGE *package, const LPWSTR source,
+ LPWSTR tmp_file)
+{
+ DWORD sz=MAX_PATH;
+
+ if (MSI_GetPropertyW(package, cszTempFolder, tmp_file, &sz)
+ != ERROR_SUCCESS)
+ GetTempPathW(MAX_PATH,tmp_file);
+
+ strcatW(tmp_file,source);
+
+ if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
+ {
+ TRACE("File already exists\n");
+ return ERROR_SUCCESS;
+ }
+ else
+ {
+ /* write out the file */
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ WCHAR Query[1024] =
+ {'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','=','`',0};
+ static const WCHAR end[]={'`',0};
+ HANDLE the_file;
+ CHAR buffer[1024];
+
+ if (track_tempfile(package, source, tmp_file)!=0)
+ FIXME("File Name in temp tracking collision\n");
+
+ the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (the_file == INVALID_HANDLE_VALUE)
+ return ERROR_FUNCTION_FAILED;
+
+ strcatW(Query,source);
+ strcatW(Query,end);
+
+ rc = MSI_DatabaseOpenViewW( package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ 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(tmp_file);
+ break;
+ }
+ WriteFile(the_file,buffer,sz,&write,NULL);
+ } while (sz == 1024);
+
+ CloseHandle(the_file);
+
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+typedef UINT __stdcall CustomEntry(MSIHANDLE);
+typedef struct
+{
+ MSIPACKAGE *package;
+ WCHAR target[MAX_PATH];
+ WCHAR source[MAX_PATH];
+} thread_struct;
+
+#if 0
+static DWORD WINAPI DllThread(LPVOID info)
+{
+ HANDLE DLL;
+ LPSTR proc;
+ thread_struct *stuff;
+ CustomEntry *fn;
+
+ stuff = (thread_struct*)info;
+
+ TRACE("Asynchronous start (%s, %s) \n", debugstr_w(stuff->source),
+ debugstr_w(stuff->target));
+
+ DLL = LoadLibraryW(stuff->source);
+ if (DLL)
+ {
+ proc = strdupWtoA( stuff->target );
+ fn = (CustomEntry*)GetProcAddress(DLL,proc);
+ if (fn)
+ {
+ MSIHANDLE hPackage;
+ MSIPACKAGE *package = stuff->package;
+
+ TRACE("Calling function\n");
+ hPackage = msiobj_findhandle( &package->hdr );
+ if( !hPackage )
+ ERR("Handle for object %p not found\n", package );
+ fn(hPackage);
+ msiobj_release( &package->hdr );
+ }
+ else
+ ERR("Cannot load functon\n");
+
+ HeapFree(GetProcessHeap(),0,proc);
+ FreeLibrary(DLL);
+ }
+ else
+ ERR("Unable to load library\n");
+ msiobj_release( &stuff->package->hdr );
+ HeapFree( GetProcessHeap(), 0, info );
+ return 0;
+}
+#endif
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source,
+ const LPWSTR target, const INT type)
+{
+ WCHAR tmp_file[MAX_PATH];
+ CustomEntry *fn;
+ HANDLE DLL;
+ LPSTR proc;
+
+ store_binary_to_temp(package, source, tmp_file);
+
+ TRACE("Calling function %s from %s\n",debugstr_w(target),
+ debugstr_w(tmp_file));
+
+ if (!strchrW(tmp_file,'.'))
+ {
+ static const WCHAR dot[]={'.',0};
+ strcatW(tmp_file,dot);
+ }
+
+ if (type & 0xc0)
+ {
+ /* DWORD ThreadId; */
+ thread_struct *info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
+
+ /* msiobj_addref( &package->hdr ); */
+ info->package = package;
+ strcpyW(info->target,target);
+ strcpyW(info->source,tmp_file);
+ TRACE("Start Asynchronous execution\n");
+ FIXME("DATABASE NOT THREADSAFE... not starting\n");
+ /* CreateThread(NULL,0,DllThread,(LPVOID)&info,0,&ThreadId); */
+ /* FIXME: release the package if the CreateThread fails */
+ HeapFree( GetProcessHeap(), 0, info );
+ return ERROR_SUCCESS;
+ }
+
+ DLL = LoadLibraryW(tmp_file);
+ if (DLL)
+ {
+ proc = strdupWtoA( target );
+ fn = (CustomEntry*)GetProcAddress(DLL,proc);
+ if (fn)
+ {
+ MSIHANDLE hPackage;
+
+ TRACE("Calling function\n");
+ hPackage = msiobj_findhandle( &package->hdr );
+ if( !hPackage )
+ ERR("Handle for object %p not found\n", package );
+ fn(hPackage);
+ msiobj_release( &package->hdr );
+ }
+ else
+ ERR("Cannot load functon\n");
+
+ HeapFree(GetProcessHeap(),0,proc);
+ FreeLibrary(DLL);
+ }
+ else
+ ERR("Unable to load library\n");
+
+ return ERROR_SUCCESS;
+}
+
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source,
+ const LPWSTR target, const INT type)
+{
+ WCHAR tmp_file[MAX_PATH*2];
+ STARTUPINFOW si;
+ PROCESS_INFORMATION info;
+ BOOL rc;
+ WCHAR *deformated;
+ static const WCHAR spc[] = {' ',0};
+
+ memset(&si,0,sizeof(STARTUPINFOW));
+ memset(&info,0,sizeof(PROCESS_INFORMATION));
+
+ store_binary_to_temp(package, source, tmp_file);
+
+ strcatW(tmp_file,spc);
+ deformat_string(package,target,&deformated);
+ strcatW(tmp_file,deformated);
+
+ HeapFree(GetProcessHeap(),0,deformated);
+
+ TRACE("executing exe %s \n",debugstr_w(tmp_file));
+
+ rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
+ c_collen, &si, &info);
+
+ if ( !rc )
+ {
+ ERR("Unable to execute command\n");
+ return ERROR_SUCCESS;
+ }
+
+ if (!(type & 0xc0))
+ WaitForSingleObject(info.hProcess,INFINITE);
+
+ return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ * create_full_pathW
+ *
+ * Recursively create all directories in the path.
+ *
+ * shamelessly stolen from setupapi/queue.c
+ */
+static BOOL create_full_pathW(const WCHAR *path)
+{
+ BOOL ret = TRUE;
+ int len;
+ WCHAR *new_path;
+
+ new_path = HeapAlloc(GetProcessHeap(), 0, (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(!create_full_pathW(new_path))
+ {
+ ret = FALSE;
+ break;
+ }
+ new_path[len] = '\\';
+ }
+
+ HeapFree(GetProcessHeap(), 0, new_path);
+ return ret;
+}
+
+/*
+ * Also we cannot enable/disable components either, so for now I am just going
+ * to do all the directories for all the components.
+ */
+static UINT ACTION_CreateFolders(MSIPACKAGE *package)
+{
+ static const WCHAR ExecSeqQuery[] = {
+ 's','e','l','e','c','t',' ','D','i','r','e','c','t','o','r','y','_',' ',
+ 'f','r','o','m',' ','C','r','e','a','t','e','F','o','l','d','e','r',0 };
+ UINT rc;
+ MSIQUERY *view;
+ MSIFOLDER *folder;
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR dir[0x100];
+ WCHAR full_path[MAX_PATH];
+ DWORD sz;
+ MSIRECORD *row = NULL, *uirow;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ rc = MSI_RecordGetStringW(row,1,dir,&sz);
+
+ if (rc!= ERROR_SUCCESS)
+ {
+ ERR("Unable to get folder id \n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ sz = MAX_PATH;
+ rc = resolve_folder(package,dir,full_path,FALSE,FALSE,&folder);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ TRACE("Folder is %s\n",debugstr_w(full_path));
+
+ /* UI stuff */
+ uirow = MSI_CreateRecord(1);
+ MSI_RecordSetStringW(uirow,1,full_path);
+ ui_actiondata(package,szCreateFolders,uirow);
+ msiobj_release( &uirow->hdr );
+
+ if (folder->State == 0)
+ create_full_pathW(full_path);
+
+ folder->State = 3;
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ return rc;
+}
+
+static int load_component(MSIPACKAGE* package, MSIRECORD * row)
+{
+ int index = package->loaded_components;
+ DWORD sz;
+
+ /* fill in the data */
+
+ package->loaded_components++;
+ if (package->loaded_components == 1)
+ package->components = HeapAlloc(GetProcessHeap(),0,
+ sizeof(MSICOMPONENT));
+ else
+ package->components = HeapReAlloc(GetProcessHeap(),0,
+ package->components, package->loaded_components *
+ sizeof(MSICOMPONENT));
+
+ memset(&package->components[index],0,sizeof(MSICOMPONENT));
+
+ sz = 96;
+ MSI_RecordGetStringW(row,1,package->components[index].Component,&sz);
+
+ TRACE("Loading Component %s\n",
+ debugstr_w(package->components[index].Component));
+
+ sz = 0x100;
+ if (!MSI_RecordIsNull(row,2))
+ MSI_RecordGetStringW(row,2,package->components[index].ComponentId,&sz);
+
+ sz = 96;
+ MSI_RecordGetStringW(row,3,package->components[index].Directory,&sz);
+
+ package->components[index].Attributes = MSI_RecordGetInteger(row,4);
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,5,package->components[index].Condition,&sz);
+
+ sz = 96;
+ MSI_RecordGetStringW(row,6,package->components[index].KeyPath,&sz);
+
+ package->components[index].State = INSTALLSTATE_UNKNOWN;
+ package->components[index].Enabled = TRUE;
+ package->components[index].FeatureState= FALSE;
+
+ return index;
+}
+
+static void load_feature(MSIPACKAGE* package, MSIRECORD * row)
+{
+ int index = package->loaded_features;
+ DWORD sz;
+ static const WCHAR Query1[] = {'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};
+ static const WCHAR Query2[] = {'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};
+ WCHAR Query[1024];
+ MSIQUERY * view;
+ MSIQUERY * view2;
+ MSIRECORD * row2;
+ MSIRECORD * row3;
+ UINT rc;
+
+ /* fill in the data */
+
+ package->loaded_features ++;
+ if (package->loaded_features == 1)
+ package->features = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFEATURE));
+ else
+ package->features = HeapReAlloc(GetProcessHeap(),0,package->features,
+ package->loaded_features * sizeof(MSIFEATURE));
+
+ memset(&package->features[index],0,sizeof(MSIFEATURE));
+
+ sz = 96;
+ MSI_RecordGetStringW(row,1,package->features[index].Feature,&sz);
+
+ TRACE("Loading feature %s\n",debugstr_w(package->features[index].Feature));
+
+ sz = 96;
+ if (!MSI_RecordIsNull(row,2))
+ MSI_RecordGetStringW(row,2,package->features[index].Feature_Parent,&sz);
+
+ sz = 0x100;
+ if (!MSI_RecordIsNull(row,3))
+ MSI_RecordGetStringW(row,3,package->features[index].Title,&sz);
+
+ sz = 0x100;
+ if (!MSI_RecordIsNull(row,4))
+ MSI_RecordGetStringW(row,4,package->features[index].Description,&sz);
+
+ if (!MSI_RecordIsNull(row,5))
+ package->features[index].Display = MSI_RecordGetInteger(row,5);
+
+ package->features[index].Level= MSI_RecordGetInteger(row,6);
+
+ sz = 96;
+ if (!MSI_RecordIsNull(row,7))
+ MSI_RecordGetStringW(row,7,package->features[index].Directory,&sz);
+
+ package->features[index].Attributes= MSI_RecordGetInteger(row,8);
+ package->features[index].State = INSTALLSTATE_UNKNOWN;
+
+ /* load feature components */
+
+ sprintfW(Query,Query1,package->features[index].Feature);
+ rc = MSI_DatabaseOpenViewW(package->db,Query,&view);
+ if (rc != ERROR_SUCCESS)
+ return;
+ rc = MSI_ViewExecute(view,0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return;
+ }
+ while (1)
+ {
+ DWORD sz = 0x100;
+ WCHAR buffer[0x100];
+ DWORD rc;
+ INT c_indx;
+ INT cnt = package->features[index].ComponentCount;
+
+ rc = MSI_ViewFetch(view,&row2);
+ if (rc != ERROR_SUCCESS)
+ break;
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row2,1,buffer,&sz);
+
+ /* check to see if the component is already loaded */
+ c_indx = get_loaded_component(package,buffer);
+ if (c_indx != -1)
+ {
+ TRACE("Component %s already loaded at %i\n", debugstr_w(buffer),
+ c_indx);
+ package->features[index].Components[cnt] = c_indx;
+ package->features[index].ComponentCount ++;
+ }
+
+ sprintfW(Query,Query2,buffer);
+
+ rc = MSI_DatabaseOpenViewW(package->db,Query,&view2);
+ if (rc != ERROR_SUCCESS)
+ {
+ msiobj_release( &row2->hdr );
+ continue;
+ }
+ rc = MSI_ViewExecute(view2,0);
+ if (rc != ERROR_SUCCESS)
+ {
+ msiobj_release( &row2->hdr );
+ MSI_ViewClose(view2);
+ msiobj_release( &view2->hdr );
+ continue;
+ }
+ while (1)
+ {
+ DWORD rc;
+
+ rc = MSI_ViewFetch(view2,&row3);
+ if (rc != ERROR_SUCCESS)
+ break;
+ c_indx = load_component(package,row3);
+ msiobj_release( &row3->hdr );
+
+ package->features[index].Components[cnt] = c_indx;
+ package->features[index].ComponentCount ++;
+ }
+ MSI_ViewClose(view2);
+ msiobj_release( &view2->hdr );
+ msiobj_release( &row2->hdr );
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+}
+
+/*
+ * I am not doing any of the costing functionality yet.
+ * Mostly looking at doing the Component and Feature loading
+ *
+ * The native MSI does ALOT of modification to tables here. Mostly adding alot
+ * of temporary columns to the Feature and Component tables.
+ *
+ * note: native msi also tracks the short filename. but I am only going to
+ * track the long ones. Also looking at this directory table
+ * it appears that the directory table does not get the parents
+ * resolved base on property only based on their entrys in the
+ * directory table.
+ */
+static UINT ACTION_CostInitialize(MSIPACKAGE *package)
+{
+ MSIQUERY * view;
+ MSIRECORD * row;
+ DWORD sz;
+ UINT rc;
+ static const WCHAR Query_all[] = {
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'F','R','O','M',' ','F','e','a','t','u','r','e',0};
+ static const WCHAR szCosting[] = {
+ 'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+ static const WCHAR szZero[] = { '0', 0 };
+
+ MSI_SetPropertyW(package, szCosting, szZero);
+ MSI_SetPropertyW(package, cszRootDrive , c_collen);
+
+ sz = 0x100;
+ rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+ rc = MSI_ViewExecute(view,0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ while (1)
+ {
+ DWORD rc;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ break;
+
+ load_feature(package,row);
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ return ERROR_SUCCESS;
+}
+
+static UINT load_file(MSIPACKAGE* package, MSIRECORD * row)
+{
+ DWORD index = package->loaded_files;
+ DWORD i;
+ WCHAR buffer[0x100];
+ DWORD sz;
+
+ /* fill in the data */
+
+ package->loaded_files++;
+ if (package->loaded_files== 1)
+ package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
+ else
+ package->files = HeapReAlloc(GetProcessHeap(),0,
+ package->files , package->loaded_files * sizeof(MSIFILE));
+
+ memset(&package->files[index],0,sizeof(MSIFILE));
+
+ sz = 72;
+ MSI_RecordGetStringW(row,1,package->files[index].File,&sz);
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,2,buffer,&sz);
+
+ package->files[index].ComponentIndex = -1;
+ for (i = 0; i < package->loaded_components; i++)
+ if (strcmpW(package->components[i].Component,buffer)==0)
+ {
+ package->files[index].ComponentIndex = i;
+ break;
+ }
+ if (package->files[index].ComponentIndex == -1)
+ ERR("Unfound Component %s\n",debugstr_w(buffer));
+
+ sz = MAX_PATH;
+ MSI_RecordGetStringW(row,3,package->files[index].FileName,&sz);
+
+ reduce_to_longfilename(package->files[index].FileName);
+
+ package->files[index].FileSize = MSI_RecordGetInteger(row,4);
+
+ sz = 72;
+ if (!MSI_RecordIsNull(row,5))
+ MSI_RecordGetStringW(row,5,package->files[index].Version,&sz);
+
+ sz = 20;
+ if (!MSI_RecordIsNull(row,6))
+ MSI_RecordGetStringW(row,6,package->files[index].Language,&sz);
+
+ if (!MSI_RecordIsNull(row,7))
+ package->files[index].Attributes= MSI_RecordGetInteger(row,7);
+
+ package->files[index].Sequence= MSI_RecordGetInteger(row,8);
+
+ package->files[index].Temporary = FALSE;
+ package->files[index].State = 0;
+
+ TRACE("File Loaded (%s)\n",debugstr_w(package->files[index].File));
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ACTION_FileCost(MSIPACKAGE *package)
+{
+ MSIQUERY * view;
+ MSIRECORD * row;
+ UINT rc;
+ 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};
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return ERROR_SUCCESS;
+ }
+
+ while (1)
+ {
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+ load_file(package,row);
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ return ERROR_SUCCESS;
+}
+
+static INT load_folder(MSIPACKAGE *package, const WCHAR* dir)
+
+{
+ WCHAR Query[1024] =
+{'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
+'t','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
+'o','r','y','`',' ','=',' ','`',0};
+ static const WCHAR end[]={'`',0};
+ UINT rc;
+ MSIQUERY * view;
+ WCHAR targetbuffer[0x100];
+ WCHAR *srcdir = NULL;
+ WCHAR *targetdir = NULL;
+ WCHAR parent[0x100];
+ DWORD sz=0x100;
+ MSIRECORD * row = 0;
+ INT index = -1;
+ DWORD i;
+
+ TRACE("Looking for dir %s\n",debugstr_w(dir));
+
+ for (i = 0; i < package->loaded_folders; i++)
+ {
+ if (strcmpW(package->folders[i].Directory,dir)==0)
+ {
+ TRACE(" %s retuning on index %lu\n",debugstr_w(dir),i);
+ return i;
+ }
+ }
+
+ TRACE("Working to load %s\n",debugstr_w(dir));
+
+ index = package->loaded_folders;
+
+ package->loaded_folders++;
+ if (package->loaded_folders== 1)
+ package->folders = HeapAlloc(GetProcessHeap(),0,
+ sizeof(MSIFOLDER));
+ else
+ package->folders= HeapReAlloc(GetProcessHeap(),0,
+ package->folders, package->loaded_folders*
+ sizeof(MSIFOLDER));
+
+ memset(&package->folders[index],0,sizeof(MSIFOLDER));
+
+ strcpyW(package->folders[index].Directory,dir);
+
+ strcatW(Query,dir);
+ strcatW(Query,end);
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+
+ if (rc != ERROR_SUCCESS)
+ return -1;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return -1;
+ }
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return -1;
+ }
+
+ sz=0x100;
+ MSI_RecordGetStringW(row,3,targetbuffer,&sz);
+ targetdir=targetbuffer;
+
+ /* split src and target dir */
+ if (strchrW(targetdir,':'))
+ {
+ srcdir=strchrW(targetdir,':');
+ *srcdir=0;
+ srcdir ++;
+ }
+ else
+ srcdir=NULL;
+
+ /* for now only pick long filename versions */
+ if (strchrW(targetdir,'|'))
+ {
+ targetdir = strchrW(targetdir,'|');
+ *targetdir = 0;
+ targetdir ++;
+ }
+ if (srcdir && strchrW(srcdir,'|'))
+ {
+ srcdir= strchrW(srcdir,'|');
+ *srcdir= 0;
+ srcdir ++;
+ }
+
+ /* now check for root dirs */
+ if (targetdir[0] == '.' && targetdir[1] == 0)
+ targetdir = NULL;
+
+ if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
+ srcdir = NULL;
+
+ if (targetdir)
+ strcpyW(package->folders[index].TargetDefault,targetdir);
+
+ if (srcdir)
+ strcpyW(package->folders[index].SourceDefault,srcdir);
+ else if (targetdir)
+ strcpyW(package->folders[index].SourceDefault,targetdir);
+
+ if (MSI_RecordIsNull(row,2))
+ parent[0]=0;
+ else
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,2,parent,&sz);
+ }
+
+ if (parent[0])
+ {
+ i = load_folder(package,parent);
+ package->folders[index].ParentIndex = i;
+ TRACE("Parent is index %i... %s %s\n",
+ package->folders[index].ParentIndex,
+ debugstr_w(package->folders[package->folders[index].ParentIndex].Directory),
+ debugstr_w(parent));
+ }
+ else
+ package->folders[index].ParentIndex = -2;
+
+ sz = MAX_PATH;
+ rc = MSI_GetPropertyW(package, dir, package->folders[index].Property, &sz);
+ if (rc != ERROR_SUCCESS)
+ package->folders[index].Property[0]=0;
+
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ TRACE(" %s retuning on index %i\n",debugstr_w(dir),index);
+ return index;
+}
+
+static UINT resolve_folder(MSIPACKAGE *package, LPCWSTR name, LPWSTR path,
+ BOOL source, BOOL set_prop, MSIFOLDER **folder)
+{
+ DWORD i;
+ UINT rc = ERROR_SUCCESS;
+ DWORD sz;
+
+ TRACE("Working to resolve %s\n",debugstr_w(name));
+
+ if (!path)
+ return rc;
+
+ /* special resolving for Target and Source root dir */
+ if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
+ {
+ if (!source)
+ {
+ sz = MAX_PATH;
+ rc = MSI_GetPropertyW(package,cszTargetDir,path,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ sz = MAX_PATH;
+ rc = MSI_GetPropertyW(package,cszRootDrive,path,&sz);
+ if (set_prop)
+ MSI_SetPropertyW(package,cszTargetDir,path);
+ }
+ if (folder)
+ *folder = &(package->folders[0]);
+ return rc;
+ }
+ else
+ {
+ sz = MAX_PATH;
+ rc = MSI_GetPropertyW(package,cszSourceDir,path,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ sz = MAX_PATH;
+ rc = MSI_GetPropertyW(package,cszDatabase,path,&sz);
+ if (rc == ERROR_SUCCESS)
+ {
+ LPWSTR ptr = strrchrW(path,'\\');
+ if (ptr)
+ {
+ ptr++;
+ *ptr = 0;
+ }
+ }
+ }
+ if (folder)
+ *folder = &(package->folders[0]);
+ return rc;
+ }
+ }
+
+ for (i = 0; i < package->loaded_folders; i++)
+ {
+ if (strcmpW(package->folders[i].Directory,name)==0)
+ break;
+ }
+
+ if (i >= package->loaded_folders)
+ return ERROR_FUNCTION_FAILED;
+
+ if (folder)
+ *folder = &(package->folders[i]);
+
+ if (!source && package->folders[i].ResolvedTarget[0])
+ {
+ strcpyW(path,package->folders[i].ResolvedTarget);
+ TRACE(" already resolved to %s\n",debugstr_w(path));
+ return ERROR_SUCCESS;
+ }
+ else if (source && package->folders[i].ResolvedSource[0])
+ {
+ strcpyW(path,package->folders[i].ResolvedSource);
+ return ERROR_SUCCESS;
+ }
+ else if (!source && package->folders[i].Property[0])
+ {
+ strcpyW(path,package->folders[i].Property);
+ TRACE(" internally set to %s\n",debugstr_w(path));
+ if (set_prop)
+ MSI_SetPropertyW(package,name,path);
+ return ERROR_SUCCESS;
+ }
+
+ if (package->folders[i].ParentIndex >= 0)
+ {
+ int len;
+ TRACE(" ! Parent is %s\n", debugstr_w(package->folders[
+ package->folders[i].ParentIndex].Directory));
+ resolve_folder(package, package->folders[
+ package->folders[i].ParentIndex].Directory, path,source,
+ set_prop, NULL);
+
+ len = strlenW(path);
+ if (len && path[len-1] != '\\')
+ strcatW(path, cszbs);
+
+ if (!source)
+ {
+ if (package->folders[i].TargetDefault[0])
+ {
+ strcatW(path,package->folders[i].TargetDefault);
+ strcatW(path,cszbs);
+ }
+ strcpyW(package->folders[i].ResolvedTarget,path);
+ TRACE(" resolved into %s\n",debugstr_w(path));
+ if (set_prop)
+ MSI_SetPropertyW(package,name,path);
+ }
+ else
+ {
+ if (package->folders[i].SourceDefault[0])
+ {
+ strcatW(path,package->folders[i].SourceDefault);
+ strcatW(path,cszbs);
+ }
+ strcpyW(package->folders[i].ResolvedSource,path);
+ }
+ }
+ return rc;
+}
+
+/*
+ * Alot is done in this function aside from just the costing.
+ * The costing needs to be implemented at some point but for now I am going
+ * to focus on the directory building
+ *
+ */
+static UINT ACTION_CostFinalize(MSIPACKAGE *package)
+{
+ static const WCHAR ExecSeqQuery[] = {
+ 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+ 'D','i','r','e','c','t','o','r','y',0};
+ static const WCHAR ConditionQuery[] = {
+ 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+ 'C','o','n','d','i','t','i','o','n',0};
+ static const WCHAR szCosting[] = {
+ 'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+ static const WCHAR szOne[] = { '1', 0 };
+ UINT rc;
+ MSIQUERY * view;
+ DWORD i;
+
+ TRACE("Building Directory properties\n");
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR name[0x100];
+ WCHAR path[MAX_PATH];
+ MSIRECORD * row = 0;
+ DWORD sz;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ MSI_RecordGetStringW(row,1,name,&sz);
+
+ /* This helper function now does ALL the work */
+ TRACE("Dir %s ...\n",debugstr_w(name));
+ load_folder(package,name);
+ resolve_folder(package,name,path,FALSE,TRUE,NULL);
+ TRACE("resolves to %s\n",debugstr_w(path));
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+ TRACE("File calculations %i files\n",package->loaded_files);
+
+ for (i = 0; i < package->loaded_files; i++)
+ {
+ MSICOMPONENT* comp = NULL;
+ MSIFILE* file= NULL;
+
+ file = &package->files[i];
+ if (file->ComponentIndex >= 0)
+ comp = &package->components[file->ComponentIndex];
+
+ if (comp)
+ {
+ int len;
+ /* calculate target */
+ resolve_folder(package, comp->Directory, file->TargetPath, FALSE,
+ FALSE, NULL);
+ /* make sure that the path ends in a \ */
+ len = strlenW(file->TargetPath);
+ if (len && file->TargetPath[len-1] != '\\')
+ strcatW(file->TargetPath, cszbs);
+ strcatW(file->TargetPath,file->FileName);
+
+ TRACE("file %s resolves to %s\n",
+ debugstr_w(file->File),debugstr_w(file->TargetPath));
+
+ if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
+ {
+ file->State = 1;
+ comp->Cost += file->FileSize;
+ }
+ else
+ {
+ if (file->Version[0])
+ {
+ DWORD handle;
+ DWORD versize;
+ UINT sz;
+ LPVOID version;
+ static const WCHAR name[] =
+ {'\\',0};
+ static const WCHAR name_fmt[] =
+ {'%','u','.','%','u','.','%','u','.','%','u',0};
+ WCHAR filever[0x100];
+ VS_FIXEDFILEINFO *lpVer;
+
+ FIXME("Version comparison.. \n");
+ versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
+ version = HeapAlloc(GetProcessHeap(),0,versize);
+ GetFileVersionInfoW(file->TargetPath, 0, versize, version);
+
+ VerQueryValueW(version, name, (LPVOID*)&lpVer, &sz);
+
+ sprintfW(filever,name_fmt,
+ HIWORD(lpVer->dwFileVersionMS),
+ LOWORD(lpVer->dwFileVersionMS),
+ HIWORD(lpVer->dwFileVersionLS),
+ LOWORD(lpVer->dwFileVersionLS));
+
+ TRACE("new %s old %s\n", debugstr_w(file->Version),
+ debugstr_w(filever));
+ if (strcmpiW(filever,file->Version)<0)
+ {
+ file->State = 2;
+ FIXME("cost should be diff in size\n");
+ comp->Cost += file->FileSize;
+ }
+ else
+ file->State = 3;
+ HeapFree(GetProcessHeap(),0,version);
+ }
+ else
+ file->State = 3;
+ }
+ }
+ }
+
+ TRACE("Evaluating Condition Table\n");
+
+ rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR Feature[0x100];
+ MSIRECORD * row = 0;
+ DWORD sz;
+ int feature_index;
+
+ rc = MSI_ViewFetch(view,&row);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,1,Feature,&sz);
+
+ feature_index = get_loaded_feature(package,Feature);
+ if (feature_index < 0)
+ ERR("FAILED to find loaded feature %s\n",debugstr_w(Feature));
+ else
+ {
+ LPWSTR Condition;
+ Condition = load_dynamic_stringW(row,3);
+
+ if (MSI_EvaluateConditionW(package,Condition) ==
+ MSICONDITION_TRUE)
+ {
+ int level = MSI_RecordGetInteger(row,2);
+ TRACE("Reseting feature %s to level %i\n",
+ debugstr_w(Feature), level);
+ package->features[feature_index].Level = level;
+ }
+ HeapFree(GetProcessHeap(),0,Condition);
+ }
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+ TRACE("Enabling or Disabling Components\n");
+ for (i = 0; i < package->loaded_components; i++)
+ {
+ if (package->components[i].Condition[0])
+ {
+ if (MSI_EvaluateConditionW(package,
+ package->components[i].Condition) == MSICONDITION_FALSE)
+ {
+ TRACE("Disabling component %s\n",
+ debugstr_w(package->components[i].Component));
+ package->components[i].Enabled = FALSE;
+ }
+ }
+ }
+
+ MSI_SetPropertyW(package,szCosting,szOne);
+ return ERROR_SUCCESS;
+}
+
+/*
+ * This is a helper function for handling embedded cabinet media
+ */
+static UINT writeout_cabinet_stream(MSIPACKAGE *package, WCHAR* stream_name,
+ WCHAR* source)
+{
+ UINT rc;
+ USHORT* data;
+ UINT size;
+ DWORD write;
+ HANDLE the_file;
+ WCHAR tmp[MAX_PATH];
+
+ rc = read_raw_stream_data(package->db,stream_name,&data,&size);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ write = MAX_PATH;
+ if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
+ GetTempPathW(MAX_PATH,tmp);
+
+ GetTempFileNameW(tmp,stream_name,0,source);
+
+ track_tempfile(package,strrchrW(source,'\\'), source);
+ the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (the_file == INVALID_HANDLE_VALUE)
+ {
+ rc = ERROR_FUNCTION_FAILED;
+ goto end;
+ }
+
+ WriteFile(the_file,data,size,&write,NULL);
+ CloseHandle(the_file);
+ TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
+end:
+ HeapFree(GetProcessHeap(),0,data);
+ return rc;
+}
+
+
+/* Support functions for FDI functions */
+
+static void * cabinet_alloc(ULONG cb)
+{
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+static void cabinet_free(void *pv)
+{
+ HeapFree(GetProcessHeap(), 0, pv);
+}
+
+static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
+{
+ 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;
+ return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL, dwCreateDisposition, 0, NULL);
+}
+
+static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
+{
+ DWORD dwRead;
+ if (ReadFile((HANDLE)hf, pv, cb, &dwRead, NULL))
+ return dwRead;
+ return 0;
+}
+
+static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
+{
+ DWORD dwWritten;
+ if (WriteFile((HANDLE)hf, pv, cb, &dwWritten, NULL))
+ return dwWritten;
+ return 0;
+}
+
+static int cabinet_close(INT_PTR hf)
+{
+ return CloseHandle((HANDLE)hf) ? 0 : -1;
+}
+
+static long cabinet_seek(INT_PTR hf, long dist, int seektype)
+{
+ /* flags are compatible and so are passed straight through */
+ return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
+}
+
+static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
+{
+ /* FIXME: try to do more processing in this function */
+ switch (fdint)
+ {
+ case fdintCOPY_FILE:
+ {
+ ULONG len = strlen((char*)pfdin->pv) + strlen(pfdin->psz1);
+ char *file = cabinet_alloc((len+1)*sizeof(char));
+
+ strcpy(file, (char*)pfdin->pv);
+ strcat(file, pfdin->psz1);
+
+ TRACE("file: %s\n", debugstr_a(file));
+
+ return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
+ }
+ case fdintCLOSE_FILE_INFO:
+ {
+ FILETIME ft;
+ FILETIME ftLocal;
+ if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
+ return -1;
+ if (!LocalFileTimeToFileTime(&ft, &ftLocal))
+ return -1;
+ if (!SetFileTime((HANDLE)pfdin->hf, &ftLocal, 0, &ftLocal))
+ return -1;
+
+ cabinet_close(pfdin->hf);
+ return 1;
+ }
+ default:
+ return 0;
+ }
+}
+
+/***********************************************************************
+ * extract_cabinet_file
+ *
+ * Extract files from a cab file.
+ */
+static BOOL extract_cabinet_file(const WCHAR* source, const WCHAR* path)
+{
+ HFDI hfdi;
+ ERF erf;
+ BOOL ret;
+ char *cabinet;
+ char *cab_path;
+
+ TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
+
+ 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;
+ }
+
+ if (!(cabinet = strdupWtoA( source )))
+ {
+ FDIDestroy(hfdi);
+ return FALSE;
+ }
+ if (!(cab_path = strdupWtoA( path )))
+ {
+ FDIDestroy(hfdi);
+ HeapFree(GetProcessHeap(), 0, cabinet);
+ return FALSE;
+ }
+
+ ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, cab_path);
+
+ if (!ret)
+ ERR("FDICopy failed\n");
+
+ FDIDestroy(hfdi);
+
+ HeapFree(GetProcessHeap(), 0, cabinet);
+ HeapFree(GetProcessHeap(), 0, cab_path);
+
+ return ret;
+}
+
+static UINT ready_media_for_file(MSIPACKAGE *package, UINT sequence,
+ WCHAR* path)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ WCHAR source[MAX_PATH];
+ static const WCHAR ExecSeqQuery[] = {
+ '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',' ','L','a','s','t','S','e','q','u','e','n','c','e',0};
+ WCHAR Query[1024];
+ WCHAR cab[0x100];
+ DWORD sz=0x100;
+ INT seq;
+ static UINT last_sequence = 0;
+
+ if (sequence <= last_sequence)
+ {
+ TRACE("Media already ready (%u, %u)\n",sequence,last_sequence);
+ return ERROR_SUCCESS;
+ }
+
+ sprintfW(Query,ExecSeqQuery,sequence);
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ seq = MSI_RecordGetInteger(row,2);
+ last_sequence = seq;
+
+ if (!MSI_RecordIsNull(row,4))
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,4,cab,&sz);
+ TRACE("Source is CAB %s\n",debugstr_w(cab));
+ /* the stream does not contain the # character */
+ if (cab[0]=='#')
+ {
+ writeout_cabinet_stream(package,&cab[1],source);
+ strcpyW(path,source);
+ *(strrchrW(path,'\\')+1)=0;
+ }
+ else
+ {
+ sz = MAX_PATH;
+ if (MSI_GetPropertyW(package, cszSourceDir, source, &sz))
+ {
+ ERR("No Source dir defined \n");
+ rc = ERROR_FUNCTION_FAILED;
+ }
+ else
+ {
+ strcpyW(path,source);
+ strcatW(source,cab);
+ /* extract the cab file into a folder in the temp folder */
+ sz = MAX_PATH;
+ if (MSI_GetPropertyW(package, cszTempFolder,path, &sz)
+ != ERROR_SUCCESS)
+ GetTempPathW(MAX_PATH,path);
+ }
+ }
+ rc = !extract_cabinet_file(source,path);
+ }
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+inline static UINT create_component_directory ( MSIPACKAGE* package, INT component)
+{
+ UINT rc;
+ MSIFOLDER *folder;
+ WCHAR install_path[MAX_PATH];
+
+ rc = resolve_folder(package, package->components[component].Directory,
+ install_path, FALSE, FALSE, &folder);
+
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ /* create the path */
+ if (folder->State == 0)
+ {
+ create_full_pathW(install_path);
+ folder->State = 2;
+ }
+
+ return rc;
+}
+
+static UINT ACTION_InstallFiles(MSIPACKAGE *package)
+{
+ UINT rc = ERROR_SUCCESS;
+ DWORD index;
+ MSIRECORD * uirow;
+ WCHAR uipath[MAX_PATH];
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ /* increment progress bar each time action data is sent */
+ ui_progress(package,1,1,1,0);
+
+ for (index = 0; index < package->loaded_files; index++)
+ {
+ WCHAR path_to_source[MAX_PATH];
+ MSIFILE *file;
+
+ file = &package->files[index];
+
+ if (file->Temporary)
+ continue;
+
+ if (!package->components[file->ComponentIndex].Enabled ||
+ !package->components[file->ComponentIndex].FeatureState)
+ {
+ TRACE("File %s is not scheduled for install\n",
+ debugstr_w(file->File));
+ continue;
+ }
+
+ if ((file->State == 1) || (file->State == 2))
+ {
+ TRACE("Installing %s\n",debugstr_w(file->File));
+ rc = ready_media_for_file(package,file->Sequence,path_to_source);
+ /*
+ * WARNING!
+ * our file table could change here because a new temp file
+ * may have been created
+ */
+ file = &package->files[index];
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to ready media\n");
+ rc = ERROR_FUNCTION_FAILED;
+ break;
+ }
+
+ create_component_directory( package, file->ComponentIndex);
+
+ strcpyW(file->SourcePath, path_to_source);
+ strcatW(file->SourcePath, file->File);
+
+ TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
+ debugstr_w(file->TargetPath));
+
+ /* the UI chunk */
+ uirow=MSI_CreateRecord(9);
+ MSI_RecordSetStringW(uirow,1,file->File);
+ strcpyW(uipath,file->TargetPath);
+ *(strrchrW(uipath,'\\')+1)=0;
+ MSI_RecordSetStringW(uirow,9,uipath);
+ MSI_RecordSetInteger(uirow,6,file->FileSize);
+ ui_actiondata(package,szInstallFiles,uirow);
+ msiobj_release( &uirow->hdr );
+
+ if (!MoveFileW(file->SourcePath,file->TargetPath))
+ {
+ rc = GetLastError();
+ ERR("Unable to move file (%s -> %s) (error %d)\n",
+ debugstr_w(file->SourcePath), debugstr_w(file->TargetPath),
+ rc);
+ if (rc == ERROR_ALREADY_EXISTS && file->State == 2)
+ {
+ CopyFileW(file->SourcePath,file->TargetPath,FALSE);
+ DeleteFileW(file->SourcePath);
+ rc = 0;
+ }
+ else
+ break;
+ }
+ else
+ file->State = 4;
+
+ ui_progress(package,2,0,0,0);
+ }
+ }
+
+ return rc;
+}
+
+inline static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key,
+ LPWSTR file_source)
+{
+ DWORD index;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ for (index = 0; index < package->loaded_files; index ++)
+ {
+ if (strcmpW(file_key,package->files[index].File)==0)
+ {
+ if (package->files[index].State >= 3)
+ {
+ strcpyW(file_source,package->files[index].TargetPath);
+ return ERROR_SUCCESS;
+ }
+ else
+ return ERROR_FILE_NOT_FOUND;
+ }
+ }
+
+ return ERROR_FUNCTION_FAILED;
+}
+
+static UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] = {
+ 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+ 'D','u','p','l','i','c','a','t','e','F','i','l','e',0};
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR file_key[0x100];
+ WCHAR file_source[MAX_PATH];
+ WCHAR dest_name[0x100];
+ WCHAR dest_path[MAX_PATH];
+ WCHAR component[0x100];
+ INT component_index;
+
+ DWORD sz=0x100;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ rc = MSI_RecordGetStringW(row,2,component,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get component\n");
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ component_index = get_loaded_component(package,component);
+ if (!package->components[component_index].Enabled ||
+ !package->components[component_index].FeatureState)
+ {
+ TRACE("Skipping copy due to disabled component\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ sz=0x100;
+ rc = MSI_RecordGetStringW(row,3,file_key,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get file key\n");
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ rc = get_file_target(package,file_key,file_source);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Original file unknown %s\n",debugstr_w(file_key));
+ msiobj_release(&row->hdr);
+ break;
+ }
+
+ if (MSI_RecordIsNull(row,4))
+ {
+ strcpyW(dest_name,strrchrW(file_source,'\\')+1);
+ }
+ else
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,4,dest_name,&sz);
+ reduce_to_longfilename(dest_name);
+ }
+
+ if (MSI_RecordIsNull(row,5))
+ {
+ strcpyW(dest_path,file_source);
+ *strrchrW(dest_path,'\\')=0;
+ }
+ else
+ {
+ WCHAR destkey[0x100];
+ sz=0x100;
+ MSI_RecordGetStringW(row,5,destkey,&sz);
+ sz = 0x100;
+ rc = resolve_folder(package, destkey, dest_path,FALSE,FALSE,NULL);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get destination folder\n");
+ msiobj_release(&row->hdr);
+ break;
+ }
+ }
+
+ strcatW(dest_path,dest_name);
+
+ TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
+ debugstr_w(dest_path));
+
+ if (strcmpW(file_source,dest_path))
+ rc = !CopyFileW(file_source,dest_path,TRUE);
+ else
+ rc = ERROR_SUCCESS;
+
+ if (rc != ERROR_SUCCESS)
+ ERR("Failed to copy file\n");
+
+ FIXME("We should track these duplicate files as well\n");
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+
+}
+
+
+/* OK this value is "interpretted" and then formatted based on the
+ first few characters */
+static LPSTR parse_value(MSIPACKAGE *package, WCHAR *value, DWORD *type,
+ DWORD *size)
+{
+ LPSTR data = NULL;
+ if (value[0]=='#' && value[1]!='#' && value[1]!='%')
+ {
+ if (value[1]=='x')
+ {
+ LPWSTR ptr;
+ CHAR byte[5];
+ LPWSTR deformated;
+ int count;
+
+ deformat_string(package, &value[2], &deformated);
+
+ /* binary value type */
+ ptr = deformated;
+ *type=REG_BINARY;
+ *size = strlenW(ptr)/2;
+ data = HeapAlloc(GetProcessHeap(),0,*size);
+
+ byte[0] = '0';
+ byte[1] = 'x';
+ byte[4] = 0;
+ count = 0;
+ while (*ptr)
+ {
+ byte[2]= *ptr;
+ ptr++;
+ byte[3]= *ptr;
+ ptr++;
+ data[count] = (BYTE)strtol(byte,NULL,0);
+ count ++;
+ }
+ HeapFree(GetProcessHeap(),0,deformated);
+
+ TRACE("Data %li bytes(%i)\n",*size,count);
+ }
+ else
+ {
+ LPWSTR deformated;
+ deformat_string(package, &value[1], &deformated);
+
+ *type=REG_DWORD;
+ *size = sizeof(DWORD);
+ data = HeapAlloc(GetProcessHeap(),0,*size);
+ *(LPDWORD)data = atoiW(deformated);
+ TRACE("DWORD %i\n",*data);
+
+ HeapFree(GetProcessHeap(),0,deformated);
+ }
+ }
+ else
+ {
+ WCHAR *ptr;
+ *type=REG_SZ;
+
+ if (value[0]=='#')
+ {
+ if (value[1]=='%')
+ {
+ ptr = &value[2];
+ *type=REG_EXPAND_SZ;
+ }
+ else
+ ptr = &value[1];
+ }
+ else
+ ptr=value;
+
+ *size = deformat_string(package, ptr,(LPWSTR*)&data);
+ }
+ return data;
+}
+
+static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] = {
+ 's','e','l','e','c','t',' ','*',' ',
+ 'f','r','o','m',' ','R','e','g','i','s','t','r','y',0 };
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ /* increment progress bar each time action data is sent */
+ ui_progress(package,1,1,1,0);
+
+ while (1)
+ {
+ 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};
+
+ WCHAR key[0x100];
+ WCHAR name[0x100];
+ LPWSTR value;
+ LPSTR value_data = NULL;
+ HKEY root_key, hkey;
+ DWORD type,size;
+ WCHAR component[0x100];
+ INT component_index;
+ MSIRECORD * uirow;
+ WCHAR uikey[0x110];
+
+ INT root;
+ DWORD sz=0x100;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz= 0x100;
+ MSI_RecordGetStringW(row,6,component,&sz);
+ component_index = get_loaded_component(package,component);
+
+ if (!package->components[component_index].Enabled ||
+ !package->components[component_index].FeatureState)
+ {
+ TRACE("Skipping write due to disabled component\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ /* null values have special meanings during uninstalls and such */
+
+ if(MSI_RecordIsNull(row,5))
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ root = MSI_RecordGetInteger(row,2);
+ sz = 0x100;
+ MSI_RecordGetStringW(row,3,key,&sz);
+
+ sz = 0x100;
+ if (MSI_RecordIsNull(row,4))
+ name[0]=0;
+ else
+ MSI_RecordGetStringW(row,4,name,&sz);
+
+ /* get the root key */
+ switch (root)
+ {
+ case 0: root_key = HKEY_CLASSES_ROOT;
+ strcpyW(uikey,szHCR); break;
+ case 1: root_key = HKEY_CURRENT_USER;
+ strcpyW(uikey,szHCU); break;
+ case 2: root_key = HKEY_LOCAL_MACHINE;
+ strcpyW(uikey,szHLM); break;
+ case 3: root_key = HKEY_USERS;
+ strcpyW(uikey,szHU); break;
+ default:
+ ERR("Unknown root %i\n",root);
+ root_key=NULL;
+ break;
+ }
+ if (!root_key)
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ strcatW(uikey,key);
+ if (RegCreateKeyW( root_key, key, &hkey))
+ {
+ ERR("Could not create key %s\n",debugstr_w(key));
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ value = load_dynamic_stringW(row,5);
+ value_data = parse_value(package, value, &type, &size);
+
+ if (value_data)
+ {
+ TRACE("Setting value %s\n",debugstr_w(name));
+ RegSetValueExW(hkey, name, 0, type, value_data, size);
+
+ uirow = MSI_CreateRecord(3);
+ MSI_RecordSetStringW(uirow,2,name);
+ MSI_RecordSetStringW(uirow,1,uikey);
+
+ if (type == REG_SZ)
+ MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
+ else
+ MSI_RecordSetStringW(uirow,3,value);
+
+ ui_actiondata(package,szWriteRegistryValues,uirow);
+ ui_progress(package,2,0,0,0);
+ msiobj_release( &uirow->hdr );
+
+ HeapFree(GetProcessHeap(),0,value_data);
+ }
+ HeapFree(GetProcessHeap(),0,value);
+
+ msiobj_release(&row->hdr);
+ RegCloseKey(hkey);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+/*
+ * This helper function should probably go alot of places
+ *
+ * Thinking about this, maybe this should become yet another Bison file
+ */
+static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data)
+{
+ WCHAR* mark=NULL;
+ DWORD size=0;
+ DWORD chunk=0;
+ WCHAR key[0x100];
+ LPWSTR value;
+ DWORD sz;
+ UINT rc;
+
+ /* scan for special characters */
+ if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
+ {
+ /* not formatted */
+ size = (strlenW(ptr)+1) * sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ strcpyW(*data,ptr);
+ return size;
+ }
+
+ /* formatted string located */
+ mark = strchrW(ptr,'[');
+ if (mark != ptr)
+ {
+ INT cnt = (mark - ptr);
+ TRACE("%i (%i) characters before marker\n",cnt,(mark-ptr));
+ size = cnt * sizeof(WCHAR);
+ size += sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ strncpyW(*data,ptr,cnt);
+ (*data)[cnt]=0;
+ }
+ else
+ {
+ size = sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ (*data)[0]=0;
+ }
+ mark++;
+ strcpyW(key,mark);
+ *strchrW(key,']')=0;
+ mark = strchrW(mark,']');
+ mark++;
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+ sz = 0;
+ rc = MSI_GetPropertyW(package, key, NULL, &sz);
+ if ((rc == ERROR_SUCCESS) || (rc == ERROR_MORE_DATA))
+ {
+ LPWSTR newdata;
+
+ sz++;
+ value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
+ MSI_GetPropertyW(package, key, value, &sz);
+
+ chunk = (strlenW(value)+1) * sizeof(WCHAR);
+ size+=chunk;
+ newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+ *data = newdata;
+ strcatW(*data,value);
+ }
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+ if (*mark!=0)
+ {
+ LPWSTR newdata;
+ chunk = (strlenW(mark)+1) * sizeof(WCHAR);
+ size+=chunk;
+ newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+ *data = newdata;
+ strcatW(*data,mark);
+ }
+ (*data)[strlenW(*data)]=0;
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+
+ /* recursively do this to clean up */
+ mark = HeapAlloc(GetProcessHeap(),0,size);
+ strcpyW(mark,*data);
+ TRACE("String at this point %s\n",debugstr_w(mark));
+ size = deformat_string(package,mark,data);
+ HeapFree(GetProcessHeap(),0,mark);
+ return size;
+}
+
+static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
+{
+ WCHAR level[10000];
+ INT install_level;
+ DWORD sz;
+ DWORD i;
+ INT j;
+ DWORD rc;
+ LPWSTR override = NULL;
+ static const WCHAR addlocal[]={'A','D','D','L','O','C','A','L',0};
+ static const WCHAR all[]={'A','L','L',0};
+ static const WCHAR szlevel[] = {
+ 'I','N','S','T','A','L','L','L','E','V','E','L',0};
+ static const WCHAR szAddLocal[] = {
+ 'A','D','D','L','O','C','A','L',0};
+
+ /* I do not know if this is where it should happen.. but */
+
+ TRACE("Checking Install Level\n");
+
+ sz = 10000;
+ if (MSI_GetPropertyW(package,szlevel,level,&sz)==ERROR_SUCCESS)
+ install_level = atoiW(level);
+ else
+ install_level = 1;
+
+ sz = 0;
+ rc = MSI_GetPropertyW(package,szAddLocal,NULL,&sz);
+ if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
+ {
+ sz++;
+ override = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
+ MSI_GetPropertyW(package, addlocal,override,&sz);
+ }
+
+ /*
+ * Components FeatureState defaults to FALSE. The idea is we want to
+ * enable the component is ANY feature that uses it is enabled to install
+ */
+ for(i = 0; i < package->loaded_features; i++)
+ {
+ BOOL feature_state= ((package->features[i].Level > 0) &&
+ (package->features[i].Level <= install_level));
+
+ if (override && (strcmpiW(override,all)==0 ||
+ strstrW(override,package->features[i].Feature)))
+ {
+ TRACE("Override of install level found\n");
+ feature_state = TRUE;
+ package->features[i].Enabled = feature_state;
+ }
+
+ TRACE("Feature %s has a state of %i\n",
+ debugstr_w(package->features[i].Feature), feature_state);
+ for( j = 0; j < package->features[i].ComponentCount; j++)
+ {
+ package->components[package->features[i].Components[j]].FeatureState
+ |= feature_state;
+ }
+ }
+ if (override != NULL)
+ HeapFree(GetProcessHeap(),0,override);
+ /*
+ * So basically we ONLY want to install a component if its Enabled AND
+ * FeatureState are both TRUE
+ */
+ return ERROR_SUCCESS;
+}
+
+static UINT ACTION_InstallValidate(MSIPACKAGE *package)
+{
+ DWORD progress = 0;
+ static const WCHAR q1[]={
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'F','R','O','M',' ','R','e','g','i','s','t','r','y',0};
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+
+ TRACE(" InstallValidate \n");
+
+ rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ while (1)
+ {
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+ progress +=1;
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ ui_progress(package,0,progress+package->loaded_files,0,0);
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view = NULL;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] = {
+ '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};
+ static const WCHAR title[]=
+ {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
+
+ TRACE("Checking launch conditions\n");
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ rc = ERROR_SUCCESS;
+ while (rc == ERROR_SUCCESS)
+ {
+ LPWSTR cond = NULL;
+ LPWSTR message = NULL;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ cond = load_dynamic_stringW(row,1);
+
+ if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
+ {
+ message = load_dynamic_stringW(row,2);
+ MessageBoxW(NULL,message,title,MB_OK);
+ HeapFree(GetProcessHeap(),0,message);
+ rc = ERROR_FUNCTION_FAILED;
+ }
+ HeapFree(GetProcessHeap(),0,cond);
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+static void resolve_keypath( MSIPACKAGE* package, INT
+ component_index, WCHAR *keypath)
+{
+ MSICOMPONENT* cmp = &package->components[component_index];
+
+ if (cmp->KeyPath[0]==0)
+ {
+ resolve_folder(package,cmp->Directory,keypath,FALSE,FALSE,NULL);
+ return;
+ }
+ if ((cmp->Attributes & 0x4) || (cmp->Attributes & 0x20))
+ {
+ FIXME("UNIMPLEMENTED keypath as Registry or ODBC Source\n");
+ keypath[0]=0;
+ }
+ else
+ {
+ int j;
+ j = get_loaded_file(package,cmp->KeyPath);
+
+ if (j>=0)
+ strcpyW(keypath,package->files[j].TargetPath);
+ }
+}
+
+/*
+ * Ok further analysis makes me think that this work is
+ * actually done in the PublishComponents and PublishFeatures
+ * step, and not here. It appears like the keypath and all that is
+ * resolved in this step, however actually written in the Publish steps.
+ * But we will leave it here for now because it is unclear
+ */
+static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
+{
+ WCHAR productcode[0x100];
+ WCHAR squished_pc[0x100];
+ WCHAR squished_cc[0x100];
+ DWORD sz;
+ UINT rc;
+ DWORD i;
+ HKEY hkey=0,hkey2=0,hkey3=0;
+ static const WCHAR szProductCode[]=
+{'P','r','o','d','u','c','t','C','o','d','e',0};
+ static const WCHAR szInstaller[] = {
+'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',0 };
+ static const WCHAR szFeatures[] = {
+'F','e','a','t','u','r','e','s',0 };
+ static const WCHAR szComponents[] = {
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ /* writes the Component and Features values to the registry */
+ sz = 0x100;
+ rc = MSI_GetPropertyW(package,szProductCode,productcode,&sz);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ squash_guid(productcode,squished_pc);
+ rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller,&hkey);
+ if (rc != ERROR_SUCCESS)
+ goto end;
+
+ rc = RegCreateKeyW(hkey,szFeatures,&hkey2);
+ if (rc != ERROR_SUCCESS)
+ goto end;
+
+ rc = RegCreateKeyW(hkey2,squished_pc,&hkey3);
+ if (rc != ERROR_SUCCESS)
+ goto end;
+
+ /* here the guids are base 85 encoded */
+ for (i = 0; i < package->loaded_features; i++)
+ {
+ LPWSTR data = NULL;
+ GUID clsid;
+ int j;
+ INT size;
+
+ size = package->features[i].ComponentCount*21*sizeof(WCHAR);
+ data = HeapAlloc(GetProcessHeap(), 0, size);
+
+ data[0] = 0;
+ for (j = 0; j < package->features[i].ComponentCount; j++)
+ {
+ WCHAR buf[21];
+ TRACE("From %s\n",debugstr_w(package->components
+ [package->features[i].Components[j]].ComponentId));
+ CLSIDFromString(package->components
+ [package->features[i].Components[j]].ComponentId,
+ &clsid);
+ encode_base85_guid(&clsid,buf);
+ TRACE("to %s\n",debugstr_w(buf));
+ strcatW(data,buf);
+ }
+
+ size = strlenW(data)*sizeof(WCHAR);
+ RegSetValueExW(hkey3,package->features[i].Feature,0,REG_SZ,
+ (LPSTR)data,size);
+ HeapFree(GetProcessHeap(),0,data);
+ }
+
+ RegCloseKey(hkey3);
+ RegCloseKey(hkey2);
+
+ rc = RegCreateKeyW(hkey,szComponents,&hkey2);
+ if (rc != ERROR_SUCCESS)
+ goto end;
+
+ for (i = 0; i < package->loaded_components; i++)
+ {
+ if (package->components[i].ComponentId[0]!=0)
+ {
+ WCHAR keypath[0x1000];
+ MSIRECORD * uirow;
+
+ squash_guid(package->components[i].ComponentId,squished_cc);
+ rc = RegCreateKeyW(hkey2,squished_cc,&hkey3);
+ if (rc != ERROR_SUCCESS)
+ continue;
+
+ resolve_keypath(package,i,keypath);
+
+ RegSetValueExW(hkey3,squished_pc,0,REG_SZ,(LPVOID)keypath,
+ (strlenW(keypath)+1)*sizeof(WCHAR));
+ RegCloseKey(hkey3);
+
+ /* UI stuff */
+ uirow = MSI_CreateRecord(3);
+ MSI_RecordSetStringW(uirow,1,productcode);
+ MSI_RecordSetStringW(uirow,2,package->components[i].ComponentId);
+ MSI_RecordSetStringW(uirow,3,keypath);
+ ui_actiondata(package,szProcessComponents,uirow);
+ msiobj_release( &uirow->hdr );
+ }
+ }
+end:
+ RegCloseKey(hkey2);
+ RegCloseKey(hkey);
+ return rc;
+}
+
+static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
+{
+ /*
+ * OK this is a bit confusing.. I am given a _Component key and I believe
+ * that the file that is being registered as a type library is the "key file
+ * of that component" which I interpret to mean "The file in the KeyPath of
+ * that component".
+ */
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR Query[] = {
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'f','r','o','m',' ','T','y','p','e','L','i','b',0};
+ ITypeLib *ptLib;
+ HRESULT res;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR component[0x100];
+ DWORD sz;
+ INT index;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,3,component,&sz);
+
+ index = get_loaded_component(package,component);
+ if (index < 0)
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ if (!package->components[index].Enabled ||
+ !package->components[index].FeatureState)
+ {
+ TRACE("Skipping typelib reg due to disabled component\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ index = get_loaded_file(package,package->components[index].KeyPath);
+
+ if (index < 0)
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+// res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
+ if (SUCCEEDED(res))
+ {
+ WCHAR help[MAX_PATH];
+ WCHAR helpid[0x100];
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,6,helpid,&sz);
+
+ resolve_folder(package,helpid,help,FALSE,FALSE,NULL);
+
+// res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
+ if (!SUCCEEDED(res))
+ ERR("Failed to register type library %s\n",
+ debugstr_w(package->files[index].TargetPath));
+ else
+ {
+ /* Yes the row has more fields than I need, but #1 is
+ correct and the only one I need. Why make a new row? */
+
+ ui_actiondata(package,szRegisterTypeLibraries,row);
+
+ TRACE("Registered %s\n",
+ debugstr_w(package->files[index].TargetPath));
+ }
+
+ if (ptLib){
+ //ITypeLib_Release(ptLib);
+ }
+ }
+ else
+ ERR("Failed to load type library %s\n",
+ debugstr_w(package->files[index].TargetPath));
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+
+}
+
+static UINT register_appid(MSIPACKAGE *package, LPCWSTR clsid, LPCWSTR app )
+{
+ static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] =
+{'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};
+ WCHAR Query[0x1000];
+ HKEY hkey2,hkey3;
+ LPWSTR buffer=0;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ sprintfW(Query,ExecSeqQuery,clsid);
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
+ RegCreateKeyW(hkey2,clsid,&hkey3);
+ RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)app,
+ (strlenW(app)+1)*sizeof(WCHAR));
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ if (!MSI_RecordIsNull(row,2))
+ {
+ LPWSTR deformated=0;
+ UINT size;
+ static const WCHAR szRemoteServerName[] =
+{'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
+ buffer = load_dynamic_stringW(row,2);
+ size = deformat_string(package,buffer,&deformated);
+ RegSetValueExW(hkey3,szRemoteServerName,0,REG_SZ,(LPVOID)deformated,
+ size);
+ HeapFree(GetProcessHeap(),0,deformated);
+ HeapFree(GetProcessHeap(),0,buffer);
+ }
+
+ if (!MSI_RecordIsNull(row,3))
+ {
+ static const WCHAR szLocalService[] =
+{'L','o','c','a','l','S','e','r','v','i','c','e',0};
+ UINT size;
+ buffer = load_dynamic_stringW(row,3);
+ size = (strlenW(buffer)+1) * sizeof(WCHAR);
+ RegSetValueExW(hkey3,szLocalService,0,REG_SZ,(LPVOID)buffer,size);
+ HeapFree(GetProcessHeap(),0,buffer);
+ }
+
+ if (!MSI_RecordIsNull(row,4))
+ {
+ static const WCHAR szService[] =
+{'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
+ UINT size;
+ buffer = load_dynamic_stringW(row,4);
+ size = (strlenW(buffer)+1) * sizeof(WCHAR);
+ RegSetValueExW(hkey3,szService,0,REG_SZ,(LPVOID)buffer,size);
+ HeapFree(GetProcessHeap(),0,buffer);
+ }
+
+ if (!MSI_RecordIsNull(row,5))
+ {
+ static const WCHAR szDLL[] =
+{'D','l','l','S','u','r','r','o','g','a','t','e',0};
+ UINT size;
+ buffer = load_dynamic_stringW(row,5);
+ size = (strlenW(buffer)+1) * sizeof(WCHAR);
+ RegSetValueExW(hkey3,szDLL,0,REG_SZ,(LPVOID)buffer,size);
+ HeapFree(GetProcessHeap(),0,buffer);
+ }
+
+ if (!MSI_RecordIsNull(row,6))
+ {
+ 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};
+
+ if (MSI_RecordGetInteger(row,6))
+ RegSetValueExW(hkey3,szActivate,0,REG_SZ,(LPVOID)szY,4);
+ }
+
+ if (!MSI_RecordIsNull(row,7))
+ {
+ 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};
+
+ if (MSI_RecordGetInteger(row,7))
+ RegSetValueExW(hkey3,szRunAs,0,REG_SZ,(LPVOID)szUser,34);
+ }
+
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ RegCloseKey(hkey3);
+ RegCloseKey(hkey2);
+ return rc;
+}
+
+static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
+{
+ /*
+ * Again I am assuming the words, "Whose key file represents" when referring
+ * to a Component as to meaning that Components KeyPath file
+ *
+ * Also there is a very strong connection between ClassInfo and ProgID
+ * that I am mostly glossing over.
+ * What would be more propper is to load the ClassInfo and the ProgID info
+ * into memory data structures and then be able to enable and disable them
+ * based on component.
+ */
+
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] = {
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'f','r','o','m',' ','C','l','a','s','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 szAppID[] = { 'A','p','p','I','D',0 };
+ HKEY hkey,hkey2,hkey3;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_FUNCTION_FAILED;
+
+ rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ goto end;
+ }
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ goto end;
+ }
+
+ while (1)
+ {
+ WCHAR clsid[0x100];
+ WCHAR buffer[0x100];
+ WCHAR desc[0x100];
+ DWORD sz;
+ INT index;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ MSI_RecordGetStringW(row,3,buffer,&sz);
+
+ index = get_loaded_component(package,buffer);
+
+ if (index < 0)
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ if (!package->components[index].Enabled ||
+ !package->components[index].FeatureState)
+ {
+ TRACE("Skipping class reg due to disabled component\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ sz=0x100;
+ MSI_RecordGetStringW(row,1,clsid,&sz);
+ RegCreateKeyW(hkey,clsid,&hkey2);
+
+ if (!MSI_RecordIsNull(row,5))
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,5,desc,&sz);
+
+ RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)desc,
+ (strlenW(desc)+1)*sizeof(WCHAR));
+ }
+ else
+ desc[0]=0;
+
+ sz=0x100;
+ MSI_RecordGetStringW(row,2,buffer,&sz);
+
+ RegCreateKeyW(hkey2,buffer,&hkey3);
+
+ index = get_loaded_file(package,package->components[index].KeyPath);
+ RegSetValueExW(hkey3,NULL,0,REG_SZ,
+ (LPVOID)package->files[index].TargetPath,
+ (strlenW(package->files[index].TargetPath)+1)
+ *sizeof(WCHAR));
+
+ RegCloseKey(hkey3);
+
+ if (!MSI_RecordIsNull(row,4))
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,4,buffer,&sz);
+
+ RegCreateKeyW(hkey2,szProgID,&hkey3);
+
+ RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)buffer,
+ (strlenW(buffer)+1)*sizeof(WCHAR));
+
+ RegCloseKey(hkey3);
+ }
+
+ if (!MSI_RecordIsNull(row,6))
+ {
+ sz=0x100;
+ MSI_RecordGetStringW(row,6,buffer,&sz);
+
+ RegSetValueExW(hkey2,szAppID,0,REG_SZ,(LPVOID)buffer,
+ (strlenW(buffer)+1)*sizeof(WCHAR));
+
+ register_appid(package,buffer,desc);
+ }
+
+ RegCloseKey(hkey2);
+
+ FIXME("Process the rest of the fields >7\n");
+
+ ui_actiondata(package,szRegisterClassInfo,row);
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+end:
+ RegCloseKey(hkey);
+ return rc;
+}
+
+static UINT register_progid_base(MSIRECORD * row, LPWSTR clsid)
+{
+ static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+ HKEY hkey,hkey2;
+ WCHAR buffer[0x100];
+ DWORD sz;
+
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,1,buffer,&sz);
+ RegCreateKeyW(HKEY_CLASSES_ROOT,buffer,&hkey);
+
+ if (!MSI_RecordIsNull(row,4))
+ {
+ sz = 0x100;
+ MSI_RecordGetStringW(row,4,buffer,&sz);
+ RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
+ sizeof(WCHAR));
+ }
+
+ if (!MSI_RecordIsNull(row,3))
+ {
+ sz = 0x100;
+
+ MSI_RecordGetStringW(row,3,buffer,&sz);
+ RegCreateKeyW(hkey,szCLSID,&hkey2);
+ RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
+ sizeof(WCHAR));
+
+ if (clsid)
+ strcpyW(clsid,buffer);
+
+ RegCloseKey(hkey2);
+ }
+ else
+ {
+ FIXME("UNHANDLED case, Parent progid but classid is NULL\n");
+ return ERROR_FUNCTION_FAILED;
+ }
+ if (!MSI_RecordIsNull(row,5))
+ FIXME ("UNHANDLED icon in Progid\n");
+ return ERROR_SUCCESS;
+}
+
+static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid);
+
+static UINT register_parent_progid(MSIPACKAGE *package, LPCWSTR parent,
+ LPWSTR clsid)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR Query_t[] =
+{'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};
+ WCHAR Query[0x1000];
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ sprintfW(Query,Query_t,parent);
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ register_progid(package,row,clsid);
+
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid)
+{
+ UINT rc = ERROR_SUCCESS;
+
+ if (MSI_RecordIsNull(row,2))
+ rc = register_progid_base(row,clsid);
+ else
+ {
+ WCHAR buffer[0x1000];
+ DWORD sz, disp;
+ HKEY hkey,hkey2;
+ static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+
+ /* check if already registered */
+ sz = 0x100;
+ MSI_RecordGetStringW(row,1,buffer,&sz);
+ RegCreateKeyExW(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
+ KEY_ALL_ACCESS, NULL, &hkey, &disp );
+ if (disp == REG_OPENED_EXISTING_KEY)
+ {
+ TRACE("Key already registered\n");
+ RegCloseKey(hkey);
+ return rc;
+ }
+ /* clsid is same as parent */
+ RegCreateKeyW(hkey,szCLSID,&hkey2);
+ RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)clsid, (strlenW(clsid)+1) *
+ sizeof(WCHAR));
+
+ RegCloseKey(hkey2);
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,2,buffer,&sz);
+ rc = register_parent_progid(package,buffer,clsid);
+
+ if (!MSI_RecordIsNull(row,4))
+ {
+ sz = 0x100;
+ MSI_RecordGetStringW(row,4,buffer,&sz);
+ RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer,
+ (strlenW(buffer)+1) * sizeof(WCHAR));
+ }
+
+ if (!MSI_RecordIsNull(row,5))
+ FIXME ("UNHANDLED icon in Progid\n");
+
+ RegCloseKey(hkey);
+ }
+ return rc;
+}
+
+static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
+{
+ /*
+ * Sigh, here I am just brute force registering all progids
+ * this needs to be linked to the Classes that have been registered
+ * but the easiest way to do that is to load all these stuff into
+ * memory for easy checking.
+ *
+ * Gives me something to continue to work toward.
+ */
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR Query[] = {
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'F','R','O','M',' ','P','r','o','g','I','d',0};
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR clsid[0x1000];
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ register_progid(package,row,clsid);
+ ui_actiondata(package,szRegisterProgIdInfo,row);
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+
+static UINT build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name,
+ LPWSTR FilePath)
+{
+ WCHAR ProductCode[0x100];
+ WCHAR SystemFolder[MAX_PATH];
+ DWORD sz;
+
+ static const WCHAR szInstaller[] =
+{'I','n','s','t','a','l','l','e','r','\\',0};
+ static const WCHAR szProductCode[] =
+{'P','r','o','d','u','c','t','C','o','d','e',0};
+ static const WCHAR szFolder[] =
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+
+ sz = 0x100;
+ MSI_GetPropertyW(package,szProductCode,ProductCode,&sz);
+ if (strlenW(ProductCode)==0)
+ return ERROR_FUNCTION_FAILED;
+
+ sz = MAX_PATH;
+ MSI_GetPropertyW(package,szFolder,SystemFolder,&sz);
+ strcatW(SystemFolder,szInstaller);
+ strcatW(SystemFolder,ProductCode);
+ create_full_pathW(SystemFolder);
+
+ strcpyW(FilePath,SystemFolder);
+ strcatW(FilePath,cszbs);
+ strcatW(FilePath,icon_name);
+ return ERROR_SUCCESS;
+}
+
+static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR Query[] = {
+ 'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ',
+ 'S','h','o','r','t','c','u','t',0};
+ IShellLinkW *sl;
+ IPersistFile *pf;
+ HRESULT res;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ res = CoInitialize( NULL );
+ if (FAILED (res))
+ {
+ ERR("CoInitialize failed\n");
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR target_file[MAX_PATH];
+ WCHAR buffer[0x100];
+ DWORD sz;
+ DWORD index;
+ static const WCHAR szlnk[]={'.','l','n','k',0};
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,4,buffer,&sz);
+
+ index = get_loaded_component(package,buffer);
+
+ if (index < 0)
+ {
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ if (!package->components[index].Enabled ||
+ !package->components[index].FeatureState)
+ {
+ TRACE("Skipping shortcut creation due to disabled component\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ ui_actiondata(package,szCreateShortcuts,row);
+
+ res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkW, (LPVOID *) &sl );
+
+ if (FAILED(res))
+ {
+ ERR("Is IID_IShellLink\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
+ if( FAILED( res ) )
+ {
+ ERR("Is IID_IPersistFile\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,2,buffer,&sz);
+ resolve_folder(package, buffer,target_file,FALSE,FALSE,NULL);
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,3,buffer,&sz);
+ reduce_to_longfilename(buffer);
+ strcatW(target_file,buffer);
+ if (!strchrW(target_file,'.'))
+ strcatW(target_file,szlnk);
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,5,buffer,&sz);
+ if (strchrW(buffer,'['))
+ {
+ LPWSTR deformated;
+ deformat_string(package,buffer,&deformated);
+ IShellLinkW_SetPath(sl,deformated);
+ HeapFree(GetProcessHeap(),0,deformated);
+ }
+ else
+ {
+ FIXME("UNHANDLED shortcut format, advertised shortcut\n");
+ IPersistFile_Release( pf );
+ IShellLinkW_Release( sl );
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ if (!MSI_RecordIsNull(row,6))
+ {
+ LPWSTR deformated;
+ sz = 0x100;
+ MSI_RecordGetStringW(row,6,buffer,&sz);
+ deformat_string(package,buffer,&deformated);
+ IShellLinkW_SetArguments(sl,deformated);
+ HeapFree(GetProcessHeap(),0,deformated);
+ }
+
+ if (!MSI_RecordIsNull(row,7))
+ {
+ LPWSTR deformated;
+ deformated = load_dynamic_stringW(row,7);
+ IShellLinkW_SetDescription(sl,deformated);
+ HeapFree(GetProcessHeap(),0,deformated);
+ }
+
+ if (!MSI_RecordIsNull(row,8))
+ IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
+
+ if (!MSI_RecordIsNull(row,9))
+ {
+ WCHAR Path[MAX_PATH];
+ INT index;
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,9,buffer,&sz);
+
+ build_icon_path(package,buffer,Path);
+ index = MSI_RecordGetInteger(row,10);
+
+ IShellLinkW_SetIconLocation(sl,Path,index);
+ }
+
+ if (!MSI_RecordIsNull(row,11))
+ IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
+
+ if (!MSI_RecordIsNull(row,12))
+ {
+ WCHAR Path[MAX_PATH];
+
+ sz = 0x100;
+ MSI_RecordGetStringW(row,12,buffer,&sz);
+ resolve_folder(package, buffer, Path, FALSE, FALSE, NULL);
+ IShellLinkW_SetWorkingDirectory(sl,Path);
+ }
+
+ TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
+ IPersistFile_Save(pf,target_file,FALSE);
+
+ IPersistFile_Release( pf );
+ IShellLinkW_Release( sl );
+
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+
+ CoUninitialize();
+
+ return rc;
+}
+
+
+/*
+ * 99% of the work done here is only done for
+ * advertised installs. However this is where the
+ * Icon table is processed and written out
+ * so that is what I am going to do here.
+ */
+static UINT ACTION_PublishProduct(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR Query[]={
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'f','r','o','m',' ','I','c','o','n',0};
+ DWORD sz;
+
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ HANDLE the_file;
+ WCHAR FilePath[MAX_PATH];
+ WCHAR FileName[MAX_PATH];
+ CHAR buffer[1024];
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz = MAX_PATH;
+ MSI_RecordGetStringW(row,1,FileName,&sz);
+ if (sz == 0)
+ {
+ ERR("Unable to get FileName\n");
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ build_icon_path(package,FileName,FilePath);
+
+ 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));
+ msiobj_release(&row->hdr);
+ continue;
+ }
+
+ 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);
+
+ CloseHandle(the_file);
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+
+}
+
+/* Msi functions that seem appropriate here */
+UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
+{
+ LPWSTR szwAction;
+ UINT rc;
+
+ TRACE(" exteral attempt at action %s\n",szAction);
+
+ if (!szAction)
+ return ERROR_FUNCTION_FAILED;
+ if (hInstall == 0)
+ return ERROR_FUNCTION_FAILED;
+
+ szwAction = strdupAtoW(szAction);
+
+ if (!szwAction)
+ return ERROR_FUNCTION_FAILED;
+
+
+ rc = MsiDoActionW(hInstall, szwAction);
+ HeapFree(GetProcessHeap(),0,szwAction);
+ return rc;
+}
+
+UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
+{
+ MSIPACKAGE *package;
+ UINT ret = ERROR_INVALID_HANDLE;
+
+ TRACE(" external attempt at action %s \n",debugstr_w(szAction));
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if( package )
+ {
+ ret = ACTION_PerformAction(package,szAction);
+ msiobj_release( &package->hdr );
+ }
+ return ret;
+}
+
+UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder,
+ LPSTR szPathBuf, DWORD* pcchPathBuf)
+{
+ LPWSTR szwFolder;
+ LPWSTR szwPathBuf;
+ UINT rc;
+
+ TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
+
+ if (!szFolder)
+ return ERROR_FUNCTION_FAILED;
+ if (hInstall == 0)
+ return ERROR_FUNCTION_FAILED;
+
+ szwFolder = strdupAtoW(szFolder);
+
+ if (!szwFolder)
+ return ERROR_FUNCTION_FAILED;
+
+ szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
+
+ rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
+
+ WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
+ *pcchPathBuf, NULL, NULL );
+
+ HeapFree(GetProcessHeap(),0,szwFolder);
+ HeapFree(GetProcessHeap(),0,szwPathBuf);
+
+ return rc;
+}
+
+UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
+ szPathBuf, DWORD* pcchPathBuf)
+{
+ WCHAR path[MAX_PATH];
+ UINT rc;
+ MSIPACKAGE *package;
+
+ TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if( !package )
+ return ERROR_INVALID_HANDLE;
+ rc = resolve_folder(package, szFolder, path, FALSE, FALSE, NULL);
+ msiobj_release( &package->hdr );
+
+ if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
+ {
+ *pcchPathBuf = strlenW(path)+1;
+ return ERROR_MORE_DATA;
+ }
+ else if (rc == ERROR_SUCCESS)
+ {
+ *pcchPathBuf = strlenW(path)+1;
+ strcpyW(szPathBuf,path);
+ TRACE("Returning Path %s\n",debugstr_w(path));
+ }
+
+ return rc;
+}
+
+
+UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder,
+ LPSTR szPathBuf, DWORD* pcchPathBuf)
+{
+ LPWSTR szwFolder;
+ LPWSTR szwPathBuf;
+ UINT rc;
+
+ TRACE("getting source %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
+
+ if (!szFolder)
+ return ERROR_FUNCTION_FAILED;
+ if (hInstall == 0)
+ return ERROR_FUNCTION_FAILED;
+
+ szwFolder = strdupAtoW(szFolder);
+ if (!szwFolder)
+ return ERROR_FUNCTION_FAILED;
+
+ szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
+
+ rc = MsiGetSourcePathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
+
+ WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
+ *pcchPathBuf, NULL, NULL );
+
+ HeapFree(GetProcessHeap(),0,szwFolder);
+ HeapFree(GetProcessHeap(),0,szwPathBuf);
+
+ return rc;
+}
+
+UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
+ szPathBuf, DWORD* pcchPathBuf)
+{
+ WCHAR path[MAX_PATH];
+ UINT rc;
+ MSIPACKAGE *package;
+
+ TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if( !package )
+ return ERROR_INVALID_HANDLE;
+ rc = resolve_folder(package, szFolder, path, TRUE, FALSE, NULL);
+ msiobj_release( &package->hdr );
+
+ if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
+ {
+ *pcchPathBuf = strlenW(path)+1;
+ return ERROR_MORE_DATA;
+ }
+ else if (rc == ERROR_SUCCESS)
+ {
+ *pcchPathBuf = strlenW(path)+1;
+ strcpyW(szPathBuf,path);
+ TRACE("Returning Path %s\n",debugstr_w(path));
+ }
+
+ return rc;
+}
+
+
+UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder,
+ LPCSTR szFolderPath)
+{
+ LPWSTR szwFolder;
+ LPWSTR szwFolderPath;
+ UINT rc;
+
+ if (!szFolder)
+ return ERROR_FUNCTION_FAILED;
+ if (hInstall == 0)
+ return ERROR_FUNCTION_FAILED;
+
+ szwFolder = strdupAtoW(szFolder);
+ if (!szwFolder)
+ return ERROR_FUNCTION_FAILED;
+
+ szwFolderPath = strdupAtoW(szFolderPath);
+ if (!szwFolderPath)
+ {
+ HeapFree(GetProcessHeap(),0,szwFolder);
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
+
+ HeapFree(GetProcessHeap(),0,szwFolder);
+ HeapFree(GetProcessHeap(),0,szwFolderPath);
+
+ return rc;
+}
+
+UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder,
+ LPCWSTR szFolderPath)
+{
+ DWORD i;
+ WCHAR path[MAX_PATH];
+ MSIFOLDER *folder;
+
+ TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+ if (package==NULL)
+ return ERROR_INVALID_HANDLE;
+
+ if (szFolderPath[0]==0)
+ return ERROR_FUNCTION_FAILED;
+
+ if (GetFileAttributesW(szFolderPath) == INVALID_FILE_ATTRIBUTES)
+ return ERROR_FUNCTION_FAILED;
+
+ resolve_folder(package,szFolder,path,FALSE,FALSE,&folder);
+
+ if (!folder)
+ return ERROR_INVALID_PARAMETER;
+
+ strcpyW(folder->Property,szFolderPath);
+
+ for (i = 0; i < package->loaded_folders; i++)
+ package->folders[i].ResolvedTarget[0]=0;
+
+ for (i = 0; i < package->loaded_folders; i++)
+ resolve_folder(package, package->folders[i].Directory, path, FALSE,
+ TRUE, NULL);
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder,
+ LPCWSTR szFolderPath)
+{
+ MSIPACKAGE *package;
+ UINT ret;
+
+ TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ ret = MSI_SetTargetPathW( package, szFolder, szFolderPath );
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, DWORD iRunMode)
+{
+ FIXME("STUB (%li)\n",iRunMode);
+ return FALSE;
+}
+
+/*
+ * 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);
+
+ HeapFree(GetProcessHeap(),0,szwFeature);
+
+ return rc;
+}
+
+UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature,
+ INSTALLSTATE iState)
+{
+ MSIPACKAGE* package;
+ INT index;
+
+ TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+
+ index = get_loaded_feature(package,szFeature);
+ if (index < 0)
+ return ERROR_UNKNOWN_FEATURE;
+
+ package->features[index].State = iState;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ LPWSTR szwFeature = NULL;
+ UINT rc;
+
+ szwFeature = strdupAtoW(szFeature);
+
+ rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction);
+
+ HeapFree( GetProcessHeap(), 0 , szwFeature);
+
+ return rc;
+}
+
+UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ INT index;
+
+ index = get_loaded_feature(package,szFeature);
+ if (index < 0)
+ return ERROR_UNKNOWN_FEATURE;
+
+ if (piInstalled)
+ *piInstalled = package->features[index].State;
+
+ if (piAction)
+ {
+ if (package->features[index].Enabled)
+ *piAction = INSTALLSTATE_LOCAL;
+ else
+ *piAction = INSTALLSTATE_UNKNOWN;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ MSIPACKAGE* package;
+ UINT ret;
+
+ TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled,
+piAction);
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction);
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ LPWSTR szwComponent= NULL;
+ UINT rc;
+
+ szwComponent= strdupAtoW(szComponent);
+
+ rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction);
+
+ HeapFree( GetProcessHeap(), 0 , szwComponent);
+
+ return rc;
+}
+
+UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ INT index;
+
+ TRACE("%p %s %p %p\n", package, debugstr_w(szComponent), piInstalled,
+piAction);
+
+ index = get_loaded_component(package,szComponent);
+ if (index < 0)
+ return ERROR_UNKNOWN_COMPONENT;
+
+ if (piInstalled)
+ *piInstalled = package->components[index].State;
+
+ if (piAction)
+ {
+ if (package->components[index].Enabled &&
+ package->components[index].FeatureState)
+ *piAction = INSTALLSTATE_LOCAL;
+ else
+ *piAction = INSTALLSTATE_UNKNOWN;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent,
+ INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+ MSIPACKAGE* package;
+ UINT ret;
+
+ TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent),
+ piInstalled, piAction);
+
+ package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction);
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+#if 0
+static UINT ACTION_Template(MSIPACKAGE *package)
+{
+ UINT rc;
+ MSIQUERY * view;
+ MSIRECORD * row = 0;
+ static const WCHAR ExecSeqQuery[] = {0};
+
+ rc = MsiDatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+
+ while (1)
+ {
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ msiobj_release(&row->hdr);
+ }
+ MsiViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+}
+#endif
--- /dev/null
+%{
+
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+static int COND_error(char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_yyinput
+{
+ MSIPACKAGE *package;
+ LPCWSTR str;
+ INT n;
+ MSICONDITION result;
+} COND_input;
+
+struct cond_str {
+ LPCWSTR data;
+ INT len;
+};
+
+static LPWSTR COND_GetString( struct cond_str *str );
+static LPWSTR COND_GetLiteral( struct cond_str *str );
+static int COND_lex( void *COND_lval, COND_input *info);
+
+typedef INT (*comp_int)(INT a, INT b);
+typedef INT (*comp_str)(LPWSTR a, LPWSTR b, BOOL caseless);
+typedef INT (*comp_m1)(LPWSTR a,int b);
+typedef INT (*comp_m2)(int a,LPWSTR b);
+
+static INT comp_lt_i(INT a, INT b);
+static INT comp_gt_i(INT a, INT b);
+static INT comp_le_i(INT a, INT b);
+static INT comp_ge_i(INT a, INT b);
+static INT comp_eq_i(INT a, INT b);
+static INT comp_ne_i(INT a, INT b);
+static INT comp_bitand(INT a, INT b);
+static INT comp_highcomp(INT a, INT b);
+static INT comp_lowcomp(INT a, INT b);
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless);
+
+static INT comp_eq_m1(LPWSTR a, INT b);
+static INT comp_ne_m1(LPWSTR a, INT b);
+static INT comp_lt_m1(LPWSTR a, INT b);
+static INT comp_gt_m1(LPWSTR a, INT b);
+static INT comp_le_m1(LPWSTR a, INT b);
+static INT comp_ge_m1(LPWSTR a, INT b);
+
+static INT comp_eq_m2(INT a, LPWSTR b);
+static INT comp_ne_m2(INT a, LPWSTR b);
+static INT comp_lt_m2(INT a, LPWSTR b);
+static INT comp_gt_m2(INT a, LPWSTR b);
+static INT comp_le_m2(INT a, LPWSTR b);
+static INT comp_ge_m2(INT a, LPWSTR b);
+
+%}
+
+%pure-parser
+
+%union
+{
+ struct cond_str str;
+ LPWSTR string;
+ INT value;
+ comp_int fn_comp_int;
+ comp_str fn_comp_str;
+ comp_m1 fn_comp_m1;
+ comp_m2 fn_comp_m2;
+}
+
+%token COND_SPACE COND_EOF COND_SPACE
+%token COND_OR COND_AND COND_NOT
+%token COND_LT COND_GT COND_EQ
+%token COND_LPAR COND_RPAR COND_TILDA
+%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
+%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
+
+%nonassoc COND_EOF COND_ERROR
+
+%type <value> expression boolean_term boolean_factor
+%type <value> term value_i symbol_i integer
+%type <string> identifier value_s symbol_s literal
+%type <fn_comp_int> comp_op_i
+%type <fn_comp_str> comp_op_s
+%type <fn_comp_m1> comp_op_m1
+%type <fn_comp_m2> comp_op_m2
+
+%%
+
+condition:
+ expression
+ {
+ COND_input* cond = (COND_input*) info;
+ cond->result = $1;
+ }
+ ;
+
+expression:
+ boolean_term
+ {
+ $$ = $1;
+ }
+ | boolean_term COND_OR expression
+ {
+ $$ = $1 || $3;
+ }
+ ;
+
+boolean_term:
+ boolean_factor
+ {
+ $$ = $1;
+ }
+ | boolean_term COND_AND boolean_factor
+ {
+ $$ = $1 && $3;
+ }
+ ;
+
+boolean_factor:
+ term
+ {
+ $$ = $1;
+ }
+ | COND_NOT term
+ {
+ $$ = ! $2;
+ }
+ ;
+
+
+term:
+ value_i
+ {
+ $$ = $1;
+ }
+ | value_s
+ {
+ $$ = atoiW($1);
+ }
+ | value_i comp_op_i value_i
+ {
+ $$ = $2( $1, $3 );
+ }
+ | value_s comp_op_s value_s
+ {
+ $$ = $2( $1, $3, FALSE );
+ }
+ | value_s COND_TILDA comp_op_s value_s
+ {
+ $$ = $3( $1, $4, TRUE );
+ }
+ | value_s comp_op_m1 value_i
+ {
+ $$ = $2( $1, $3 );
+ }
+ | value_i comp_op_m2 value_s
+ {
+ $$ = $2( $1, $3 );
+ }
+ | COND_LPAR expression COND_RPAR
+ {
+ $$ = $2;
+ }
+ ;
+
+comp_op_i:
+ /* common functions */
+ COND_EQ
+ {
+ $$ = comp_eq_i;
+ }
+ | COND_LT COND_GT
+ {
+ $$ = comp_ne_i;
+ }
+ | COND_LT
+ {
+ $$ = comp_lt_i;
+ }
+ | COND_GT
+ {
+ $$ = comp_gt_i;
+ }
+ | COND_LT COND_EQ
+ {
+ $$ = comp_le_i;
+ }
+ | COND_GT COND_EQ
+ {
+ $$ = comp_ge_i;
+ }
+ /*Int only*/
+ | COND_GT COND_LT
+ {
+ $$ = comp_bitand;
+ }
+ | COND_LT COND_LT
+ {
+ $$ = comp_highcomp;
+ }
+ | COND_GT COND_GT
+ {
+ $$ = comp_lowcomp;
+ }
+ ;
+
+comp_op_s:
+ /* common functions */
+ COND_EQ
+ {
+ $$ = comp_eq_s;
+ }
+ | COND_LT COND_GT
+ {
+ $$ = comp_ne_s;
+ }
+ | COND_LT
+ {
+ $$ = comp_lt_s;
+ }
+ | COND_GT
+ {
+ $$ = comp_gt_s;
+ }
+ | COND_LT COND_EQ
+ {
+ $$ = comp_le_s;
+ }
+ | COND_GT COND_EQ
+ {
+ $$ = comp_ge_s;
+ }
+ /*string only*/
+ | COND_GT COND_LT
+ {
+ $$ = comp_substring;
+ }
+ | COND_LT COND_LT
+ {
+ $$ = comp_start;
+ }
+ | COND_GT COND_GT
+ {
+ $$ = comp_end;
+ }
+ ;
+
+comp_op_m1:
+ /* common functions */
+ COND_EQ
+ {
+ $$ = comp_eq_m1;
+ }
+ | COND_LT COND_GT
+ {
+ $$ = comp_ne_m1;
+ }
+ | COND_LT
+ {
+ $$ = comp_lt_m1;
+ }
+ | COND_GT
+ {
+ $$ = comp_gt_m1;
+ }
+ | COND_LT COND_EQ
+ {
+ $$ = comp_le_m1;
+ }
+ | COND_GT COND_EQ
+ {
+ $$ = comp_ge_m1;
+ }
+ /*Not valid for mixed compares*/
+ | COND_GT COND_LT
+ {
+ $$ = 0;
+ }
+ | COND_LT COND_LT
+ {
+ $$ = 0;
+ }
+ | COND_GT COND_GT
+ {
+ $$ = 0;
+ }
+ ;
+
+comp_op_m2:
+ /* common functions */
+ COND_EQ
+ {
+ $$ = comp_eq_m2;
+ }
+ | COND_LT COND_GT
+ {
+ $$ = comp_ne_m2;
+ }
+ | COND_LT
+ {
+ $$ = comp_lt_m2;
+ }
+ | COND_GT
+ {
+ $$ = comp_gt_m2;
+ }
+ | COND_LT COND_EQ
+ {
+ $$ = comp_le_m2;
+ }
+ | COND_GT COND_EQ
+ {
+ $$ = comp_ge_m2;
+ }
+ /*Not valid for mixed compares*/
+ | COND_GT COND_LT
+ {
+ $$ = 0;
+ }
+ | COND_LT COND_LT
+ {
+ $$ = 0;
+ }
+ | COND_GT COND_GT
+ {
+ $$ = 0;
+ }
+ ;
+
+value_i:
+ symbol_i
+ {
+ $$ = $1;
+ }
+ | integer
+ {
+ $$ = $1;
+ }
+ ;
+
+value_s:
+ symbol_s
+ {
+ $$ = $1;
+ }
+ | literal
+ {
+ $$ = $1;
+ }
+ ;
+
+literal:
+ COND_LITER
+ {
+ $$ = COND_GetLiteral(&$1);
+ if( !$$ )
+ YYABORT;
+ }
+ ;
+
+symbol_i:
+ COND_DOLLARS identifier
+ {
+ COND_input* cond = (COND_input*) info;
+ INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+
+ MSI_GetComponentStateW(cond->package, $2, &install, &action );
+ $$ = action;
+ HeapFree( GetProcessHeap(), 0, $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;
+ HeapFree( GetProcessHeap(), 0, $2 );
+ }
+ | COND_AMPER identifier
+ {
+ COND_input* cond = (COND_input*) info;
+ INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+
+ MSI_GetFeatureStateW(cond->package, $2, &install, &action );
+ $$ = action;
+ HeapFree( GetProcessHeap(), 0, $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;
+ HeapFree( GetProcessHeap(), 0, $2 );
+ }
+ ;
+
+symbol_s:
+ identifier
+ {
+ DWORD sz;
+ COND_input* cond = (COND_input*) info;
+ $$ = HeapAlloc( GetProcessHeap(), 0, 0x100*sizeof (WCHAR) );
+
+ /* Lookup the identifier */
+
+ sz=0x100;
+ if (MSI_GetPropertyW(cond->package,$1,$$,&sz) != ERROR_SUCCESS)
+ {
+ $$[0]=0;
+ }
+ HeapFree( GetProcessHeap(), 0, $1 );
+ }
+ | COND_PERCENT identifier
+ {
+ UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
+ if( len++ )
+ {
+ $$ = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
+ if( $$ )
+ GetEnvironmentVariableW( $2, $$, len );
+ }
+ HeapFree( GetProcessHeap(), 0, $2 );
+ }
+ ;
+
+identifier:
+ COND_IDENT
+ {
+ $$ = COND_GetString(&$1);
+ if( !$$ )
+ YYABORT;
+ }
+ ;
+
+integer:
+ COND_NUMBER
+ {
+ LPWSTR szNum = COND_GetString(&$1);
+ if( !szNum )
+ YYABORT;
+ $$ = atoiW( szNum );
+ HeapFree( GetProcessHeap(), 0, 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 =='.') );
+}
+
+
+/* the mess of comparison functions */
+
+static INT comp_lt_i(INT a, INT b)
+{ return (a < b); }
+static INT comp_gt_i(INT a, INT b)
+{ return (a > b); }
+static INT comp_le_i(INT a, INT b)
+{ return (a <= b); }
+static INT comp_ge_i(INT a, INT b)
+{ return (a >= b); }
+static INT comp_eq_i(INT a, INT b)
+{ return (a == b); }
+static INT comp_ne_i(INT a, INT b)
+{ return (a != b); }
+static INT comp_bitand(INT a, INT b)
+{ return a & b;}
+static INT comp_highcomp(INT a, INT b)
+{ return HIWORD(a)==b; }
+static INT comp_lowcomp(INT a, INT b)
+{ return LOWORD(a)==b; }
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);}
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b); else return strcmpW(a,b);}
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;}
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;}
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;}
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>=0; else return strcmpW(a,b)>=0;}
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless)
+/* ERROR NOT WORKING REWRITE */
+{ if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;}
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strncmpiW(a,b,strlenW(b))==0;
+ else return strncmpW(a,b,strlenW(b))==0;}
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless)
+{
+ int i = strlenW(a);
+ int j = strlenW(b);
+ if (j>i)
+ return 0;
+ if (casless) return (!strcmpiW(&a[i-j-1],b));
+ else return (!strcmpW(&a[i-j-1],b));
+}
+
+
+static INT comp_eq_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;}
+static INT comp_ne_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;}
+static INT comp_lt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<b; else return 0;}
+static INT comp_gt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>b; else return 0;}
+static INT comp_le_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;}
+static INT comp_ge_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;}
+
+static INT comp_eq_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;}
+static INT comp_ne_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;}
+static INT comp_lt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;}
+static INT comp_gt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;}
+static INT comp_le_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;}
+static INT comp_ge_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;}
+
+
+
+static int COND_IsIdent( WCHAR x )
+{
+ return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' )
+ || ( x == '#' ) || (x == '.') );
+}
+
+static int COND_GetOne( struct cond_str *str, COND_input *cond )
+{
+ static const WCHAR szNot[] = {'N','O','T',0};
+ static const WCHAR szAnd[] = {'A','N','D',0};
+ static const WCHAR szOr[] = {'O','R',0};
+ WCHAR ch;
+ int rc, len = 1;
+
+ 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 '~': rc = COND_TILDA; break;
+ case '<': rc = COND_LT; break;
+ case '>': rc = COND_GT; break;
+ case '"':
+ {
+ const WCHAR *ch2 = str->data + 1;
+
+
+ while ( *ch2 && *ch2 != '"' )
+ ++ch2;
+ if (*ch2 == '"')
+ {
+ len = ch2 - str->data + 1;
+ rc = COND_LITER;
+ break;
+ }
+ }
+ ERR("Unterminated string\n");
+ rc = COND_ERROR;
+ break;
+ default:
+ if( COND_IsAlpha( ch ) )
+ {
+ while( COND_IsIdent( str->data[len] ) )
+ len++;
+ rc = COND_IDENT;
+ break;
+ }
+
+ if( COND_IsNumber( ch ) )
+ {
+ while( COND_IsNumber( str->data[len] ) )
+ len++;
+ rc = COND_NUMBER;
+ break;
+ }
+
+ ERR("Got unknown character %c(%x)\n",ch,ch);
+ rc = COND_ERROR;
+ break;
+ }
+
+ /* keyword identifiers */
+ if( rc == COND_IDENT )
+ {
+ if( (len==3) && (strncmpiW(str->data,szNot,len)==0) )
+ rc = COND_NOT;
+ else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) )
+ rc = COND_AND;
+ else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) )
+ rc = COND_OR;
+ }
+
+ 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( struct cond_str *str )
+{
+ LPWSTR ret;
+
+ ret = HeapAlloc( GetProcessHeap(), 0, (str->len+1) * sizeof (WCHAR) );
+ if( ret )
+ {
+ strncpyW( ret, str->data, str->len );
+ ret[str->len]=0;
+ }
+ TRACE("Got identifier %s\n",debugstr_w(ret));
+ return ret;
+}
+
+static LPWSTR COND_GetLiteral( struct cond_str *str )
+{
+ LPWSTR ret;
+
+ ret = HeapAlloc( GetProcessHeap(), 0, (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 int COND_error(char *str)
+{
+ return 0;
+}
+
+MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
+{
+ COND_input cond;
+ MSICONDITION r;
+
+ cond.package = package;
+ cond.str = szCondition;
+ cond.n = 0;
+ cond.result = -1;
+
+ TRACE("Evaluating %s\n",debugstr_w(szCondition));
+
+ if( !COND_parse( &cond ) )
+ r = cond.result;
+ else
+ r = MSICONDITION_ERROR;
+
+ TRACE("Evaluates to %i\n",r);
+ return r;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
+{
+ MSIPACKAGE *package;
+ UINT ret;
+
+ package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+ if( !package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_EvaluateConditionW( package, szCondition );
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
+{
+ LPWSTR szwCond = NULL;
+ MSICONDITION r;
+
+ if( szCondition )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 );
+ szwCond = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+ MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len );
+ }
+
+ r = MsiEvaluateConditionW( hInstall, szwCond );
+
+ if( szwCond )
+ HeapFree( GetProcessHeap(), 0, szwCond );
+
+ return r;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSICREATEVIEW
+{
+ MSIVIEW view;
+ MSIDATABASE *db;
+ LPWSTR name;
+ BOOL bIsTemp;
+ create_col_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;
+ create_col_info *col;
+ UINT r, nField, row, table_val, column_val;
+ static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
+ static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+ MSIVIEW *tv = NULL;
+
+ TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name),
+ cv->bIsTemp?"temporary":"permanent");
+
+ /* only add tables that don't exist already */
+ if( TABLE_Exists(cv->db, cv->name ) )
+ return ERROR_BAD_QUERY_SYNTAX;
+
+ /* add the name to the _Tables table */
+ table_val = msi_addstringW( cv->db->strings, 0, cv->name, -1, 1 );
+ TRACE("New string %s -> %d\n", debugstr_w( cv->name ), table_val );
+ if( table_val < 0 )
+ return ERROR_FUNCTION_FAILED;
+
+ r = TABLE_CreateView( cv->db, szTables, &tv );
+ TRACE("CreateView returned %x\n", r);
+ if( r )
+ return r;
+
+ r = tv->ops->execute( tv, 0 );
+ TRACE("tv execute returned %x\n", r);
+ if( r )
+ return r;
+
+ row = -1;
+ r = tv->ops->insert_row( tv, &row );
+ TRACE("insert_row returned %x\n", r);
+ if( r )
+ goto err;
+
+ r = tv->ops->set_int( tv, row, 1, table_val );
+ if( r )
+ goto err;
+ tv->ops->delete( tv );
+ tv = NULL;
+
+ /* add each column to the _Columns table */
+ r = TABLE_CreateView( cv->db, szColumns, &tv );
+ if( r )
+ return r;
+
+ r = tv->ops->execute( tv, 0 );
+ TRACE("tv execute returned %x\n", r);
+ if( r )
+ return r;
+
+ /*
+ * need to set the table, column number, col name and type
+ * for each column we enter in the table
+ */
+ nField = 1;
+ for( col = cv->col_info; col; col = col->next )
+ {
+ row = -1;
+ r = tv->ops->insert_row( tv, &row );
+ if( r )
+ goto err;
+
+ column_val = msi_addstringW( cv->db->strings, 0, col->colname, -1, 1 );
+ TRACE("New string %s -> %d\n", debugstr_w( col->colname ), column_val );
+ if( column_val < 0 )
+ break;
+
+ /* add the string again here so we increase the reference count */
+ table_val = msi_addstringW( cv->db->strings, 0, cv->name, -1, 1 );
+ if( table_val < 0 )
+ break;
+
+ r = tv->ops->set_int( tv, row, 1, table_val );
+ if( r )
+ break;
+
+ r = tv->ops->set_int( tv, row, 2, 0x8000|nField );
+ if( r )
+ break;
+
+ r = tv->ops->set_int( tv, row, 3, column_val );
+ if( r )
+ break;
+
+ r = tv->ops->set_int( tv, row, 4, 0x8000|col->type );
+ if( r )
+ break;
+
+ nField++;
+ }
+ if( !col )
+ r = ERROR_SUCCESS;
+
+err:
+ /* FIXME: remove values from the string table on error */
+ if( tv )
+ tv->ops->delete( tv );
+ return r;
+}
+
+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, LPWSTR *name, UINT *type )
+{
+ MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+ TRACE("%p %d %p %p\n", cv, n, name, type );
+
+ return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+ TRACE("%p %d %ld\n", cv, eModifyMode, hrec );
+
+ return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_delete( struct tagMSIVIEW *view )
+{
+ MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+ create_col_info *col;
+
+ TRACE("%p\n", cv );
+
+ col = cv->col_info;
+ while( col )
+ {
+ create_col_info *t = col;
+ col = col->next;
+ HeapFree( GetProcessHeap(), 0, t->colname );
+ HeapFree( GetProcessHeap(), 0, t );
+ }
+ msiobj_release( &cv->db->hdr );
+ HeapFree( GetProcessHeap(), 0, cv->name );
+ HeapFree( GetProcessHeap(), 0, cv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS create_ops =
+{
+ CREATE_fetch_int,
+ NULL,
+ NULL,
+ NULL,
+ CREATE_execute,
+ CREATE_close,
+ CREATE_get_dimensions,
+ CREATE_get_column_info,
+ CREATE_modify,
+ CREATE_delete
+};
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+ create_col_info *col_info, BOOL temp )
+{
+ MSICREATEVIEW *cv = NULL;
+
+ TRACE("%p\n", cv );
+
+ cv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *cv );
+ if( !cv )
+ return ERROR_FUNCTION_FAILED;
+
+ /* fill the structure */
+ cv->view.ops = &create_ops;
+ msiobj_addref( &db->hdr );
+ cv->db = db;
+ cv->name = table; /* FIXME: strdupW it? */
+ cv->col_info = col_info;
+ cv->bIsTemp = temp;
+ *view = (MSIVIEW*) cv;
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+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 = HeapAlloc( GetProcessHeap(), 0, 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 );
+ HeapFree( GetProcessHeap(), 0, 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 = HeapAlloc( GetProcessHeap(), 0, 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;
+
+ if( dv->translation )
+ HeapFree( GetProcessHeap(), 0, 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, LPWSTR *name, UINT *type )
+{
+ MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+ TRACE("%p %d %p %p\n", dv, n, name, type );
+
+ if( !dv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return dv->table->ops->get_column_info( dv->table, n, name, type );
+}
+
+static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+ TRACE("%p %d %ld\n", dv, eModifyMode, hrec );
+
+ if( !dv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return dv->table->ops->modify( dv->table, eModifyMode, hrec );
+}
+
+static UINT DISTINCT_delete( struct tagMSIVIEW *view )
+{
+ MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+ TRACE("%p\n", dv );
+
+ if( dv->table )
+ dv->table->ops->delete( dv->table );
+
+ if( dv->translation )
+ HeapFree( GetProcessHeap(), 0, dv->translation );
+ msiobj_release( &dv->db->hdr );
+ HeapFree( GetProcessHeap(), 0, dv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS distinct_ops =
+{
+ DISTINCT_fetch_int,
+ NULL,
+ NULL,
+ NULL,
+ DISTINCT_execute,
+ DISTINCT_close,
+ DISTINCT_get_dimensions,
+ DISTINCT_get_column_info,
+ DISTINCT_modify,
+ DISTINCT_delete
+};
+
+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 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 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;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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, { 0, (DWORD)(__FILE__ ": MSI_handle_cs") }
+};
+static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 };
+
+MSIOBJECTHDR *msihandletable[MSIMAXHANDLES];
+
+MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj )
+{
+ MSIHANDLE ret = 0;
+ UINT i;
+
+ EnterCriticalSection( &MSI_handle_cs );
+
+ /* find a slot */
+ for(i=0; i<MSIMAXHANDLES; i++)
+ if( !msihandletable[i] )
+ break;
+ if( (i>=MSIMAXHANDLES) || msihandletable[i] )
+ goto out;
+
+ msiobj_addref( obj );
+ msihandletable[i] = obj;
+ ret = (MSIHANDLE) (i+1);
+out:
+ TRACE("%p -> %ld\n", obj, ret );
+
+ LeaveCriticalSection( &MSI_handle_cs );
+ return ret;
+}
+
+void *msihandle2msiinfo(MSIHANDLE handle, UINT type)
+{
+ MSIOBJECTHDR *ret = NULL;
+
+ EnterCriticalSection( &MSI_handle_cs );
+ handle--;
+ if( handle<0 )
+ goto out;
+ if( handle>=MSIMAXHANDLES )
+ goto out;
+ if( !msihandletable[handle] )
+ goto out;
+ if( msihandletable[handle]->magic != MSIHANDLE_MAGIC )
+ goto out;
+ if( type && (msihandletable[handle]->type != type) )
+ goto out;
+ ret = msihandletable[handle];
+ msiobj_addref( ret );
+
+out:
+ LeaveCriticalSection( &MSI_handle_cs );
+
+ return (void*) ret;
+}
+
+MSIHANDLE msiobj_findhandle( MSIOBJECTHDR *hdr )
+{
+ MSIHANDLE ret = 0;
+ UINT i;
+
+ TRACE("%p\n", hdr);
+
+ EnterCriticalSection( &MSI_handle_cs );
+ for(i=0; (i<MSIMAXHANDLES) && !ret; i++)
+ if( msihandletable[i] == hdr )
+ ret = i+1;
+ LeaveCriticalSection( &MSI_handle_cs );
+
+ TRACE("%p -> %ld\n", hdr, ret);
+
+ msiobj_addref( hdr );
+ return ret;
+}
+
+void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy )
+{
+ MSIOBJECTHDR *info;
+
+ info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
+ if( info )
+ {
+ info->magic = MSIHANDLE_MAGIC;
+ info->type = type;
+ info->refcount = 1;
+ info->destructor = destroy;
+ }
+
+ return info;
+}
+
+void msiobj_addref( MSIOBJECTHDR *info )
+{
+ TRACE("%p\n", info);
+
+ if( !info )
+ return;
+
+ if( info->magic != MSIHANDLE_MAGIC )
+ {
+ ERR("Invalid handle!\n");
+ return;
+ }
+
+ info->refcount++;
+}
+
+int msiobj_release( MSIOBJECTHDR *info )
+{
+ int ret;
+
+ TRACE("%p\n",info);
+
+ if( !info )
+ return -1;
+
+ if( info->magic != MSIHANDLE_MAGIC )
+ {
+ ERR("Invalid handle!\n");
+ return -1;
+ }
+
+ ret = info->refcount--;
+ if (info->refcount == 0)
+ {
+ if( info->destructor )
+ info->destructor( info );
+ HeapFree( GetProcessHeap(), 0, info );
+ TRACE("object %p destroyed\n", info);
+ }
+
+ return ret;
+}
+
+UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
+{
+ MSIOBJECTHDR *info;
+ UINT ret = ERROR_INVALID_HANDLE;
+
+ TRACE("%lx\n",handle);
+
+ EnterCriticalSection( &MSI_handle_cs );
+
+ info = msihandle2msiinfo(handle, 0);
+ if( !info )
+ goto out;
+
+ if( info->magic != MSIHANDLE_MAGIC )
+ {
+ ERR("Invalid handle!\n");
+ goto out;
+ }
+
+ msiobj_release( info );
+ msihandletable[handle-1] = NULL;
+ ret = ERROR_SUCCESS;
+
+ TRACE("handle %lx Destroyed\n", handle);
+out:
+ LeaveCriticalSection( &MSI_handle_cs );
+ if( info )
+ msiobj_release( info );
+
+ return ret;
+}
+
+UINT WINAPI MsiCloseAllHandles(void)
+{
+ UINT i;
+
+ TRACE("\n");
+
+ for(i=0; i<MSIMAXHANDLES; i++)
+ MsiCloseHandle( i+1 );
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIINSERTVIEW
+{
+ MSIVIEW view;
+ MSIDATABASE *db;
+ BOOL bIsTemp;
+ MSIVIEW *sv;
+ value_list *vals; /* looks like these may be ignored... */
+} 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;
+}
+
+static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+ MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+ UINT n, type, val, r, row, col_count = 0;
+ MSIVIEW *sv;
+
+ TRACE("%p %p\n", iv, record );
+
+ sv = iv->sv;
+ if( !sv )
+ return ERROR_FUNCTION_FAILED;
+
+ r = sv->ops->execute( sv, 0 );
+ TRACE("tv execute returned %x\n", r);
+ if( r )
+ return r;
+
+ r = sv->ops->get_dimensions( sv, NULL, &col_count );
+ if( r )
+ goto err;
+
+ n = MSI_RecordGetFieldCount( record );
+ if( n != col_count )
+ {
+ ERR("Number of fields do not match\n");
+ goto err;
+ }
+
+ row = -1;
+ r = sv->ops->insert_row( sv, &row );
+ TRACE("insert_row returned %x\n", r);
+ if( r )
+ goto err;
+
+ for( n = 1; n <= col_count; n++ )
+ {
+ r = sv->ops->get_column_info( sv, n, NULL, &type );
+ if( r )
+ break;
+
+ if( type & MSITYPE_STRING )
+ {
+ const WCHAR *str = MSI_RecordGetString( record, n );
+ val = msi_addstringW( iv->db->strings, 0, str, -1, 1 );
+ }
+ else
+ {
+ val = MSI_RecordGetInteger( record, n );
+ val |= 0x8000;
+ }
+ r = sv->ops->set_int( sv, row, n, val );
+ if( r )
+ break;
+ }
+
+err:
+ return ERROR_SUCCESS;
+}
+
+
+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, LPWSTR *name, UINT *type )
+{
+ MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+ MSIVIEW *sv;
+
+ TRACE("%p %d %p %p\n", iv, n, name, type );
+
+ sv = iv->sv;
+ if( !sv )
+ return ERROR_FUNCTION_FAILED;
+
+ return sv->ops->get_column_info( sv, n, name, type );
+}
+
+static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+
+ TRACE("%p %d %ld\n", iv, eModifyMode, hrec );
+
+ 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 );
+ delete_value_list( iv->vals );
+ msiobj_release( &iv->db->hdr );
+ HeapFree( GetProcessHeap(), 0, iv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS insert_ops =
+{
+ INSERT_fetch_int,
+ NULL,
+ NULL,
+ NULL,
+ INSERT_execute,
+ INSERT_close,
+ INSERT_get_dimensions,
+ INSERT_get_column_info,
+ INSERT_modify,
+ INSERT_delete
+};
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+ string_list *columns, value_list *values, BOOL temp )
+{
+ MSIINSERTVIEW *iv = NULL;
+ UINT r;
+ MSIVIEW *tv = NULL, *sv = NULL;
+
+ TRACE("%p\n", iv );
+
+ 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 = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *iv );
+ if( !iv )
+ return ERROR_FUNCTION_FAILED;
+
+ /* fill the structure */
+ iv->view.ops = &insert_ops;
+ msiobj_addref( &db->hdr );
+ iv->db = db;
+ iv->vals = values;
+ iv->bIsTemp = temp;
+ iv->sv = sv;
+ *view = (MSIVIEW*) iv;
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002,2003,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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "objidl.h"
+#include "wincrypt.h"
+#include "wine/unicode.h"
+#include "objbase.h"
+#include "winver.h"
+
+#include "initguid.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR,
+ * which is a problem because LPCTSTR isn't defined when compiling wine.
+ * To work around this problem, we need to define LPCTSTR as LPCWSTR here,
+ * and make sure to only use it in W functions.
+ */
+#define LPCTSTR LPCWSTR
+
+DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+static const WCHAR szInstaller[] = {
+'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',0 };
+
+static const WCHAR szFeatures[] = {
+'F','e','a','t','u','r','e','s',0 };
+static const WCHAR szComponents[] = {
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+/* the UI level */
+INSTALLUILEVEL gUILevel;
+HWND gUIhwnd;
+INSTALLUI_HANDLERA gUIHandler;
+DWORD gUIFilter;
+LPVOID gUIContext;
+WCHAR gszLogFile[MAX_PATH];
+
+/*
+ * .MSI file format
+ *
+ * A .msi file is a structured storage file.
+ * It should contain a number of streams.
+ */
+
+BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
+{
+ DWORD i,n=0;
+
+ 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=0;
+
+ if(in[n++] != '{')
+ return FALSE;
+ for(i=0; i<8; i++)
+ out[7-i] = in[n++];
+ if(in[n++] != '-')
+ return FALSE;
+ for(i=0; i<4; i++)
+ out[11-i] = in[n++];
+ if(in[n++] != '-')
+ return FALSE;
+ for(i=0; i<4; i++)
+ out[15-i] = in[n++];
+ if(in[n++] != '-')
+ return FALSE;
+ for(i=0; i<2; i++)
+ {
+ out[17+i*2] = in[n++];
+ out[16+i*2] = in[n++];
+ }
+ if(in[n++] != '-')
+ return FALSE;
+ for( ; i<8; i++)
+ {
+ out[17+i*2] = in[n++];
+ out[16+i*2] = in[n++];
+ }
+ out[32]=0;
+ if(in[n++] != '}')
+ return FALSE;
+ if(in[n])
+ return FALSE;
+ 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;
+
+ 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;
+}
+
+
+VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
+{
+ MSIDATABASE *db = (MSIDATABASE *) arg;
+
+ free_cached_tables( db );
+ IStorage_Release( db->storage );
+}
+
+UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
+{
+ IStorage *stg = NULL;
+ HRESULT r;
+ MSIDATABASE *db = NULL;
+ UINT ret = ERROR_FUNCTION_FAILED;
+ LPWSTR szMode;
+ STATSTG stat;
+
+ TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
+
+ if( !pdb )
+ return ERROR_INVALID_PARAMETER;
+
+ szMode = (LPWSTR) szPersist;
+ if( HIWORD( szPersist ) )
+ {
+ /* UINT len = lstrlenW( szPerist ) + 1; */
+ FIXME("don't support persist files yet\b");
+ return ERROR_INVALID_PARAMETER;
+ /* szMode = HeapAlloc( GetProcessHeap(), 0, len * sizeof (DWORD) ); */
+ }
+ else 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_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
+ if( r == ERROR_SUCCESS )
+ {
+ IStorage_SetClass( stg, &CLSID_MsiDatabase );
+ r = init_string_table( stg );
+ }
+ }
+ else if( szPersist == MSIDBOPEN_TRANSACT )
+ {
+ 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 ) )
+ {
+ FIXME("open failed r = %08lx!\n",r);
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
+ if( FAILED( r ) )
+ {
+ FIXME("Failed to stat storage\n");
+ goto end;
+ }
+
+ if( memcmp( &stat.clsid, &CLSID_MsiDatabase, sizeof (GUID) ) )
+ {
+ ERR("storage GUID is not a MSI database GUID %s\n",
+ debugstr_guid(&stat.clsid) );
+ goto end;
+ }
+
+
+ db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
+ MSI_CloseDatabase );
+ if( !db )
+ {
+ FIXME("Failed to allocate a handle\n");
+ goto end;
+ }
+
+ if( TRACE_ON( msi ) )
+ enum_stream_names( stg );
+
+ db->storage = stg;
+ db->mode = szMode;
+
+ ret = load_string_table( db );
+ if( ret != ERROR_SUCCESS )
+ goto end;
+
+ 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 );
+ 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;
+ UINT len;
+
+ TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
+
+ if( szDBPath )
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szDBPath, -1, NULL, 0 );
+ szwDBPath = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwDBPath )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szDBPath, -1, szwDBPath, len );
+ }
+
+ if( HIWORD(szPersist) )
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szPersist, -1, NULL, 0 );
+ szwPersist = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwPersist )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szPersist, -1, szwPersist, len );
+ }
+ else
+ szwPersist = (LPWSTR) szPersist;
+
+ r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
+
+end:
+ if( szwPersist )
+ HeapFree( GetProcessHeap(), 0, szwPersist );
+ if( szwDBPath )
+ HeapFree( GetProcessHeap(), 0, szwDBPath );
+
+ return r;
+}
+
+UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct)
+{
+ UINT len, ret;
+ LPWSTR szwProd = NULL;
+
+ TRACE("%s %p\n",debugstr_a(szProduct), phProduct);
+
+ if( szProduct )
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProd = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+ if( szwProd )
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProd, len );
+ }
+
+ ret = MsiOpenProductW( szwProd, phProduct );
+
+ if( szwProd )
+ HeapFree( GetProcessHeap(), 0, szwProd );
+
+ return ret;
+}
+
+UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct)
+{
+ static const WCHAR szKey[] = {
+ '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 szLocalPackage[] = {
+ 'L','o','c','a','l','P','a','c','k','a','g','e', 0
+ };
+ LPWSTR path = NULL;
+ UINT r;
+ HKEY hKeyProduct = NULL, hKeyUninstall = NULL;
+ DWORD count, type;
+
+ TRACE("%s %p\n",debugstr_w(szProduct), phProduct);
+
+ r = RegOpenKeyW( HKEY_LOCAL_MACHINE, szKey, &hKeyUninstall );
+ if( r != ERROR_SUCCESS )
+ return ERROR_UNKNOWN_PRODUCT;
+
+ r = RegOpenKeyW( hKeyUninstall, szProduct, &hKeyProduct );
+ if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
+ goto end;
+ }
+
+ /* find the size of the path */
+ type = count = 0;
+ r = RegQueryValueExW( hKeyProduct, szLocalPackage,
+ NULL, &type, NULL, &count );
+ if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
+ goto end;
+ }
+
+ /* now alloc and fetch the path of the database to open */
+ path = HeapAlloc( GetProcessHeap(), 0, count );
+ if( !path )
+ goto end;
+
+ r = RegQueryValueExW( hKeyProduct, szLocalPackage,
+ NULL, &type, (LPBYTE) path, &count );
+ if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
+ goto end;
+ }
+
+ r = MsiOpenPackageW( path, phProduct );
+
+end:
+ if( path )
+ HeapFree( GetProcessHeap(), 0, path );
+ if( hKeyProduct )
+ RegCloseKey( hKeyProduct );
+ RegCloseKey( hKeyUninstall );
+
+ return r;
+}
+
+UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, LPCSTR szTransforms, LANGID lgidLanguage)
+{
+ FIXME("%s %s %s 0x%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 0x%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 0x%08x 0x%08lx 0x%08lx\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 0x%08x 0x%08lx 0x%08lx\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_FUNCTION_FAILED; /* FIXME: check return code */
+
+ TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine));
+
+ if( szPackagePath )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szPackagePath, -1, NULL, 0 );
+ szwPath = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwPath )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szPackagePath, -1, szwPath, len );
+ }
+
+ if( szCommandLine )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szCommandLine, -1, NULL, 0 );
+ szwCommand = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwCommand )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szCommandLine, -1, szwCommand, len );
+ }
+
+ r = MsiInstallProductW( szwPath, szwCommand );
+
+end:
+ if( szwPath )
+ HeapFree( GetProcessHeap(), 0, szwPath );
+
+ if( szwCommand )
+ HeapFree( GetProcessHeap(), 0, szwCommand );
+
+ return r;
+}
+
+UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine)
+{
+ MSIPACKAGE *package = NULL;
+ UINT rc = ERROR_SUCCESS;
+ MSIHANDLE handle;
+
+ FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine));
+
+ rc = MsiVerifyPackageW(szPackagePath);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_OpenPackageW(szPackagePath,&package);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ handle = alloc_msihandle( &package->hdr );
+
+ rc = ACTION_DoTopLevelINSTALL(package, szPackagePath, szCommandLine);
+
+ MsiCloseHandle(handle);
+ msiobj_release( &package->hdr );
+ return rc;
+}
+
+UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode)
+{
+ FIXME("%s 0x%08lx\n", debugstr_a(szProduct), dwReinstallMode);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode)
+{
+ FIXME("%s 0x%08lx\n", debugstr_w(szProduct), dwReinstallMode);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage, INSTALLTYPE eInstallType, LPCSTR szCommandLine)
+{
+ FIXME("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage), eInstallType, debugstr_a(szCommandLine));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage, INSTALLTYPE eInstallType, LPCWSTR szCommandLine)
+{
+ FIXME("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage), eInstallType, debugstr_w(szCommandLine));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
+{
+ LPWSTR szwProduct = NULL;
+ UINT hr = ERROR_SUCCESS;
+
+ FIXME("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState);
+
+ if( szProduct )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwProduct )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+ }
+
+ hr = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState );
+
+end:
+ if( szwProduct )
+ HeapFree( GetProcessHeap(), 0, szwProduct );
+
+ return hr;
+}
+
+UINT WINAPI MsiConfigureProductW(LPCWSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
+{
+ FIXME("%s %d %d\n",debugstr_w(szProduct), iInstallLevel, eInstallState);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer)
+{
+ LPWSTR szwComponent = NULL, szwBuffer = NULL;
+ UINT hr = ERROR_INSTALL_FAILURE;
+
+ FIXME("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer));
+
+ if( szComponent )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+ szwComponent = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwComponent )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ {
+ szwBuffer = HeapAlloc( GetProcessHeap(), 0, GUID_SIZE * sizeof(WCHAR) );
+ if( !szwBuffer )
+ goto end;
+ }
+
+ hr = MsiGetProductCodeW( szwComponent, szwBuffer );
+
+ if( ERROR_SUCCESS == hr )
+ {
+ WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL);
+ }
+
+end:
+ if( szwComponent )
+ HeapFree( GetProcessHeap(), 0, szwComponent );
+ if( szwBuffer )
+ HeapFree( GetProcessHeap(), 0, szwBuffer );
+
+ return hr;
+}
+
+UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer)
+{
+ FIXME("%s %s\n",debugstr_w(szComponent), debugstr_w(szBuffer));
+ if (NULL == szComponent) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+
+
+
+UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, LPSTR szBuffer, DWORD *pcchValueBuf)
+{
+ LPWSTR szwProduct = NULL, szwAttribute = NULL, szwBuffer = NULL;
+ UINT hr = ERROR_INSTALL_FAILURE;
+
+ FIXME("%s %s %p %p\n",debugstr_a(szProduct), debugstr_a(szAttribute), szBuffer, pcchValueBuf);
+
+ if (NULL != szBuffer && NULL == pcchValueBuf) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if( szProduct )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwProduct )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if( szAttribute )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szAttribute, -1, NULL, 0 );
+ szwAttribute = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwAttribute )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szAttribute, -1, szwAttribute, len );
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if( szBuffer )
+ {
+ szwBuffer = HeapAlloc( GetProcessHeap(), 0, (*pcchValueBuf) * sizeof(WCHAR) );
+ if( !szwBuffer )
+ goto end;
+ }
+
+ hr = MsiGetProductInfoW( szwProduct, szwAttribute, szwBuffer, pcchValueBuf );
+
+ if( ERROR_SUCCESS == hr )
+ {
+ WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, *pcchValueBuf, NULL, NULL);
+ }
+
+end:
+ if( szwProduct )
+ HeapFree( GetProcessHeap(), 0, szwProduct );
+ if( szwAttribute )
+ HeapFree( GetProcessHeap(), 0, szwAttribute );
+ if( szwBuffer )
+ HeapFree( GetProcessHeap(), 0, szwBuffer );
+
+ return hr;
+}
+
+UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szBuffer, DWORD *pcchValueBuf)
+{
+ MSIHANDLE hProduct;
+ UINT hr;
+
+ FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute), szBuffer, pcchValueBuf);
+
+ if (NULL != szBuffer && NULL == pcchValueBuf) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if (NULL == szProduct || NULL == szAttribute) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ hr = MsiOpenProductW(szProduct, &hProduct);
+ if (ERROR_SUCCESS != hr) return hr;
+
+ hr = MsiGetPropertyW(hProduct, szAttribute, szBuffer, pcchValueBuf);
+ MsiCloseHandle(hProduct);
+ return hr;
+}
+
+UINT WINAPI MsiDatabaseImportA(LPCSTR szFolderPath, LPCSTR szFilename)
+{
+ FIXME("%s %s\n",debugstr_a(szFolderPath), debugstr_a(szFilename));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseImportW(LPCWSTR szFolderPath, LPCWSTR szFilename)
+{
+ FIXME("%s %s\n",debugstr_w(szFolderPath), debugstr_w(szFilename));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, DWORD attributes)
+{
+ LPWSTR szwLogFile = NULL;
+ UINT hr = ERROR_INSTALL_FAILURE;
+
+ FIXME("%08lx %s %08lx\n", dwLogMode, debugstr_a(szLogFile), attributes);
+
+ if( szLogFile )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szLogFile, -1, NULL, 0 );
+ szwLogFile = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwLogFile )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szLogFile, -1, szwLogFile, len );
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ hr = MsiEnableLogW( dwLogMode, szwLogFile, attributes );
+
+end:
+ if( szwLogFile )
+ HeapFree( GetProcessHeap(), 0, szwLogFile );
+
+ return hr;
+}
+
+UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes)
+{
+ HANDLE the_file = INVALID_HANDLE_VALUE;
+ TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_w(szLogFile), attributes);
+ strcpyW(gszLogFile,szLogFile);
+ if (!(attributes & INSTALLLOGATTRIBUTES_APPEND))
+ DeleteFileW(szLogFile);
+ the_file = CreateFileW(szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (the_file != INVALID_HANDLE_VALUE)
+ CloseHandle(the_file);
+ else
+ ERR("Unable to enable log %s\n",debugstr_w(szLogFile));
+
+ return ERROR_SUCCESS;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct)
+{
+ FIXME("%s\n", debugstr_a(szProduct));
+ return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct)
+{
+ FIXME("%s\n", debugstr_w(szProduct));
+ return INSTALLSTATE_UNKNOWN;
+}
+
+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 = gUIHandler;
+
+ TRACE("(%p %lx %p)\n",puiHandler,dwMessageFilter,pvContext);
+ gUIHandler = puiHandler;
+ gUIFilter = dwMessageFilter;
+ gUIContext = pvContext;
+
+ return prev;
+}
+
+UINT WINAPI MsiLoadStringA(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int nBufferMax, DWORD e)
+{
+ /*FIXME("%08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e);*/
+ FIXME("%p %u %p %d %08lx\n",hInstance,uID,lpBuffer,nBufferMax,e);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax, DWORD e)
+{
+ FIXME("%p %u %p %d %08lx\n",hInstance,uID,lpBuffer,nBufferMax,e);
+ /*
+ int ret = LoadStringW(hInstance,uID,lpBuffer,nBufferMax);
+ FIXME("%s\n",debugstr_w(lpBuffer));
+ return ret;
+ */
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf, DWORD *pcchBuf)
+{
+ FIXME("%s %p %08lx\n", debugstr_a(szComponent), lpPathBuf, *pcchBuf);
+ return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPSTR lpPathBuf, DWORD *pcchBuf)
+{
+ FIXME("%s %p %08lx\n", debugstr_w(szComponent), lpPathBuf, *pcchBuf);
+ return INSTALLSTATE_UNKNOWN;
+}
+
+#include "winuser.h"
+
+UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, WORD wLanguageId, DWORD f)
+{
+ FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_a(lpText),debugstr_a(lpCaption),uType,wLanguageId,f);
+ /*
+ MessageBoxExA(hWnd,lpText,lpCaption,uType|MB_OK,wLanguageId);
+ */
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId, DWORD f)
+{
+ /*FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);*/
+ FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_w(lpText),debugstr_w(lpCaption),uType,wLanguageId,f);
+ /*
+ MessageBoxExW(hWnd,lpText,lpCaption,uType|MB_OK,wLanguageId);
+ */
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
+{
+ DWORD r;
+ WCHAR szwGuid[GUID_SIZE];
+
+ TRACE("%ld %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)
+{
+ HKEY hkey = 0, hkeyFeatures = 0;
+ DWORD r;
+ WCHAR szKeyName[33];
+
+ TRACE("%ld %p\n",index,lpguid);
+
+ if (NULL == lpguid) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegEnumKeyW(hkeyFeatures, index, szKeyName, GUID_SIZE);
+
+ unsquash_guid(szKeyName, lpguid);
+
+end:
+
+ if( hkeyFeatures )
+ RegCloseKey(hkeyFeatures);
+ if( hkey )
+ RegCloseKey(hkey);
+
+ return r;
+}
+
+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 %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent);
+
+ if( szProduct )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+ if( szwProduct )
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+ else
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ 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);
+ }
+
+ if( szwProduct )
+ HeapFree( GetProcessHeap(), 0, szwProduct);
+
+ return r;
+}
+
+UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index,
+ LPWSTR szFeature, LPWSTR szParent)
+{
+ HKEY hkey = 0, hkeyFeatures = 0, hkeyProduct = 0;
+ DWORD r, sz;
+ WCHAR szRegName[GUID_SIZE];
+
+ TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent);
+
+ if( !squash_guid(szProduct, szRegName) )
+ return ERROR_INVALID_PARAMETER;
+
+ r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkeyFeatures, szRegName, &hkeyProduct);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ sz = GUID_SIZE;
+ r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
+
+end:
+ if( hkeyProduct )
+ RegCloseKey(hkeyProduct);
+ if( hkeyFeatures )
+ RegCloseKey(hkeyFeatures);
+ if( hkey )
+ RegCloseKey(hkey);
+
+ return r;
+}
+
+UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
+{
+ DWORD r;
+ WCHAR szwGuid[GUID_SIZE];
+
+ TRACE("%ld %p\n",index,lpguid);
+
+ 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)
+{
+ HKEY hkey = 0, hkeyComponents = 0;
+ DWORD r;
+ WCHAR szKeyName[33];
+
+ TRACE("%ld %p\n",index,lpguid);
+
+ r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkey, szComponents, &hkeyComponents);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegEnumKeyW(hkeyComponents, index, szKeyName, GUID_SIZE);
+
+ unsquash_guid(szKeyName, lpguid);
+
+end:
+
+ if( hkeyComponents )
+ RegCloseKey(hkeyComponents);
+ if( hkey )
+ RegCloseKey(hkey);
+
+ return r;
+}
+
+UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
+{
+ DWORD r;
+ WCHAR szwProduct[GUID_SIZE];
+ LPWSTR szwComponent = NULL;
+
+ TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct);
+
+ if( szComponent )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+ szwComponent = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+ if( szwComponent )
+ MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+ else
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
+ if( r == ERROR_SUCCESS )
+ {
+ WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
+ szProduct, GUID_SIZE, NULL, NULL);
+ }
+
+ if( szwComponent )
+ HeapFree( GetProcessHeap(), 0, szwComponent);
+
+ return r;
+}
+
+UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
+{
+ HKEY hkey = 0, hkeyComponents = 0, hkeyComp = 0;
+ DWORD r, sz;
+ WCHAR szRegName[GUID_SIZE], szValName[GUID_SIZE];
+
+ TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct);
+
+ if( !squash_guid(szComponent, szRegName) )
+ return ERROR_INVALID_PARAMETER;
+
+ r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkey, szComponents, &hkeyComponents);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ r = RegOpenKeyW(hkeyComponents, szRegName, &hkeyComp);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ sz = GUID_SIZE;
+ r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
+ if( r != ERROR_SUCCESS )
+ goto end;
+
+ unsquash_guid(szValName, szProduct);
+
+end:
+ if( hkeyComp )
+ RegCloseKey(hkeyComp);
+ if( hkeyComponents )
+ RegCloseKey(hkeyComponents);
+ if( hkey )
+ RegCloseKey(hkey);
+
+ return r;
+}
+
+UINT WINAPI MsiEnumComponentQualifiersA(
+ LPSTR szComponent, DWORD iIndex, LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf, LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
+{
+FIXME("%s 0x%08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, pcchApplicationDataBuf);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnumComponentQualifiersW(
+ LPWSTR szComponent, DWORD iIndex, LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf, LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
+{
+FIXME("%s 0x%08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, pcchApplicationDataBuf);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideAssemblyA(
+ LPCSTR szAssemblyName, LPCSTR szAppContext, DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf, DWORD* pcchPathBuf)
+{
+ FIXME("%s %s 0x%08lx 0x%08lx %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, DWORD* pcchPathBuf)
+{
+ FIXME("%s %s 0x%08lx 0x%08lx %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, DWORD *pcchPath, DWORD *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, DWORD *pcchPath, DWORD *pcchArgs )
+{
+ FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs );
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MsiGetFileSignatureInformationA(
+ LPCSTR szSignedObjectPath, DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, DWORD* pcbHashData)
+{
+ FIXME("%s 0x%08lx %p %p %p\n", debugstr_a(szSignedObjectPath), dwFlags, ppcCertContext, pbHashData, pcbHashData);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MsiGetFileSignatureInformationW(
+ LPCWSTR szSignedObjectPath, DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, DWORD* pcbHashData)
+{
+ FIXME("%s 0x%08lx %p %p %p\n", debugstr_w(szSignedObjectPath), dwFlags, ppcCertContext, pbHashData, pcbHashData);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyA( MSIHANDLE hProduct, LPCSTR szProperty,
+ LPSTR szValue, DWORD *pccbValue )
+{
+ FIXME("%ld %s %p %p\n", hProduct, debugstr_a(szProperty), szValue, pccbValue);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyW( MSIHANDLE hProduct, LPCWSTR szProperty,
+ LPWSTR szValue, DWORD *pccbValue )
+{
+ FIXME("%ld %s %p %p\n", hProduct, debugstr_w(szProperty), szValue, pccbValue);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage )
+{
+ UINT r, len;
+ LPWSTR szPack = NULL;
+
+ TRACE("%s\n", debugstr_a(szPackage) );
+
+ if( szPackage )
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 );
+ szPack = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szPack )
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szPack, len );
+ }
+
+ r = MsiVerifyPackageW( szPack );
+
+ HeapFree( GetProcessHeap(), 0, 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;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent,
+ LPSTR lpPathBuf, DWORD* pcchBuf)
+{
+ INSTALLSTATE rc;
+ UINT len;
+ LPWSTR szwProduct= NULL;
+ LPWSTR szwComponent= NULL;
+ LPWSTR lpwPathBuf= NULL;
+
+ if( szProduct)
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProduct= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwProduct)
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+ }
+
+ if( szComponent)
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+ szwComponent= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwComponent)
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+ }
+
+ if (pcchBuf && *pcchBuf > 0)
+ lpwPathBuf = HeapAlloc( GetProcessHeap(), 0, *pcchBuf * sizeof(WCHAR));
+ else
+ lpwPathBuf = NULL;
+
+ rc = MsiGetComponentPathW(szwProduct, szwComponent, lpwPathBuf, pcchBuf);
+
+ HeapFree( GetProcessHeap(), 0, szwProduct);
+ HeapFree( GetProcessHeap(), 0, szwComponent);
+ if (lpwPathBuf)
+ {
+ WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, *pcchBuf,
+ lpPathBuf, GUID_SIZE, NULL, NULL);
+ HeapFree( GetProcessHeap(), 0, lpwPathBuf);
+ }
+
+ return rc;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent,
+ LPWSTR lpPathBuf, DWORD* pcchBuf)
+{
+ FIXME("STUB: (%s %s %p %p)\n", debugstr_w(szProduct),
+ debugstr_w(szComponent), lpPathBuf, pcchBuf);
+
+ return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature)
+{
+ INSTALLSTATE rc;
+ UINT len;
+ LPWSTR szwProduct= NULL;
+ LPWSTR szwFeature= NULL;
+
+ if( szProduct)
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+ szwProduct= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwProduct)
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+ }
+
+ if( szFeature)
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szFeature, -1, NULL, 0 );
+ szwFeature= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwFeature)
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szFeature, -1, szwFeature, len );
+ }
+
+ rc = MsiQueryFeatureStateW(szwProduct, szwFeature);
+
+ HeapFree( GetProcessHeap(), 0, szwProduct);
+ HeapFree( GetProcessHeap(), 0, szwFeature);
+
+ return rc;
+}
+
+INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature)
+{
+ FIXME("STUB: (%s %s)\n", debugstr_w(szProduct), debugstr_w(szFeature));
+ return INSTALLSTATE_UNKNOWN;
+}
+
+UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf, DWORD* pcchVersionBuf, LPSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+ UINT len;
+ UINT ret;
+ LPWSTR szwFilePath = NULL;
+ LPWSTR lpwVersionBuff = NULL;
+ LPWSTR lpwLangBuff = NULL;
+
+ if(szFilePath) {
+ len = MultiByteToWideChar( CP_ACP, 0, szFilePath, -1, NULL, 0 );
+ szwFilePath = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwFilePath)
+ return ERROR_OUTOFMEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szFilePath, -1, szwFilePath, len );
+ }
+
+ if(lpVersionBuf && pcchVersionBuf && *pcchVersionBuf) {
+ lpwVersionBuff = HeapAlloc(GetProcessHeap(), 0, *pcchVersionBuf * sizeof(WCHAR));
+ if( !lpwVersionBuff)
+ return ERROR_OUTOFMEMORY;
+ }
+
+ if(lpLangBuf && pcchLangBuf && *pcchLangBuf) {
+ lpwLangBuff = HeapAlloc(GetProcessHeap(), 0, *pcchVersionBuf * sizeof(WCHAR));
+ if( !lpwLangBuff)
+ return ERROR_OUTOFMEMORY;
+ }
+
+ ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf, lpwLangBuff, pcchLangBuf);
+
+ if(lpwVersionBuff)
+ WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1, lpVersionBuf, *pcchVersionBuf, NULL, NULL);
+ if(lpwLangBuff)
+ WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1, lpLangBuf, *pcchLangBuf, NULL, NULL);
+
+ if(szwFilePath) HeapFree(GetProcessHeap(), 0, szwFilePath);
+ if(lpwVersionBuff) HeapFree(GetProcessHeap(), 0, lpwVersionBuff);
+ if(lpwLangBuff) HeapFree(GetProcessHeap(), 0, lpwLangBuff);
+
+ return ret;
+}
+
+UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf, DWORD* pcchVersionBuf, LPWSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+ static const WCHAR szVersionResource[] = {'\\',0};
+ static const WCHAR szVersionFormat[] = {'%','d','.','%','d','.','%','d','.','%','d',0};
+ static const WCHAR szLangFormat[] = {'%','d',0};
+ UINT ret = 0;
+ DWORD dwVerLen;
+ LPVOID lpVer = NULL;
+ VS_FIXEDFILEINFO *ffi;
+ UINT puLen;
+ WCHAR tmp[32];
+
+ TRACE("(%s,%p,%ld,%p,%ld)\n", debugstr_w(szFilePath),
+ lpVersionBuf, pcchVersionBuf?*pcchVersionBuf:0,
+ lpLangBuf, pcchLangBuf?*pcchLangBuf:0);
+
+ dwVerLen = GetFileVersionInfoSizeW(szFilePath, NULL);
+ if(!dwVerLen)
+ return GetLastError();
+
+ lpVer = HeapAlloc(GetProcessHeap(), 0, dwVerLen);
+ if(!lpVer) {
+ ret = ERROR_OUTOFMEMORY;
+ goto end;
+ }
+
+ if(!GetFileVersionInfoW(szFilePath, 0, dwVerLen, lpVer)) {
+ ret = GetLastError();
+ goto end;
+ }
+ if(lpVersionBuf && pcchVersionBuf && *pcchVersionBuf) {
+ if(VerQueryValueW(lpVer, szVersionResource, (LPVOID*)&ffi, &puLen) && puLen > 0) {
+ wsprintfW(tmp, szVersionFormat, HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS));
+ lstrcpynW(lpVersionBuf, tmp, *pcchVersionBuf);
+ *pcchVersionBuf = strlenW(lpVersionBuf);
+ }
+ else {
+ *lpVersionBuf = 0;
+ *pcchVersionBuf = 0;
+ }
+ }
+
+ if(lpLangBuf && pcchLangBuf && *pcchLangBuf) {
+ DWORD lang = GetUserDefaultLangID();
+ FIXME("Retrieve language from file\n");
+ wsprintfW(tmp, szLangFormat, lang);
+ lstrcpynW(lpLangBuf, tmp, *pcchLangBuf);
+ *pcchLangBuf = strlenW(lpLangBuf);
+ }
+
+end:
+ if(lpVer) HeapFree(GetProcessHeap(), 0, lpVer);
+ return ret;
+}
+
+
+/******************************************************************
+ * DllMain
+ *
+ * @todo: maybe we can check here if MsiServer service is declared no ?
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+ if (fdwReason == DLL_PROCESS_ATTACH) {
+ DisableThreadLibraryCalls(hinstDLL);
+ /*
+ * UI Initialization
+ */
+ gUILevel = INSTALLUILEVEL_BASIC;
+ gUIhwnd = 0;
+ gUIHandler = NULL;
+ gUIFilter = 0;
+ gUIContext = NULL;
+ gszLogFile[0]=0;
+ /* FIXME: Initialisation */
+ } else if (fdwReason == DLL_PROCESS_DETACH) {
+ /* FIXME: Cleanup */
+ }
+ /*
+ static const WCHAR szMSIServerSvc[] = { 'M','S','I','S','e','r','v','e','r',0 };
+ static const WCHAR szNull[] = { 0 };
+ if (!strcmpW(lpServiceName, szMSIServerSvc)) {
+ hKey = CreateServiceW(hSCManager,
+ szMSIServerSvc,
+ szMSIServerSvc,
+ SC_MANAGER_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_IGNORE,
+ szNull,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ szNull);
+ */
+ return TRUE;
+}
+
+typedef struct {
+ /* IUnknown fields */
+ IClassFactoryVtbl *lpVtbl;
+ DWORD ref;
+} IClassFactoryImpl;
+
+static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ FIXME("(%p, %s, %p): stub\n",This,debugstr_guid(riid),ppobj);
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface) {
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ return ++(This->ref);
+}
+
+static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface) {
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ /* static class, won't be freed */
+ return --(This->ref);
+}
+
+static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj) {
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ FIXME ("(%p, %p, %s, %p): to implement\n", This, pOuter, debugstr_guid(riid), ppobj);
+ return 0;
+}
+
+static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
+ IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+ FIXME("(%p, %d): stub\n", This, dolock);
+ return S_OK;
+}
+
+static IClassFactoryVtbl MsiCF_Vtbl = {
+ MsiCF_QueryInterface,
+ MsiCF_AddRef,
+ MsiCF_Release,
+ MsiCF_CreateInstance,
+ MsiCF_LockServer
+};
+
+static IClassFactoryImpl Msi_CF = {&MsiCF_Vtbl, 1 };
+
+/******************************************************************
+ * DllGetClassObject (MSI.@)
+ */
+HRESULT WINAPI MSI_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
+ FIXME("(%s, %s, %p): almost a stub.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+ if (IsEqualCLSID (rclsid, &CLSID_IMsiServer)) {
+ *ppv = (LPVOID) &Msi_CF;
+ IClassFactory_AddRef((IClassFactory*)*ppv);
+ return S_OK;
+ } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerMessage)) {
+ *ppv = (LPVOID) &Msi_CF;
+ IClassFactory_AddRef((IClassFactory*)*ppv);
+ return S_OK;
+ } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX1)) {
+ *ppv = (LPVOID) &Msi_CF;
+ IClassFactory_AddRef((IClassFactory*)*ppv);
+ return S_OK;
+ } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX2)) {
+ *ppv = (LPVOID) &Msi_CF;
+ IClassFactory_AddRef((IClassFactory*)*ppv);
+ return S_OK;
+ } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX3)) {
+ *ppv = (LPVOID) &Msi_CF;
+ IClassFactory_AddRef((IClassFactory*)*ppv);
+ return S_OK;
+ }
+ WARN("(%s, %s, %p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+ return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+/******************************************************************
+ * DllGetVersion (MSI.@)
+ */
+HRESULT WINAPI MSI_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 = 1;
+
+ return S_OK;
+}
+
+/******************************************************************
+ * DllCanUnloadNow (MSI.@)
+ */
+BOOL WINAPI MSI_DllCanUnloadNow(void)
+{
+ return S_FALSE;
+}
+
+UINT WINAPI MsiEnumRelatedProductsA (LPCSTR lpUpgradeCode, DWORD dwReserved,
+ DWORD iProductIndex, LPSTR lpProductBuf)
+{
+ FIXME("STUB: (%s, %li %li %s)\n",lpUpgradeCode, dwReserved, iProductIndex,
+ lpProductBuf);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
--- /dev/null
+1 stdcall -private DllCanUnloadNow() MSI_DllCanUnloadNow
+2 stdcall -private DllGetClassObject(ptr ptr ptr) MSI_DllGetClassObject
+3 stdcall -private DllRegisterServer() MSI_DllRegisterServer
+4 stdcall -private DllUnregisterServer() MSI_DllUnregisterServer
+5 stdcall MsiAdvertiseProductA(str str str long)
+6 stdcall MsiAdvertiseProductW(wstr wstr wstr long)
+7 stdcall MsiCloseAllHandles()
+8 stdcall MsiCloseHandle(long)
+9 stub MsiCollectUserInfoA
+10 stub MsiCollectUserInfoW
+11 stub MsiConfigureFeatureA
+12 stub MsiConfigureFeatureFromDescriptorA
+13 stub MsiConfigureFeatureFromDescriptorW
+14 stub MsiConfigureFeatureW
+15 stdcall MsiConfigureProductA(str long long)
+16 stdcall MsiConfigureProductW(wstr long long)
+17 stdcall MsiCreateRecord(long)
+18 stdcall MsiDatabaseApplyTransformA(long str long)
+19 stdcall MsiDatabaseApplyTransformW(long wstr long)
+20 stdcall MsiDatabaseCommit(long)
+21 stub MsiDatabaseExportA
+22 stub MsiDatabaseExportW
+23 stdcall MsiDatabaseGenerateTransformA(long long str long long)
+24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long)
+25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr)
+26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr)
+27 stdcall MsiDatabaseImportA(str str)
+28 stdcall MsiDatabaseImportW(wstr wstr)
+29 stub MsiDatabaseMergeA
+30 stub MsiDatabaseMergeW
+31 stdcall MsiDatabaseOpenViewA(long str ptr)
+32 stdcall MsiDatabaseOpenViewW(long wstr ptr)
+33 stdcall MsiDoActionA(long str)
+34 stdcall MsiDoActionW(long wstr)
+35 stub MsiEnableUIPreview
+36 stdcall MsiEnumClientsA(str long ptr)
+37 stdcall MsiEnumClientsW(wstr long ptr)
+38 stdcall MsiEnumComponentQualifiersA(str long str ptr str ptr)
+39 stdcall MsiEnumComponentQualifiersW(wstr long wstr ptr wstr ptr)
+40 stdcall MsiEnumComponentsA(long ptr)
+41 stdcall MsiEnumComponentsW(long ptr)
+42 stdcall MsiEnumFeaturesA(str long ptr ptr)
+43 stdcall MsiEnumFeaturesW(wstr long ptr ptr)
+44 stdcall MsiEnumProductsA(long ptr)
+45 stdcall MsiEnumProductsW(long ptr)
+46 stdcall MsiEvaluateConditionA(long str)
+47 stdcall MsiEvaluateConditionW(long wstr)
+48 stub MsiGetLastErrorRecord
+49 stdcall MsiGetActiveDatabase(long)
+50 stdcall MsiGetComponentStateA(long str ptr ptr)
+51 stdcall MsiGetComponentStateW(long wstr ptr ptr)
+52 stub MsiGetDatabaseState
+53 stub MsiGetFeatureCostA
+54 stub MsiGetFeatureCostW
+55 stub MsiGetFeatureInfoA
+56 stub MsiGetFeatureInfoW
+57 stdcall MsiGetFeatureStateA(long str ptr ptr)
+58 stdcall MsiGetFeatureStateW(long wstr ptr ptr)
+59 stub MsiGetFeatureUsageA
+60 stub MsiGetFeatureUsageW
+61 stub MsiGetFeatureValidStatesA
+62 stub MsiGetFeatureValidStatesW
+63 stub MsiGetLanguage
+64 stdcall MsiGetMode(long long)
+65 stdcall MsiGetProductCodeA(str str)
+66 stdcall MsiGetProductCodeW(wstr wstr)
+67 stdcall MsiGetProductInfoA(str str str long)
+68 stub MsiGetProductInfoFromScriptA
+69 stub MsiGetProductInfoFromScriptW
+70 stdcall MsiGetProductInfoW(wstr wstr wstr long)
+71 stdcall MsiGetProductPropertyA(long str ptr ptr)
+72 stdcall MsiGetProductPropertyW(long wstr ptr ptr)
+73 stdcall MsiGetPropertyA(ptr str str ptr)
+74 stdcall MsiGetPropertyW(ptr wstr wstr ptr)
+75 stdcall MsiGetSourcePathA(long str ptr ptr)
+76 stdcall MsiGetSourcePathW(long wstr ptr ptr)
+77 stdcall MsiGetSummaryInformationA(long str long ptr)
+78 stdcall MsiGetSummaryInformationW(long wstr long ptr)
+79 stdcall MsiGetTargetPathA(long str ptr ptr)
+80 stdcall MsiGetTargetPathW(long wstr ptr ptr)
+81 stub MsiGetUserInfoA
+82 stub MsiGetUserInfoW
+83 stub MsiInstallMissingComponentA
+84 stub MsiInstallMissingComponentW
+85 stub MsiInstallMissingFileA
+86 stub MsiInstallMissingFileW
+87 stdcall MsiInstallProductA(str str)
+88 stdcall MsiInstallProductW(wstr wstr)
+89 stdcall MsiLocateComponentA(str ptr long)
+90 stdcall MsiLocateComponentW(wstr ptr long)
+91 stdcall MsiOpenDatabaseA(str str ptr)
+92 stdcall MsiOpenDatabaseW(wstr wstr ptr)
+93 stdcall MsiOpenPackageA(str ptr)
+94 stdcall MsiOpenPackageW(wstr ptr)
+95 stdcall MsiOpenProductA(str ptr)
+96 stdcall MsiOpenProductW(wstr ptr)
+97 stub MsiPreviewBillboardA
+98 stub MsiPreviewBillboardW
+99 stub MsiPreviewDialogA
+100 stub MsiPreviewDialogW
+101 stub MsiProcessAdvertiseScriptA
+102 stub MsiProcessAdvertiseScriptW
+103 stdcall MsiProcessMessage(long long long)
+104 stub MsiProvideComponentA
+105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr)
+106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr)
+107 stub MsiProvideComponentW
+108 stub MsiProvideQualifiedComponentA
+109 stub MsiProvideQualifiedComponentW
+110 stdcall MsiQueryFeatureStateA(str str)
+111 stdcall MsiQueryFeatureStateW(wstr wstr)
+112 stdcall MsiQueryProductStateA(str)
+113 stdcall MsiQueryProductStateW(wstr)
+114 stdcall MsiRecordDataSize(long long)
+115 stdcall MsiRecordGetFieldCount(long)
+116 stdcall MsiRecordGetInteger(long long)
+117 stdcall MsiRecordGetStringA(long long ptr ptr)
+118 stdcall MsiRecordGetStringW(long long ptr ptr)
+119 stdcall MsiRecordIsNull(long long)
+120 stdcall MsiRecordReadStream(long long ptr ptr)
+121 stdcall MsiRecordSetInteger(long long long)
+122 stdcall MsiRecordSetStreamA(long long str)
+123 stdcall MsiRecordSetStreamW(long long wstr)
+124 stdcall MsiRecordSetStringA(long long str)
+125 stdcall MsiRecordSetStringW(long long wstr)
+126 stub MsiReinstallFeatureA
+127 stub MsiReinstallFeatureFromDescriptorA
+128 stub MsiReinstallFeatureFromDescriptorW
+129 stub MsiReinstallFeatureW
+130 stdcall MsiReinstallProductA(str long)
+131 stdcall MsiReinstallProductW(wstr long)
+132 stub MsiSequenceA
+133 stub MsiSequenceW
+134 stub MsiSetComponentStateA
+135 stub MsiSetComponentStateW
+136 stdcall MsiSetExternalUIA(ptr long ptr)
+137 stub MsiSetExternalUIW
+138 stdcall MsiSetFeatureStateA(long str long)
+139 stdcall MsiSetFeatureStateW(long wstr long)
+140 stub MsiSetInstallLevel
+141 stdcall MsiSetInternalUI(long ptr)
+142 stub MsiVerifyDiskSpace
+143 stub MsiSetMode
+144 stdcall MsiSetPropertyA(long str str)
+145 stdcall MsiSetPropertyW(long wstr wstr)
+146 stdcall MsiSetTargetPathA(long str str)
+147 stdcall MsiSetTargetPathW(long wstr wstr)
+148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr)
+149 stdcall MsiSummaryInfoGetPropertyCount(long ptr)
+150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr)
+151 stub MsiSummaryInfoPersist
+152 stub MsiSummaryInfoSetPropertyA
+153 stub MsiSummaryInfoSetPropertyW
+154 stub MsiUseFeatureA
+155 stub MsiUseFeatureW
+156 stdcall MsiVerifyPackageA(str)
+157 stdcall MsiVerifyPackageW(wstr)
+158 stdcall MsiViewClose(long)
+159 stdcall MsiViewExecute(long long)
+160 stdcall MsiViewFetch(long ptr)
+161 stub MsiViewGetErrorA
+162 stub MsiViewGetErrorW
+163 stdcall MsiViewModify(long long long)
+164 stdcall MsiDatabaseIsTablePersistentA(long str)
+165 stdcall MsiDatabaseIsTablePersistentW(long wstr)
+166 stdcall MsiViewGetColumnInfo(long long ptr)
+167 stdcall MsiRecordClearData(long)
+168 stdcall MsiEnableLogA(long str long)
+169 stdcall MsiEnableLogW(long wstr long)
+170 stdcall MsiFormatRecordA(long long ptr ptr)
+171 stdcall MsiFormatRecordW(long long ptr ptr)
+172 stdcall MsiGetComponentPathA(str str ptr ptr)
+173 stdcall MsiGetComponentPathW(wstr wstr ptr ptr)
+174 stdcall MsiApplyPatchA(str str long str)
+175 stdcall MsiApplyPatchW(wstr wstr long wstr)
+176 stub MsiAdvertiseScriptA
+177 stub MsiAdvertiseScriptW
+178 stub MsiGetPatchInfoA
+179 stub MsiGetPatchInfoW
+180 stub MsiEnumPatchesA
+181 stub MsiEnumPatchesW
+182 stdcall DllGetVersion(ptr) MSI_DllGetVersion
+183 stub MsiGetProductCodeFromPackageCodeA
+184 stub MsiGetProductCodeFromPackageCodeW
+185 stub MsiCreateTransformSummaryInfoA
+186 stub MsiCreateTransformSummaryInfoW
+187 stub MsiQueryFeatureStateFromDescriptorA
+188 stub MsiQueryFeatureStateFromDescriptorW
+189 stub MsiConfigureProductExA
+190 stub MsiConfigureProductExW
+191 stub MsiInvalidateFeatureCache
+192 stub MsiUseFeatureExA
+193 stub MsiUseFeatureExW
+194 stdcall MsiGetFileVersionA(str str ptr str ptr)
+195 stdcall MsiGetFileVersionW(wstr wstr ptr wstr ptr)
+196 stdcall MsiLoadStringA(long long long long long)
+197 stdcall MsiLoadStringW(long long long long long)
+198 stdcall MsiMessageBoxA(long long long long long long)
+199 stdcall MsiMessageBoxW(long long long long long long)
+200 stub MsiDecomposeDescriptorA
+201 stub MsiDecomposeDescriptorW
+202 stub MsiProvideQualifiedComponentExA
+203 stub MsiProvideQualifiedComponentExW
+204 stdcall MsiEnumRelatedProductsA(str long long str)
+205 stub MsiEnumRelatedProductsW
+206 stub MsiSetFeatureAttributesA
+207 stub MsiSetFeatureAttributesW
+208 stub MsiSourceListClearAllA
+209 stub MsiSourceListClearAllW
+210 stub MsiSourceListAddSourceA
+211 stub MsiSourceListAddSourceW
+212 stub MsiSourceListForceResolutionA
+213 stub MsiSourceListForceResolutionW
+214 stub MsiIsProductElevatedA
+215 stub MsiIsProductElevatedW
+216 stub MsiGetShortcutTargetA
+217 stub MsiGetShortcutTargetW
+218 stub MsiGetFileHashA
+219 stub MsiGetFileHashW
+220 stub MsiEnumComponentCostsA
+221 stub MsiEnumComponentCostsW
+222 stub MsiCreateAndVerifyInstallerDirectory
+223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr)
+224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr)
+225 stdcall MsiProvideAssemblyA(str str long long str ptr)
+226 stdcall MsiProvideAssemblyW(wstr wstr long long wstr ptr)
+227 stdcall MsiAdvertiseProductExA(str str str long long long)
+228 stdcall MsiAdvertiseProductExW(wstr wstr wstr long long long)
+229 stub MsiNotifySidChangeA
+230 stub MsiNotifySidChangeW
+231 stdcall MsiOpenPackageExA(str long ptr)
+232 stdcall MsiOpenPackageExW(wstr long ptr)
+233 stub MsiDeleteUserDataA
+234 stub MsiDeleteUserDataW
+235 stub Migrate10CachedPackagesA
+236 stub Migrate10CachedPackagesW
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_MSI_PRIVATE__
+#define __WINE_MSI_PRIVATE__
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+
+#define MSI_DATASIZEMASK 0x00ff
+#define MSITYPE_VALID 0x0100
+#define MSITYPE_STRING 0x0800
+#define MSITYPE_NULLABLE 0x1000
+#define MSITYPE_KEY 0x2000
+
+#define MSITYPE_BINARY 0x8900
+
+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;
+ UINT refcount;
+ msihandledestructor destructor;
+ struct tagMSIOBJECTHDR *next;
+ struct tagMSIOBJECTHDR *prev;
+};
+
+typedef struct tagMSIDATABASE
+{
+ MSIOBJECTHDR hdr;
+ IStorage *storage;
+ string_table *strings;
+ LPWSTR mode;
+ MSITABLE *first_table, *last_table;
+} MSIDATABASE;
+
+typedef struct tagMSIVIEW MSIVIEW;
+
+typedef struct tagMSIQUERY
+{
+ MSIOBJECTHDR hdr;
+ MSIVIEW *view;
+ UINT row;
+ MSIDATABASE *db;
+} MSIQUERY;
+
+/* maybe we can use a Variant instead of doing it ourselves? */
+typedef struct tagMSIFIELD
+{
+ UINT type;
+ union
+ {
+ INT iVal;
+ 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 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 *, UINT row, UINT col, UINT *val );
+
+ /*
+ * fetch_int - reads one integer 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 *, UINT row, UINT col, IStream **stm );
+
+ /*
+ * get_int - sets one integer at {row,col} in the table
+ *
+ * Similar semantics to fetch_int
+ */
+ UINT (*set_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT val );
+
+ /*
+ * Inserts a new, blank row into the database
+ * *row receives the number of the new row
+ */
+ UINT (*insert_row)( struct tagMSIVIEW *, UINT *row );
+
+ /*
+ * execute - loads the underlying data into memory so it can be read
+ */
+ UINT (*execute)( struct tagMSIVIEW *, MSIRECORD * );
+
+ /*
+ * close - clears the data read by execute from memory
+ */
+ UINT (*close)( struct tagMSIVIEW * );
+
+ /*
+ * 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 *, UINT *rows, UINT *cols );
+
+ /*
+ * get_column_info - returns the name and type of a specific column
+ *
+ * The name is HeapAlloc'ed by this function and should be freed by
+ * the caller.
+ * The column information can be queried at any time.
+ */
+ UINT (*get_column_info)( struct tagMSIVIEW *, UINT n, LPWSTR *name, UINT *type );
+
+ /*
+ * modify - not yet implemented properly
+ */
+ UINT (*modify)( struct tagMSIVIEW *, MSIMODIFY, MSIHANDLE );
+
+ /*
+ * delete - destroys the structure completely
+ */
+ UINT (*delete)( struct tagMSIVIEW * );
+
+} MSIVIEWOPS;
+
+typedef struct tagMSISUMMARYINFO
+{
+ MSIOBJECTHDR hdr;
+ IPropertyStorage *propstg;
+} MSISUMMARYINFO;
+
+struct tagMSIVIEW
+{
+ MSIOBJECTHDR hdr;
+ MSIVIEWOPS *ops;
+};
+
+typedef struct tagMSIPACKAGE
+{
+ MSIOBJECTHDR hdr;
+ MSIDATABASE *db;
+ struct tagMSIFEATURE *features;
+ UINT loaded_features;
+ struct tagMSIFOLDER *folders;
+ UINT loaded_folders;
+ struct tagMSICOMPONENT *components;
+ UINT loaded_components;
+ struct tagMSIFILE *files;
+ UINT loaded_files;
+} MSIPACKAGE;
+
+#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 MSI_MAJORVERSION 2
+#define MSI_MINORVERSION 0
+#define MSI_BUILDNUMBER 2600
+
+#define GUID_SIZE 39
+
+#define MSIHANDLE_MAGIC 0x4d434923
+#define MSIMAXHANDLES 0x80
+
+#define MSISUMINFO_OFFSET 0x30LL
+
+DEFINE_GUID(CLSID_IMsiServer, 0x000C101C,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX1, 0x000C103E,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX2, 0x000C1090,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX3, 0x000C1094,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+DEFINE_GUID(CLSID_IMsiServerMessage, 0x000C101D,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+
+/* handle functions */
+extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
+extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * );
+extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy );
+extern void msiobj_addref(MSIOBJECTHDR *);
+extern int msiobj_release(MSIOBJECTHDR *);
+extern MSIHANDLE msiobj_findhandle( MSIOBJECTHDR *hdr );
+
+/* add this table to the list of cached tables in the database */
+extern void add_table(MSIDATABASE *db, MSITABLE *table);
+extern void remove_table( MSIDATABASE *db, MSITABLE *table );
+extern void free_table( MSIDATABASE *db, MSITABLE *table );
+extern void free_cached_tables( MSIDATABASE *db );
+extern UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table);
+extern UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table);
+extern UINT load_string_table( MSIDATABASE *db );
+extern UINT MSI_CommitTables( MSIDATABASE *db );
+extern HRESULT init_string_table( IStorage *stg );
+
+
+/* string table functions */
+extern BOOL msi_addstring( string_table *st, int string_no, const CHAR *data, int len, UINT refcount );
+extern BOOL msi_addstringW( string_table *st, int string_no, const WCHAR *data, int len, UINT refcount );
+extern UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz );
+extern UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz );
+
+extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid);
+extern UINT msi_string2idW( string_table *st, LPCWSTR buffer, UINT *id );
+extern UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id );
+extern string_table *msi_init_stringtable( int entries, UINT codepage );
+extern VOID msi_destroy_stringtable( string_table *st );
+extern UINT msi_string_count( string_table *st );
+extern UINT msi_id_refcount( string_table *st, UINT i );
+extern UINT msi_string_totalsize( string_table *st, UINT *last );
+extern UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res );
+extern const WCHAR *msi_string_lookup_id( string_table *st, UINT id );
+extern UINT msi_string_get_codepage( string_table *st );
+
+
+extern UINT VIEW_find_column( MSIVIEW *view, LPWSTR name, UINT *n );
+
+extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name );
+
+extern UINT read_raw_stream_data( MSIDATABASE*, LPCWSTR stname,
+ USHORT **pdata, UINT *psz );
+extern UINT ACTION_DoTopLevelINSTALL( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern void ACTION_remove_tracked_tempfiles( MSIPACKAGE* );
+
+/* record internals */
+extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *);
+extern const WCHAR *MSI_RecordGetString( MSIRECORD *, unsigned int );
+extern MSIRECORD *MSI_CreateRecord( unsigned int );
+extern UINT MSI_RecordSetInteger( MSIRECORD *, unsigned int, int );
+extern UINT MSI_RecordSetStringW( MSIRECORD *, unsigned int, LPCWSTR );
+extern BOOL MSI_RecordIsNull( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordGetStringW( MSIRECORD * , unsigned int, LPWSTR, DWORD *);
+extern UINT MSI_RecordGetStringA( MSIRECORD *, unsigned int, LPSTR, DWORD *);
+extern int MSI_RecordGetInteger( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordReadStream( MSIRECORD *, unsigned int, char *, DWORD *);
+extern unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec );
+
+/* stream internals */
+extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm );
+extern UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm );
+extern void enum_stream_names( IStorage *stg );
+
+/* database internals */
+extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** );
+extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** );
+
+/* view internals */
+extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * );
+extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** );
+extern UINT MSI_ViewClose( MSIQUERY* );
+
+/* package internals */
+extern UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE ** );
+extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR);
+extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD* );
+extern UINT MSI_GetPropertyW( MSIPACKAGE *, LPCWSTR, LPWSTR, DWORD*);
+extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR );
+extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern UINT MSI_GetComponentStateW(MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE *);
+extern UINT MSI_GetFeatureStateW(MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE *);
+
+/* registry data encoding/decoding functions */
+BOOL unsquash_guid(LPCWSTR in, LPWSTR out);
+BOOL squash_guid(LPCWSTR in, LPWSTR out);
+BOOL encode_base85_guid(GUID *,LPWSTR);
+BOOL decode_base85_guid(LPCWSTR,GUID*);
+
+/* UI globals */
+extern INSTALLUILEVEL gUILevel;
+extern HWND gUIhwnd;
+extern INSTALLUI_HANDLERA gUIHandler;
+extern DWORD gUIFilter;
+extern LPVOID gUIContext;
+extern WCHAR gszLogFile[MAX_PATH];
+
+#endif /* __WINE_MSI_PRIVATE__ */
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+#if 0
+typedef struct tagMSIQUERY
+{
+ MSIOBJECTHDR hdr;
+ MSIVIEW *view;
+ UINT row;
+ MSIDATABASE *db;
+} MSIQUERY;
+#endif
+
+UINT WINAPI MsiDatabaseIsTablePersistentA(
+ MSIHANDLE hDatabase, LPSTR szTableName)
+{
+ FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentW(
+ MSIHANDLE hDatabase, LPWSTR szTableName)
+{
+ FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+void MSI_CloseView( MSIOBJECTHDR *arg )
+{
+ MSIQUERY *query = (MSIQUERY*) arg;
+
+ if( query->view && query->view->ops->delete )
+ query->view->ops->delete( query->view );
+ msiobj_release( &query->db->hdr );
+}
+
+UINT VIEW_find_column( MSIVIEW *table, LPWSTR name, UINT *n )
+{
+ LPWSTR col_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;
+
+ col_name = NULL;
+ r = table->ops->get_column_info( table, i, &col_name, NULL );
+ if( r != ERROR_SUCCESS )
+ return r;
+ x = lstrcmpW( name, col_name );
+ HeapFree( GetProcessHeap(), 0, col_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("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
+
+ if( szQuery )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szQuery, -1, NULL, 0 );
+ szwQuery = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwQuery )
+ return ERROR_FUNCTION_FAILED;
+ MultiByteToWideChar( CP_ACP, 0, szQuery, -1, szwQuery, len );
+ }
+ else
+ szwQuery = NULL;
+
+ r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
+
+ 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->row = 0;
+ query->db = db;
+ query->view = NULL;
+
+ r = MSI_ParseSQL( db, szQuery, &query->view );
+ if( r == ERROR_SUCCESS )
+ {
+ msiobj_addref( &query->hdr );
+ *pView = query;
+ }
+
+ msiobj_release( &query->hdr );
+ return r;
+}
+
+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 )
+ return ERROR_INVALID_HANDLE;
+
+ ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
+ if( ret == ERROR_SUCCESS )
+ {
+ *phView = alloc_msihandle( &query->hdr );
+ msiobj_release( &query->hdr );
+ }
+ msiobj_release( &db->hdr );
+
+ return ret;
+}
+
+UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
+{
+ MSIVIEW *view;
+ MSIRECORD *rec;
+ UINT row_count = 0, col_count = 0, i, ival, ret, type;
+
+ TRACE("%p %p\n", query, prec );
+
+ view = query->view;
+ if( !view )
+ return ERROR_FUNCTION_FAILED;
+
+ ret = view->ops->get_dimensions( view, &row_count, &col_count );
+ if( ret )
+ return ret;
+ if( !col_count )
+ return ERROR_INVALID_PARAMETER;
+
+ if( query->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 );
+ if( ret )
+ {
+ ERR("Error getting column type for %d\n", i );
+ continue;
+ }
+ if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
+ MSITYPE_NULLABLE)))
+ {
+ ret = view->ops->fetch_int( view, query->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 )
+ {
+ LPWSTR sval;
+
+ sval = MSI_makestring( query->db, ival );
+ MSI_RecordSetStringW( rec, i, sval );
+ HeapFree( GetProcessHeap(), 0, sval );
+ }
+ else
+ {
+ if( (type & MSI_DATASIZEMASK) == 2 )
+ MSI_RecordSetInteger( rec, i, ival - (1<<15) );
+ else
+ MSI_RecordSetInteger( rec, i, ival - (1<<31) );
+ }
+ }
+ else
+ {
+ IStream *stm = NULL;
+
+ ret = view->ops->fetch_stream( view, query->row, i, &stm );
+ if( ( ret == ERROR_SUCCESS ) && stm )
+ {
+ MSI_RecordSetIStream( rec, i, stm );
+ IStream_Release( stm );
+ }
+ else
+ ERR("failed to get stream\n");
+ }
+ }
+ query->row ++;
+
+ *prec = rec;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
+{
+ MSIQUERY *query;
+ MSIRECORD *rec = NULL;
+ UINT ret;
+
+ TRACE("%ld %p\n", hView, record);
+
+ 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 );
+ 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("%ld\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("%ld %ld\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;
+ }
+ }
+
+ ret = MSI_ViewExecute( query, rec );
+out:
+ if( query )
+ msiobj_release( &query->hdr );
+ if( rec )
+ msiobj_release( &rec->hdr );
+
+ return ret;
+}
+
+UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
+{
+ MSIVIEW *view;
+ MSIQUERY *query;
+ MSIHANDLE handle;
+ UINT ret, i, count = 0, type;
+ LPWSTR name;
+
+ TRACE("%ld %d %p\n", hView, info, hRec);
+
+ query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+ if( !query )
+ return ERROR_INVALID_HANDLE;
+
+ view = query->view;
+ if( !view )
+ return ERROR_FUNCTION_FAILED;
+
+ if( !view->ops->get_dimensions )
+ return ERROR_FUNCTION_FAILED;
+
+ ret = view->ops->get_dimensions( view, NULL, &count );
+ if( ret )
+ return ret;
+ if( !count )
+ return ERROR_INVALID_PARAMETER;
+
+ handle = MsiCreateRecord( count );
+ if( !handle )
+ return ERROR_FUNCTION_FAILED;
+
+ for( i=0; i<count; i++ )
+ {
+ name = NULL;
+ ret = view->ops->get_column_info( view, i+1, &name, &type );
+ if( ret != ERROR_SUCCESS )
+ continue;
+ MsiRecordSetStringW( handle, i+1, name );
+ HeapFree( GetProcessHeap(), 0, name );
+ }
+
+ *hRec = handle;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
+ LPCSTR szTransformFile, int iErrorCond)
+{
+ FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
+ LPCWSTR szTransformFile, int iErrorCond)
+{
+ FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
+ LPCSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+ FIXME("%ld %ld %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("%ld %ld %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("%ld\n", hdb);
+
+ db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+ if( !db )
+ return ERROR_INVALID_HANDLE;
+
+ /* FIXME: lock the database */
+
+ r = MSI_CommitTables( db );
+
+ /* FIXME: unlock the database */
+
+ return r;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
+ LPCSTR table, MSIHANDLE* rec)
+{
+ FIXME("%ld %s %p\n", hdb, debugstr_a(table), rec);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE hdb,
+ LPCWSTR table, MSIHANDLE* rec)
+{
+ FIXME("%ld %s %p\n", hdb, debugstr_w(table), rec);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiViewModify(MSIHANDLE hView, MSIMODIFY eModifyMode, MSIHANDLE
+hRecord)
+{
+ FIXME("%ld %x %ld\n",hView, eModifyMode, hRecord);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIORDERVIEW
+{
+ MSIVIEW view;
+ MSIDATABASE *db;
+ MSIVIEW *table;
+ UINT *reorder;
+ UINT num_cols;
+ UINT cols[1];
+} MSIORDERVIEW;
+
+static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
+{
+ UINT r, i, a_val = 0, b_val = 0;
+
+ *swap = 0;
+ for( i=0; i<ov->num_cols; i++ )
+ {
+ r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ if( a_val != b_val )
+ {
+ if( a_val > b_val )
+ *swap = 1;
+ break;
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
+{
+ UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
+ UINT *array = ov->reorder;
+
+ if( left == right )
+ return ERROR_SUCCESS;
+
+ /* sort the left half */
+ r = ORDER_mergesort( ov, left, centre );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ /* sort the right half */
+ r = ORDER_mergesort( ov, centre+1, right );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
+ {
+ r = ORDER_compare( ov, array[i], array[j], &swap );
+ if( r != ERROR_SUCCESS )
+ return r;
+ if( swap )
+ {
+ temp = array[j];
+ memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
+ array[i] = temp;
+ j++;
+ centre++;
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
+{
+ UINT i, swap, r;
+
+ for( i=1; i<num_rows; i++ )
+ {
+ r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
+ if( r != ERROR_SUCCESS )
+ return r;
+ if( !swap )
+ continue;
+ ERR("Bad order! %d\n", i);
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p %d %d %p\n", ov, row, col, val );
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ row = ov->reorder[ row ];
+
+ return ov->table->ops->fetch_int( ov->table, row, col, val );
+}
+
+static UINT ORDER_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+ UINT r, num_rows = 0, i;
+
+ TRACE("%p %p\n", ov, record);
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ r = ov->table->ops->execute( ov->table, record );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ ov->reorder = HeapAlloc( GetProcessHeap(), 0, num_rows*sizeof(UINT) );
+ if( !ov->reorder )
+ return ERROR_FUNCTION_FAILED;
+
+ for( i=0; i<num_rows; i++ )
+ ov->reorder[i] = i;
+
+ r = ORDER_mergesort( ov, 0, num_rows - 1 );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = ORDER_verify( ov, num_rows );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ return ERROR_SUCCESS;
+}
+
+static UINT ORDER_close( struct tagMSIVIEW *view )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p\n", ov );
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( ov->reorder )
+ HeapFree( GetProcessHeap(), 0, ov->reorder );
+ ov->reorder = NULL;
+
+ return ov->table->ops->close( ov->table );
+}
+
+static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p %p %p\n", ov, rows, cols );
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return ov->table->ops->get_dimensions( ov->table, rows, cols );
+}
+
+static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
+ UINT n, LPWSTR *name, UINT *type )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p %d %p %p\n", ov, n, name, type );
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return ov->table->ops->get_column_info( ov->table, n, name, type );
+}
+
+static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p %d %ld\n", ov, eModifyMode, hrec );
+
+ if( !ov->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return ov->table->ops->modify( ov->table, eModifyMode, hrec );
+}
+
+static UINT ORDER_delete( struct tagMSIVIEW *view )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+ TRACE("%p\n", ov );
+
+ if( ov->table )
+ ov->table->ops->delete( ov->table );
+
+ if( ov->reorder )
+ HeapFree( GetProcessHeap(), 0, ov->reorder );
+ ov->reorder = NULL;
+
+ msiobj_release( &ov->db->hdr );
+ HeapFree( GetProcessHeap(), 0, ov );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS order_ops =
+{
+ ORDER_fetch_int,
+ NULL,
+ NULL,
+ NULL,
+ ORDER_execute,
+ ORDER_close,
+ ORDER_get_dimensions,
+ ORDER_get_column_info,
+ ORDER_modify,
+ ORDER_delete
+};
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+ MSIORDERVIEW *ov = NULL;
+ UINT count = 0, r;
+
+ TRACE("%p\n", ov );
+
+ r = table->ops->get_dimensions( table, NULL, &count );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("can't get table dimensions\n");
+ return r;
+ }
+
+ ov = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof *ov + sizeof (UINT) * count );
+ if( !ov )
+ return ERROR_FUNCTION_FAILED;
+
+ /* fill the structure */
+ ov->view.ops = &order_ops;
+ msiobj_addref( &db->hdr );
+ ov->db = db;
+ ov->table = table;
+ ov->reorder = NULL;
+ ov->num_cols = 0;
+ *view = (MSIVIEW*) ov;
+
+ return ERROR_SUCCESS;
+}
+
+UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name )
+{
+ MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+ UINT n, count, r;
+ MSIVIEW *table;
+
+ TRACE("%p adding %s\n", ov, debugstr_w( name ) );
+
+ if( ov->view.ops != &order_ops )
+ return ERROR_FUNCTION_FAILED;
+
+ table = ov->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;
+
+ r = table->ops->get_dimensions( table, NULL, &count );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ if( ov->num_cols >= count )
+ return ERROR_FUNCTION_FAILED;
+
+ r = VIEW_find_column( table, name, &n );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ ov->cols[ov->num_cols] = n;
+ TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
+
+ ov->num_cols++;
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NONAMELESSUNION
+
+#include <stdarg.h>
+#include <stdio.h>
+#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 "objidl.h"
+#include "wincrypt.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "objbase.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR,
+ * which is a problem because LPCTSTR isn't defined when compiling wine.
+ * To work around this problem, we need to define LPCTSTR as LPCWSTR here,
+ * and make sure to only use it in W functions.
+ */
+#define LPCTSTR LPCWSTR
+
+void MSI_FreePackage( MSIOBJECTHDR *arg)
+{
+ MSIPACKAGE *package= (MSIPACKAGE*) arg;
+
+ ACTION_remove_tracked_tempfiles(package);
+
+ if (package->features && package->loaded_features > 0)
+ HeapFree(GetProcessHeap(),0,package->features);
+
+ if (package->folders && package->loaded_folders > 0)
+ HeapFree(GetProcessHeap(),0,package->folders);
+
+ if (package->components && package->loaded_components > 0)
+ HeapFree(GetProcessHeap(),0,package->components);
+
+ if (package->files && package->loaded_files > 0)
+ HeapFree(GetProcessHeap(),0,package->files);
+ msiobj_release( &package->db->hdr );
+}
+
+UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
+{
+ LPWSTR szwPack = NULL;
+ UINT len, ret;
+
+ TRACE("%s %p\n",debugstr_a(szPackage), phPackage);
+
+ if( szPackage )
+ {
+ len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 );
+ szwPack = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+ if( szwPack )
+ MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szwPack, len );
+ }
+
+ ret = MsiOpenPackageW( szwPack, phPackage );
+
+ if( szwPack )
+ HeapFree( GetProcessHeap(), 0, szwPack );
+
+ return ret;
+}
+
+
+static const UINT clone_properties(MSIDATABASE *db)
+{
+ MSIQUERY * view = NULL;
+ UINT rc;
+ static const WCHAR CreateSql[] = {
+ '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',',',' ','`','V','a','l','u','e','`',' ','C','H','A','R','(','9',
+ '8',')',' ','N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R',
+ 'Y',' ','K','E','Y',' ','`','_','P','r','o','p','e','r','t','y','`',')',0};
+ static const WCHAR Query[] = {
+ 'S','E','L','E','C','T',' ','*',' ',
+ 'f','r','o','m',' ','P','r','o','p','e','r','t','y',0};
+ static const WCHAR 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};
+
+ /* create the temporary properties table */
+ rc = MSI_DatabaseOpenViewW(db, CreateSql, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+ rc = MSI_ViewExecute(view,0);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ /* clone the existing properties */
+ rc = MSI_DatabaseOpenViewW(db, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MSI_ViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ return rc;
+ }
+ while (1)
+ {
+ MSIRECORD * row;
+ MSIQUERY * view2;
+
+ rc = MSI_ViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ break;
+
+ rc = MSI_DatabaseOpenViewW(db,Insert,&view2);
+ if (rc!= ERROR_SUCCESS)
+ continue;
+ rc = MSI_ViewExecute(view2,row);
+ MSI_ViewClose(view2);
+ msiobj_release(&view2->hdr);
+
+ if (rc == ERROR_SUCCESS)
+ msiobj_release(&row->hdr);
+ }
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ return rc;
+}
+
+/*
+ * There are a whole slew of these we need to set
+ *
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
+ */
+static VOID set_installer_properties(MSIPACKAGE *package)
+{
+ WCHAR pth[MAX_PATH];
+ OSVERSIONINFOA OSVersion;
+ DWORD verval;
+ WCHAR verstr[10], msiver[10];
+
+ static const WCHAR cszbs[]={'\\',0};
+ static const WCHAR CFF[] =
+{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
+ static const WCHAR PFF[] =
+{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
+ static const WCHAR CADF[] =
+{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR FaF[] =
+{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
+ static const WCHAR FoF[] =
+{'F','o','n','t','s','F','o','l','d','e','r',0};
+ static const WCHAR SendTF[] =
+{'S','e','n','d','T','o','F','o','l','d','e','r',0};
+ static const WCHAR SMF[] =
+{'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0};
+ static const WCHAR StF[] =
+{'S','t','a','r','t','u','p','F','o','l','d','e','r',0};
+ static const WCHAR TemplF[] =
+{'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0};
+ static const WCHAR DF[] =
+{'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
+ static const WCHAR PMF[] =
+{'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0};
+ static const WCHAR ATF[] =
+{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
+ static const WCHAR ADF[] =
+{'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR SF[] =
+{'S','y','s','t','e','m','F','o','l','d','e','r',0};
+ static const WCHAR LADF[] =
+{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR MPF[] =
+{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
+ static const WCHAR PF[] =
+{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
+ static const WCHAR WF[] =
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+ static const WCHAR TF[]=
+{'T','e','m','p','F','o','l','d','e','r',0};
+ static const WCHAR szAdminUser[] =
+{'A','d','m','i','n','U','s','e','r',0};
+ static const WCHAR szPriv[] =
+{'P','r','i','v','i','l','e','g','e','d',0};
+ static const WCHAR szOne[] =
+{'1',0};
+ static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 };
+ static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 };
+ static const WCHAR szFormat[] = {'%','l','i',0};
+ static const WCHAR szWinBuild[] =
+{'W','i','n','d','o','w','s','B','u','i','l','d', 0 };
+ static const WCHAR szSPL[] =
+{'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 };
+ static const WCHAR szSix[] = {'6',0 };
+
+ static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 };
+ static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0};
+
+/*
+ * Other things I notice set
+ *
+ScreenY
+ScreenX
+SystemLanguageID
+ComputerName
+UserLanguageID
+LogonUser
+VirtualMemory
+PhysicalMemory
+Intel
+ShellAdvSupport
+DefaultUIFont
+VersionDatabase
+PackagecodeChanging
+ProductState
+CaptionHeight
+BorderTop
+BorderSide
+TextHeight
+ColorBits
+RedirectedDllSupport
+Time
+Date
+Privileged
+*/
+
+ SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, CFF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, PFF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, CADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, FaF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, FoF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, SendTF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, SMF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, StF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, TemplF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, DF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, PMF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, ATF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, ADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, SF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, LADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, MPF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, PF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ MSI_SetPropertyW(package, WF, pth);
+
+ GetTempPathW(MAX_PATH,pth);
+ MSI_SetPropertyW(package, TF, pth);
+
+
+ /* in a wine environment the user is always admin and privileged */
+ MSI_SetPropertyW(package,szAdminUser,szOne);
+ MSI_SetPropertyW(package,szPriv,szOne);
+
+ /* set the os things */
+ OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+ GetVersionExA(&OSVersion);
+ verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
+ sprintfW(verstr,szFormat,verval);
+ switch (OSVersion.dwPlatformId)
+ {
+ case VER_PLATFORM_WIN32_WINDOWS:
+ MSI_SetPropertyW(package,v9x,verstr);
+ break;
+ case VER_PLATFORM_WIN32_NT:
+ MSI_SetPropertyW(package,vNT,verstr);
+ break;
+ }
+ sprintfW(verstr,szFormat,OSVersion.dwBuildNumber);
+ MSI_SetPropertyW(package,szWinBuild,verstr);
+ /* just fudge this */
+ MSI_SetPropertyW(package,szSPL,szSix);
+
+ sprintfW( msiver, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION);
+ MSI_SetPropertyW( package, szVersionMsi, msiver );
+}
+
+UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage)
+{
+ UINT rc;
+ MSIDATABASE *db = NULL;
+ MSIPACKAGE *package = NULL;
+ WCHAR uilevel[10];
+ UINT ret = ERROR_FUNCTION_FAILED;
+
+ static const WCHAR OriginalDatabase[] =
+{'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
+ static const WCHAR Database[] =
+{'D','A','T','A','B','A','S','E',0};
+ static const WCHAR szpi[] = {'%','i',0};
+ static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 };
+
+ TRACE("%s %p\n",debugstr_w(szPackage), pPackage);
+
+ if (szPackage[0] == '#')
+ {
+ INT handle = atoiW(&szPackage[1]);
+ db = msihandle2msiinfo( handle , MSIHANDLETYPE_DATABASE);
+ }
+ else
+ {
+ rc = MSI_OpenDatabaseW(szPackage, MSIDBOPEN_READONLY, &db);
+ if (rc != ERROR_SUCCESS)
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
+ MSI_FreePackage );
+
+ if (package)
+ {
+ msiobj_addref( &db->hdr );
+
+ package->db = db;
+ package->features = NULL;
+ package->folders = NULL;
+ package->components = NULL;
+ package->files = NULL;
+ package->loaded_features = 0;
+ package->loaded_folders = 0;
+ package->loaded_components= 0;
+ package->loaded_files = 0;
+
+ /* OK, here is where we do a slew of things to the database to
+ * prep for all that is to come as a package */
+
+ clone_properties(db);
+ set_installer_properties(package);
+ MSI_SetPropertyW(package, OriginalDatabase, szPackage);
+ MSI_SetPropertyW(package, Database, szPackage);
+ sprintfW(uilevel,szpi,gUILevel);
+ MSI_SetPropertyW(package, szLevel, uilevel);
+
+ msiobj_addref( &package->hdr );
+ *pPackage = package;
+ ret = ERROR_SUCCESS;
+ }
+
+ if( package )
+ msiobj_release( &package->hdr );
+ if( db )
+ msiobj_release( &db->hdr );
+
+ return ret;
+}
+
+UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
+{
+ MSIPACKAGE *package = NULL;
+ UINT ret;
+
+ ret = MSI_OpenPackageW( szPackage, &package);
+ if( ret == ERROR_SUCCESS )
+ {
+ *phPackage = alloc_msihandle( &package->hdr );
+ msiobj_release( &package->hdr );
+ }
+ return ret;
+}
+
+UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+ FIXME("%s 0x%08lx %p\n",debugstr_a(szPackage), dwOptions, phPackage);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+ FIXME("%s 0x%08lx %p\n",debugstr_w(szPackage), dwOptions, phPackage);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
+{
+ MSIPACKAGE *package;
+ MSIHANDLE handle = 0;
+
+ TRACE("(%ld)\n",hInstall);
+
+ package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+ if( package)
+ {
+ handle = alloc_msihandle( &package->db->hdr );
+ msiobj_release( &package->hdr );
+ }
+
+ return handle;
+}
+
+INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType,
+ MSIRECORD *record)
+{
+ DWORD log_type = 0;
+ LPWSTR message;
+ DWORD sz;
+ DWORD total_size = 0;
+ INT msg_field=1;
+ INT i;
+ INT rc;
+ char *msg;
+ int len;
+
+ TRACE("%x \n",eMessageType);
+ rc = 0;
+
+ 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_COMMONDATA)
+ log_type |= INSTALLLOGMODE_COMMONDATA;
+ if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
+ log_type |= INSTALLLOGMODE_ACTIONSTART;
+ if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA)
+ log_type |= INSTALLLOGMODE_ACTIONDATA;
+ /* just a guess */
+ if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS)
+ log_type |= 0x800;
+
+ message = HeapAlloc(GetProcessHeap(),0,1*sizeof (WCHAR));
+ message[0]=0;
+ msg_field = MSI_RecordGetFieldCount(record);
+ for (i = 1; i <= msg_field; i++)
+ {
+ LPWSTR tmp;
+ WCHAR number[3];
+ const static WCHAR format[] = { '%','i',':',' ',0};
+ const static WCHAR space[] = { ' ',0};
+ sz = 0;
+ MSI_RecordGetStringW(record,i,NULL,&sz);
+ sz+=4;
+ total_size+=sz*sizeof(WCHAR);
+ tmp = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
+ message = HeapReAlloc(GetProcessHeap(),0,message,total_size*sizeof (WCHAR));
+
+ MSI_RecordGetStringW(record,i,tmp,&sz);
+
+ if (msg_field > 1)
+ {
+ sprintfW(number,format,i);
+ strcatW(message,number);
+ }
+ strcatW(message,tmp);
+ if (msg_field > 1)
+ strcatW(message,space);
+
+ HeapFree(GetProcessHeap(),0,tmp);
+ }
+
+ TRACE("(%p %lx %lx %s)\n",gUIHandler, gUIFilter, log_type,
+ debugstr_w(message));
+
+ /* convert it to ASCII */
+ len = WideCharToMultiByte( CP_ACP, 0, message, -1,
+ NULL, 0, NULL, NULL );
+ msg = HeapAlloc( GetProcessHeap(), 0, len );
+ WideCharToMultiByte( CP_ACP, 0, message, -1,
+ msg, len, NULL, NULL );
+
+ if (gUIHandler && (gUIFilter & log_type))
+ {
+ rc = gUIHandler(gUIContext,eMessageType,msg);
+ }
+
+ if ((!rc) && (gszLogFile[0]) && !((eMessageType & 0xff000000) ==
+ INSTALLMESSAGE_PROGRESS))
+ {
+ DWORD write;
+ HANDLE log_file = CreateFileW(gszLogFile,GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (log_file != INVALID_HANDLE_VALUE)
+ {
+ SetFilePointer(log_file,0, NULL, FILE_END);
+ WriteFile(log_file,msg,strlen(msg),&write,NULL);
+ WriteFile(log_file,"\n",1,&write,NULL);
+ CloseHandle(log_file);
+ }
+ }
+ HeapFree( GetProcessHeap(), 0, msg );
+
+ HeapFree(GetProcessHeap(),0,message);
+ 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 )
+ goto out;
+
+ record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+ if( !record )
+ goto out;
+
+ ret = MSI_ProcessMessage( package, eMessageType, record );
+
+out:
+ if( package )
+ 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 hr = ERROR_INSTALL_FAILURE;
+ UINT len;
+
+ if (0 == hInstall) {
+ return ERROR_INVALID_HANDLE;
+ }
+ if (NULL == szName) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if (NULL == szValue) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
+ szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwName )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
+
+ len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
+ szwValue = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwValue)
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szValue , -1, szwValue, len );
+
+ hr = MsiSetPropertyW( hInstall, szwName, szwValue);
+
+end:
+ if( szwName )
+ HeapFree( GetProcessHeap(), 0, szwName );
+ if( szwValue )
+ HeapFree( GetProcessHeap(), 0, szwValue );
+
+ return hr;
+}
+
+UINT MSI_SetPropertyW( MSIPACKAGE *package, LPCWSTR szName, LPCWSTR szValue)
+{
+ MSIQUERY *view;
+ MSIRECORD *row;
+ UINT rc;
+ DWORD sz = 0;
+ static const WCHAR 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 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','`',' ','=',' ','\'','%','s','\'',0};
+ WCHAR Query[1024];
+
+ TRACE("Setting property (%s %s)\n",debugstr_w(szName),
+ debugstr_w(szValue));
+
+ rc = MSI_GetPropertyW(package,szName,0,&sz);
+ if (rc==ERROR_MORE_DATA || rc == ERROR_SUCCESS)
+ {
+ sprintfW(Query,Update,szName);
+
+ row = MSI_CreateRecord(1);
+ MSI_RecordSetStringW(row,1,szValue);
+
+ }
+ else
+ {
+ strcpyW(Query,Insert);
+
+ row = MSI_CreateRecord(2);
+ MSI_RecordSetStringW(row,1,szName);
+ MSI_RecordSetStringW(row,2,szValue);
+ }
+
+
+ rc = MSI_DatabaseOpenViewW(package->db,Query,&view);
+ if (rc!= ERROR_SUCCESS)
+ {
+ msiobj_release(&row->hdr);
+ return rc;
+ }
+
+ rc = MSI_ViewExecute(view,row);
+
+ msiobj_release(&row->hdr);
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+
+ return rc;
+}
+
+UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
+{
+ MSIPACKAGE *package;
+ UINT ret;
+
+ package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+ if( !package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_SetPropertyW( package, szName, szValue);
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+static UINT MSI_GetPropertyRow(MSIPACKAGE *package, LPCWSTR szName, MSIRECORD **row)
+{
+ MSIQUERY *view;
+ UINT rc, sz;
+ static const WCHAR select[]=
+ {'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','=','`','%','s','`',0};
+ LPWSTR query;
+
+ if (!szName)
+ return ERROR_INVALID_PARAMETER;
+
+ sz = sizeof select + strlenW(szName)*sizeof(WCHAR);
+ query = HeapAlloc(GetProcessHeap(), 0, sz);
+ sprintfW(query,select,szName);
+
+ rc = MSI_DatabaseOpenViewW(package->db, query, &view);
+ HeapFree(GetProcessHeap(), 0, query);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_ViewExecute(view, 0);
+ if (rc == ERROR_SUCCESS)
+ rc = MSI_ViewFetch(view,row);
+
+ MSI_ViewClose(view);
+ msiobj_release(&view->hdr);
+ }
+
+ return rc;
+}
+
+UINT MSI_GetPropertyW(MSIPACKAGE *package, LPCWSTR szName,
+ LPWSTR szValueBuf, DWORD* pchValueBuf)
+{
+ MSIRECORD *row;
+ UINT rc;
+
+ rc = MSI_GetPropertyRow(package, szName, &row);
+ if (rc == ERROR_SUCCESS)
+ {
+ 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
+ {
+ *pchValueBuf = 0;
+ TRACE("property not found\n");
+ }
+
+ return rc;
+}
+
+UINT MSI_GetPropertyA(MSIPACKAGE *package, LPCSTR szName,
+ LPSTR szValueBuf, DWORD* pchValueBuf)
+{
+ MSIRECORD *row;
+ UINT rc, len;
+ LPWSTR szwName;
+
+ len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
+ szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if (!szwName)
+ return ERROR_NOT_ENOUGH_MEMORY;
+ MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
+
+ rc = MSI_GetPropertyRow(package, szwName, &row);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MSI_RecordGetStringA(row,1,szValueBuf,pchValueBuf);
+ msiobj_release(&row->hdr);
+ }
+
+ if (rc == ERROR_SUCCESS)
+ TRACE("returning %s for property %s\n", debugstr_a(szValueBuf),
+ debugstr_a(szName));
+ else
+ {
+ *pchValueBuf = 0;
+ TRACE("property not found\n");
+ }
+ HeapFree( GetProcessHeap(), 0, szwName );
+
+ return rc;
+}
+
+UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf)
+{
+ MSIPACKAGE *package;
+ UINT ret;
+
+ TRACE("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf);
+
+ if (0 == hInstall)
+ return ERROR_INVALID_HANDLE;
+ if (NULL == szName)
+ return ERROR_INVALID_PARAMETER;
+ if (NULL != szValueBuf && NULL == pchValueBuf)
+ return ERROR_INVALID_PARAMETER;
+
+ package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_GetPropertyA(package, szName, szValueBuf, pchValueBuf );
+ msiobj_release( &package->hdr );
+ return ret;
+}
+
+
+UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName,
+ LPWSTR szValueBuf, DWORD* pchValueBuf)
+{
+ MSIPACKAGE *package;
+ UINT ret;
+
+ if (0 == hInstall)
+ return ERROR_INVALID_HANDLE;
+ if (NULL == szName)
+ return ERROR_INVALID_PARAMETER;
+ if (NULL != szValueBuf && NULL == pchValueBuf)
+ return ERROR_INVALID_PARAMETER;
+
+ package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+ if (!package)
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_GetPropertyW(package, szName, szValueBuf, pchValueBuf );
+ msiobj_release( &package->hdr );
+ return ret;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
+
+
+#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_UTF8 8
+#define EXPR_WILDCARD 9
+
+struct sql_str {
+ LPCWSTR data;
+ INT len;
+};
+
+typedef struct _string_list
+{
+ LPWSTR string;
+ struct _string_list *next;
+} string_list;
+
+struct complex_expr
+{
+ UINT op;
+ struct expr *left;
+ struct expr *right;
+};
+
+struct expr
+{
+ int type;
+ union
+ {
+ struct complex_expr expr;
+ INT ival;
+ UINT uval;
+ LPWSTR sval;
+ LPWSTR column;
+ UINT col_number;
+ char *utf8;
+ } u;
+};
+
+typedef struct _create_col_info
+{
+ LPWSTR colname;
+ UINT type;
+ struct _create_col_info *next;
+} create_col_info;
+
+typedef struct _value_list
+{
+ struct expr *val;
+ struct _value_list *next;
+} value_list;
+
+typedef struct _column_assignment
+{
+ string_list *col_list;
+ value_list *val_list;
+} column_assignment;
+
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phView);
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view );
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+ string_list *columns );
+
+UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+UINT ORDER_AddColumn( MSIVIEW *group, LPWSTR name );
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+ struct expr *cond );
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+ create_col_info *col_info, BOOL temp );
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+ string_list *columns, value_list *values, BOOL temp );
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **, LPWSTR table,
+ column_assignment *list, struct expr *expr );
+
+void delete_expr( struct expr *e );
+void delete_string_list( string_list *sl );
+void delete_value_list( value_list *vl );
+
+int sqliteGetToken(const WCHAR *z, int *tokenType);
+
+#endif /* __WINE_MSI_QUERY_H */
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+#define MSIFIELD_NULL 0
+#define MSIFIELD_INT 1
+#define MSIFIELD_STR 2
+#define MSIFIELD_WSTR 3
+#define MSIFIELD_STREAM 4
+
+void MSI_FreeField( MSIFIELD *field )
+{
+ switch( field->type )
+ {
+ case MSIFIELD_NULL:
+ case MSIFIELD_INT:
+ break;
+ case MSIFIELD_WSTR:
+ HeapFree( GetProcessHeap(), 0, 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( unsigned int cParams )
+{
+ MSIRECORD *rec;
+ UINT len;
+
+ TRACE("%d\n", cParams);
+
+ len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
+ rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
+ if( rec )
+ rec->count = cParams;
+ return rec;
+}
+
+MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
+{
+ MSIRECORD *rec;
+ MSIHANDLE ret = 0;
+
+ TRACE("%d\n", cParams);
+
+ rec = MSI_CreateRecord( cParams );
+ if( rec )
+ ret = alloc_msihandle( &rec->hdr );
+ return ret;
+}
+
+unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec )
+{
+ return rec->count;
+}
+
+unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld\n", handle );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ {
+ ERR("Record not found!\n");
+ return 0;
+ }
+
+ ret = MSI_RecordGetFieldCount( rec );
+ 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;
+}
+
+int MSI_RecordGetInteger( MSIRECORD *rec, unsigned int 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_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, unsigned int iField)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d\n", handle, iField );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return MSI_NULL_INTEGER;
+
+ ret = MSI_RecordGetInteger( rec, iField );
+ msiobj_release( &rec->hdr );
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
+{
+ MSIRECORD *rec;
+ UINT i;
+
+ TRACE("%ld\n", handle );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ for( i=0; i<=rec->count; i++)
+ {
+ MSI_FreeField( &rec->fields[i] );
+ rec->fields[i].type = MSIFIELD_NULL;
+ rec->fields[i].u.iVal = 0;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal )
+{
+ TRACE("%p %u %d\n", rec, iField, iVal);
+
+ if( iField <= rec->count )
+ {
+ 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, unsigned int iField, int iVal )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %u %d\n", handle, iField, iVal);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ ret = MSI_RecordSetInteger( rec, iField, iVal );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int 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, unsigned int iField )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d\n", handle, iField );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_RecordIsNull( rec, iField );
+ msiobj_release( &rec->hdr );
+ return ret;
+
+}
+
+UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField,
+ LPSTR szValue, DWORD *pcchValue)
+{
+ UINT len=0, ret;
+ CHAR buffer[16];
+
+ TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_PARAMETER;
+
+ ret = ERROR_SUCCESS;
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
+ len = lstrlenA( buffer );
+ lstrcpynA(szValue, buffer, *pcchValue);
+ break;
+ case MSIFIELD_WSTR:
+ len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+ NULL, 0 , NULL, NULL);
+ WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+ szValue, *pcchValue, NULL, NULL);
+ break;
+ case MSIFIELD_NULL:
+ len = 1;
+ if( *pcchValue > 0 )
+ szValue[0] = 0;
+ break;
+ default:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ if( *pcchValue < len )
+ ret = ERROR_MORE_DATA;
+ *pcchValue = len;
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField,
+ LPSTR szValue, DWORD *pcchValue)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+const WCHAR *MSI_RecordGetString( MSIRECORD *rec, unsigned int 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, unsigned int iField,
+ LPWSTR szValue, DWORD *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 )
+ return ERROR_INVALID_PARAMETER;
+
+ ret = ERROR_SUCCESS;
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
+ len = lstrlenW( buffer );
+ lstrcpynW(szValue, buffer, *pcchValue);
+ break;
+ case MSIFIELD_WSTR:
+ len = lstrlenW( rec->fields[iField].u.szwVal );
+ lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
+ break;
+ case MSIFIELD_NULL:
+ len = 1;
+ if( *pcchValue > 0 )
+ szValue[0] = 0;
+ default:
+ break;
+ }
+
+ if( *pcchValue < len )
+ ret = ERROR_MORE_DATA;
+ *pcchValue = len;
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
+ LPWSTR szValue, DWORD *pcchValue)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT WINAPI MsiRecordDataSize(MSIHANDLE hRecord, unsigned int iField)
+{
+ FIXME("%ld %d\n", hRecord, iField);
+ return 0;
+}
+
+UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
+{
+ LPWSTR str;
+ UINT len;
+
+ TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
+ str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ MultiByteToWideChar( CP_ACP, 0, szValue, -1, str, len );
+ MSI_FreeField( &rec->fields[iField] );
+ rec->fields[iField].type = MSIFIELD_WSTR;
+ rec->fields[iField].u.szwVal = str;
+
+ return 0;
+}
+
+UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_RecordSetStringA( rec, iField, szValue );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
+{
+ LPWSTR str;
+ UINT len;
+
+ TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ len = lstrlenW(szValue) + 1;
+ str = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR));
+ lstrcpyW( str, szValue );
+
+ MSI_FreeField( &rec->fields[iField] );
+ rec->fields[iField].type = MSIFIELD_WSTR;
+ rec->fields[iField].u.szwVal = str;
+
+ return 0;
+}
+
+UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ ret = MSI_RecordSetStringW( rec, iField, szValue );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR szResult, DWORD *sz)
+{
+ FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, LPWSTR szResult, DWORD *sz)
+{
+ FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
+{
+ FIXME("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE hRecord, unsigned int iField, LPCWSTR szFilename)
+{
+ FIXME("%ld %d %s\n", hRecord, iField, debugstr_w(szFilename));
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
+{
+ ULONG count;
+ HRESULT r;
+ IStream *stm;
+
+ TRACE("%p %d %p %p\n", rec, iField, buf, sz);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ if( rec->fields[iField].type != MSIFIELD_STREAM )
+ {
+ *sz = 0;
+ return ERROR_INVALID_FIELD;
+ }
+
+ stm = rec->fields[iField].u.stream;
+ if( !stm )
+ return ERROR_INVALID_FIELD;
+
+ /* 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;
+ r = 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 ) )
+ return ERROR_FUNCTION_FAILED;
+
+ *sz = count;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ ret = MSI_RecordReadStream( rec, iField, buf, sz );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int 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;
+}
--- /dev/null
+/*
+ * self-registerable dll functions for msi.dll
+ *
+ * Copyright (C) 2004 Raphael Junqueira
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "winerror.h"
+
+#include "ole2.h"
+#include "olectl.h"
+
+#include "wine/debug.h"
+
+#include "msi.h"
+#include "initguid.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * Near the bottom of this file are the exported DllRegisterServer and
+ * DllUnregisterServer, which make all this worthwhile.
+ */
+
+/***********************************************************************
+ * interface for self-registering
+ */
+struct regsvr_interface {
+ IID const *iid; /* NULL for end of list */
+ LPCSTR name; /* can be NULL to omit */
+ IID const *base_iid; /* can be NULL to omit */
+ int num_methods; /* can be <0 to omit */
+ CLSID const *ps_clsid; /* can be NULL to omit */
+ CLSID const *ps_clsid32; /* can be NULL to omit */
+};
+
+static HRESULT register_interfaces(struct regsvr_interface const *list);
+static HRESULT unregister_interfaces(struct regsvr_interface const *list);
+
+/**
+ * @todo: maybe adding typelibs support here
+ * [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217
+ * @="{000C1092-0000-0000-C000-000000000046}"
+ */
+struct regsvr_coclass {
+ CLSID const *clsid; /* NULL for end of list */
+ LPCSTR name; /* can be NULL to omit */
+ LPCSTR iph32; /* can be NULL to omit */
+ LPCSTR ips; /* can be NULL to omit */
+ LPCSTR ips32; /* can be NULL to omit */
+ LPCSTR ips32_tmodel; /* can be NULL to omit, if apartment, iph32 must be set */
+ LPCSTR progid; /* can be NULL to omit */
+ LPCSTR viprogid; /* can be NULL to omit */
+ LPCSTR progid_extra; /* can be NULL to omit */
+};
+
+static HRESULT register_coclasses(struct regsvr_coclass const *list);
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
+
+/***********************************************************************
+ * static string constants
+ */
+static WCHAR const interface_keyname[10] = {
+ 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
+static WCHAR const base_ifa_keyname[14] = {
+ 'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
+ 'e', 0 };
+static WCHAR const num_methods_keyname[11] = {
+ 'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
+static WCHAR const ps_clsid_keyname[15] = {
+ 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+ 'i', 'd', 0 };
+static WCHAR const ps_clsid32_keyname[17] = {
+ 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+ 'i', 'd', '3', '2', 0 };
+static WCHAR const clsid_keyname[6] = {
+ 'C', 'L', 'S', 'I', 'D', 0 };
+static WCHAR const curver_keyname[7] = {
+ 'C', 'u', 'r', 'V', 'e', 'r', 0 };
+static WCHAR const iph32_keyname[] = {
+ 'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
+ '3', '2', 0 };
+static WCHAR const ips_keyname[13] = {
+ 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+ 0 };
+static WCHAR const ips32_keyname[15] = {
+ 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+ '3', '2', 0 };
+static WCHAR const progid_keyname[7] = {
+ 'P', 'r', 'o', 'g', 'I', 'D', 0 };
+static WCHAR const viprogid_keyname[25] = {
+ '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 char const tmodel_valuename[] = "ThreadingModel";
+
+/***********************************************************************
+ * static helper functions
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
+static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
+ WCHAR const *value);
+static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
+ char const *value);
+static LONG register_progid(WCHAR const *clsid,
+ char const *progid, char const *curver_progid,
+ char const *name, char const *extra);
+static LONG recursive_delete_key(HKEY key);
+static LONG recursive_delete_keyA(HKEY base, char const *name);
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
+
+/***********************************************************************
+ * register_interfaces
+ */
+static HRESULT register_interfaces(struct regsvr_interface const *list) {
+ LONG res = ERROR_SUCCESS;
+ HKEY interface_key;
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->iid; ++list) {
+ WCHAR buf[39];
+ HKEY iid_key;
+
+ StringFromGUID2(list->iid, buf, 39);
+ res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_interface_key;
+
+ if (list->name) {
+ res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)(list->name),
+ strlen(list->name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->base_iid) {
+ register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (0 <= list->num_methods) {
+ static WCHAR const fmt[3] = { '%', 'd', 0 };
+ HKEY key;
+
+ res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+
+ wsprintfW(buf, fmt, list->num_methods);
+ res = RegSetValueExW(key, NULL, 0, REG_SZ,
+ (CONST BYTE*)buf,
+ (lstrlenW(buf) + 1) * sizeof(WCHAR));
+ RegCloseKey(key);
+
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->ps_clsid) {
+ register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ if (list->ps_clsid32) {
+ register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
+ if (res != ERROR_SUCCESS) goto error_close_iid_key;
+ }
+
+ error_close_iid_key:
+ RegCloseKey(iid_key);
+ }
+
+error_close_interface_key:
+ RegCloseKey(interface_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_interfaces
+ */
+static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
+ LONG res = ERROR_SUCCESS;
+ HKEY interface_key;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
+ KEY_READ | KEY_WRITE, &interface_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->iid; ++list) {
+ WCHAR buf[39];
+
+ StringFromGUID2(list->iid, buf, 39);
+ res = recursive_delete_keyW(interface_key, buf);
+ }
+
+ RegCloseKey(interface_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * register_coclasses
+ */
+static HRESULT register_coclasses(struct regsvr_coclass const *list) {
+ LONG res = ERROR_SUCCESS;
+ HKEY coclass_key;
+
+ res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+ WCHAR buf[39];
+ HKEY clsid_key;
+
+ StringFromGUID2(list->clsid, buf, 39);
+ res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+ if (list->name) {
+ res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)(list->name),
+ strlen(list->name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->iph32) {
+ HKEY iph32_key;
+
+ res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL,
+ &iph32_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)list->iph32,
+ lstrlenA(list->iph32) + 1);
+ RegCloseKey(iph32_key);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->ips) {
+ res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->ips32) {
+ HKEY ips32_key;
+
+ res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL,
+ &ips32_key, NULL);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)list->ips32,
+ lstrlenA(list->ips32) + 1);
+ if (res == ERROR_SUCCESS && list->ips32_tmodel)
+ res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
+ (CONST BYTE*)list->ips32_tmodel,
+ strlen(list->ips32_tmodel) + 1);
+ RegCloseKey(ips32_key);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->progid) {
+ res = register_key_defvalueA(clsid_key, progid_keyname,
+ list->progid);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = register_progid(buf, list->progid, NULL,
+ list->name, list->progid_extra);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ if (list->viprogid) {
+ res = register_key_defvalueA(clsid_key, viprogid_keyname,
+ list->viprogid);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+ res = register_progid(buf, list->viprogid, list->progid,
+ list->name, list->progid_extra);
+ if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+ }
+
+ error_close_clsid_key:
+ RegCloseKey(clsid_key);
+ }
+
+error_close_coclass_key:
+ RegCloseKey(coclass_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * unregister_coclasses
+ */
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
+ LONG res = ERROR_SUCCESS;
+ HKEY coclass_key;
+
+ res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
+ KEY_READ | KEY_WRITE, &coclass_key);
+ if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+ if (res != ERROR_SUCCESS) goto error_return;
+
+ for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+ WCHAR buf[39];
+
+ StringFromGUID2(list->clsid, buf, 39);
+ res = recursive_delete_keyW(coclass_key, buf);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+ if (list->progid) {
+ res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+ }
+
+ if (list->viprogid) {
+ res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
+ if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+ }
+ }
+
+error_close_coclass_key:
+ RegCloseKey(coclass_key);
+error_return:
+ return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ * regsvr_key_guid
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
+ WCHAR buf[39];
+
+ StringFromGUID2(guid, buf, 39);
+ return register_key_defvalueW(base, name, buf);
+}
+
+/***********************************************************************
+ * regsvr_key_defvalueW
+ */
+static LONG register_key_defvalueW(
+ HKEY base,
+ WCHAR const *name,
+ WCHAR const *value) {
+ LONG res;
+ HKEY key;
+
+ res = RegCreateKeyExW(base, name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+ res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+ (lstrlenW(value) + 1) * sizeof(WCHAR));
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * regsvr_key_defvalueA
+ */
+static LONG register_key_defvalueA(
+ HKEY base,
+ WCHAR const *name,
+ char const *value) {
+ LONG res;
+ HKEY key;
+
+ res = RegCreateKeyExW(base, name, 0, NULL, 0,
+ KEY_READ | KEY_WRITE, NULL, &key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+ res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+ lstrlenA(value) + 1);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * regsvr_progid
+ */
+static LONG register_progid(
+ WCHAR const *clsid,
+ char const *progid,
+ char const *curver_progid,
+ char const *name,
+ char const *extra) {
+ LONG res;
+ HKEY progid_key;
+
+ res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
+ NULL, 0, KEY_READ | KEY_WRITE, NULL,
+ &progid_key, NULL);
+ if (res != ERROR_SUCCESS) return res;
+
+ if (name) {
+ res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
+ (CONST BYTE*)name, strlen(name) + 1);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (clsid) {
+ res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (curver_progid) {
+ res = register_key_defvalueA(progid_key, curver_keyname,
+ curver_progid);
+ if (res != ERROR_SUCCESS) goto error_close_progid_key;
+ }
+
+ if (extra) {
+ HKEY extra_key;
+
+ res = RegCreateKeyExA(progid_key, extra, 0,
+ NULL, 0, KEY_READ | KEY_WRITE, NULL,
+ &extra_key, NULL);
+ if (res == ERROR_SUCCESS)
+ RegCloseKey(extra_key);
+ }
+
+error_close_progid_key:
+ RegCloseKey(progid_key);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_key
+ */
+static LONG recursive_delete_key(HKEY key) {
+ LONG res;
+ WCHAR subkey_name[MAX_PATH];
+ DWORD cName;
+ HKEY subkey;
+
+ for (;;) {
+ cName = sizeof(subkey_name) / sizeof(WCHAR);
+ res = RegEnumKeyExW(key, 0, subkey_name, &cName,
+ NULL, NULL, NULL, NULL);
+ if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
+ res = ERROR_SUCCESS; /* presumably we're done enumerating */
+ break;
+ }
+ res = RegOpenKeyExW(key, subkey_name, 0,
+ KEY_READ | KEY_WRITE, &subkey);
+ if (res == ERROR_FILE_NOT_FOUND) continue;
+ if (res != ERROR_SUCCESS) break;
+
+ res = recursive_delete_key(subkey);
+ RegCloseKey(subkey);
+ if (res != ERROR_SUCCESS) break;
+ }
+
+ if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_keyA
+ */
+static LONG recursive_delete_keyA(HKEY base, char const *name) {
+ LONG res;
+ HKEY key;
+
+ res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
+ if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+ if (res != ERROR_SUCCESS) return res;
+ res = recursive_delete_key(key);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * recursive_delete_keyW
+ */
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
+ LONG res;
+ HKEY key;
+
+ res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
+ if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+ if (res != ERROR_SUCCESS) return res;
+ res = recursive_delete_key(key);
+ RegCloseKey(key);
+ return res;
+}
+
+/***********************************************************************
+ * coclass list
+ */
+static struct regsvr_coclass const coclass_list[] = {
+ {
+ &CLSID_IMsiServer,
+ "Msi install server",
+ "ole32.dll",
+ NULL,
+ "msi.dll",
+ "Apartment",
+ "WindowsInstaller.Installer",
+ NULL
+ },
+ {
+ &CLSID_IMsiServerMessage,
+ "Wine Installer Message RPC",
+ NULL,
+ NULL,
+ "msi.dll",
+ NULL,
+ "WindowsInstaller.Message",
+ NULL
+ },
+ {
+ &CLSID_IMsiServerX1,
+ "Msi install server",
+ "ole32.dll",
+ NULL,
+ "msi.dll",
+ "Apartment",
+ "WindowsInstaller.Installer",
+ NULL
+ },
+ {
+ &CLSID_IMsiServerX2,
+ "Msi install server",
+ "ole32.dll",
+ NULL,
+ "msi.dll",
+ "Apartment",
+ "WindowsInstaller.Installer",
+ NULL
+ },
+ {
+ &CLSID_IMsiServerX3,
+ "Msi install server",
+ "ole32.dll",
+ NULL,
+ "msi.dll",
+ "Apartment",
+ "WindowsInstaller.Installer",
+ NULL
+ },
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * interface list
+ */
+/*
+ * we should declare: (@see ole32/regsvr.c for examples)
+ [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}]
+ [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
+*/
+static struct regsvr_interface const interface_list[] = {
+ { NULL } /* list terminator */
+};
+
+/***********************************************************************
+ * DllRegisterServer
+ */
+HRESULT WINAPI MSI_DllRegisterServer(void) {
+ HRESULT hr;
+
+ TRACE("\n");
+
+ hr = register_coclasses(coclass_list);
+ if (SUCCEEDED(hr))
+ hr = register_interfaces(interface_list);
+ return hr;
+}
+
+/***********************************************************************
+ * DllUnregisterServer
+ */
+HRESULT WINAPI MSI_DllUnregisterServer(void) {
+ HRESULT hr;
+
+ TRACE("\n");
+
+ hr = unregister_coclasses(coclass_list);
+ if (SUCCEEDED(hr))
+ hr = unregister_interfaces(interface_list);
+ return hr;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* 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==0) || (col>sv->num_cols) )
+ return ERROR_FUNCTION_FAILED;
+
+ col = sv->cols[ col - 1 ];
+
+ 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==0) || (col>sv->num_cols) )
+ return ERROR_FUNCTION_FAILED;
+
+ col = sv->cols[ col - 1 ];
+
+ return sv->table->ops->fetch_stream( sv->table, row, col, stm );
+}
+
+static UINT SELECT_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+ MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+ TRACE("%p %d %d %04x\n", sv, row, col, val );
+
+ 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->set_int( sv->table, row, col, val );
+}
+
+static UINT SELECT_insert_row( struct tagMSIVIEW *view, UINT *num )
+{
+ MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+ TRACE("%p %p\n", sv, num );
+
+ if( !sv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return sv->table->ops->insert_row( sv->table, num );
+}
+
+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, LPWSTR *name, UINT *type )
+{
+ MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+ TRACE("%p %d %p %p\n", sv, n, name, type );
+
+ if( !sv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( (n==0) || (n>sv->num_cols) )
+ return ERROR_FUNCTION_FAILED;
+
+ n = sv->cols[ n - 1 ];
+
+ return sv->table->ops->get_column_info( sv->table, n, name, type );
+}
+
+static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+ TRACE("%p %d %ld\n", sv, eModifyMode, hrec );
+
+ if( !sv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return sv->table->ops->modify( sv->table, eModifyMode, hrec );
+}
+
+static UINT SELECT_delete( struct tagMSIVIEW *view )
+{
+ MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+ TRACE("%p\n", sv );
+
+ if( sv->table )
+ sv->table->ops->delete( sv->table );
+
+ HeapFree( GetProcessHeap(), 0, sv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS select_ops =
+{
+ SELECT_fetch_int,
+ SELECT_fetch_stream,
+ SELECT_set_int,
+ SELECT_insert_row,
+ SELECT_execute,
+ SELECT_close,
+ SELECT_get_dimensions,
+ SELECT_get_column_info,
+ SELECT_modify,
+ SELECT_delete
+};
+
+static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPWSTR name )
+{
+ UINT r, n=0;
+ MSIVIEW *table;
+
+ TRACE("%p adding %s\n", sv, 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;
+
+ r = VIEW_find_column( 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;
+}
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+ string_list *columns )
+{
+ MSISELECTVIEW *sv = NULL;
+ UINT count = 0, r;
+
+ TRACE("%p\n", sv );
+
+ r = table->ops->get_dimensions( table, NULL, &count );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("can't get table dimensions\n");
+ return r;
+ }
+
+ sv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ 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->string );
+ if( r )
+ break;
+ columns = columns->next;
+ }
+
+ if( r != ERROR_SUCCESS )
+ {
+ sv->view.ops->delete( &sv->view );
+ sv = NULL;
+ }
+
+ *view = &sv->view;
+
+ return r;
+}
--- /dev/null
+%{
+
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "query.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+extern int SQL_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_SQL_input
+{
+ MSIDATABASE *db;
+ LPCWSTR command;
+ DWORD n, len;
+ MSIVIEW **view; /* view structure for the resulting query */
+} SQL_input;
+
+static LPWSTR SQL_getstring( struct sql_str *str );
+static INT SQL_getint( SQL_input *sql );
+static int SQL_lex( void *SQL_lval, SQL_input *info);
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in,
+ string_list *columns );
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in,
+ string_list *columns );
+
+static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
+ string_list *keys);
+
+static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r );
+static struct expr * EXPR_column( LPWSTR );
+static struct expr * EXPR_ival( struct sql_str *);
+static struct expr * EXPR_sval( struct sql_str *);
+static struct expr * EXPR_wildcard();
+
+%}
+
+%pure-parser
+
+%union
+{
+ struct sql_str str;
+ LPWSTR string;
+ string_list *column_list;
+ value_list *val_list;
+ MSIVIEW *query;
+ struct expr *expr;
+ USHORT column_type;
+ create_col_info *column_info;
+ column_assignment update_col_info;
+}
+
+%token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC
+%token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY
+%token TK_CASCADE TK_CASE TK_CHAR TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN
+%token TK_COMMA TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT
+%token TK_CONSTRAINT TK_COPY TK_CREATE
+%token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC
+%token TK_DISTINCT TK_DOT TK_DROP TK_EACH
+%token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN
+%token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION
+%token TK_GE TK_GLOB TK_GROUP TK_GT
+%token TK_HAVING TK_HOLD
+%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
+%token <str> TK_ID
+%token TK_INSERT TK_INSTEAD TK_INT
+%token <str> TK_INTEGER
+%token TK_INTERSECT TK_INTO TK_IS
+%token TK_ISNULL
+%token TK_JOIN TK_JOIN_KW
+%token TK_KEY
+%token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT
+%token TK_LOCALIZABLE
+%token TK_MATCH TK_MINUS
+%token TK_NE TK_NOT TK_NOTNULL TK_NULL
+%token TK_OBJECT TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER
+%token TK_PLUS TK_PRAGMA TK_PRIMARY
+%token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK
+%token TK_ROW TK_RP TK_RSHIFT
+%token TK_SELECT TK_SEMI TK_SET TK_SHORT TK_SLASH TK_SPACE TK_STAR TK_STATEMENT
+%token <str> TK_STRING
+%token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER
+%token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE
+%token TK_UPDATE TK_UPLUS TK_USING
+%token TK_VACUUM TK_VALUES TK_VIEW
+%token TK_WHEN 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> column table string_or_id
+%type <column_list> selcollist
+%type <query> from unorderedsel oneselect onequery onecreate oneinsert oneupdate
+%type <expr> expr val column_val const_val
+%type <column_type> column_type data_type data_type_l data_count
+%type <column_info> column_def table_def
+%type <val_list> constlist
+%type <update_col_info> column_assignment update_assign_list
+
+%%
+
+onequery:
+ oneselect
+ {
+ SQL_input* sql = (SQL_input*) info;
+ *sql->view = $1;
+ }
+ | onecreate
+ {
+ SQL_input* sql = (SQL_input*) info;
+ *sql->view = $1;
+ }
+ | oneinsert
+ {
+ SQL_input* sql = (SQL_input*) info;
+ *sql->view = $1;
+ }
+ | oneupdate
+ {
+ SQL_input* sql = (SQL_input*) info;
+ *sql->view = $1;
+ }
+ ;
+
+oneinsert:
+ TK_INSERT TK_INTO table TK_LP selcollist 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 );
+ $$ = insert;
+ }
+ | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP
+ {
+ SQL_input *sql = (SQL_input*) info;
+ MSIVIEW *insert = NULL;
+
+ INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE );
+ $$ = insert;
+ }
+ ;
+
+onecreate:
+ TK_CREATE TK_TABLE table TK_LP table_def TK_RP
+ {
+ SQL_input* sql = (SQL_input*) info;
+ MSIVIEW *create = NULL;
+
+ if( !$5 )
+ YYABORT;
+ CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
+ $$ = 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 );
+ $$ = 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 );
+ $$ = update;
+ }
+ ;
+
+table_def:
+ column_def TK_PRIMARY TK_KEY selcollist
+ {
+ if( SQL_MarkPrimaryKeys( $1, $4 ) )
+ $$ = $1;
+ else
+ $$ = NULL;
+ }
+ ;
+
+column_def:
+ column_def TK_COMMA column column_type
+ {
+ create_col_info *ci;
+
+ for( ci = $1; ci->next; ci = ci->next )
+ ;
+
+ ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
+ if( !ci->next )
+ {
+ /* FIXME: free $1 */
+ YYABORT;
+ }
+ ci->next->colname = $3;
+ ci->next->type = $4;
+ ci->next->next = NULL;
+
+ $$ = $1;
+ }
+ | column column_type
+ {
+ $$ = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
+ if( ! $$ )
+ YYABORT;
+ $$->colname = $1;
+ $$->type = $2;
+ $$->next = NULL;
+ }
+ ;
+
+column_type:
+ data_type_l
+ {
+ $$ = $1 | MSITYPE_VALID;
+ }
+ | data_type_l TK_LOCALIZABLE
+ {
+ FIXME("LOCALIZABLE ignored\n");
+ $$ = $1 | MSITYPE_VALID;
+ }
+ ;
+
+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
+ {
+ $$ = 2;
+ }
+ | TK_SHORT
+ {
+ $$ = 2;
+ }
+ | TK_INT
+ {
+ $$ = 2;
+ }
+ | TK_LONG
+ {
+ $$ = 4;
+ }
+ | TK_OBJECT
+ {
+ $$ = 0;
+ }
+ ;
+
+data_count:
+ TK_INTEGER
+ {
+ SQL_input* sql = (SQL_input*) info;
+ int val = SQL_getint(sql);
+ if( ( val > 255 ) || ( val < 0 ) )
+ YYABORT;
+ $$ = val;
+ }
+ ;
+
+oneselect:
+ unorderedsel TK_ORDER TK_BY selcollist
+ {
+ SQL_input* sql = (SQL_input*) info;
+
+ if( !$1 )
+ YYABORT;
+ if( $4 )
+ $$ = do_order_by( sql->db, $1, $4 );
+ else
+ $$ = $1;
+ }
+ | unorderedsel
+ ;
+
+unorderedsel:
+ TK_SELECT selcollist from
+ {
+ SQL_input* sql = (SQL_input*) info;
+ if( !$3 )
+ YYABORT;
+ if( $2 )
+ {
+ $$ = do_one_select( sql->db, $3, $2 );
+ if( !$$ )
+ YYABORT;
+ }
+ else
+ $$ = $3;
+ }
+ | TK_SELECT TK_DISTINCT selcollist from
+ {
+ SQL_input* sql = (SQL_input*) info;
+ MSIVIEW *view = $4;
+
+ if( !view )
+ YYABORT;
+ if( $3 )
+ {
+ view = do_one_select( sql->db, view, $3 );
+ if( !view )
+ YYABORT;
+ }
+ DISTINCT_CreateView( sql->db, & $$, view );
+ }
+ ;
+
+selcollist:
+ column
+ {
+ string_list *list;
+
+ list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
+ if( !list )
+ YYABORT;
+ list->string = $1;
+ list->next = NULL;
+
+ $$ = list;
+ TRACE("Collist %s\n",debugstr_w($$->string));
+ }
+ | column TK_COMMA selcollist
+ {
+ string_list *list;
+
+ list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
+ if( !list )
+ YYABORT;
+ list->string = $1;
+ list->next = $3;
+
+ $$ = list;
+ TRACE("From table: %s\n",debugstr_w($$->string));
+ }
+ | TK_STAR
+ {
+ $$ = NULL;
+ }
+ ;
+
+from:
+ TK_FROM table
+ {
+ SQL_input* sql = (SQL_input*) info;
+ UINT r;
+
+ $$ = NULL;
+ TRACE("From table: %s\n",debugstr_w($2));
+ r = TABLE_CreateView( sql->db, $2, & $$ );
+ if( r != ERROR_SUCCESS )
+ YYABORT;
+ }
+ | TK_FROM table TK_WHERE expr
+ {
+ SQL_input* sql = (SQL_input*) info;
+ MSIVIEW *view = NULL;
+ UINT r;
+
+ $$ = NULL;
+ TRACE("From table: %s\n",debugstr_w($2));
+ r = TABLE_CreateView( sql->db, $2, &view );
+ if( r != ERROR_SUCCESS )
+ YYABORT;
+ r = WHERE_CreateView( sql->db, &view, view, $4 );
+ if( r != ERROR_SUCCESS )
+ YYABORT;
+ $$ = view;
+ }
+ ;
+
+expr:
+ TK_LP expr TK_RP
+ {
+ $$ = $2;
+ }
+ | column_val TK_EQ column_val
+ {
+ $$ = EXPR_complex( $1, OP_EQ, $3 );
+ }
+ | expr TK_AND expr
+ {
+ $$ = EXPR_complex( $1, OP_AND, $3 );
+ }
+ | expr TK_OR expr
+ {
+ $$ = EXPR_complex( $1, OP_OR, $3 );
+ }
+ | column_val TK_EQ val
+ {
+ $$ = EXPR_complex( $1, OP_EQ, $3 );
+ }
+ | column_val TK_GT val
+ {
+ $$ = EXPR_complex( $1, OP_GT, $3 );
+ }
+ | column_val TK_LT val
+ {
+ $$ = EXPR_complex( $1, OP_LT, $3 );
+ }
+ | column_val TK_LE val
+ {
+ $$ = EXPR_complex( $1, OP_LE, $3 );
+ }
+ | column_val TK_GE val
+ {
+ $$ = EXPR_complex( $1, OP_GE, $3 );
+ }
+ | column_val TK_NE val
+ {
+ $$ = EXPR_complex( $1, OP_NE, $3 );
+ }
+ | column_val TK_IS TK_NULL
+ {
+ $$ = EXPR_complex( $1, OP_ISNULL, NULL );
+ }
+ | column_val TK_IS TK_NOT TK_NULL
+ {
+ $$ = EXPR_complex( $1, OP_NOTNULL, NULL );
+ }
+ ;
+
+val:
+ column_val
+ | const_val
+ ;
+
+constlist:
+ const_val
+ {
+ value_list *vals;
+
+ vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
+ if( vals )
+ {
+ vals->val = $1;
+ vals->next = NULL;
+ }
+ $$ = vals;
+ }
+ | constlist TK_COMMA const_val
+ {
+ value_list *vals;
+
+ vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
+ if( vals )
+ {
+ vals->val = $3;
+ vals->next = NULL;
+ }
+ $1->next = vals;
+ $$ = $1;
+ }
+ ;
+
+update_assign_list:
+ column_assignment
+ | column_assignment TK_COMMA update_assign_list
+ {
+ $1.col_list->next = $3.col_list;
+ $1.val_list->next = $3.val_list;
+ $$ = $1;
+ }
+ ;
+
+column_assignment:
+ column TK_EQ const_val
+ {
+ $$.col_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.col_list );
+ if( !$$.col_list )
+ YYABORT;
+ $$.col_list->string = $1;
+ $$.col_list->next = NULL;
+ $$.val_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.val_list );
+ if( !$$.val_list )
+ YYABORT;
+ $$.val_list->val = $3;
+ $$.val_list->next = 0;
+ }
+ ;
+
+const_val:
+ TK_INTEGER
+ {
+ $$ = EXPR_ival( &$1 );
+ }
+ | TK_STRING
+ {
+ $$ = EXPR_sval( &$1 );
+ }
+ | TK_WILDCARD
+ {
+ $$ = EXPR_wildcard();
+ }
+ ;
+
+column_val:
+ column
+ {
+ $$ = EXPR_column( $1 );
+ }
+ ;
+
+column:
+ table TK_DOT string_or_id
+ {
+ $$ = $3; /* FIXME */
+ }
+ | string_or_id
+ {
+ $$ = $1;
+ }
+ ;
+
+table:
+ string_or_id
+ {
+ $$ = $1;
+ }
+ ;
+
+string_or_id:
+ TK_ID
+ {
+ $$ = SQL_getstring( &$1 );
+ }
+ | TK_STRING
+ {
+ $$ = SQL_getstring( &$1 );
+ }
+ ;
+
+%%
+
+int SQL_lex( void *SQL_lval, SQL_input *sql)
+{
+ int token;
+ 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 );
+ if( sql->len==0 )
+ break;
+ str->data = &sql->command[sql->n];
+ str->len = sql->len;
+ }
+ while( token == TK_SPACE );
+
+ TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
+
+ return token;
+}
+
+LPWSTR SQL_getstring( struct sql_str *strdata)
+{
+ LPCWSTR p = strdata->data;
+ UINT len = strdata->len;
+ LPWSTR str;
+
+ /* if there's quotes, remove them */
+ if( ( (p[0]=='`') && (p[len-1]=='`') ) ||
+ ( (p[0]=='\'') && (p[len-1]=='\'') ) )
+ {
+ p++;
+ len -= 2;
+ }
+ str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
+ if(!str )
+ return str;
+ memcpy(str, p, len*sizeof(WCHAR) );
+ str[len]=0;
+
+ return str;
+}
+
+INT SQL_getint( SQL_input *sql )
+{
+ LPCWSTR p = &sql->command[sql->n];
+
+ return atoiW( p );
+}
+
+int SQL_error(const char *str)
+{
+ return 0;
+}
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in,
+ string_list *columns )
+{
+ MSIVIEW *view = NULL;
+
+ SELECT_CreateView( db, &view, in, columns );
+ delete_string_list( columns );
+ if( !view )
+ ERR("Error creating select query\n");
+ return view;
+}
+
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in,
+ string_list *columns )
+{
+ MSIVIEW *view = NULL;
+
+ ORDER_CreateView( db, &view, in );
+ if( view )
+ {
+ string_list *x = columns;
+
+ for( x = columns; x ; x = x->next )
+ ORDER_AddColumn( view, x->string );
+ }
+ else
+ ERR("Error creating select query\n");
+ delete_string_list( columns );
+ return view;
+}
+
+static struct expr * EXPR_wildcard()
+{
+ struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+ if( e )
+ {
+ e->type = EXPR_WILDCARD;
+ }
+ return e;
+}
+
+static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r )
+{
+ struct expr *e = HeapAlloc( GetProcessHeap(), 0, 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_column( LPWSTR str )
+{
+ struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+ if( e )
+ {
+ e->type = EXPR_COLUMN;
+ e->u.sval = str;
+ }
+ return e;
+}
+
+static struct expr * EXPR_ival( struct sql_str *str )
+{
+ struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+ if( e )
+ {
+ e->type = EXPR_IVAL;
+ e->u.ival = atoiW( str->data );
+ }
+ return e;
+}
+
+static struct expr * EXPR_sval( struct sql_str *str )
+{
+ struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+ if( e )
+ {
+ e->type = EXPR_SVAL;
+ e->u.sval = SQL_getstring( str );
+ }
+ return e;
+}
+
+void delete_expr( struct expr *e )
+{
+ if( !e )
+ return;
+ if( e->type == EXPR_COMPLEX )
+ {
+ delete_expr( e->u.expr.left );
+ delete_expr( e->u.expr.right );
+ }
+ else if( e->type == EXPR_UTF8 )
+ HeapFree( GetProcessHeap(), 0, e->u.utf8 );
+ else if( e->type == EXPR_SVAL )
+ HeapFree( GetProcessHeap(), 0, e->u.sval );
+ HeapFree( GetProcessHeap(), 0, e );
+}
+
+void delete_string_list( string_list *sl )
+{
+ while( sl )
+ {
+ string_list *t = sl->next;
+ HeapFree( GetProcessHeap(), 0, sl->string );
+ HeapFree( GetProcessHeap(), 0, sl );
+ sl = t;
+ }
+}
+
+void delete_value_list( value_list *vl )
+{
+ while( vl )
+ {
+ value_list *t = vl->next;
+ delete_expr( vl->val );
+ HeapFree( GetProcessHeap(), 0, vl );
+ vl = t;
+ }
+}
+
+static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
+ string_list *keys )
+{
+ string_list *k;
+ BOOL found = TRUE;
+
+ for( k = keys; k && found; k = k->next )
+ {
+ create_col_info *c;
+
+ found = FALSE;
+ for( c = cols; c && !found; c = c->next )
+ {
+ if( lstrcmpW( k->string, c->colname ) )
+ continue;
+ c->type |= MSITYPE_KEY;
+ found = TRUE;
+ }
+ }
+
+ return found;
+}
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
+{
+ SQL_input sql;
+ int r;
+
+ *phview = NULL;
+
+ sql.db = db;
+ sql.command = command;
+ sql.n = 0;
+ sql.len = 0;
+ sql.view = phview;
+
+ 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 ERROR_BAD_QUERY_SYNTAX;
+ }
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+typedef struct _msistring
+{
+ UINT hash;
+ UINT refcount;
+ LPWSTR str;
+} msistring;
+
+struct string_table
+{
+ UINT maxcount; /* the number of strings */
+ UINT freeslot;
+ UINT codepage;
+ msistring *strings; /* an array of strings (in the tree) */
+};
+
+static UINT msistring_makehash( const WCHAR *str )
+{
+ UINT hash = 0;
+
+ if (str==NULL)
+ return hash;
+
+ while( *str )
+ {
+ hash ^= *str++;
+ hash *= 53;
+ hash = (hash<<5) | (hash>>27);
+ }
+ return hash;
+}
+
+string_table *msi_init_stringtable( int entries, UINT codepage )
+{
+ string_table *st;
+
+ st = HeapAlloc( GetProcessHeap(), 0, sizeof (string_table) );
+ if( !st )
+ return NULL;
+ st->strings = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof (msistring) * entries );
+ if( !st )
+ {
+ HeapFree( GetProcessHeap(), 0, st );
+ return NULL;
+ }
+ if( entries < 1 )
+ entries = 1;
+ st->maxcount = entries;
+ st->freeslot = 1;
+ st->codepage = codepage;
+
+ return st;
+}
+
+VOID msi_destroy_stringtable( string_table *st )
+{
+ UINT i;
+
+ for( i=0; i<st->maxcount; i++ )
+ {
+ if( st->strings[i].refcount )
+ HeapFree( GetProcessHeap(), 0, st->strings[i].str );
+ }
+ HeapFree( GetProcessHeap(), 0, st->strings );
+ HeapFree( GetProcessHeap(), 0, st );
+}
+
+static int st_find_free_entry( string_table *st )
+{
+ UINT i, sz;
+ msistring *p;
+
+ TRACE("%p\n", st);
+
+ if( st->freeslot )
+ {
+ for( i = st->freeslot; i < st->maxcount; i++ )
+ if( !st->strings[i].refcount )
+ return i;
+ }
+ for( i = 1; i < st->maxcount; i++ )
+ if( !st->strings[i].refcount )
+ return i;
+
+ /* dynamically resize */
+ sz = st->maxcount + 1 + st->maxcount/2;
+ p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ st->strings, sz*sizeof(msistring) );
+ if( !p )
+ return -1;
+ st->strings = p;
+ st->freeslot = st->maxcount;
+ st->maxcount = sz;
+ if( st->strings[st->freeslot].refcount )
+ ERR("oops. expected freeslot to be free...\n");
+ return st->freeslot;
+}
+
+static void st_mark_entry_used( string_table *st, UINT n )
+{
+ if( n >= st->maxcount )
+ return;
+ st->freeslot = n + 1;
+}
+
+int msi_addstring( string_table *st, int n, const CHAR *data, int len, UINT refcount )
+{
+ int sz;
+
+ if( !data )
+ return 0;
+ if( !data[0] )
+ return 0;
+ if( n > 0 )
+ {
+ if( st->strings[n].refcount )
+ return -1;
+ }
+ else
+ {
+ if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
+ {
+ st->strings[n].refcount++;
+ return n;
+ }
+ n = st_find_free_entry( st );
+ if( n < 0 )
+ 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 );
+ st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (sz+1)*sizeof(WCHAR) );
+ if( !st->strings[n].str )
+ return -1;
+ MultiByteToWideChar( st->codepage, 0, data, len, st->strings[n].str, sz );
+ st->strings[n].str[sz] = 0;
+ st->strings[n].refcount = 1;
+ st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+ st_mark_entry_used( st, n );
+
+ return n;
+}
+
+int msi_addstringW( string_table *st, int n, const WCHAR *data, int len, UINT refcount )
+{
+ /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
+
+ if( !data )
+ return 0;
+ if( !data[0] )
+ return 0;
+ if( n > 0 )
+ {
+ if( st->strings[n].refcount )
+ return -1;
+ }
+ else
+ {
+ if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
+ {
+ st->strings[n].refcount++;
+ return n;
+ }
+ n = st_find_free_entry( st );
+ if( n < 0 )
+ return -1;
+ }
+
+ if( n < 1 )
+ {
+ ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
+ return -1;
+ }
+
+ /* allocate a new string */
+ if(len<0)
+ len = strlenW(data);
+ TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len );
+
+ st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof(WCHAR) );
+ if( !st->strings[n].str )
+ return -1;
+ TRACE("%d\n",__LINE__);
+ memcpy( st->strings[n].str, data, len*sizeof(WCHAR) );
+ st->strings[n].str[len] = 0;
+ st->strings[n].refcount = 1;
+ st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+ st_mark_entry_used( st, n );
+
+ return n;
+}
+
+/* find the string identified by an id - return null if there's none */
+const WCHAR *msi_string_lookup_id( string_table *st, UINT id )
+{
+ static const WCHAR zero[] = { 0 };
+ if( id == 0 )
+ return zero;
+
+ if( id >= st->maxcount )
+ return NULL;
+
+ if( id && !st->strings[id].refcount )
+ return NULL;
+
+ return st->strings[id].str;
+}
+
+/*
+ * msi_id2stringW
+ *
+ * [in] st - pointer to the string table
+ * [in] id - id of the string to retrieve
+ * [out] buffer - destination of the string
+ * [in/out] sz - number of bytes available in the buffer on input
+ * number of bytes used on output
+ *
+ * The size includes the terminating nul character. Short buffers
+ * will be filled, but not nul terminated.
+ */
+UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz )
+{
+ UINT len;
+ 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;
+
+ len = strlenW( str ) + 1;
+
+ if( !buffer )
+ {
+ *sz = len;
+ return ERROR_SUCCESS;
+ }
+
+ if( *sz < len )
+ *sz = len;
+ memcpy( buffer, str, (*sz)*sizeof(WCHAR) );
+ *sz = len;
+
+ return ERROR_SUCCESS;
+}
+
+/*
+ * 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
+ *
+ * The size includes the terminating nul character. Short buffers
+ * will be filled, but not nul terminated.
+ */
+UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz )
+{
+ UINT len;
+ const WCHAR *str;
+ int n;
+
+ TRACE("Finding string %d of %d\n", id, st->maxcount);
+
+ str = msi_string_lookup_id( st, id );
+ if( !str )
+ return ERROR_FUNCTION_FAILED;
+
+ len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL );
+
+ if( !buffer )
+ {
+ *sz = len;
+ return ERROR_SUCCESS;
+ }
+
+ if( len > *sz )
+ {
+ n = strlenW( str ) + 1;
+ while( n && (len > *sz) )
+ len = WideCharToMultiByte( st->codepage, 0,
+ str, --n, NULL, 0, NULL, NULL );
+ }
+ else
+ n = -1;
+
+ *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, 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( string_table *st, LPCWSTR str, UINT *id )
+{
+ UINT hash;
+ UINT i, r = ERROR_INVALID_PARAMETER;
+
+ hash = msistring_makehash( str );
+ for( i=0; i<st->maxcount; i++ )
+ {
+ if ( (str == NULL && st->strings[i].str == NULL) ||
+ ( ( st->strings[i].hash == hash ) &&
+ !strcmpW( st->strings[i].str, str ) ))
+ {
+ r = ERROR_SUCCESS;
+ *id = i;
+ break;
+ }
+ }
+
+ return r;
+}
+
+UINT msi_string2idA( 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 = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
+ if( !str )
+ return ERROR_NOT_ENOUGH_MEMORY;
+ MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz );
+
+ r = msi_string2idW( st, str, id );
+ if( str )
+ HeapFree( GetProcessHeap(), 0, str );
+
+ return r;
+}
+
+UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res )
+{
+ const WCHAR *l_str, *r_str;
+
+ l_str = msi_string_lookup_id( st, lval );
+ if( !l_str )
+ return ERROR_INVALID_PARAMETER;
+
+ r_str = msi_string_lookup_id( st, rval );
+ if( !r_str )
+ return ERROR_INVALID_PARAMETER;
+
+ /* does this do the right thing for all UTF-8 strings? */
+ *res = strcmpW( l_str, r_str );
+
+ return ERROR_SUCCESS;
+}
+
+UINT msi_string_count( string_table *st )
+{
+ return st->maxcount;
+}
+
+UINT msi_id_refcount( string_table *st, UINT i )
+{
+ if( i >= st->maxcount )
+ return 0;
+ return st->strings[i].refcount;
+}
+
+UINT msi_string_totalsize( string_table *st, UINT *total )
+{
+ UINT size = 0, i, len;
+
+ if( st->strings[0].str || st->strings[0].refcount )
+ ERR("oops. element 0 has a string\n");
+ *total = 0;
+ for( i=1; i<st->maxcount; i++ )
+ {
+ 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--;
+ size += len;
+ *total = (i+1);
+ }
+ }
+ TRACE("%u/%u strings %u bytes codepage %x\n", *total, st->maxcount, size, st->codepage );
+ return size;
+}
+
+UINT msi_string_get_codepage( string_table *st )
+{
+ return st->codepage;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#define PRSPEC_PROPID (1)
+
+#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 "objidl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+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 void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg )
+{
+ MSISUMMARYINFO *suminfo = (MSISUMMARYINFO *) arg;
+ IPropertyStorage_Release( suminfo->propstg );
+}
+
+UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase,
+ LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo)
+{
+ LPWSTR szwDatabase = NULL;
+ UINT ret;
+
+ TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase),
+ uiUpdateCount, phSummaryInfo);
+
+ if( szDatabase )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szDatabase, -1, NULL, 0 );
+ szwDatabase = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ if( !szwDatabase )
+ return ERROR_FUNCTION_FAILED;
+ MultiByteToWideChar( CP_ACP, 0, szDatabase, -1, szwDatabase, len );
+ }
+
+ ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, phSummaryInfo);
+
+ if( szwDatabase )
+ HeapFree( GetProcessHeap(), 0, szwDatabase );
+
+ return ret;
+}
+
+UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE hDatabase,
+ LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo)
+{
+ HRESULT r;
+ MSIHANDLE handle;
+ MSISUMMARYINFO *suminfo;
+ MSIDATABASE *db;
+ UINT ret = ERROR_SUCCESS;
+ IPropertySetStorage *psstg = NULL;
+ IPropertyStorage *ps = NULL;
+ DWORD grfMode;
+
+ TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase),
+ uiUpdateCount, phSummaryInfo);
+
+ if( !phSummaryInfo )
+ return ERROR_INVALID_PARAMETER;
+
+ if( szDatabase )
+ {
+ UINT res;
+
+ res = MSI_OpenDatabaseW(szDatabase, NULL, &db);
+ if( res != ERROR_SUCCESS )
+ return res;
+ }
+ else
+ {
+ db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
+ if( !db )
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ r = IStorage_QueryInterface( db->storage,
+ &IID_IPropertySetStorage, (LPVOID)&psstg);
+ if( FAILED( r ) )
+ {
+ ERR("IStorage -> IPropertySetStorage failed\n");
+ if (db)
+ msiobj_release(&db->hdr);
+ return ERROR_FUNCTION_FAILED;
+ }
+ ERR("storage = %p propertysetstorage = %p\n", db->storage, psstg);
+
+ grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
+ r = IPropertySetStorage_Open( psstg, &FMTID_SummaryInformation, grfMode, &ps );
+ if( FAILED( r ) )
+ {
+ ERR("failed to get IPropertyStorage r=%08lx\n",r);
+ ret = ERROR_FUNCTION_FAILED;
+ goto end;
+ }
+
+ suminfo = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO,
+ sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
+ if( !suminfo )
+ {
+ ret = ERROR_FUNCTION_FAILED;
+ goto end;
+ }
+
+ IPropertyStorage_AddRef(ps);
+ suminfo->propstg = ps;
+ handle = alloc_msihandle( &suminfo->hdr );
+ if( handle )
+ *phSummaryInfo = handle;
+ else
+ ret = ERROR_FUNCTION_FAILED;
+ msiobj_release( &suminfo->hdr );
+
+end:
+ if( ps )
+ IPropertyStorage_Release(ps);
+ if( psstg )
+ IPropertySetStorage_Release(psstg);
+ if (db)
+ msiobj_release(&db->hdr);
+
+ return ret;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
+{
+ MSISUMMARYINFO *suminfo;
+
+ FIXME("%ld %p\n",hSummaryInfo, pCount);
+
+ suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
+ if( !suminfo )
+ return ERROR_INVALID_HANDLE;
+
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyA(
+ MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue,
+ FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
+{
+ MSISUMMARYINFO *suminfo;
+ HRESULT r;
+ PROPSPEC spec;
+ PROPVARIANT var;
+
+ TRACE("%ld %d %p %p %p %p %p\n",
+ hSummaryInfo, uiProperty, puiDataType, piValue,
+ pftValue, szValueBuf, pcchValueBuf);
+
+ suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
+ if( !suminfo )
+ return ERROR_INVALID_HANDLE;
+
+ spec.ulKind = PRSPEC_PROPID;
+ spec.u.propid = uiProperty;
+
+ r = IPropertyStorage_ReadMultiple( suminfo->propstg, 1, &spec, &var);
+ if( FAILED(r) )
+ return ERROR_FUNCTION_FAILED;
+
+ if( puiDataType )
+ *puiDataType = var.vt;
+
+ switch( var.vt )
+ {
+ case VT_I4:
+ if( piValue )
+ *piValue = var.u.lVal;
+ break;
+ case VT_LPSTR:
+ if( pcchValueBuf && szValueBuf )
+ {
+ lstrcpynA(szValueBuf, var.u.pszVal, *pcchValueBuf );
+ *pcchValueBuf = lstrlenA( var.u.pszVal );
+ }
+ break;
+ case VT_FILETIME:
+ if( pftValue )
+ memcpy(pftValue, &var.u.filetime, sizeof (FILETIME) );
+ break;
+ case VT_EMPTY:
+ break;
+ default:
+ FIXME("Unknown property variant type\n");
+ break;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyW(
+ MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue,
+ FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
+{
+ FIXME("%ld %d %p %p %p %p %p\n",
+ hSummaryInfo, uiProperty, puiDataType, piValue,
+ pftValue, szValueBuf, pcchValueBuf);
+
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#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 "wine/unicode.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSICOLUMNINFO
+{
+ LPWSTR tablename;
+ UINT number;
+ LPWSTR colname;
+ UINT type;
+ UINT offset;
+} MSICOLUMNINFO;
+
+struct tagMSITABLE
+{
+ USHORT **data;
+ UINT ref_count;
+ UINT row_count;
+ struct tagMSITABLE *next;
+ struct tagMSITABLE *prev;
+ WCHAR name[1];
+};
+
+#define MAX_STREAM_NAME 0x1f
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
+ MSICOLUMNINFO **pcols, UINT *pcount );
+static UINT get_tablecolumns( MSIDATABASE *db,
+ LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
+
+static inline UINT bytes_per_column( MSICOLUMNINFO *col )
+{
+ if( col->type & MSITYPE_STRING )
+ return 2;
+ if( (col->type & 0xff) > 4 )
+ ERR("Invalid column size!\n");
+ return col->type & 0xff;
+}
+
+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;
+}
+
+static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
+{
+ DWORD count = MAX_STREAM_NAME;
+ DWORD ch, next;
+ LPWSTR out, p;
+
+ if( !bTable )
+ count = strlenW( in )+2;
+ out = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
+ 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 >= 0 )
+ {
+ next += 0x3ffffc0;
+ ch += (next<<6);
+ in++;
+ }
+ }
+ }
+ *p++ = ch;
+ }
+ ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
+ HeapFree( GetProcessHeap(), 0, 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 '_';
+}
+
+static BOOL decode_streamname(LPWSTR 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 );
+ ERR("stream %2ld -> %s %s\n", n,
+ debugstr_w(stat.pwcsName), debugstr_w(name) );
+ n++;
+ }
+
+ IEnumSTATSTG_Release( stgenum );
+}
+
+static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
+ USHORT **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(TRUE, stname);
+
+ TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+ r = IStorage_OpenStream(stg, encname, NULL,
+ STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
+ HeapFree( GetProcessHeap(), 0, encname );
+ if( FAILED( r ) )
+ {
+ WARN("open stream failed r = %08lx - empty table?\n",r);
+ return ret;
+ }
+
+ r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
+ if( FAILED( r ) )
+ {
+ ERR("open stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ if( stat.cbSize.QuadPart >> 32 )
+ {
+ ERR("Too big!\n");
+ goto end;
+ }
+
+ sz = stat.cbSize.QuadPart;
+ data = HeapAlloc( GetProcessHeap(), 0, sz );
+ if( !data )
+ {
+ ERR("couldn't allocate memory r=%08lx!\n",r);
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ goto end;
+ }
+
+ r = IStream_Read(stm, data, sz, &count );
+ if( FAILED( r ) || ( count != sz ) )
+ {
+ HeapFree( GetProcessHeap(), 0, data );
+ ERR("read stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ *pdata = data;
+ *psz = sz;
+ ret = ERROR_SUCCESS;
+
+end:
+ IStream_Release( stm );
+
+ return ret;
+}
+
+UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
+{
+ LPWSTR encname;
+ HRESULT r;
+
+ encname = encode_streamname(FALSE, stname);
+
+ TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+ r = IStorage_OpenStream(db->storage, encname, NULL,
+ STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
+ HeapFree( GetProcessHeap(), 0, encname );
+ if( FAILED( r ) )
+ {
+ WARN("open stream failed r = %08lx - empty table?\n",r);
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
+ USHORT **pdata, UINT *psz )
+{
+ HRESULT r;
+ UINT ret = ERROR_FUNCTION_FAILED;
+ VOID *data;
+ ULONG sz, count;
+ IStream *stm = NULL;
+ STATSTG stat;
+
+ r = db_get_raw_stream( db, stname, &stm );
+ if( r != ERROR_SUCCESS)
+ return ret;
+ r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
+ if( FAILED( r ) )
+ {
+ ERR("open stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ if( stat.cbSize.QuadPart >> 32 )
+ {
+ ERR("Too big!\n");
+ goto end;
+ }
+
+ sz = stat.cbSize.QuadPart;
+ data = HeapAlloc( GetProcessHeap(), 0, sz );
+ if( !data )
+ {
+ ERR("couldn't allocate memory r=%08lx!\n",r);
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ goto end;
+ }
+
+ r = IStream_Read(stm, data, sz, &count );
+ if( FAILED( r ) || ( count != sz ) )
+ {
+ HeapFree( GetProcessHeap(), 0, data );
+ ERR("read stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ *pdata = data;
+ *psz = sz;
+ ret = ERROR_SUCCESS;
+
+end:
+ IStream_Release( stm );
+
+ return ret;
+}
+
+static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
+ LPVOID data, UINT sz )
+{
+ HRESULT r;
+ UINT ret = ERROR_FUNCTION_FAILED;
+ ULONG count;
+ IStream *stm = NULL;
+ ULARGE_INTEGER size;
+ LARGE_INTEGER pos;
+ LPWSTR encname;
+
+ encname = encode_streamname(TRUE, stname );
+ r = IStorage_OpenStream( stg, encname, NULL,
+ STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+ HeapFree( GetProcessHeap(), 0, encname );
+ if( FAILED(r) )
+ {
+ r = IStorage_CreateStream( stg, encname,
+ STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+ }
+ if( FAILED( r ) )
+ {
+ ERR("open stream failed r = %08lx\n",r);
+ return ret;
+ }
+
+ size.QuadPart = sz;
+ r = IStream_SetSize( stm, size );
+ if( FAILED( r ) )
+ {
+ ERR("Failed to SetSize\n");
+ goto end;
+ }
+
+ pos.QuadPart = 0;
+ r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
+ if( FAILED( r ) )
+ {
+ ERR("Failed to Seek\n");
+ goto end;
+ }
+
+ r = IStream_Write(stm, data, sz, &count );
+ if( FAILED( r ) || ( count != sz ) )
+ {
+ ERR("Failed to Write\n");
+ goto end;
+ }
+
+ ret = ERROR_SUCCESS;
+
+end:
+ IStream_Release( stm );
+
+ return ret;
+}
+
+UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+ MSITABLE *t;
+ USHORT *rawdata = NULL;
+ UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
+ MSICOLUMNINFO *cols, *last_col;
+
+ TRACE("%s\n",debugstr_w(name));
+
+ /* non-existing tables should be interpretted as empty tables */
+ t = HeapAlloc( GetProcessHeap(), 0,
+ sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
+ if( !t )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ r = table_get_column_info( db, name, &cols, &num_cols );
+ if( r != ERROR_SUCCESS )
+ {
+ HeapFree( GetProcessHeap(), 0, t );
+ return r;
+ }
+ last_col = &cols[num_cols-1];
+ row_size = last_col->offset + bytes_per_column( last_col );
+
+ t->row_count = 0;
+ t->data = NULL;
+ lstrcpyW( t->name, name );
+ t->ref_count = 1;
+ *ptable = t;
+
+ /* if we can't read the table, just assume that it's empty */
+ read_stream_data( db->storage, name, &rawdata, &rawsize );
+ if( !rawdata )
+ return ERROR_SUCCESS;
+
+ TRACE("Read %d bytes\n", rawsize );
+
+ if( rawsize % row_size )
+ {
+ ERR("Table size is invalid %d/%d\n", rawsize, row_size );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ t->row_count = rawsize / row_size;
+ t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ t->row_count * sizeof (USHORT*) );
+ if( !t->data )
+ return ERROR_NOT_ENOUGH_MEMORY; /* FIXME: memory leak */
+
+ /* transpose all the data */
+ TRACE("Transposing data from %d columns\n", t->row_count );
+ for( i=0; i<t->row_count; i++ )
+ {
+ t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
+ if( !t->data[i] )
+ return ERROR_NOT_ENOUGH_MEMORY; /* FIXME: memory leak */
+ for( j=0; j<num_cols; j++ )
+ {
+ UINT ofs = cols[j].offset/2;
+ UINT n = bytes_per_column( &cols[j] );
+
+ switch( n )
+ {
+ case 2:
+ t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
+ break;
+ case 4:
+ t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
+ t->data[i][ofs+1] = rawdata[ofs*t->row_count + i + 1];
+ break;
+ default:
+ ERR("oops - unknown column width %d\n", n);
+ return ERROR_FUNCTION_FAILED;
+ }
+ }
+ }
+
+ HeapFree( GetProcessHeap(), 0, cols );
+ HeapFree( GetProcessHeap(), 0, rawdata );
+
+ return ERROR_SUCCESS;
+}
+
+/* add this table to the list of cached tables in the database */
+void add_table(MSIDATABASE *db, MSITABLE *table)
+{
+ table->next = db->first_table;
+ table->prev = NULL;
+ if( db->first_table )
+ db->first_table->prev = table;
+ else
+ db->last_table = table;
+ db->first_table = table;
+}
+
+/* remove from the list of cached tables */
+void remove_table( MSIDATABASE *db, MSITABLE *table )
+{
+ if( table->next )
+ table->next->prev = table->prev;
+ else
+ db->last_table = table->prev;
+ if( table->prev )
+ table->prev->next = table->next;
+ else
+ db->first_table = table->next;
+ table->next = NULL;
+ table->prev = NULL;
+}
+
+void release_table( MSIDATABASE *db, MSITABLE *table )
+{
+ if( !table->ref_count )
+ ERR("Trying to destroy table with refcount 0\n");
+ table->ref_count --;
+ if( !table->ref_count )
+ {
+ remove_table( db, table );
+ HeapFree( GetProcessHeap(), 0, table->data );
+ HeapFree( GetProcessHeap(), 0, table );
+ TRACE("Destroyed table %s\n", debugstr_w(table->name));
+ }
+}
+
+void free_cached_tables( MSIDATABASE *db )
+{
+ while( db->first_table )
+ {
+ MSITABLE *t = db->first_table;
+
+ if ( --t->ref_count )
+ ERR("table ref count not zero for %s\n", debugstr_w(t->name));
+ remove_table( db, t );
+ HeapFree( GetProcessHeap(), 0, t->data );
+ HeapFree( GetProcessHeap(), 0, t );
+ }
+}
+
+UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+ MSITABLE *t;
+
+ for( t = db->first_table; t; t=t->next )
+ {
+ if( !lstrcmpW( name, t->name ) )
+ {
+ *ptable = t;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ return ERROR_FUNCTION_FAILED;
+}
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
+{
+ UINT r, column_count;
+ 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;
+
+ /* if there's no columns, there's no table */
+ if( column_count == 0 )
+ return ERROR_INVALID_PARAMETER;
+
+ TRACE("Table %s found\n", debugstr_w(name) );
+
+ columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
+ if( !columns )
+ return ERROR_FUNCTION_FAILED;
+
+ r = get_tablecolumns( db, name, columns, &column_count );
+ if( r != ERROR_SUCCESS )
+ {
+ HeapFree( GetProcessHeap(), 0, columns );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ *pcols = columns;
+ *pcount = column_count;
+
+ return r;
+}
+
+UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+ UINT r;
+
+ *ptable = NULL;
+
+ /* first, see if the table is cached */
+ r = find_cached_table( db, name, ptable );
+ if( r == ERROR_SUCCESS )
+ {
+ (*ptable)->ref_count++;
+ return r;
+ }
+
+ r = read_table_from_storage( db, name, ptable );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ /* add the table to the list */
+ add_table( db, *ptable );
+ (*ptable)->ref_count++;
+
+ return ERROR_SUCCESS;
+}
+
+UINT save_table( MSIDATABASE *db, MSITABLE *t )
+{
+ USHORT *rawdata = NULL, *p;
+ UINT rawsize, r, i, j, row_size, num_cols = 0;
+ MSICOLUMNINFO *cols, *last_col;
+
+ TRACE("Saving %s\n", debugstr_w( t->name ) );
+
+ r = table_get_column_info( db, t->name, &cols, &num_cols );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ last_col = &cols[num_cols-1];
+ row_size = last_col->offset + bytes_per_column( last_col );
+
+ rawsize = t->row_count * row_size;
+ rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
+ if( !rawdata )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ p = rawdata;
+ for( i=0; i<num_cols; i++ )
+ {
+ for( j=0; j<t->row_count; j++ )
+ {
+ UINT offset = cols[i].offset;
+
+ *p++ = t->data[j][offset/2];
+ if( 4 == bytes_per_column( &cols[i] ) )
+ *p++ = t->data[j][offset/2+1];
+ }
+ }
+
+ TRACE("writing %d bytes\n", rawsize);
+ r = write_stream_data( db->storage, t->name, rawdata, rawsize );
+
+ HeapFree( GetProcessHeap(), 0, rawdata );
+
+ return r;
+}
+
+HRESULT init_string_table( IStorage *stg )
+{
+ HRESULT r;
+ 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 };
+ USHORT zero[2] = { 0, 0 };
+ ULONG count = 0;
+ IStream *stm = NULL;
+ LPWSTR encname;
+
+ encname = encode_streamname(TRUE, szStringPool );
+
+ /* create the StringPool stream... add the zero string to it*/
+ r = IStorage_CreateStream( stg, encname,
+ STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+ HeapFree( GetProcessHeap(), 0, encname );
+ if( r )
+ {
+ TRACE("Failed\n");
+ return r;
+ }
+
+ r = IStream_Write(stm, zero, sizeof zero, &count );
+ IStream_Release( stm );
+
+ if( FAILED( r ) || ( count != sizeof zero ) )
+ {
+ TRACE("Failed\n");
+ return E_FAIL;
+ }
+
+ /* create the StringData stream... make it zero length */
+ encname = encode_streamname(TRUE, szStringData );
+ r = IStorage_CreateStream( stg, encname,
+ STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+ HeapFree( GetProcessHeap(), 0, encname );
+ if( r )
+ {
+ TRACE("Failed\n");
+ return E_FAIL;
+ }
+ IStream_Release( stm );
+
+ return r;
+}
+
+UINT load_string_table( MSIDATABASE *db )
+{
+ CHAR *data;
+ USHORT *pool;
+ UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
+ DWORD i, count, offset, len, n;
+ 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 };
+
+ if( db->strings )
+ {
+ msi_destroy_stringtable( db->strings );
+ db->strings = NULL;
+ }
+
+ r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
+ if( r != ERROR_SUCCESS)
+ goto end;
+ r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
+ if( r != ERROR_SUCCESS)
+ goto end;
+
+ count = poolsize/4;
+ if( poolsize > 4 )
+ codepage = pool[0] | ( pool[1] << 16 );
+ else
+ codepage = CP_ACP;
+ db->strings = msi_init_stringtable( count, codepage );
+
+ offset = 0;
+ for( i=1; i<count; i++ )
+ {
+ len = pool[i*2];
+ n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
+ if( n != i )
+ ERR("Failed to add string %ld\n", i );
+ offset += len;
+ }
+
+ TRACE("Loaded %ld strings\n", count);
+
+ ret = ERROR_SUCCESS;
+
+end:
+ if( pool )
+ HeapFree( GetProcessHeap(), 0, pool );
+ if( data )
+ HeapFree( GetProcessHeap(), 0, data );
+
+ return ret;
+}
+
+UINT save_string_table( MSIDATABASE *db )
+{
+ UINT i, count, datasize, poolsize, sz, used, r, codepage;
+ UINT ret = ERROR_FUNCTION_FAILED;
+ 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 };
+ CHAR *data = NULL;
+ USHORT *pool = NULL;
+
+ TRACE("\n");
+
+ /* construct the new table in memory first */
+ datasize = msi_string_totalsize( db->strings, &count );
+ poolsize = count*2*sizeof(USHORT);
+
+ pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
+ if( ! pool )
+ {
+ ERR("Failed to alloc pool %d bytes\n", poolsize );
+ goto err;
+ }
+ data = HeapAlloc( GetProcessHeap(), 0, datasize );
+ if( ! data )
+ {
+ ERR("Failed to alloc data %d bytes\n", poolsize );
+ goto err;
+ }
+
+ used = 0;
+ codepage = msi_string_get_codepage( db->strings );
+ pool[0]=codepage&0xffff;
+ pool[1]=(codepage>>16);
+ for( i=1; i<count; i++ )
+ {
+ sz = datasize - used;
+ r = msi_id2stringA( db->strings, i, data+used, &sz );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("failed to fetch string\n");
+ sz = 0;
+ }
+ if( sz && (sz < (datasize - used ) ) )
+ sz--;
+ TRACE("adding %u bytes %s\n", sz, data+used );
+ pool[ i*2 ] = sz;
+ pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
+ 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( db->storage, szStringData, data, datasize );
+ TRACE("Wrote StringData r=%08x\n", r);
+ if( r )
+ goto err;
+ r = write_stream_data( db->storage, szStringPool, pool, poolsize );
+ TRACE("Wrote StringPool r=%08x\n", r);
+ if( r )
+ goto err;
+
+ ret = ERROR_SUCCESS;
+
+err:
+ if( data )
+ HeapFree( GetProcessHeap(), 0, data );
+ if( pool )
+ HeapFree( GetProcessHeap(), 0, pool );
+
+ return ret;
+}
+
+static LPWSTR strdupW( LPCWSTR str )
+{
+ UINT len = lstrlenW( str ) + 1;
+ LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
+ if( ret )
+ lstrcpyW( ret, str );
+ return ret;
+}
+
+/* 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 szName[] = { 'N','a','m','e',0 };
+static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+static const WCHAR szColumn[] = { 'C','o','l','u','m','n',0 };
+static const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 };
+static const WCHAR szType[] = { 'T','y','p','e',0 };
+
+struct standard_table {
+ LPCWSTR tablename;
+ LPCWSTR columnname;
+ UINT number;
+ UINT type;
+} MSI_standard_tables[] =
+{
+ { szTables, szName, 1, MSITYPE_VALID | MSITYPE_STRING | 32},
+ { szColumns, szTable, 1, MSITYPE_VALID | MSITYPE_STRING | 32},
+ { szColumns, szNumber, 2, MSITYPE_VALID | 2},
+ { szColumns, szName, 3, MSITYPE_VALID | MSITYPE_STRING | 32},
+ { szColumns, szType, 4, MSITYPE_VALID | 2},
+};
+
+#define STANDARD_TABLE_COUNT \
+ (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
+
+UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+ DWORD i, n=0;
+
+ for(i=0; i<STANDARD_TABLE_COUNT; i++)
+ {
+ if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
+ continue;
+ if(colinfo && (n < *sz) )
+ {
+ colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
+ colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
+ colinfo[n].number = MSI_standard_tables[i].number;
+ colinfo[n].type = MSI_standard_tables[i].type;
+ /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
+ debugstr_w(colinfo[n].colname)); */
+ if( n )
+ colinfo[n].offset = colinfo[n-1].offset
+ + bytes_per_column( &colinfo[n-1] );
+ else
+ colinfo[n].offset = 0;
+ }
+ n++;
+ if( colinfo && (n >= *sz) )
+ break;
+ }
+ *sz = n;
+ return ERROR_SUCCESS;
+}
+
+LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
+{
+ UINT sz=0, r;
+ LPWSTR str;
+
+ r = msi_id2stringW( db->strings, stringid, NULL, &sz );
+ if( r != ERROR_SUCCESS )
+ return NULL;
+ str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
+ if( !str )
+ return str;
+ r = msi_id2stringW( db->strings, stringid, str, &sz );
+ if( r == ERROR_SUCCESS )
+ return str;
+ HeapFree( GetProcessHeap(), 0, str );
+ return NULL;
+}
+
+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;
+ static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+
+ /* first check if there is a default table with that name */
+ r = get_defaulttablecolumns( szTableName, colinfo, sz );
+ if( ( r == ERROR_SUCCESS ) && *sz )
+ return r;
+
+ r = get_table( db, szColumns, &table);
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("table %s not available\n", debugstr_w(szColumns));
+ return r;
+ }
+
+ /* convert table and column names to IDs from the string table */
+ r = msi_string2idW( db->strings, szTableName, &table_id );
+ if( r != ERROR_SUCCESS )
+ {
+ release_table( db, table );
+ ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
+ return r;
+ }
+
+ TRACE("Table id is %d\n", table_id);
+
+ count = table->row_count;
+ for( i=0; i<count; i++ )
+ {
+ if( table->data[ i ][ 0 ] != table_id )
+ continue;
+ if( colinfo )
+ {
+ UINT id = table->data[ i ] [ 2 ];
+ colinfo[n].tablename = MSI_makestring( db, table_id );
+ colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
+ colinfo[n].colname = MSI_makestring( db, id );
+ colinfo[n].type = table->data[ i ] [ 3 ];
+ /* this assumes that columns are in order in the table */
+ if( n )
+ colinfo[n].offset = colinfo[n-1].offset
+ + bytes_per_column( &colinfo[n-1] );
+ else
+ colinfo[n].offset = 0;
+ TRACE("table %s column %d is [%s] (%d) with type %08x "
+ "offset %d at row %d\n", debugstr_w(szTableName),
+ colinfo[n].number, debugstr_w(colinfo[n].colname),
+ id, colinfo[n].type, colinfo[n].offset, i);
+ if( n != (colinfo[n].number-1) )
+ {
+ ERR("oops. data in the _Columns table isn't in the right "
+ "order for table %s\n", debugstr_w(szTableName));
+ return ERROR_FUNCTION_FAILED;
+ }
+ }
+ n++;
+ if( colinfo && ( n >= maxcount ) )
+ break;
+ }
+ *sz = n;
+
+ release_table( db, table );
+
+ return ERROR_SUCCESS;
+}
+
+/* try to find the table name in the _Tables table */
+BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
+{
+ static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
+ static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+ UINT r, table_id = 0, i, count;
+ MSITABLE *table = NULL;
+
+ if( !lstrcmpW( name, szTables ) )
+ return TRUE;
+ if( !lstrcmpW( name, szColumns ) )
+ 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;
+ }
+
+ /* count = table->size/2; */
+ count = table->row_count;
+ for( i=0; i<count; i++ )
+ if( table->data[ i ][ 0 ] == table_id )
+ break;
+
+ release_table( db, table );
+
+ if (i!=count)
+ return TRUE;
+
+ ERR("Searched %d tables, but %d was not found\n", count, table_id );
+
+ 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, num_rows, n;
+
+ if( !tv->table )
+ return ERROR_INVALID_PARAMETER;
+
+ if( (col==0) || (col>tv->num_cols) )
+ return ERROR_INVALID_PARAMETER;
+
+ /* how many rows are there ? */
+ num_rows = tv->table->row_count;
+ if( row >= num_rows )
+ 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;
+ }
+
+ offset = row + (tv->columns[col-1].offset/2) * num_rows;
+ n = bytes_per_column( &tv->columns[col-1] );
+ switch( n )
+ {
+ case 4:
+ offset = tv->columns[col-1].offset/2;
+ *val = tv->table->data[row][offset] +
+ (tv->table->data[row][offset + 1] << 16);
+ break;
+ case 2:
+ offset = tv->columns[col-1].offset/2;
+ *val = tv->table->data[row][offset];
+ break;
+ default:
+ ERR("oops! what is %d bytes per column?\n", n );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ TRACE("Data [%d][%d] = %d \n", row, col, *val );
+
+ return ERROR_SUCCESS;
+}
+
+/*
+ * 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 ival = 0, refcol = 0, r;
+ LPWSTR sval;
+ LPWSTR full_name;
+ DWORD len;
+ static const WCHAR szDot[] = { '.', 0 };
+
+ if( !view->ops->fetch_int )
+ return ERROR_INVALID_PARAMETER;
+
+ /*
+ * The column marked with the type stream data seems to have a single number
+ * which references the column containing the name of the stream data
+ *
+ * Fetch the column to reference first.
+ */
+ r = view->ops->fetch_int( view, row, col, &ival );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ /* now get the column with the name of the stream */
+ r = view->ops->fetch_int( view, row, ival, &refcol );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ /* lookup the string value from the string table */
+ sval = MSI_makestring( tv->db, refcol );
+ if( !sval )
+ return ERROR_INVALID_PARAMETER;
+
+ len = strlenW( tv->name ) + 2 + strlenW( sval );
+ full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+ strcpyW( full_name, tv->name );
+ strcatW( full_name, szDot );
+ strcatW( full_name, sval );
+
+ r = db_get_raw_stream( tv->db, full_name, stm );
+ if( r )
+ ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
+ HeapFree( GetProcessHeap(), 0, full_name );
+ HeapFree( GetProcessHeap(), 0, sval );
+
+ return r;
+}
+
+static UINT TABLE_set_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;
+
+ 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->columns[col-1] );
+ switch( n )
+ {
+ case 4:
+ offset = tv->columns[col-1].offset/2;
+ tv->table->data[row][offset] = val & 0xffff;
+ tv->table->data[row][offset + 1] = (val>>16)&0xffff;
+ break;
+ case 2:
+ offset = tv->columns[col-1].offset/2;
+ tv->table->data[row][offset] = val;
+ break;
+ default:
+ ERR("oops! what is %d bytes per column?\n", n );
+ return ERROR_FUNCTION_FAILED;
+ }
+ return ERROR_SUCCESS;
+}
+
+UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
+{
+ MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+ USHORT **p, *row;
+ UINT sz;
+
+ TRACE("%p\n", view);
+
+ if( !tv->table )
+ return ERROR_INVALID_PARAMETER;
+
+ row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
+ if( !row )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ sz = (tv->table->row_count + 1) * sizeof (UINT*);
+ if( tv->table->data )
+ p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
+ else
+ p = HeapAlloc( GetProcessHeap(), 0, sz );
+ if( !p )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ tv->table->data = p;
+ tv->table->data[tv->table->row_count] = row;
+ *num = tv->table->row_count;
+ tv->table->row_count++;
+
+ return ERROR_SUCCESS;
+}
+
+static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+ MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+ UINT r;
+
+ TRACE("%p %p\n", tv, record);
+
+ if( tv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ r = get_table( tv->db, tv->name, &tv->table );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ return ERROR_SUCCESS;
+}
+
+static UINT TABLE_close( struct tagMSIVIEW *view )
+{
+ MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+ TRACE("%p\n", view );
+
+ if( !tv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ release_table( tv->db, tv->table );
+ tv->table = NULL;
+
+ 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, LPWSTR *name, UINT *type )
+{
+ 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 = strdupW( tv->columns[n-1].colname );
+ if( !*name )
+ return ERROR_FUNCTION_FAILED;
+ }
+ if( type )
+ *type = tv->columns[n-1].type;
+
+ return ERROR_SUCCESS;
+}
+
+static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ FIXME("%p %d %ld\n", view, eModifyMode, hrec );
+ return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+static UINT TABLE_delete( struct tagMSIVIEW *view )
+{
+ MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+ TRACE("%p\n", view );
+
+ if( tv->table )
+ release_table( tv->db, tv->table );
+ tv->table = NULL;
+
+ if( tv->columns )
+ {
+ UINT i;
+ for( i=0; i<tv->num_cols; i++)
+ {
+ HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
+ HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
+ }
+ HeapFree( GetProcessHeap(), 0, tv->columns );
+ }
+ tv->columns = NULL;
+
+ HeapFree( GetProcessHeap(), 0, tv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS table_ops =
+{
+ TABLE_fetch_int,
+ TABLE_fetch_stream,
+ TABLE_set_int,
+ TABLE_insert_row,
+ TABLE_execute,
+ TABLE_close,
+ TABLE_get_dimensions,
+ TABLE_get_column_info,
+ TABLE_modify,
+ TABLE_delete
+};
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
+{
+ MSITABLEVIEW *tv ;
+ UINT r, sz, column_count;
+ MSICOLUMNINFO *columns, *last_col;
+
+ TRACE("%p %s %p\n", db, debugstr_w(name), view );
+
+ /* 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;
+
+ /* if there's no columns, there's no table */
+ if( column_count == 0 )
+ return ERROR_INVALID_PARAMETER;
+
+ TRACE("Table found\n");
+
+ sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
+ tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
+ if( !tv )
+ return ERROR_FUNCTION_FAILED;
+
+ columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
+ if( !columns )
+ {
+ HeapFree( GetProcessHeap(), 0, tv );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ r = get_tablecolumns( db, name, columns, &column_count );
+ if( r != ERROR_SUCCESS )
+ {
+ HeapFree( GetProcessHeap(), 0, columns );
+ HeapFree( GetProcessHeap(), 0, tv );
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ TRACE("Table has %d columns\n", column_count);
+
+ last_col = &columns[column_count-1];
+
+ /* fill the structure */
+ tv->view.ops = &table_ops;
+ tv->db = db;
+ tv->columns = columns;
+ tv->num_cols = column_count;
+ tv->table = NULL;
+ tv->row_size = last_col->offset + bytes_per_column( last_col );
+
+ TRACE("one row is %d bytes\n", tv->row_size );
+
+ *view = (MSIVIEW*) tv;
+ lstrcpyW( tv->name, name );
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_CommitTables( MSIDATABASE *db )
+{
+ UINT r;
+ MSITABLE *table = NULL;
+
+ TRACE("%p\n",db);
+
+ r = save_string_table( db );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("failed to save string table r=%08x\n",r);
+ return r;
+ }
+
+ for( table = db->first_table; table; table = table->next )
+ {
+ r = save_table( db, table );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("failed to save table %s (r=%08x)\n",
+ debugstr_w(table->name), r);
+ return r;
+ }
+ }
+
+ /* force everything to reload next time */
+ free_cached_tables( db );
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+** 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.
+**
+*************************************************************************
+** An 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/debug.h"
+#include "winnls.h"
+#include "query.h"
+#include "sql.tab.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+** 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 char *zName; /* The keyword name */
+ int tokenType; /* The token value for this keyword */
+};
+
+/*
+** These are the keywords
+*/
+static const Keyword aKeywordTable[] = {
+ { "ABORT", TK_ABORT },
+ { "AFTER", TK_AFTER },
+ { "ALL", TK_ALL },
+ { "AND", TK_AND },
+ { "AS", TK_AS },
+ { "ASC", TK_ASC },
+ { "BEFORE", TK_BEFORE },
+ { "BEGIN", TK_BEGIN },
+ { "BETWEEN", TK_BETWEEN },
+ { "BY", TK_BY },
+ { "CASCADE", TK_CASCADE },
+ { "CASE", TK_CASE },
+ { "CHAR", TK_CHAR },
+ { "CHARACTER", TK_CHAR },
+ { "CHECK", TK_CHECK },
+ { "CLUSTER", TK_CLUSTER },
+ { "COLLATE", TK_COLLATE },
+ { "COMMIT", TK_COMMIT },
+ { "CONFLICT", TK_CONFLICT },
+ { "CONSTRAINT", TK_CONSTRAINT },
+ { "COPY", TK_COPY },
+ { "CREATE", TK_CREATE },
+ { "CROSS", TK_JOIN_KW },
+ { "DEFAULT", TK_DEFAULT },
+ { "DEFERRED", TK_DEFERRED },
+ { "DEFERRABLE", TK_DEFERRABLE },
+ { "DELETE", TK_DELETE },
+ { "DELIMITERS", TK_DELIMITERS },
+ { "DESC", TK_DESC },
+ { "DISTINCT", TK_DISTINCT },
+ { "DROP", TK_DROP },
+ { "END", TK_END },
+ { "EACH", TK_EACH },
+ { "ELSE", TK_ELSE },
+ { "EXCEPT", TK_EXCEPT },
+ { "EXPLAIN", TK_EXPLAIN },
+ { "FAIL", TK_FAIL },
+ { "FOR", TK_FOR },
+ { "FOREIGN", TK_FOREIGN },
+ { "FROM", TK_FROM },
+ { "FULL", TK_JOIN_KW },
+ { "GLOB", TK_GLOB },
+ { "GROUP", TK_GROUP },
+ { "HAVING", TK_HAVING },
+ { "HOLD", TK_HOLD },
+ { "IGNORE", TK_IGNORE },
+ { "IMMEDIATE", TK_IMMEDIATE },
+ { "IN", TK_IN },
+ { "INDEX", TK_INDEX },
+ { "INITIALLY", TK_INITIALLY },
+ { "INNER", TK_JOIN_KW },
+ { "INSERT", TK_INSERT },
+ { "INSTEAD", TK_INSTEAD },
+ { "INT", TK_INT },
+ { "INTERSECT", TK_INTERSECT },
+ { "INTO", TK_INTO },
+ { "IS", TK_IS },
+ { "ISNULL", TK_ISNULL },
+ { "JOIN", TK_JOIN },
+ { "KEY", TK_KEY },
+ { "LEFT", TK_JOIN_KW },
+ { "LIKE", TK_LIKE },
+ { "LIMIT", TK_LIMIT },
+ { "LOCALIZABLE", TK_LOCALIZABLE },
+ { "LONG", TK_LONG },
+ { "LONGCHAR", TK_LONGCHAR },
+ { "MATCH", TK_MATCH },
+ { "NATURAL", TK_JOIN_KW },
+ { "NOT", TK_NOT },
+ { "NOTNULL", TK_NOTNULL },
+ { "NULL", TK_NULL },
+ { "OBJECT", TK_OBJECT },
+ { "OF", TK_OF },
+ { "OFFSET", TK_OFFSET },
+ { "ON", TK_ON },
+ { "OR", TK_OR },
+ { "ORDER", TK_ORDER },
+ { "OUTER", TK_JOIN_KW },
+ { "PRAGMA", TK_PRAGMA },
+ { "PRIMARY", TK_PRIMARY },
+ { "RAISE", TK_RAISE },
+ { "REFERENCES", TK_REFERENCES },
+ { "REPLACE", TK_REPLACE },
+ { "RESTRICT", TK_RESTRICT },
+ { "RIGHT", TK_JOIN_KW },
+ { "ROLLBACK", TK_ROLLBACK },
+ { "ROW", TK_ROW },
+ { "SELECT", TK_SELECT },
+ { "SET", TK_SET },
+ { "SHORT", TK_SHORT },
+ { "STATEMENT", TK_STATEMENT },
+ { "TABLE", TK_TABLE },
+ { "TEMP", TK_TEMP },
+ { "TEMPORARY", TK_TEMP },
+ { "THEN", TK_THEN },
+ { "TRANSACTION", TK_TRANSACTION },
+ { "TRIGGER", TK_TRIGGER },
+ { "UNION", TK_UNION },
+ { "UNIQUE", TK_UNIQUE },
+ { "UPDATE", TK_UPDATE },
+ { "USING", TK_USING },
+ { "VACUUM", TK_VACUUM },
+ { "VALUES", TK_VALUES },
+ { "VIEW", TK_VIEW },
+ { "WHEN", TK_WHEN },
+ { "WHERE", TK_WHERE },
+};
+
+#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) )
+
+/*
+** 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.
+*/
+int sqliteKeywordCode(const WCHAR *z, int n){
+ UINT i, len;
+ char buffer[0x10];
+
+ len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL );
+ for(i=0; i<len; i++)
+ buffer[i] = toupper(buffer[i]);
+ for(i=0; i<KEYWORD_COUNT; i++)
+ {
+ if(memcmp(buffer, aKeywordTable[i].zName, len))
+ continue;
+ if(strlen(aKeywordTable[i].zName) == len )
+ return aKeywordTable[i].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, 0, 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, 0, 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 i;
+ switch( *z ){
+ case ' ': case '\t': case '\n': case '\f': case '\r': {
+ for(i=1; isspace(z[i]); i++){}
+ *tokenType = TK_SPACE;
+ return i;
+ }
+ case '-': {
+ if( z[1]==0 ) return -1;
+ if( z[1]=='-' ){
+ for(i=2; z[i] && z[i]!='\n'; i++){}
+ *tokenType = TK_COMMENT;
+ return i;
+ }
+ *tokenType = TK_MINUS;
+ return 1;
+ }
+ case '(': {
+ if( z[1]=='+' && z[2]==')' ){
+ *tokenType = TK_ORACLE_OUTER_JOIN;
+ return 3;
+ }else{
+ *tokenType = TK_LP;
+ return 1;
+ }
+ }
+ case ')': {
+ *tokenType = TK_RP;
+ return 1;
+ }
+ case ';': {
+ *tokenType = TK_SEMI;
+ return 1;
+ }
+ case '+': {
+ *tokenType = TK_PLUS;
+ return 1;
+ }
+ case '*': {
+ *tokenType = TK_STAR;
+ return 1;
+ }
+ case '/': {
+ if( z[1]!='*' || z[2]==0 ){
+ *tokenType = TK_SLASH;
+ return 1;
+ }
+ for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
+ if( z[i] ) i++;
+ *tokenType = TK_COMMENT;
+ return i;
+ }
+ case '%': {
+ *tokenType = TK_REM;
+ return 1;
+ }
+ case '=': {
+ *tokenType = TK_EQ;
+ return 1 + (z[1]=='=');
+ }
+ case '<': {
+ if( z[1]=='=' ){
+ *tokenType = TK_LE;
+ return 2;
+ }else if( z[1]=='>' ){
+ *tokenType = TK_NE;
+ return 2;
+ }else if( z[1]=='<' ){
+ *tokenType = TK_LSHIFT;
+ return 2;
+ }else{
+ *tokenType = TK_LT;
+ return 1;
+ }
+ }
+ case '>': {
+ if( z[1]=='=' ){
+ *tokenType = TK_GE;
+ return 2;
+ }else if( z[1]=='>' ){
+ *tokenType = TK_RSHIFT;
+ return 2;
+ }else{
+ *tokenType = TK_GT;
+ return 1;
+ }
+ }
+ case '!': {
+ if( z[1]!='=' ){
+ *tokenType = TK_ILLEGAL;
+ return 2;
+ }else{
+ *tokenType = TK_NE;
+ return 2;
+ }
+ }
+ case '|': {
+ if( z[1]!='|' ){
+ *tokenType = TK_BITOR;
+ return 1;
+ }else{
+ *tokenType = TK_CONCAT;
+ return 2;
+ }
+ }
+ case '?': {
+ *tokenType = TK_WILDCARD;
+ return 1;
+ }
+ case ',': {
+ *tokenType = TK_COMMA;
+ return 1;
+ }
+ case '&': {
+ *tokenType = TK_BITAND;
+ return 1;
+ }
+ case '~': {
+ *tokenType = TK_BITNOT;
+ return 1;
+ }
+ case '`': case '\'': case '"': {
+ int delim = z[0];
+ for(i=1; z[i]; i++){
+ if( z[i]==delim ){
+ if( z[i+1]==delim ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ }
+ if( z[i] ) i++;
+ *tokenType = TK_STRING;
+ return i;
+ }
+ case '.': {
+ if( !isdigit(z[1]) ){
+ *tokenType = TK_DOT;
+ return 1;
+ }
+ /* Fall thru into the next case */
+ }
+ 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++){}
+ if( z[i]=='.' ){
+ i++;
+ while( isdigit(z[i]) ){ i++; }
+ *tokenType = TK_FLOAT;
+ }
+ if( (z[i]=='e' || z[i]=='E') &&
+ ( isdigit(z[i+1])
+ || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+ )
+ ){
+ i += 2;
+ while( isdigit(z[i]) ){ i++; }
+ *tokenType = TK_FLOAT;
+ }else if( z[0]=='.' ){
+ *tokenType = TK_FLOAT;
+ }
+ 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);
+ return i;
+ }
+ }
+ *tokenType = TK_ILLEGAL;
+ return 1;
+}
--- /dev/null
+/*
+ * 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 receuved a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIUPDATEVIEW
+{
+ MSIVIEW view;
+ MSIDATABASE *db;
+ MSIVIEW *wv;
+ value_list *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 n, type, val, r, row, col_count = 0, row_count = 0;
+ MSIVIEW *wv;
+
+ TRACE("%p %p\n", uv, record );
+
+ if( !record )
+ return ERROR_FUNCTION_FAILED;
+
+ wv = uv->wv;
+ if( !wv )
+ return ERROR_FUNCTION_FAILED;
+
+ r = wv->ops->execute( wv, 0 );
+ TRACE("tv execute returned %x\n", r);
+ if( r )
+ return r;
+
+ r = wv->ops->get_dimensions( wv, &row_count, &col_count );
+ if( r )
+ goto err;
+
+ for( row = 0; row < row_count; row++ )
+ {
+ for( n = 1; n <= col_count; n++ )
+ {
+ r = wv->ops->get_column_info( wv, n, NULL, &type );
+ if( r )
+ break;
+
+ if( type & MSITYPE_STRING )
+ {
+ const WCHAR *str = MSI_RecordGetString( record, n );
+ val = msi_addstringW( uv->db->strings, 0, str, -1, 1 );
+ }
+ else
+ {
+ val = MSI_RecordGetInteger( record, n );
+ val |= 0x8000;
+ }
+ r = wv->ops->set_int( wv, row, n, val );
+ if( r )
+ break;
+ }
+ }
+
+err:
+ return ERROR_SUCCESS;
+}
+
+
+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, LPWSTR *name, UINT *type )
+{
+ MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+ MSIVIEW *wv;
+
+ TRACE("%p %d %p %p\n", uv, n, name, type );
+
+ wv = uv->wv;
+ if( !wv )
+ return ERROR_FUNCTION_FAILED;
+
+ return wv->ops->get_column_info( wv, n, name, type );
+}
+
+static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+
+ TRACE("%p %d %ld\n", uv, eModifyMode, hrec );
+
+ 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 );
+ delete_value_list( uv->vals );
+ msiobj_release( &uv->db->hdr );
+ HeapFree( GetProcessHeap(), 0, uv );
+
+ return ERROR_SUCCESS;
+}
+
+
+static MSIVIEWOPS update_ops =
+{
+ UPDATE_fetch_int,
+ NULL,
+ NULL,
+ NULL,
+ UPDATE_execute,
+ UPDATE_close,
+ UPDATE_get_dimensions,
+ UPDATE_get_column_info,
+ UPDATE_modify,
+ UPDATE_delete
+};
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+ column_assignment *list, struct expr *expr )
+{
+ MSIUPDATEVIEW *uv = NULL;
+ UINT r;
+ MSIVIEW *tv = NULL, *sv = NULL, *wv = NULL;
+
+ TRACE("%p\n", uv );
+
+ r = TABLE_CreateView( db, table, &tv );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ /* add conditions first */
+ r = WHERE_CreateView( db, &wv, tv, expr );
+ if( r != ERROR_SUCCESS )
+ {
+ if( sv )
+ sv->ops->delete( tv );
+ return r;
+ }
+
+ /* then select the columns we want */
+ r = SELECT_CreateView( db, &sv, wv, list->col_list );
+ if( r != ERROR_SUCCESS )
+ {
+ if( tv )
+ tv->ops->delete( sv );
+ return r;
+ }
+
+ uv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *uv );
+ if( !uv )
+ return ERROR_FUNCTION_FAILED;
+
+ /* fill the structure */
+ uv->view.ops = &update_ops;
+ msiobj_addref( &db->hdr );
+ uv->db = db;
+ uv->vals = list->val_list;
+ uv->wv = sv;
+ *view = (MSIVIEW*) uv;
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+/*
+ * Copyright (c) 2004 Christian Costa
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define WINE_FILEDESCRIPTION_STR "Wine MSI dll"
+#define WINE_FILENAME_STR "msi.dll"
+#define WINE_FILEVERSION 2,0,2600,0
+#define WINE_FILEVERSION_STR "2.0.2600.0"
+#define WINE_PRODUCTVERSION 2,0,2600,0
+#define WINE_PRODUCTVERSION_STR "2.0.2600.0"
+
+#include "wine/wine_common_ver.rc"
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIWHEREVIEW
+{
+ MSIVIEW view;
+ MSIDATABASE *db;
+ MSIVIEW *table;
+ UINT row_count;
+ UINT *reorder;
+ struct expr *cond;
+} MSIWHEREVIEW;
+
+static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p %d %d %p\n", wv, row, col, val );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( row > wv->row_count )
+ return ERROR_NO_MORE_ITEMS;
+
+ row = wv->reorder[ row ];
+
+ return wv->table->ops->fetch_int( wv->table, row, col, val );
+}
+
+static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p %d %d %p\n", wv, row, col, stm );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( row > wv->row_count )
+ return ERROR_NO_MORE_ITEMS;
+
+ row = wv->reorder[ row ];
+
+ return wv->table->ops->fetch_stream( wv->table, row, col, stm );
+}
+
+static UINT WHERE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p %d %d %04x\n", wv, row, col, val );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( row > wv->row_count )
+ return ERROR_NO_MORE_ITEMS;
+
+ row = wv->reorder[ row ];
+
+ return wv->table->ops->set_int( wv->table, row, col, val );
+}
+
+static UINT INT_evaluate( UINT lval, UINT op, UINT rval )
+{
+ switch( op )
+ {
+ case OP_EQ:
+ return ( lval == rval );
+ case OP_AND:
+ return ( lval && rval );
+ case OP_OR:
+ return ( lval || rval );
+ case OP_GT:
+ return ( lval > rval );
+ case OP_LT:
+ return ( lval < rval );
+ case OP_LE:
+ return ( lval <= rval );
+ case OP_GE:
+ return ( lval >= rval );
+ case OP_NE:
+ return ( lval != rval );
+ case OP_ISNULL:
+ return ( !lval );
+ case OP_NOTNULL:
+ return ( lval );
+ default:
+ ERR("Unknown operator %d\n", op );
+ }
+ return 0;
+}
+
+static const WCHAR *STRING_evaluate( string_table *st,
+ MSIVIEW *table, UINT row, struct expr *expr, MSIRECORD *record )
+{
+ UINT val = 0, r;
+
+ switch( expr->type )
+ {
+ case EXPR_COL_NUMBER:
+ r = table->ops->fetch_int( table, row, expr->u.col_number, &val );
+ if( r != ERROR_SUCCESS )
+ return NULL;
+ return msi_string_lookup_id( st, val );
+
+ case EXPR_SVAL:
+ return expr->u.sval;
+
+ case EXPR_WILDCARD:
+ return MSI_RecordGetString( record, 1 );
+
+ default:
+ ERR("Invalid expression type\n");
+ break;
+ }
+ return NULL;
+}
+
+static UINT STRCMP_Evaluate( string_table *st, MSIVIEW *table, UINT row,
+ struct expr *cond, UINT *val, MSIRECORD *record )
+{
+ int sr;
+ const WCHAR *l_str, *r_str;
+
+ l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record );
+ r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record );
+ if( l_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 = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
+ ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
+ ( cond->u.expr.op == OP_GT && ( sr > 0 ) );
+
+ return ERROR_SUCCESS;
+}
+
+static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row,
+ struct expr *cond, UINT *val, MSIRECORD *record )
+{
+ UINT r, lval, rval;
+
+ if( !cond )
+ return ERROR_SUCCESS;
+
+ switch( cond->type )
+ {
+ case EXPR_COL_NUMBER:
+ return table->ops->fetch_int( table, row, cond->u.col_number, val );
+
+ case EXPR_UVAL:
+ *val = cond->u.uval;
+ return ERROR_SUCCESS;
+
+ case EXPR_COMPLEX:
+ r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record );
+ if( r != ERROR_SUCCESS )
+ return r;
+ r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record );
+ if( r != ERROR_SUCCESS )
+ return r;
+ *val = INT_evaluate( lval, cond->u.expr.op, rval );
+ return ERROR_SUCCESS;
+
+ case EXPR_STRCMP:
+ return STRCMP_Evaluate( db->strings, table, row, cond, val, record );
+
+ case EXPR_WILDCARD:
+ *val = MSI_RecordGetInteger( record, 1 );
+ return ERROR_SUCCESS;
+
+ default:
+ ERR("Invalid expression type\n");
+ break;
+ }
+
+ return ERROR_SUCCESS;
+
+}
+
+static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+ UINT count = 0, r, val, i;
+ MSIVIEW *table = wv->table;
+
+ TRACE("%p %p\n", wv, record);
+
+ if( !table )
+ return ERROR_FUNCTION_FAILED;
+
+ r = table->ops->execute( table, record );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = table->ops->get_dimensions( table, &count, NULL );
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ wv->reorder = HeapAlloc( GetProcessHeap(), 0, count*sizeof(UINT) );
+ if( !wv->reorder )
+ return ERROR_FUNCTION_FAILED;
+
+ for( i=0; i<count; i++ )
+ {
+ val = 0;
+ r = WHERE_evaluate( wv->db, table, i, wv->cond, &val, record );
+ if( r != ERROR_SUCCESS )
+ return r;
+ if( val )
+ wv->reorder[ wv->row_count ++ ] = i;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static UINT WHERE_close( struct tagMSIVIEW *view )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p\n", wv );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( wv->reorder )
+ HeapFree( GetProcessHeap(), 0, wv->reorder );
+ wv->reorder = NULL;
+
+ return wv->table->ops->close( wv->table );
+}
+
+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->table )
+ return ERROR_FUNCTION_FAILED;
+
+ if( rows )
+ {
+ if( !wv->reorder )
+ return ERROR_FUNCTION_FAILED;
+ *rows = wv->row_count;
+ }
+
+ return wv->table->ops->get_dimensions( wv->table, NULL, cols );
+}
+
+static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
+ UINT n, LPWSTR *name, UINT *type )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p %d %p %p\n", wv, n, name, type );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return wv->table->ops->get_column_info( wv->table, n, name, type );
+}
+
+static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p %d %ld\n", wv, eModifyMode, hrec );
+
+ if( !wv->table )
+ return ERROR_FUNCTION_FAILED;
+
+ return wv->table->ops->modify( wv->table, eModifyMode, hrec );
+}
+
+static UINT WHERE_delete( struct tagMSIVIEW *view )
+{
+ MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+ TRACE("%p\n", wv );
+
+ if( wv->table )
+ wv->table->ops->delete( wv->table );
+
+ if( wv->reorder )
+ HeapFree( GetProcessHeap(), 0, wv->reorder );
+ wv->reorder = NULL;
+ wv->row_count = 0;
+
+ if( wv->cond )
+ delete_expr( wv->cond );
+
+ msiobj_release( &wv->db->hdr );
+ HeapFree( GetProcessHeap(), 0, wv );
+
+ return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS where_ops =
+{
+ WHERE_fetch_int,
+ WHERE_fetch_stream,
+ WHERE_set_int,
+ NULL,
+ WHERE_execute,
+ WHERE_close,
+ WHERE_get_dimensions,
+ WHERE_get_column_info,
+ WHERE_modify,
+ WHERE_delete
+};
+
+static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
+ UINT *valid )
+{
+ UINT r, val = 0;
+
+ switch( cond->type )
+ {
+ case EXPR_COLUMN:
+ r = VIEW_find_column( table, cond->u.column, &val );
+ if( r == ERROR_SUCCESS )
+ {
+ *valid = 1;
+ cond->type = EXPR_COL_NUMBER;
+ cond->u.col_number = val;
+ }
+ else
+ {
+ *valid = 0;
+ ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
+ }
+ break;
+ case EXPR_COMPLEX:
+ r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
+ if( r != ERROR_SUCCESS )
+ return r;
+ if( !*valid )
+ return ERROR_SUCCESS;
+ r = WHERE_VerifyCondition( db, table, 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.right->type == EXPR_SVAL ) )
+ {
+ switch( cond->u.expr.op )
+ {
+ case OP_EQ:
+ case OP_GT:
+ case OP_LT:
+ 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_IVAL:
+ *valid = 1;
+ cond->type = EXPR_UVAL;
+ cond->u.uval = cond->u.ival + (1<<15);
+ 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, MSIVIEW *table,
+ struct expr *cond )
+{
+ MSIWHEREVIEW *wv = NULL;
+ UINT count = 0, r, valid = 0;
+
+ TRACE("%p\n", wv );
+
+ r = table->ops->get_dimensions( table, NULL, &count );
+ if( r != ERROR_SUCCESS )
+ {
+ ERR("can't get table dimensions\n");
+ return r;
+ }
+
+ if( cond )
+ {
+ r = WHERE_VerifyCondition( db, table, cond, &valid );
+ if( r != ERROR_SUCCESS )
+ return r;
+ if( !valid )
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ wv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *wv );
+ if( !wv )
+ return ERROR_FUNCTION_FAILED;
+
+ /* fill the structure */
+ wv->view.ops = &where_ops;
+ msiobj_addref( &db->hdr );
+ wv->db = db;
+ wv->table = table;
+ wv->row_count = 0;
+ wv->reorder = NULL;
+ wv->cond = cond;
+ *view = (MSIVIEW*) wv;
+
+ return ERROR_SUCCESS;
+}
--- /dev/null
+Only in /root/wine/wine/dlls/msi: .cvsignore
+Only in /root/wine/wine/dlls/msi: CVS
+Only in ./: Makefile
+Only in ./: Makefile.ros-template
+diff -u /root/wine/wine/dlls/msi/action.c ./action.c
+--- /root/wine/wine/dlls/msi/action.c 2004-10-27 20:43:05.000000000 -0500
++++ ./action.c 2004-10-30 12:14:17.000000000 -0500
+@@ -28,6 +28,7 @@
+
+ #include <stdarg.h>
+ #include <stdio.h>
++#include <fcntl.h>
+
+ #define COBJMACROS
+
+@@ -39,7 +40,7 @@
+ #include "fdi.h"
+ #include "msi.h"
+ #include "msiquery.h"
+-#include "msvcrt/fcntl.h"
++//#include "msvcrt/fcntl.h"
+ #include "objbase.h"
+ #include "objidl.h"
+ #include "msipriv.h"
+@@ -3477,7 +3478,7 @@
+ continue;
+ }
+
+- res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
++// res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
+ if (SUCCEEDED(res))
+ {
+ WCHAR help[MAX_PATH];
+@@ -3488,7 +3489,7 @@
+
+ resolve_folder(package,helpid,help,FALSE,FALSE,NULL);
+
+- res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
++// res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
+ if (!SUCCEEDED(res))
+ ERR("Failed to register type library %s\n",
+ debugstr_w(package->files[index].TargetPath));
+@@ -3503,8 +3504,9 @@
+ debugstr_w(package->files[index].TargetPath));
+ }
+
+- if (ptLib)
+- ITypeLib_Release(ptLib);
++ if (ptLib){
++ //ITypeLib_Release(ptLib);
++ }
+ }
+ else
+ ERR("Failed to load type library %s\n",
+Only in ./: patch.diff
+diff -u /root/wine/wine/dlls/msi/suminfo.c ./suminfo.c
+--- /root/wine/wine/dlls/msi/suminfo.c 2004-10-18 13:20:12.000000000 -0500
++++ ./suminfo.c 2004-10-30 00:09:12.000000000 -0500
+@@ -23,6 +23,8 @@
+ #define COBJMACROS
+ #define NONAMELESSUNION
+
++#define PRSPEC_PROPID (1)
++
+ #include "windef.h"
+ #include "winbase.h"
+ #include "winreg.h"