Sync to Wine-20050111:
Vitaly Lipatov <lav@etersoft.ru>
- Get real screen properties.
- Add description for MsiGetMode, MSIRUNMODE constants.
- Add ScreenX, ScreenY, ColorBits installer properties.
Mike McCormack <mike@codeweavers.com>
- Add a simple test case for MSI databases.
- Remove unneeded whitespace, indent correctly.
- Remove more fixed length buffers, rewrite functions to return
malloc'ed memory.
- Remove a lot of fixed length buffers.
- Implement thread safety for records.
- Fix selecting string columns and matching against a wildcard.
- Implement MsiRecordSetStreamA/W and add tests for records containing
streams.
- Fix records according to test cases.
- Fix transposition of 4 byte values when reading in table data from
storage.
- MsiCloseAllHandles only closes handles allocated in the calling
thread.
Francois Gouget <fgouget@free.fr>
- Assorted spelling fixes.
Aric Stewart <aric@codeweavers.com>
- Make all custom type 1 actions happen in a seperate thread and close
all handles for that thread when it exits. Honors the concept of
temporary MSI handles for custom actions.
- Properly deformat keys written to the registry.
- Because directory mappings can change between the CostFinalize step
  and the InstallFiles step we need to do a final resolution of the
  target file name before installing.
- When checking for an existing .lnk shortcut extension on the filename
do not just search for '.' but actually verify it is '.lnk'.
- CustomAction 35 should call SetTargetPath not just set the property.
- TARGETDIR and SOURCEDIR may not be entries 0 in the directory
  tables. So when resolving the folder we need to seek them out.
- When we handle SetTargetPath we need to be sure to recalculate the
  resulting paths as things with the now set Directory as the parent
  will change.
- Change how we install files so that we extract files as we need them,
cuts down on extraction time and unused files.
- Improve progress bar tracking.
- Be sure that set paths are terminated with a backslash.
- Free allocated buffers.
- Parse out the full features by using the ',' character and do
comparisons based on the full feature names.
- Continue when a duplicate component is found and loaded.
- Rework how we handle Feature and Component States. I have confirmed
  from testing that, although documented nowhere, having ADDLOCAL on
  the install line overrides INSTALLLEVEL.
- Track all files extracted from cabinents as tempfiles so they can be
  removed at the end of the install to not leave uninstalled but
  uncabbed files laying around.
- Move Install Features selection and evaluation into CostFinalize.
- Allow for end of install actions.
- Create the shortcut directory if it does not exist.
- Set the INSTALLLEVEL in CostFinalize if it is not set.
- Eliminate some fixed length buffers.
- Enable asynchronous dll custom action calls.
- Make sure to include trailing backslash in path.
- Move around and rename some functions.
- Fix incorrect return code check.
- Fix folder resolution.
- Let negative number be parsed correctly. Needed for accessing actions
with sequences such as -1.
- Added MsiSetExternalUIW.
- Include a System16Folder definition.
- Free allocated buffers.
- Blank the property buffers even if the property is not found.
- Include the trailing backslash on the Windows volume.
- Fix folder resolution.
Eric Pouech <pouech-eric@wanadoo.fr>
- Fixed some errors in function prototypes.
Steven Edwards <steven@codeweavers.com>
- Add and fix some stubs.
Ulrich Czekalla <ulrich@codeweavers.com>
- Set the out buffer count to zero on read error.
Michael Stefaniuc <mstefani@redhat.de>
- Add missing HeapFree's (found by smatch).
Paul Vriens <Paul.Vriens@xs4all.nl>
- Use Interlocked* functions in AddRef and Release.
Modified: trunk/reactos/include/wine/msiquery.h
Modified: trunk/reactos/lib/msi/Makefile.in
Modified: trunk/reactos/lib/msi/action.c
Modified: trunk/reactos/lib/msi/handle.c
Modified: trunk/reactos/lib/msi/msi.c
Modified: trunk/reactos/lib/msi/msi.spec
Modified: trunk/reactos/lib/msi/msipriv.h
Modified: trunk/reactos/lib/msi/package.c
Modified: trunk/reactos/lib/msi/query.h
Modified: trunk/reactos/lib/msi/record.c
Modified: trunk/reactos/lib/msi/sql.tab.c
Modified: trunk/reactos/lib/msi/sql.tab.h
Modified: trunk/reactos/lib/msi/sql.y
Modified: trunk/reactos/lib/msi/table.c
Modified: trunk/reactos/lib/msi/where.c

Modified: trunk/reactos/include/wine/msiquery.h
--- trunk/reactos/include/wine/msiquery.h	2005-01-12 09:03:31 UTC (rev 12947)
+++ trunk/reactos/include/wine/msiquery.h	2005-01-12 09:31:44 UTC (rev 12948)
@@ -59,7 +59,30 @@
 #define MSIDBOPEN_TRANSACT (LPCTSTR)1
 #define MSIDBOPEN_DIRECT   (LPCTSTR)2
 #define MSIDBOPEN_CREATE   (LPCTSTR)3
+#define MSIDBOPEN_CREATEDIRECT (LPCTSTR)4
 
+typedef enum tagMSIRUNMODE
+{
+    MSIRUNMODE_ADMIN = 0,
+    MSIRUNMODE_ADVERTISE = 1,
+    MSIRUNMODE_MAINTENANCE = 2,
+    MSIRUNMODE_ROLLBACKENABLED = 3,
+    MSIRUNMODE_LOGENABLED = 4,
+    MSIRUNMODE_OPERATIONS = 5,
+    MSIRUNMODE_REBOOTATEND = 6,
+    MSIRUNMODE_REBOOTNOW = 7,
+    MSIRUNMODE_CABINET = 8,
+    MSIRUNMODE_SOURCESHORTNAMES = 9,
+    MSIRUNMODE_TARGETSHORTNAMES = 10,
+    MSIRUNMODE_RESERVED11 = 11,
+    MSIRUNMODE_WINDOWS9X = 12,
+    MSIRUNMODE_ZAWENABLED = 13,
+    MSIRUNMODE_RESERVED14 = 14,
+    MSIRUNMODE_RESERVED15 = 15,
+    MSIRUNMODE_SCHEDULED = 16,
+    MSIRUNMODE_ROLLBACK = 17,
+    MSIRUNMODE_COMMIT = 18
+} MSIRUNMODE;
 
 /* view manipulation */
 UINT WINAPI MsiViewFetch(MSIHANDLE,MSIHANDLE*);
@@ -123,15 +146,12 @@
 #define     MsiEvaluateCondition WINELIB_NAME_AW(MsiEvaluateCondition)
 
 /* property functions */
-UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName,
-                           LPSTR szValueBuf, DWORD* pchValueBuf);
-UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName,
-                           LPWSTR szValueBuf, DWORD* pchValueBuf);
+UINT WINAPI MsiGetPropertyA(MSIHANDLE, LPCSTR, LPSTR, DWORD*);
+UINT WINAPI MsiGetPropertyW(MSIHANDLE, LPCWSTR, LPWSTR, DWORD*);
 #define     MsiGetProperty WINELIB_NAME_AW(MsiGetProperty)
 
-UINT WINAPI MsiSetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue);
-UINT WINAPI MsiSetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, 
-                              LPCWSTR szValue);
+UINT WINAPI MsiSetPropertyA(MSIHANDLE, LPCSTR, LPCSTR);
+UINT WINAPI MsiSetPropertyW(MSIHANDLE, LPCWSTR, LPCWSTR);
 #define     MsiSetProperty WINELIB_NAME_AW(MsiSetProperty)
 
 UINT WINAPI MsiGetTargetPathA(MSIHANDLE,LPCSTR,LPSTR,DWORD*);
@@ -148,11 +168,13 @@
 
 MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE);
 
-UINT WINAPI MsiViewGetColumnInfo( MSIHANDLE, MSICOLINFO, MSIHANDLE*);
-INT WINAPI MsiProcessMessage( MSIHANDLE, INSTALLMESSAGE, MSIHANDLE);
+UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE, MSICOLINFO, MSIHANDLE*);
+INT WINAPI MsiProcessMessage(MSIHANDLE, INSTALLMESSAGE, MSIHANDLE);
 
-UINT WINAPI MsiSetFeatureStateA( MSIHANDLE, LPCSTR, INSTALLSTATE);
-UINT WINAPI MsiSetFeatureStateW( MSIHANDLE, LPCWSTR, INSTALLSTATE);
+UINT WINAPI MsiSetFeatureStateA(MSIHANDLE, LPCSTR, INSTALLSTATE);
+UINT WINAPI MsiSetFeatureStateW(MSIHANDLE, LPCWSTR, INSTALLSTATE);
 #define     MsiSetFeatureState WINELIB_NAME_AW(MsiSetFeatureState)
 
+BOOL WINAPI MsiGetMode(MSIHANDLE, MSIRUNMODE);
+
 #endif /* __WINE_MSIQUERY_H */

Modified: trunk/reactos/lib/msi/Makefile.in
--- trunk/reactos/lib/msi/Makefile.in	2005-01-12 09:03:31 UTC (rev 12947)
+++ trunk/reactos/lib/msi/Makefile.in	2005-01-12 09:31:44 UTC (rev 12948)
@@ -3,7 +3,7 @@
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = msi.dll
-IMPORTS   = shell32 cabinet oleaut32 ole32 version user32 advapi32 kernel32
+IMPORTS   = shell32 cabinet oleaut32 ole32 version user32 gdi32 advapi32 kernel32
 EXTRALIBS = -luuid $(LIBUNICODE)
 
 C_SRCS = \
@@ -31,6 +31,8 @@
 EXTRA_SRCS = sql.y cond.y
 EXTRA_OBJS = sql.tab.o cond.tab.o
 
+SUBDIRS = tests
+
 @MAKE_DLL_RULES@
 
 sql.tab.c sql.tab.h: sql.y

Modified: trunk/reactos/lib/msi/action.c
--- trunk/reactos/lib/msi/action.c	2005-01-12 09:03:31 UTC (rev 12947)
+++ trunk/reactos/lib/msi/action.c	2005-01-12 09:31:44 UTC (rev 12948)
@@ -51,6 +51,8 @@
 #include "ver.h"
 
 #define CUSTOM_ACTION_TYPE_MASK 0x3F
+#define REG_PROGRESS_VALUE 13200
+#define COMPONENT_PROGRESS_VALUE 24000
 
 WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
@@ -65,8 +67,10 @@
     WCHAR Directory[96];
     INT Attributes;
     
-    INSTALLSTATE State;
-    BOOL Enabled;
+    INSTALLSTATE Installed;
+    INSTALLSTATE ActionRequest;
+    INSTALLSTATE Action;
+
     INT ComponentCount;
     INT Components[1024]; /* yes hardcoded limit.... I am bad */
     INT Cost;
@@ -81,21 +85,23 @@
     WCHAR Condition[0x100];
     WCHAR KeyPath[96];
 
-    INSTALLSTATE State;
-    BOOL FeatureState;
+    INSTALLSTATE Installed;
+    INSTALLSTATE ActionRequest;
+    INSTALLSTATE Action;
+
     BOOL Enabled;
     INT  Cost;
 } MSICOMPONENT;
 
 typedef struct tagMSIFOLDER
 {
-    WCHAR Directory[96];
-    WCHAR TargetDefault[96];
-    WCHAR SourceDefault[96];
+    LPWSTR Directory;
+    LPWSTR TargetDefault;
+    LPWSTR SourceDefault;
 
-    WCHAR ResolvedTarget[MAX_PATH];
-    WCHAR ResolvedSource[MAX_PATH];
-    WCHAR Property[MAX_PATH];   /* initially set property */
+    LPWSTR ResolvedTarget;
+    LPWSTR ResolvedSource;
+    LPWSTR Property;   /* initially set property */
     INT   ParentIndex;
     INT   State;
         /* 0 = uninitialized */
@@ -108,12 +114,12 @@
 
 typedef struct tagMSIFILE
 {
-    WCHAR File[72];
+    LPWSTR File;
     INT ComponentIndex;
-    WCHAR FileName[MAX_PATH];
+    LPWSTR FileName;
     INT FileSize;
-    WCHAR Version[72];
-    WCHAR Language[20];
+    LPWSTR Version;
+    LPWSTR Language;
     INT Attributes;
     INT Sequence;   
 
@@ -123,8 +129,8 @@
        /* 2 = present but replace */
        /* 3 = present do not replace */
        /* 4 = Installed */
-    WCHAR   SourcePath[MAX_PATH];
-    WCHAR   TargetPath[MAX_PATH];
+    LPWSTR  SourcePath;
+    LPWSTR  TargetPath;
     BOOL    Temporary; 
 }MSIFILE;
 
@@ -134,6 +140,7 @@
 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
 
+static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq);
 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action);
 
 static UINT ACTION_LaunchConditions(MSIPACKAGE *package);
@@ -158,15 +165,15 @@
                                 const LPWSTR target, const INT type);
 static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source, 
                                 const LPWSTR target, const INT type);
-static UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source, 
+static UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source,
                                 const LPWSTR target, const INT type);
-static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source, 
+static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source,
                                 const LPWSTR target, const INT type);
-static UINT HANDLE_CustomType34(MSIPACKAGE *package, const LPWSTR source, 
+static UINT HANDLE_CustomType34(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, 
+static LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name,
                            BOOL source, BOOL set_prop, MSIFOLDER **folder);
 
 static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
@@ -223,12 +230,9 @@
  ********************************************************/
 inline static void reduce_to_longfilename(WCHAR* filename)
 {
-    if (strchrW(filename,'|'))
-    {
-        WCHAR newname[MAX_PATH];
-        strcpyW(newname,strchrW(filename,'|')+1);
-        strcpyW(filename,newname);
-    }
+    LPWSTR p = strchrW(filename,'|');
+    if (p)
+        memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
 }
 
 inline static char *strdupWtoA( const WCHAR *str )
@@ -256,6 +260,15 @@
     return ret;
 }
 
+static LPWSTR dupstrW(LPCWSTR src)
+{
+    LPWSTR dest;
+    if (!src) return NULL;
+    dest = HeapAlloc(GetProcessHeap(), 0, (strlenW(src)+1)*sizeof(WCHAR));
+    strcpyW(dest, src);
+    return dest;
+}
+
 inline static WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
 {
     UINT rc;
@@ -263,16 +276,58 @@
     LPWSTR ret;
    
     sz = 0; 
-    rc = MSI_RecordGetStringW(row,index,NULL,&sz);
-    if (sz <= 0)
+    if (MSI_RecordIsNull(row,index))
         return NULL;
 
+    rc = MSI_RecordGetStringW(row,index,NULL,&sz);
+
+    /* having an empty string is different than NULL */
+    if (sz == 0)
+    {
+        ret = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR));
+        ret[0] = 0;
+        return ret;
+    }
+
     sz ++;
     ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
     rc = MSI_RecordGetStringW(row,index,ret,&sz);
+    if (rc!=ERROR_SUCCESS)
+    {
+        ERR("Unable to load dynamic string\n");
+        HeapFree(GetProcessHeap(), 0, ret);
+        ret = NULL;
+    }
     return ret;
 }
 
+inline static LPWSTR load_dynamic_property(MSIPACKAGE *package, LPCWSTR prop,
+                                           UINT* rc)
+{
+    DWORD sz = 0;
+    LPWSTR str;
+    UINT r;
+
+    r = MSI_GetPropertyW(package, prop, NULL, &sz);
+    if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
+    {
+        if (rc)
+            *rc = r;
+        return NULL;
+    }
+    sz++;
+    str = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
+    r = MSI_GetPropertyW(package, prop, str, &sz);
+    if (r != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(),0,str);
+        str = NULL;
+    }
+    if (rc)
+        *rc = r;
+    return str;
+}
+
 inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
 {
     int rc = -1;
@@ -321,6 +376,7 @@
     return rc;
 }
 
+
 static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
 {
     DWORD i;
@@ -343,8 +399,8 @@
 
     memset(&package->files[index],0,sizeof(MSIFILE));
 
-    strcpyW(package->files[index].File,name);
-    strcpyW(package->files[index].TargetPath,path);
+    package->files[index].File = dupstrW(name);
+    package->files[index].TargetPath = dupstrW(path);
     package->files[index].Temporary = TRUE;
 
     TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));  
@@ -362,11 +418,103 @@
     for (i = 0; i < package->loaded_files; i++)
     {
         if (package->files[i].Temporary)
+        {
+            TRACE("Cleaning up %s\n",debugstr_w(package->files[i].TargetPath));
             DeleteFileW(package->files[i].TargetPath);
+        }
 
     }
 }
 
+/* Called when the package is being closed */
+extern void ACTION_free_package_structures( MSIPACKAGE* package)
+{
+    INT i;
+    
+    TRACE("Freeing package action data\n");
+
+    /* No dynamic buffers in features */
+    if (package->features && package->loaded_features > 0)
+        HeapFree(GetProcessHeap(),0,package->features);
+
+    for (i = 0; i < package->loaded_folders; i++)
+    {
+        HeapFree(GetProcessHeap(),0,package->folders[i].Directory);
+        HeapFree(GetProcessHeap(),0,package->folders[i].TargetDefault);
+        HeapFree(GetProcessHeap(),0,package->folders[i].SourceDefault);
+        HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedTarget);
+        HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedSource);
+        HeapFree(GetProcessHeap(),0,package->folders[i].Property);
+    }
+    if (package->folders && package->loaded_folders > 0)
+        HeapFree(GetProcessHeap(),0,package->folders);
+
+    /* no dynamic buffers in components */ 
+    if (package->components && package->loaded_components > 0)
+        HeapFree(GetProcessHeap(),0,package->components);
+
+    for (i = 0; i < package->loaded_files; i++)
+    {
+        HeapFree(GetProcessHeap(),0,package->files[i].File);
+        HeapFree(GetProcessHeap(),0,package->files[i].FileName);
+        HeapFree(GetProcessHeap(),0,package->files[i].Version);
+        HeapFree(GetProcessHeap(),0,package->files[i].Language);
+        HeapFree(GetProcessHeap(),0,package->files[i].SourcePath);
+        HeapFree(GetProcessHeap(),0,package->files[i].TargetPath);
+    }
+
+    if (package->files && package->loaded_files > 0)
+        HeapFree(GetProcessHeap(),0,package->files);
+}
+
+static UINT ACTION_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
+{
+    LPWSTR szQuery;
+    LPCWSTR p;
+    UINT sz, rc;
+    va_list va;
+
+    /* figure out how much space we need to allocate */
+    va_start(va, fmt);
+    sz = strlenW(fmt) + 1;
+    p = fmt;
+    while (*p)
+    {
+        p = strchrW(p, '%');
+        if (!p)
+            break;
+        p++;
+        switch (*p)
+        {
+        case 's':  /* a string */
+            sz += strlenW(va_arg(va,LPCWSTR));
+            break;
+        case 'd':
+        case 'i':  /* an integer -2147483648 seems to be longest */
+            sz += 3*sizeof(int);
+            (void)va_arg(va,int);
+            break;
+        case '%':  /* a single % - leave it alone */
+            break;
+        default:
+            FIXME("Unhandled character type %c\n",*p);
+        }
+        p++;
+    }
+    va_end(va);
+
+    /* construct the string */
+    szQuery = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
+    va_start(va, fmt);
+    vsnprintfW(szQuery, sz, fmt, va);
+    va_end(va);
+
+    /* perform the query */
+    rc = MSI_DatabaseOpenViewW(db, szQuery, view);
+    HeapFree(GetProcessHeap(), 0, szQuery);
+    return rc;
+}
+
 static void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
 {
     MSIRECORD * row;
@@ -390,17 +538,14 @@
     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)
+    if (!package->LastAction || strcmpW(package->LastAction,action))
     {
-        sprintfW(Query,Query_t,action);
-        rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+        rc = ACTION_OpenQuery(package->db, &view, Query_t, action);
         if (rc != ERROR_SUCCESS)
             return;
+
         rc = MSI_ViewExecute(view, 0);
         if (rc != ERROR_SUCCESS)
         {
@@ -422,18 +567,22 @@
             return;
         }
 
-        if (ActionFormat)
-            HeapFree(GetProcessHeap(),0,ActionFormat);
+        /* update the cached actionformat */
+        if (package->ActionFormat)
+            HeapFree(GetProcessHeap(),0,package->ActionFormat);
+        package->ActionFormat = load_dynamic_stringW(row,3);
 
-        ActionFormat = load_dynamic_stringW(row,3);
-        strcpyW(LastAction,action);
+        if (package->LastAction)
+            HeapFree(GetProcessHeap(),0,package->LastAction);
+        package->LastAction = dupstrW(action);
+
         msiobj_release(&row->hdr);
         MSI_ViewClose(view);
         msiobj_release(&view->hdr);
     }
 
     message[0]=0;
-    ptr = ActionFormat;
+    ptr = package->ActionFormat;
     while (*ptr)
     {
         LPWSTR ptr2;
@@ -489,12 +638,10 @@
     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);
+    rc = ACTION_OpenQuery(package->db, &view, Query_t, action);
     if (rc != ERROR_SUCCESS)
         return;
     rc = MSI_ViewExecute(view, 0);
@@ -555,6 +702,58 @@
     msiobj_release(&row->hdr);
 }
 
+/*
+ *  build_directory_name()
+ *
+ *  This function is to save messing round with directory names
+ *  It handles adding backslashes between path segments, 
+ *   and can add \ at the end of the directory name if told to.
+ *
+ *  It takes a variable number of arguments.
+ *  It always allocates a new string for the result, so make sure
+ *   to free the return value when finished with it.
+ *
+ *  The first arg is the number of path segments that follow.
+ *  The arguments following count are a list of path segments.
+ *  A path segment may be NULL.
+ *
+ *  Path segments will be added with a \ separating them.
+ *  A \ will not be added after the last segment, however if the
+ *    last segment is NULL, then the last character will be a \
+ * 
+ */
+static LPWSTR build_directory_name(DWORD count, ...)
+{
+    DWORD sz = 1, i;
+    LPWSTR dir;
+    va_list va;
+
+    va_start(va,count);
+    for(i=0; i<count; i++)
+    {
+        LPCWSTR str = va_arg(va,LPCWSTR);
+        if (str)
+            sz += strlenW(str) + 1;
+    }
+    va_end(va);
+
+    dir = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
+    dir[0]=0;
+
+    va_start(va,count);
+    for(i=0; i<count; i++)
+    {
+        LPCWSTR str = va_arg(va,LPCWSTR);
+        if (!str)
+            continue;
+        strcatW(dir, str);
+        if( ((i+1)!=count) && dir[strlenW(dir)-1]!='\\')
+            strcatW(dir, cszbs);
+    }
+    return dir;
+}
+
+
 /****************************************************
  * TOP level entry points 
  *****************************************************/
@@ -569,23 +768,23 @@
 
     if (szPackagePath)   
     {
-        LPWSTR p;
-        WCHAR check[MAX_PATH];
-        WCHAR pth[MAX_PATH];
-        DWORD size;
+        LPWSTR p, check, path;
  
-        strcpyW(pth,szPackagePath);
-        p = strrchrW(pth,'\\');    
+        path = dupstrW(szPackagePath);
+        p = strrchrW(path,'\\');    
         if (p)
         {
             p++;
             *p=0;
         }
 
-        size = MAX_PATH;
-        if (MSI_GetPropertyW(package,cszSourceDir,check,&size) 
-            != ERROR_SUCCESS )
-            MSI_SetPropertyW(package, cszSourceDir, pth);
+        check = load_dynamic_property(package, cszSourceDir,NULL);
+        if (!check)
+            MSI_SetPropertyW(package, cszSourceDir, path);
+        else
+            HeapFree(GetProcessHeap(), 0, check);
+
+        HeapFree(GetProcessHeap(), 0, path);
     }
 
     if (szCommandLine)
@@ -595,8 +794,8 @@
        
         while (*ptr)
         {
-            WCHAR prop[0x100];
-            WCHAR val[0x100];
+            WCHAR *prop = NULL;
+            WCHAR *val = NULL;
 
             TRACE("Looking at %s\n",debugstr_w(ptr));
 
@@ -607,10 +806,13 @@
                 DWORD len = 0;
 
                 while (*ptr == ' ') ptr++;
-                strncpyW(prop,ptr,ptr2-ptr);
-                prop[ptr2-ptr]=0;
+                len = ptr2-ptr;
+                prop = HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
+                strncpyW(prop,ptr,len);
+                prop[len]=0;
                 ptr2++;
-            
+           
+                len = 0; 
                 ptr = ptr2; 
                 while (*ptr && (quote || (!quote && *ptr!=' ')))
                 {
@@ -625,14 +827,18 @@
                     ptr2++;
                     len -= 2;
                 }
+                val = HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR));
                 strncpyW(val,ptr2,len);
-                val[len]=0;
+                val[len] = 0;
 
                 if (strlenW(prop) > 0)
                 {
-                    TRACE("Found commandline property (%s) = (%s)\n", debugstr_w(prop), debugstr_w(val));
+                    TRACE("Found commandline property (%s) = (%s)\n", 
+                                       debugstr_w(prop), debugstr_w(val));
                     MSI_SetPropertyW(package,prop,val);
                 }
+                HeapFree(GetProcessHeap(),0,val);
+                HeapFree(GetProcessHeap(),0,prop);
             }
             ptr++;
         }
@@ -653,10 +859,93 @@
     else
         rc = ACTION_ProcessExecSequence(package,FALSE);
 
+    /* process the ending type action */
+    if (rc == ERROR_SUCCESS)
+        rc = ACTION_PerformActionSequence(package,-1);
+    else if (rc == ERROR_FUNCTION_FAILED) 
+        rc = ACTION_PerformActionSequence(package,-3);
+    
     return rc;
 }
 
+static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
+{
+    MSIQUERY * view;
+    UINT rc;
+    WCHAR buffer[0x100];
+    DWORD sz = 0x100;
+    MSIRECORD * row = 0;
+    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',0};
 
+    rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, seq);
+
+    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"); 
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* 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);
+                    goto end;
+                }
+                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);
+            goto end;
+        }
+
+        rc = ACTION_PerformAction(package,buffer);
+        msiobj_release(&row->hdr);
+end:
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+        rc = ERROR_SUCCESS;
+
+    return rc;
+}
+
 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
 {
     MSIQUERY * view;
@@ -669,7 +958,6 @@
        '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',' ',
@@ -678,11 +966,11 @@
        '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};
+    INT seq = 0;
 
+    /* get the sequence number */
     if (UIran)
     {
-        INT seq = 0;
-        
         rc = MSI_DatabaseOpenViewW(package->db, IVQuery, &view);
         if (rc != ERROR_SUCCESS)
             return rc;
@@ -704,12 +992,9 @@
         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);
+
+    rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, seq);
     if (rc == ERROR_SUCCESS)
     {
         rc = MSI_ViewExecute(view, 0);
@@ -721,7 +1006,7 @@
             goto end;
         }
        
-        TRACE("Running the actions \n"); 
+        TRACE("Running the actions\n"); 
 
         while (1)
         {
@@ -900,7 +1185,6 @@
     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)
@@ -961,21 +1245,16 @@
     UINT rc = ERROR_SUCCESS;
     MSIQUERY * view;
     MSIRECORD * row = 0;
-    WCHAR ExecSeqQuery[1024] = 
+    static const WCHAR ExecSeqQuery[] =
     {'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};
+        ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
+        ,'o','n','`',' ','=',' ','`','%','s','`',0};
     UINT type;
     LPWSTR source;
     LPWSTR target;
     WCHAR *deformated=NULL;
 
-    strcatW(ExecSeqQuery,action);
-    strcatW(ExecSeqQuery,end);
-
-    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
-
+    rc = ACTION_OpenQuery(package->db, &view, ExecSeqQuery, action);
     if (rc != ERROR_SUCCESS)
         return rc;
 
@@ -1022,6 +1301,10 @@
             rc = HANDLE_CustomType34(package,source,target,type);
             break;
         case 35: /* Directory set with formatted text. */
+            deformat_string(package,target,&deformated);
+            MSI_SetTargetPathW(package, source, deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+            break;
         case 51: /* Property set with formatted text. */
             deformat_string(package,target,&deformated);
             rc = MSI_SetPropertyW(package,source,deformated);
@@ -1063,10 +1346,9 @@
         UINT rc;
         MSIQUERY * view;
         MSIRECORD * row = 0;
-        WCHAR Query[1024] =
+        static const WCHAR fmt[] =
         {'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};
+,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`','%','s','`',0};
         HANDLE the_file;
         CHAR buffer[1024];
 
@@ -1079,10 +1361,7 @@
         if (the_file == INVALID_HANDLE_VALUE)
             return ERROR_FUNCTION_FAILED;
 
-        strcatW(Query,source);
-        strcatW(Query,end);
-
-        rc = MSI_DatabaseOpenViewW( package->db, Query, &view);
+        rc = ACTION_OpenQuery(package->db, &view, fmt, source);
         if (rc != ERROR_SUCCESS)
             return rc;
 
@@ -1127,66 +1406,82 @@
     return ERROR_SUCCESS;
 }
 
-
 typedef UINT __stdcall CustomEntry(MSIHANDLE);
 typedef struct 
 {
         MSIPACKAGE *package;
-        WCHAR target[MAX_PATH];
-        WCHAR source[MAX_PATH];
+        WCHAR *target;
+        WCHAR *source;
 } thread_struct;
 
-#if 0
-static DWORD WINAPI DllThread(LPVOID info)
+static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff)
 {
-    HANDLE DLL;
+    HANDLE hModule;
     LPSTR proc;
-    thread_struct *stuff;
     CustomEntry *fn;
-     
-    stuff = (thread_struct*)info;
 
-    TRACE("Asynchronous start (%s, %s) \n", debugstr_w(stuff->source),
+    TRACE("calling function (%s, %s) \n", debugstr_w(stuff->source),
           debugstr_w(stuff->target));
 
-    DLL = LoadLibraryW(stuff->source);
-    if (DLL)
+    hModule = LoadLibraryW(stuff->source);
+    if (hModule)
     {
         proc = strdupWtoA( stuff->target );
-        fn = (CustomEntry*)GetProcAddress(DLL,proc);
+        fn = (CustomEntry*)GetProcAddress(hModule,proc);
         if (fn)
         {
             MSIHANDLE hPackage;
             MSIPACKAGE *package = stuff->package;
 
-            TRACE("Calling function\n");
+            TRACE("Calling function %s\n", proc);
             hPackage = msiobj_findhandle( &package->hdr );
-            if( !hPackage )
+            if (hPackage )
+            {
+                fn(hPackage);
+                msiobj_release( &package->hdr );
+            }
+            else
                 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);
+        FreeLibrary(hModule);
     }
     else
         ERR("Unable to load library\n");
     msiobj_release( &stuff->package->hdr );
-    HeapFree( GetProcessHeap(), 0, info );
+    HeapFree(GetProcessHeap(),0,stuff->source);
+    HeapFree(GetProcessHeap(),0,stuff->target);
+    HeapFree(GetProcessHeap(), 0, stuff);
     return 0;
 }
-#endif
 
+static DWORD WINAPI DllThread(LPVOID info)
+{
+    thread_struct *stuff;
+    DWORD rc = 0;
+  
+    TRACE("MSI Thread (0x%lx) started for custom action\n",
+                        GetCurrentThreadId());
+    
+    stuff = (thread_struct*)info;
+    rc = ACTION_CallDllFunction(stuff);
+
+    TRACE("MSI Thread (0x%lx) finished\n",GetCurrentThreadId());
+    /* clse all handles for this thread */
+    MsiCloseAllHandles();
+    return rc;
+}
+
 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;
+    thread_struct *info;
+    DWORD ThreadId;
+    HANDLE ThreadHandle;
 
     store_binary_to_temp(package, source, tmp_file);
 
@@ -1199,76 +1494,57 @@
         strcatW(tmp_file,dot);
     } 
 
-    if (type & 0xc0)
-    {
-        /* DWORD ThreadId; */
-        thread_struct *info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
+    info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
+    msiobj_addref( &package->hdr );
+    info->package = package;
+    info->target = dupstrW(target);
+    info->source = dupstrW(tmp_file);
 
-        /* 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;
+    ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
 
-            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");
+    if (!(type & 0xc0))
+        WaitForSingleObject(ThreadHandle,INFINITE);
 
-        HeapFree(GetProcessHeap(),0,proc);
-        FreeLibrary(DLL);
-    }
-    else
-        ERR("Unable to load library\n");
-
[truncated at 1000 lines; 8611 more skipped]