Sync to trunk (r44371)
[reactos.git] / reactos / dll / win32 / msi / action.c
index 5fbac06..35a4d7f 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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>
 
 #define COBJMACROS
 
-#include "stdio.h"
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "winreg.h"
+#include "winsvc.h"
+#include "odbcinst.h"
 #include "wine/debug.h"
 #include "msidefs.h"
 #include "msipriv.h"
 #include "winuser.h"
 #include "shlobj.h"
+#include "objbase.h"
+#include "mscoree.h"
+#include "fusion.h"
+#include "shlwapi.h"
 #include "wine/unicode.h"
 #include "winver.h"
-#include "action.h"
 
 #define REG_PROGRESS_VALUE 13200
 #define COMPONENT_PROGRESS_VALUE 24000
 
 WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
-/*
- * Prototypes
- */
-static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
-static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
-static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
-
 /*
  * consts and values used
  */
@@ -65,13 +54,8 @@ static const WCHAR szCreateFolders[] =
     {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
 static const WCHAR szCostFinalize[] =
     {'C','o','s','t','F','i','n','a','l','i','z','e',0};
-const WCHAR szInstallFiles[] =
-    {'I','n','s','t','a','l','l','F','i','l','e','s',0};
-const WCHAR szDuplicateFiles[] =
-    {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
 static const WCHAR szWriteRegistryValues[] =
-    {'W','r','i','t','e','R','e','g','i','s','t','r','y',
-            'V','a','l','u','e','s',0};
+    {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
 static const WCHAR szCostInitialize[] =
     {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
 static const WCHAR szFileCost[] = 
@@ -85,12 +69,7 @@ static const WCHAR szLaunchConditions[] =
 static const WCHAR szProcessComponents[] = 
     {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
 static const WCHAR szRegisterTypeLibraries[] = 
-    {'R','e','g','i','s','t','e','r','T','y','p','e',
-            'L','i','b','r','a','r','i','e','s',0};
-const WCHAR szRegisterClassInfo[] = 
-    {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
-const WCHAR szRegisterProgIdInfo[] = 
-    {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
+    {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
 static const WCHAR szCreateShortcuts[] = 
     {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
 static const WCHAR szPublishProduct[] = 
@@ -106,8 +85,7 @@ static const WCHAR szRegisterProduct[] =
 static const WCHAR szInstallExecute[] = 
     {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
 static const WCHAR szInstallExecuteAgain[] = 
-    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
-            'A','g','a','i','n',0};
+    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
 static const WCHAR szInstallFinalize[] = 
     {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
 static const WCHAR szForceReboot[] = 
@@ -117,8 +95,7 @@ static const WCHAR szResolveSource[] =
 static const WCHAR szAppSearch[] = 
     {'A','p','p','S','e','a','r','c','h',0};
 static const WCHAR szAllocateRegistrySpace[] = 
-    {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
-            'S','p','a','c','e',0};
+    {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
 static const WCHAR szBindImage[] = 
     {'B','i','n','d','I','m','a','g','e',0};
 static const WCHAR szCCPSearch[] = 
@@ -129,58 +106,40 @@ static const WCHAR szDisableRollback[] =
     {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
 static const WCHAR szExecuteAction[] = 
     {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
-const WCHAR szFindRelatedProducts[] = 
-    {'F','i','n','d','R','e','l','a','t','e','d',
-            'P','r','o','d','u','c','t','s',0};
 static const WCHAR szInstallAdminPackage[] = 
-    {'I','n','s','t','a','l','l','A','d','m','i','n',
-            'P','a','c','k','a','g','e',0};
+    {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
 static const WCHAR szInstallSFPCatalogFile[] = 
-    {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
-            'F','i','l','e',0};
+    {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
 static const WCHAR szIsolateComponents[] = 
     {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
-const WCHAR szMigrateFeatureStates[] = 
-    {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
-            'S','t','a','t','e','s',0};
-const WCHAR szMoveFiles[] = 
+static const WCHAR szMigrateFeatureStates[] =
+    {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
+static const WCHAR szMoveFiles[] =
     {'M','o','v','e','F','i','l','e','s',0};
 static const WCHAR szMsiPublishAssemblies[] = 
-    {'M','s','i','P','u','b','l','i','s','h',
-            'A','s','s','e','m','b','l','i','e','s',0};
+    {'M','s','i','P','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
 static const WCHAR szMsiUnpublishAssemblies[] = 
-    {'M','s','i','U','n','p','u','b','l','i','s','h',
-            'A','s','s','e','m','b','l','i','e','s',0};
+    {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
 static const WCHAR szInstallODBC[] = 
     {'I','n','s','t','a','l','l','O','D','B','C',0};
 static const WCHAR szInstallServices[] = 
     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
-const WCHAR szPatchFiles[] = 
+static const WCHAR szPatchFiles[] =
     {'P','a','t','c','h','F','i','l','e','s',0};
 static const WCHAR szPublishComponents[] = 
     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
 static const WCHAR szRegisterComPlus[] =
     {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
-const WCHAR szRegisterExtensionInfo[] =
-    {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
-            'I','n','f','o',0};
 static const WCHAR szRegisterFonts[] =
     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
-const WCHAR szRegisterMIMEInfo[] =
-    {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
 static const WCHAR szRegisterUser[] =
     {'R','e','g','i','s','t','e','r','U','s','e','r',0};
-const WCHAR szRemoveDuplicateFiles[] =
-    {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
-            'F','i','l','e','s',0};
+static const WCHAR szRemoveDuplicateFiles[] =
+    {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
 static const WCHAR szRemoveEnvironmentStrings[] =
-    {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
-            'S','t','r','i','n','g','s',0};
-const WCHAR szRemoveExistingProducts[] =
-    {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
-            'P','r','o','d','u','c','t','s',0};
-const WCHAR szRemoveFiles[] =
-    {'R','e','m','o','v','e','F','i','l','e','s',0};
+    {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
+static const WCHAR szRemoveExistingProducts[] =
+    {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
 static const WCHAR szRemoveFolders[] =
     {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
 static const WCHAR szRemoveIniValues[] =
@@ -188,8 +147,7 @@ static const WCHAR szRemoveIniValues[] =
 static const WCHAR szRemoveODBC[] =
     {'R','e','m','o','v','e','O','D','B','C',0};
 static const WCHAR szRemoveRegistryValues[] =
-    {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
-            'V','a','l','u','e','s',0};
+    {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
 static const WCHAR szRemoveShortcuts[] =
     {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
 static const WCHAR szRMCCPSearch[] =
@@ -205,44 +163,27 @@ static const WCHAR szStartServices[] =
 static const WCHAR szStopServices[] =
     {'S','t','o','p','S','e','r','v','i','c','e','s',0};
 static const WCHAR szUnpublishComponents[] =
-    {'U','n','p','u','b','l','i','s','h',
-            'C','o','m','p','o','n','e','n','t','s',0};
+    {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
 static const WCHAR szUnpublishFeatures[] =
     {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
-const WCHAR szUnregisterClassInfo[] =
-    {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
-            'I','n','f','o',0};
+static const WCHAR szUnregisterClassInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
 static const WCHAR szUnregisterComPlus[] =
     {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
-const WCHAR szUnregisterExtensionInfo[] =
-    {'U','n','r','e','g','i','s','t','e','r',
-            'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
+static const WCHAR szUnregisterExtensionInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n','I','n','f','o',0};
 static const WCHAR szUnregisterFonts[] =
     {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
-const WCHAR szUnregisterMIMEInfo[] =
+static const WCHAR szUnregisterMIMEInfo[] =
     {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
-const WCHAR szUnregisterProgIdInfo[] =
-    {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
-            'I','n','f','o',0};
+static const WCHAR szUnregisterProgIdInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
 static const WCHAR szUnregisterTypeLibraries[] =
-    {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
-            'L','i','b','r','a','r','i','e','s',0};
+    {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
 static const WCHAR szValidateProductID[] =
     {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
 static const WCHAR szWriteEnvironmentStrings[] =
-    {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
-            'S','t','r','i','n','g','s',0};
-
-/* action handlers */
-typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
-
-struct _actions {
-    LPCWSTR action;
-    STANDARDACTIONHANDLER handler;
-};
-
-static struct _actions StandardActions[];
-
+    {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
 
 /********************************************************
  * helper functions
@@ -293,7 +234,8 @@ static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
     msiobj_release(&row->hdr);
 }
 
-static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
+UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
+                             BOOL preserve_case )
 {
     LPCWSTR ptr,ptr2;
     BOOL quote;
@@ -328,6 +270,10 @@ static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
         prop = msi_alloc((len+1)*sizeof(WCHAR));
         memcpy(prop,ptr,len*sizeof(WCHAR));
         prop[len]=0;
+
+        if (!preserve_case)
+            struprW(prop);
+
         ptr2++;
        
         len = 0; 
@@ -365,6 +311,7 @@ static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
 
 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
 {
+    LPCWSTR pc;
     LPWSTR p, *ret = NULL;
     UINT count = 0;
 
@@ -372,11 +319,11 @@ static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
         return ret;
 
     /* count the number of substrings */
-    for ( p = (LPWSTR)str, count = 0; p; count++ )
+    for ( pc = str, count = 0; pc; count++ )
     {
-        p = strchrW( p, sep );
-        if (p)
-            p++;
+        pc = strchrW( pc, sep );
+        if (pc)
+            pc++;
     }
 
     /* allocate space for an array of substring pointers and the substrings */
@@ -398,6 +345,74 @@ static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
     return ret;
 }
 
+static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
+{
+    static const WCHAR szSystemLanguageID[] =
+        { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
+
+    LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    prod_code = msi_dup_property( package, szProductCode );
+    patch_product = msi_get_suminfo_product( patch );
+
+    TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
+
+    if ( strstrW( patch_product, prod_code ) )
+    {
+        MSISUMMARYINFO *si;
+        const WCHAR *p;
+
+        si = MSI_GetSummaryInformationW( patch, 0 );
+        if (!si)
+        {
+            ERR("no summary information!\n");
+            goto end;
+        }
+
+        template = msi_suminfo_dup_string( si, PID_TEMPLATE );
+        if (!template)
+        {
+            ERR("no template property!\n");
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+
+        if (!template[0])
+        {
+            ret = ERROR_SUCCESS;
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+
+        langid = msi_dup_property( package, szSystemLanguageID );
+        if (!langid)
+        {
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+
+        p = strchrW( template, ';' );
+        if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
+        {
+            TRACE("applicable transform\n");
+            ret = ERROR_SUCCESS;
+        }
+
+        /* FIXME: check platform */
+
+        msiobj_release( &si->hdr );
+    }
+
+end:
+    msi_free( patch_product );
+    msi_free( prod_code );
+    msi_free( template );
+    msi_free( langid );
+
+    return ret;
+}
+
 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
                                  MSIDATABASE *patch_db, LPCWSTR name )
 {
@@ -416,27 +431,29 @@ static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
     r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
     if (SUCCEEDED(r))
     {
-        ret = msi_table_apply_transform( package->db, stg );
+        ret = msi_check_transform_applicable( package, stg );
+        if (ret == ERROR_SUCCESS)
+            msi_table_apply_transform( package->db, stg );
+        else
+            TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
         IStorage_Release( stg );
-        ret = ERROR_SUCCESS;
     }
     else
         ERR("failed to open substorage %s\n", debugstr_w(name));
 
-    return ret;
+    return ERROR_SUCCESS;
 }
 
-static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
+UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
 {
-    static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
-    LPWSTR guid_list, *guids, product_id;
+    LPWSTR guid_list, *guids, product_code;
     UINT i, ret = ERROR_FUNCTION_FAILED;
 
-    product_id = msi_dup_property( package, szProdID );
-    if (!product_id)
+    product_code = msi_dup_property( package, szProductCode );
+    if (!product_code)
     {
-        /* FIXME: the property ProductID should be written into the DB somewhere */
-        ERR("no product ID to check\n");
+        /* FIXME: the property ProductCode should be written into the DB somewhere */
+        ERR("no product code to check\n");
         return ERROR_SUCCESS;
     }
 
@@ -444,40 +461,90 @@ static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si
     guids = msi_split_string( guid_list, ';' );
     for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
     {
-        if (!lstrcmpW( guids[i], product_id ))
+        if (!lstrcmpW( guids[i], product_code ))
             ret = ERROR_SUCCESS;
     }
     msi_free( guids );
     msi_free( guid_list );
-    msi_free( product_id );
+    msi_free( product_code );
 
     return ret;
 }
 
+static UINT msi_set_media_source_prop(MSIPACKAGE *package)
+{
+    MSIQUERY *view;
+    MSIRECORD *rec = NULL;
+    LPWSTR patch;
+    LPCWSTR prop;
+    UINT r;
+
+    static const WCHAR query[] = {'S','E','L','E','C','T',' ',
+        '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
+        '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
+        '`','S','o','u','r','c','e','`',' ','I','S',' ',
+        'N','O','T',' ','N','U','L','L',0};
+
+    r = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MSI_ViewExecute(view, 0);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
+    {
+        prop = MSI_RecordGetString(rec, 1);
+        patch = msi_dup_property(package, szPatch);
+        MSI_SetPropertyW(package, prop, patch);
+        msi_free(patch);
+    }
+
+done:
+    if (rec) msiobj_release(&rec->hdr);
+    msiobj_release(&view->hdr);
+
+    return r;
+}
+
 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
 {
     MSISUMMARYINFO *si;
     LPWSTR str, *substorage;
     UINT i, r = ERROR_SUCCESS;
 
-    si = MSI_GetSummaryInformationW( patch_db, 0 );
+    si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
     if (!si)
         return ERROR_FUNCTION_FAILED;
 
-    msi_check_patch_applicable( package, si );
+    if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
+    {
+        TRACE("Patch not applicable\n");
+        return ERROR_SUCCESS;
+    }
+
+    package->patch = msi_alloc(sizeof(MSIPATCHINFO));
+    if (!package->patch)
+        return ERROR_OUTOFMEMORY;
+
+    package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
+    if (!package->patch->patchcode)
+        return ERROR_OUTOFMEMORY;
 
     /* enumerate the substorage */
     str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
+    package->patch->transforms = str;
+
     substorage = msi_split_string( str, ';' );
     for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
         r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
-    msi_free( substorage );
-    msi_free( str );
-
-    /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
 
+    msi_free( substorage );
     msiobj_release( &si->hdr );
 
+    msi_set_media_source_prop(package);
+
     return r;
 }
 
@@ -501,6 +568,13 @@ static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
     }
 
     msi_parse_patch_summary( package, patch_db );
+
+    /*
+     * There might be a CAB file in the patch package,
+     * so append it to the list of storage to search for streams.
+     */
+    append_storage_to_db( package->db, patch_db->storage );
+
     msiobj_release( &patch_db->hdr );
 
     return ERROR_SUCCESS;
@@ -509,7 +583,6 @@ static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
 /* get the PATCH property, and apply all the patches it specifies */
 static UINT msi_apply_patches( MSIPACKAGE *package )
 {
-    static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
     LPWSTR patch_list, *patches;
     UINT i, r = ERROR_SUCCESS;
 
@@ -540,7 +613,7 @@ static UINT msi_apply_transforms( MSIPACKAGE *package )
     for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
     {
         if (xforms[i][0] == ':')
-            r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
+            r = msi_apply_substorage_transform( package, package->db, xforms[i] );
         else
             r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
     }
@@ -551,192 +624,134 @@ static UINT msi_apply_transforms( MSIPACKAGE *package )
     return r;
 }
 
-/****************************************************
- * TOP level entry points 
- *****************************************************/
-
-UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
-                         LPCWSTR szCommandLine )
+static BOOL ui_sequence_exists( MSIPACKAGE *package )
 {
+    MSIQUERY *view;
     UINT rc;
-    BOOL ui = FALSE;
-    static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
-    static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
-    static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
-
-    MSI_SetPropertyW(package, szAction, szInstall);
 
-    package->script = msi_alloc_zero(sizeof(MSISCRIPT));
-
-    package->script->InWhatSequence = SEQUENCE_INSTALL;
+    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};
 
-    if (szPackagePath)   
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc == ERROR_SUCCESS)
     {
-        LPWSTR p, check, path;
-        package->PackagePath = strdupW(szPackagePath);
-        path = strdupW(szPackagePath);
-        p = strrchrW(path,'\\');    
-        if (p)
-        {
-            p++;
-            *p=0;
-        }
-        else
-        {
-            msi_free(path);
-            path = msi_alloc(MAX_PATH*sizeof(WCHAR));
-            GetCurrentDirectoryW(MAX_PATH,path);
-            strcatW(path,cszbs);
-        }
-
-        check = msi_dup_property( package, cszSourceDir );
-        if (!check)
-            MSI_SetPropertyW(package, cszSourceDir, path);
-        msi_free(check);
-        msi_free(path);
+        msiobj_release(&view->hdr);
+        return TRUE;
     }
 
-    msi_parse_command_line( package, szCommandLine );
+    return FALSE;
+}
 
-    msi_apply_transforms( package );
-    msi_apply_patches( package );
+static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
+{
+    LPWSTR p, db;
+    LPWSTR source, check;
+    DWORD len;
+
+    static const WCHAR szOriginalDatabase[] =
+        {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
 
-    if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED )
+    db = msi_dup_property( package, szOriginalDatabase );
+    if (!db)
+        return ERROR_OUTOFMEMORY;
+
+    p = strrchrW( db, '\\' );
+    if (!p)
     {
-        package->script->InWhatSequence |= SEQUENCE_UI;
-        rc = ACTION_ProcessUISequence(package);
-        ui = TRUE;
-        if (rc == ERROR_SUCCESS)
+        p = strrchrW( db, '/' );
+        if (!p)
         {
-            package->script->InWhatSequence |= SEQUENCE_EXEC;
-            rc = ACTION_ProcessExecSequence(package,TRUE);
+            msi_free(db);
+            return ERROR_SUCCESS;
         }
     }
-    else
-        rc = ACTION_ProcessExecSequence(package,FALSE);
-    
-    if (rc == -1)
-    {
-        /* install was halted but should be considered a success */
-        rc = ERROR_SUCCESS;
-    }
-
-    package->script->CurrentlyScripting= FALSE;
 
-    /* process the ending type action */
-    if (rc == ERROR_SUCCESS)
-        ACTION_PerformActionSequence(package,-1,ui);
-    else if (rc == ERROR_INSTALL_USEREXIT) 
-        ACTION_PerformActionSequence(package,-2,ui);
-    else if (rc == ERROR_INSTALL_SUSPEND) 
-        ACTION_PerformActionSequence(package,-4,ui);
-    else  /* failed */
-        ACTION_PerformActionSequence(package,-3,ui);
+    len = p - db + 2;
+    source = msi_alloc( len * sizeof(WCHAR) );
+    lstrcpynW( source, db, len );
 
-    /* finish up running custom actions */
-    ACTION_FinishCustomActions(package);
-    
-    return rc;
-}
+    check = msi_dup_property( package, cszSourceDir );
+    if (!check || replace)
+        MSI_SetPropertyW( package, cszSourceDir, source );
 
-static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
-{
-    UINT rc = ERROR_SUCCESS;
-    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};
+    msi_free( check );
 
-    static const WCHAR UISeqQuery[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-     '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
-     '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
-        ' ', '=',' ','%','i',0};
+    check = msi_dup_property( package, cszSOURCEDIR );
+    if (!check || replace)
+        MSI_SetPropertyW( package, cszSOURCEDIR, source );
 
-    if (UI)
-        row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
-    else
-        row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
+    msi_free( check );
+    msi_free( source );
+    msi_free( db );
 
-    if (row)
-    {
-        LPCWSTR action, cond;
+    return ERROR_SUCCESS;
+}
 
-        TRACE("Running the actions\n"); 
+static BOOL needs_ui_sequence(MSIPACKAGE *package)
+{
+    INT level = msi_get_property_int(package, szUILevel, 0);
+    return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
+}
 
-        /* check conditions */
-        cond = MSI_RecordGetString(row,2);
-        if (cond)
-        {
-            /* this is a hack to skip errors in the condition code */
-            if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
-                goto end;
-        }
+static UINT msi_set_context(MSIPACKAGE *package)
+{
+    WCHAR val[10];
+    DWORD sz = 10;
+    DWORD num;
+    UINT r;
 
-        action = MSI_RecordGetString(row,1);
-        if (!action)
-        {
-            ERR("failed to fetch action\n");
-            rc = ERROR_FUNCTION_FAILED;
-            goto end;
-        }
+    package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
 
-        if (UI)
-            rc = ACTION_PerformUIAction(package,action);
-        else
-            rc = ACTION_PerformAction(package,action,FALSE);
-end:
-        msiobj_release(&row->hdr);
+    r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
+    if (r == ERROR_SUCCESS)
+    {
+        num = atolW(val);
+        if (num == 1 || num == 2)
+            package->Context = MSIINSTALLCONTEXT_MACHINE;
     }
-    else
-        rc = ERROR_SUCCESS;
 
-    return rc;
+    MSI_SetPropertyW(package, szAllUsers, szOne);
+    return ERROR_SUCCESS;
 }
 
-typedef struct {
-    MSIPACKAGE* package;
-    BOOL UI;
-} iterate_action_param;
-
 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
 {
-    iterate_action_param *iap= (iterate_action_param*)param;
     UINT rc;
     LPCWSTR cond, action;
+    MSIPACKAGE *package = param;
 
     action = MSI_RecordGetString(row,1);
     if (!action)
     {
         ERR("Error is retrieving action name\n");
-        return  ERROR_FUNCTION_FAILED;
+        return ERROR_FUNCTION_FAILED;
     }
 
     /* check conditions */
     cond = MSI_RecordGetString(row,2);
-    if (cond)
+
+    /* this is a hack to skip errors in the condition code */
+    if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
     {
-        /* this is a hack to skip errors in the condition code */
-        if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
-        {
-            TRACE("Skipping action: %s (condition is false)\n",
-                            debugstr_w(action));
-            return ERROR_SUCCESS;
-        }
+        TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
+        return ERROR_SUCCESS;
     }
 
-    if (iap->UI)
-        rc = ACTION_PerformUIAction(iap->package,action);
+    if (needs_ui_sequence(package))
+        rc = ACTION_PerformUIAction(package, action, -1);
     else
-        rc = ACTION_PerformAction(iap->package,action,FALSE);
+        rc = ACTION_PerformAction(package, action, -1, FALSE);
 
     msi_dialog_check_messages( NULL );
 
-    if (iap->package->CurrentInstallState != ERROR_SUCCESS )
-        rc = iap->package->CurrentInstallState;
+    if (package->CurrentInstallState != ERROR_SUCCESS)
+        rc = package->CurrentInstallState;
 
     if (rc == ERROR_FUNCTION_NOT_CALLED)
         rc = ERROR_SUCCESS;
@@ -758,23 +773,13 @@ UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
          '`','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};
-    iterate_action_param iap;
-
-    /*
-     * FIXME: probably should be checking UILevel in the
-     *       ACTION_PerformUIAction/ACTION_PerformAction
-     *       rather than saving the UI level here. Those
-     *       two functions can be merged too.
-     */
-    iap.package = package;
-    iap.UI = TRUE;
 
     TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
 
     r = MSI_OpenQuery( package->db, &view, query, szTable );
     if (r == ERROR_SUCCESS)
     {
-        r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
+        r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
         msiobj_release(&view->hdr);
     }
 
@@ -792,7 +797,6 @@ static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
          '`','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 };
-    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',
@@ -801,10 +805,6 @@ static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
          ' ','\'', 'I','n','s','t','a','l','l',
          'V','a','l','i','d','a','t','e','\'', 0};
     INT seq = 0;
-    iterate_action_param iap;
-
-    iap.package = package;
-    iap.UI = FALSE;
 
     if (package->script->ExecuteSequenceRun)
     {
@@ -817,7 +817,7 @@ static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
     /* get the sequence number */
     if (UIran)
     {
-        row = MSI_QueryGetRecord(package->db, IVQuery);
+        MSIRECORD *row = MSI_QueryGetRecord(package->db, IVQuery);
         if( !row )
             return ERROR_FUNCTION_FAILED;
         seq = MSI_RecordGetInteger(row,1);
@@ -829,7 +829,7 @@ static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
     {
         TRACE("Running the actions\n");
 
-        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
+        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
         msiobj_release(&view->hdr);
     }
 
@@ -848,18 +848,13 @@ static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
          '`','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};
-    iterate_action_param iap;
-
-    iap.package = package;
-    iap.UI = TRUE;
 
     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
-    
     if (rc == ERROR_SUCCESS)
     {
         TRACE("Running the actions\n"); 
 
-        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
+        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
         msiobj_release(&view->hdr);
     }
 
@@ -869,63 +864,13 @@ static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
 /********************************************************
  * ACTION helper functions and functions that perform the actions
  *******************************************************/
-static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, 
-                                        UINT* rc, BOOL force )
-{
-    BOOL ret = FALSE; 
-    BOOL run = force;
-    int i;
-
-    if (!run && !package->script->CurrentlyScripting)
-        run = TRUE;
-   
-    if (!run)
-    {
-        if (strcmpW(action,szInstallFinalize) == 0 ||
-            strcmpW(action,szInstallExecute) == 0 ||
-            strcmpW(action,szInstallExecuteAgain) == 0) 
-                run = TRUE;
-    }
-    
-    i = 0;
-    while (StandardActions[i].action != NULL)
-    {
-        if (strcmpW(StandardActions[i].action, action)==0)
-        {
-            if (!run)
-            {
-                ui_actioninfo(package, action, TRUE, 0);
-                *rc = schedule_action(package,INSTALL_SCRIPT,action);
-                ui_actioninfo(package, action, FALSE, *rc);
-            }
-            else
-            {
-                ui_actionstart(package, action);
-                if (StandardActions[i].handler)
-                {
-                    *rc = StandardActions[i].handler(package);
-                }
-                else
-                {
-                    FIXME("unhandled standard action %s\n",debugstr_w(action));
-                    *rc = ERROR_SUCCESS;
-                }
-            }
-            ret = TRUE;
-            break;
-        }
-        i++;
-    }
-    return ret;
-}
-
 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
-                                       UINT* rc, BOOL force )
+                                       UINT* rc, UINT script, BOOL force )
 {
     BOOL ret=FALSE;
     UINT arc;
 
-    arc = ACTION_CustomAction(package,action, force);
+    arc = ACTION_CustomAction(package, action, script, force);
 
     if (arc != ERROR_CALL_NOT_IMPLEMENTED)
     {
@@ -935,67 +880,13 @@ static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
     return ret;
 }
 
-/* 
- * A lot 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 translate 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, BOOL force)
-{
-    UINT rc = ERROR_SUCCESS; 
-    BOOL handled;
-
-    TRACE("Performing action (%s)\n",debugstr_w(action));
-
-    handled = ACTION_HandleStandardAction(package, action, &rc, force);
-
-    if (!handled)
-        handled = ACTION_HandleCustomAction(package, action, &rc, force);
-
-    if (!handled)
-    {
-        FIXME("unhandled msi action %s\n",debugstr_w(action));
-        rc = ERROR_FUNCTION_NOT_CALLED;
-    }
-
-    return rc;
-}
-
-UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
-{
-    UINT rc = ERROR_SUCCESS;
-    BOOL handled = FALSE;
-
-    TRACE("Performing action (%s)\n",debugstr_w(action));
-
-    handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
-
-    if (!handled)
-        handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
-
-    if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
-        handled = TRUE;
-
-    if (!handled)
-    {
-        FIXME("unhandled msi action %s\n",debugstr_w(action));
-        rc = ERROR_FUNCTION_NOT_CALLED;
-    }
-
-    return rc;
-}
-
-
 /*
  * Actual Action Handlers
  */
 
 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPCWSTR dir;
     LPWSTR full_path;
     MSIRECORD *uirow;
@@ -1008,7 +899,7 @@ static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
         return ERROR_SUCCESS;
     }
 
-    full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
+    full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
     if (!full_path)
     {
         ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
@@ -1039,7 +930,7 @@ static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
     MSIFOLDER *folder;
     LPWSTR install_path;
 
-    install_path = resolve_folder(package, dir, FALSE, FALSE, &folder);
+    install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
     if (!install_path)
         return ERROR_FUNCTION_FAILED; 
 
@@ -1118,26 +1009,8 @@ static UINT load_component( MSIRECORD *row, LPVOID param )
     comp->Condition = msi_dup_record_field( row, 5 );
     comp->KeyPath = msi_dup_record_field( row, 6 );
 
-    comp->Installed = INSTALLSTATE_ABSENT;
-
-    switch (comp->Attributes)
-    {
-    case msidbComponentAttributesLocalOnly:
-        comp->Action = INSTALLSTATE_LOCAL;
-        comp->ActionRequest = INSTALLSTATE_LOCAL;
-        break;
-    case msidbComponentAttributesSourceOnly:
-        comp->Action = INSTALLSTATE_SOURCE;
-        comp->ActionRequest = INSTALLSTATE_SOURCE;
-        break;
-    case msidbComponentAttributesOptional:
-        comp->Action = INSTALLSTATE_DEFAULT;
-        comp->ActionRequest = INSTALLSTATE_DEFAULT;
-        break;
-    default:
-        comp->Action = INSTALLSTATE_LOCAL;
-        comp->ActionRequest = INSTALLSTATE_LOCAL;
-    }
+    comp->Installed = INSTALLSTATE_UNKNOWN;
+    msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
 
     return ERROR_SUCCESS;
 }
@@ -1180,9 +1053,22 @@ static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
     return ERROR_SUCCESS;
 }
 
+static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
+{
+    FeatureList *fl;
+
+    fl = msi_alloc( sizeof(*fl) );
+    if ( !fl )
+        return ERROR_NOT_ENOUGH_MEMORY;
+    fl->feature = child;
+    list_add_tail( &parent->Children, &fl->entry );
+
+    return ERROR_SUCCESS;
+}
+
 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
 {
-    _ilfs* ilfs= (_ilfs*)param;
+    _ilfs* ilfs = param;
     LPCWSTR component;
     MSICOMPONENT *comp;
 
@@ -1202,9 +1088,25 @@ static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
     return ERROR_SUCCESS;
 }
 
+static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
+{
+    MSIFEATURE *feature;
+
+    if ( !name )
+        return NULL;
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        if ( !lstrcmpW( feature->Feature, name ) )
+            return feature;
+    }
+
+    return NULL;
+}
+
 static UINT load_feature(MSIRECORD * row, LPVOID param)
 {
-    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIPACKAGE* package = param;
     MSIFEATURE* feature;
     static const WCHAR Query1[] = 
         {'S','E','L','E','C','T',' ',
@@ -1223,6 +1125,7 @@ static UINT load_feature(MSIRECORD * row, LPVOID param)
     if (!feature)
         return ERROR_NOT_ENOUGH_MEMORY;
 
+    list_init( &feature->Children );
     list_init( &feature->Components );
     
     feature->Feature = msi_dup_record_field( row, 1 );
@@ -1240,9 +1143,8 @@ static UINT load_feature(MSIRECORD * row, LPVOID param)
     feature->Directory = msi_dup_record_field( row, 7 );
     feature->Attributes = MSI_RecordGetInteger(row,8);
 
-    feature->Installed = INSTALLSTATE_ABSENT;
-    feature->Action = INSTALLSTATE_UNKNOWN;
-    feature->ActionRequest = INSTALLSTATE_UNKNOWN;
+    feature->Installed = INSTALLSTATE_UNKNOWN;
+    msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
 
     list_add_tail( &package->features, &feature->entry );
 
@@ -1261,6 +1163,26 @@ static UINT load_feature(MSIRECORD * row, LPVOID param)
     return ERROR_SUCCESS;
 }
 
+static UINT find_feature_children(MSIRECORD * row, LPVOID param)
+{
+    MSIPACKAGE* package = param;
+    MSIFEATURE *parent, *child;
+
+    child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
+    if (!child)
+        return ERROR_FUNCTION_FAILED;
+
+    if (!child->Feature_Parent)
+        return ERROR_SUCCESS;
+
+    parent = find_feature_by_name( package, child->Feature_Parent );
+    if (!parent)
+        return ERROR_FUNCTION_FAILED;
+
+    add_feature_child( parent, child );
+    return ERROR_SUCCESS;
+}
+
 static UINT load_all_features( MSIPACKAGE *package )
 {
     static const WCHAR query[] = {
@@ -1278,7 +1200,12 @@ static UINT load_all_features( MSIPACKAGE *package )
         return r;
 
     r = MSI_IterateRecords( view, NULL, load_feature, package );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MSI_IterateRecords( view, NULL, find_feature_children, package );
     msiobj_release( &view->hdr );
+
     return r;
 }
 
@@ -1293,9 +1220,45 @@ static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
     return p+1;
 }
 
+static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+        '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
+        'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
+    MSIQUERY *view = NULL;
+    MSIRECORD *row = NULL;
+    UINT r;
+
+    TRACE("%s\n", debugstr_w(file->File));
+
+    r = MSI_OpenQuery(package->db, &view, query, file->File);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    r = MSI_ViewExecute(view, NULL);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    r = MSI_ViewFetch(view, &row);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
+    file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
+    file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
+    file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
+    file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
+
+done:
+    if (view) msiobj_release(&view->hdr);
+    if (row) msiobj_release(&row->hdr);
+    return r;
+}
+
 static UINT load_file(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIPACKAGE* package = param;
     LPCWSTR component;
     MSIFILE *file;
 
@@ -1311,7 +1274,12 @@ static UINT load_file(MSIRECORD *row, LPVOID param)
     file->Component = get_loaded_component( package, component );
 
     if (!file->Component)
-        ERR("Unfound Component %s\n",debugstr_w(component));
+    {
+        WARN("Component not found: %s\n", debugstr_w(component));
+        msi_free(file->File);
+        msi_free(file);
+        return ERROR_SUCCESS;
+    }
 
     file->FileName = msi_dup_record_field( row, 3 );
     reduce_to_longfilename( file->FileName );
@@ -1330,7 +1298,12 @@ static UINT load_file(MSIRECORD *row, LPVOID param)
     /* if the compressed bits are not set in the file attributes,
      * then read the information from the package word count property
      */
-    if (file->Attributes & msidbFileAttributesCompressed)
+    if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
+    {
+        file->IsCompressed = FALSE;
+    }
+    else if (file->Attributes &
+             (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
     {
         file->IsCompressed = TRUE;
     }
@@ -1340,15 +1313,10 @@ static UINT load_file(MSIRECORD *row, LPVOID param)
     }
     else
     {
-        file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED;
+        file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
     }
 
-    if (file->IsCompressed)
-    {
-        file->Component->ForceLocalState = TRUE;
-        file->Component->Action = INSTALLSTATE_LOCAL;
-        file->Component->ActionRequest = INSTALLSTATE_LOCAL;
-    }
+    load_file_hash(package, file);
 
     TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
 
@@ -1379,6 +1347,88 @@ static UINT load_all_files(MSIPACKAGE *package)
     return ERROR_SUCCESS;
 }
 
+static UINT load_folder( MSIRECORD *row, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    static WCHAR szEmpty[] = { 0 };
+    LPWSTR p, tgt_short, tgt_long, src_short, src_long;
+    MSIFOLDER *folder;
+
+    folder = msi_alloc_zero( sizeof (MSIFOLDER) );
+    if (!folder)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    folder->Directory = msi_dup_record_field( row, 1 );
+
+    TRACE("%s\n", debugstr_w(folder->Directory));
+
+    p = msi_dup_record_field(row, 3);
+
+    /* split src and target dir */
+    tgt_short = p;
+    src_short = folder_split_path( p, ':' );
+
+    /* split the long and short paths */
+    tgt_long = folder_split_path( tgt_short, '|' );
+    src_long = folder_split_path( src_short, '|' );
+
+    /* check for no-op dirs */
+    if (!lstrcmpW(szDot, tgt_short))
+        tgt_short = szEmpty;
+    if (!lstrcmpW(szDot, src_short))
+        src_short = szEmpty;
+
+    if (!tgt_long)
+        tgt_long = tgt_short;
+
+    if (!src_short) {
+        src_short = tgt_short;
+        src_long = tgt_long;
+    }
+
+    if (!src_long)
+        src_long = src_short;
+
+    /* FIXME: use the target short path too */
+    folder->TargetDefault = strdupW(tgt_long);
+    folder->SourceShortPath = strdupW(src_short);
+    folder->SourceLongPath = strdupW(src_long);
+    msi_free(p);
+
+    TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
+    TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
+    TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
+
+    folder->Parent = msi_dup_record_field( row, 2 );
+
+    folder->Property = msi_dup_property( package, folder->Directory );
+
+    list_add_tail( &package->folders, &folder->entry );
+
+    TRACE("returning %p\n", folder);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT load_all_folders( MSIPACKAGE *package )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
+         '`','D','i','r','e','c','t','o','r','y','`',0 };
+    MSIQUERY *view;
+    UINT r;
+
+    if (!list_empty(&package->folders))
+        return ERROR_SUCCESS;
+
+    r = MSI_DatabaseOpenViewW( package->db, query, &view );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MSI_IterateRecords(view, NULL, load_folder, package);
+    msiobj_release(&view->hdr);
+    return r;
+}
 
 /*
  * I am not doing any of the costing functionality yet.
@@ -1397,14 +1447,11 @@ static UINT ACTION_CostInitialize(MSIPACKAGE *package)
 {
     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 };
-
-    if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
-        return ERROR_SUCCESS;
 
     MSI_SetPropertyW(package, szCosting, szZero);
     MSI_SetPropertyW(package, cszRootDrive, c_colon);
 
+    load_all_folders( package );
     load_all_components( package );
     load_all_features( package );
     load_all_files( package );
@@ -1414,7 +1461,7 @@ static UINT ACTION_CostInitialize(MSIPACKAGE *package)
 
 static UINT execute_script(MSIPACKAGE *package, UINT script )
 {
-    int i;
+    UINT i;
     UINT rc = ERROR_SUCCESS;
 
     TRACE("Executing Script %i\n",script);
@@ -1431,15 +1478,11 @@ static UINT execute_script(MSIPACKAGE *package, UINT script )
         action = package->script->Actions[script][i];
         ui_actionstart(package, action);
         TRACE("Executing Action (%s)\n",debugstr_w(action));
-        rc = ACTION_PerformAction(package, action, TRUE);
-        msi_free(package->script->Actions[script][i]);
+        rc = ACTION_PerformAction(package, action, script, TRUE);
         if (rc != ERROR_SUCCESS)
             break;
     }
-    msi_free(package->script->Actions[script]);
-
-    package->script->ActionCount[script] = 0;
-    package->script->Actions[script] = NULL;
+    msi_free_action_script(package, script);
     return rc;
 }
 
@@ -1448,156 +1491,71 @@ static UINT ACTION_FileCost(MSIPACKAGE *package)
     return ERROR_SUCCESS;
 }
 
-static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir )
+static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
 {
-    static const WCHAR Query[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-         '`','D','i','r','e','c', 't','o','r','y','`',' ',
-         'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
-         ' ','=',' ','\'','%','s','\'',
-         0};
-    static const WCHAR szDot[] = { '.',0 };
-    static WCHAR szEmpty[] = { 0 };
-    LPWSTR p, tgt_short, tgt_long, src_short, src_long;
-    LPCWSTR parent;
-    MSIRECORD *row;
-    MSIFOLDER *folder;
-
-    TRACE("Looking for dir %s\n",debugstr_w(dir));
-
-    folder = get_loaded_folder( package, dir );
-    if (folder)
-        return folder;
-
-    TRACE("Working to load %s\n",debugstr_w(dir));
-
-    folder = msi_alloc_zero( sizeof (MSIFOLDER) );
-    if (!folder)
-        return NULL;
-
-    folder->Directory = strdupW(dir);
-
-    row = MSI_QueryGetRecord(package->db, Query, dir);
-    if (!row)
-        return NULL;
-
-    p = msi_dup_record_field(row, 3);
-
-    /* split src and target dir */
-    tgt_short = p;
-    src_short = folder_split_path( p, ':' );
-
-    /* split the long and short paths */
-    tgt_long = folder_split_path( tgt_short, '|' );
-    src_long = folder_split_path( src_short, '|' );
-
-    /* check for no-op dirs */
-    if (!lstrcmpW(szDot, tgt_short))
-        tgt_short = szEmpty;
-    if (!lstrcmpW(szDot, src_short))
-        src_short = szEmpty;
-
-    if (!tgt_long)
-        tgt_long = tgt_short;
-       
-    if (!src_short) {
-        src_short = tgt_short;
-        src_long = tgt_long;
-    }
-    
-    if (!src_long)
-        src_long = src_short;
-
-    /* FIXME: use the target short path too */
-    folder->TargetDefault = strdupW(tgt_long);
-    folder->SourceShortPath = strdupW(src_short);
-    folder->SourceLongPath = strdupW(src_long);
-    msi_free(p);
+    MSICOMPONENT *comp;
+    INSTALLSTATE state;
+    UINT r;
 
-    TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
-    TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
-    TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
+    state = MsiQueryProductStateW(package->ProductCode);
 
-    parent = MSI_RecordGetString(row, 2);
-    if (parent) 
+    LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
     {
-        folder->Parent = load_folder( package, parent );
-        if ( folder->Parent )
-            TRACE("loaded parent %p %s\n", folder->Parent,
-                  debugstr_w(folder->Parent->Directory));
+        if (!comp->ComponentId)
+            continue;
+
+        if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
+            comp->Installed = INSTALLSTATE_ABSENT;
         else
-            ERR("failed to load parent folder %s\n", debugstr_w(parent));
+        {
+            r = MsiQueryComponentStateW(package->ProductCode, NULL,
+                                        package->Context, comp->ComponentId,
+                                        &comp->Installed);
+            if (r != ERROR_SUCCESS)
+                comp->Installed = INSTALLSTATE_ABSENT;
+        }
     }
-
-    folder->Property = msi_dup_property( package, dir );
-
-    msiobj_release(&row->hdr);
-
-    list_add_tail( &package->folders, &folder->entry );
-
-    TRACE("%s returning %p\n",debugstr_w(dir),folder);
-
-    return folder;
 }
 
-/* scan for and update current install states */
-static void ACTION_UpdateInstallStates(MSIPACKAGE *package)
+static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
 {
-    MSICOMPONENT *comp;
     MSIFEATURE *feature;
+    INSTALLSTATE state;
 
-    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
-    {
-        INSTALLSTATE res;
-        res = MsiGetComponentPathW( package->ProductCode, 
-                                    comp->ComponentId, NULL, NULL);
-        if (res < 0)
-            res = INSTALLSTATE_ABSENT;
-        comp->Installed = res;
-    }
+    state = MsiQueryProductStateW(package->ProductCode);
 
     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
     {
-        ComponentList *cl;
-        INSTALLSTATE res = -10;
-
-        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
+            feature->Installed = INSTALLSTATE_ABSENT;
+        else
         {
-            comp= cl->component;
-
-            if (res == -10)
-                res = comp->Installed;
-            else
-            {
-                if (res == comp->Installed)
-                    continue;
-
-                if (res != comp->Installed)
-                        res = INSTALLSTATE_INCOMPLETE;
-            }
+            feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
+                                                       feature->Feature);
         }
-        feature->Installed = res;
     }
 }
 
-static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
-                                    INSTALLSTATE state)
+static BOOL process_state_property(MSIPACKAGE* package, int level,
+                                   LPCWSTR property, INSTALLSTATE state)
 {
-    static const WCHAR all[]={'A','L','L',0};
     LPWSTR override;
     MSIFEATURE *feature;
 
     override = msi_dup_property( package, property );
     if (!override)
         return FALSE;
+
     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
     {
-        if (strcmpiW(override,all)==0)
-        {
-            feature->ActionRequest= state;
-            feature->Action = state;
-        }
+        if (lstrcmpW(property, szRemove) &&
+            (feature->Level <= 0 || feature->Level > level))
+            continue;
+
+        if (!strcmpW(property, szReinstall)) state = feature->Installed;
+
+        if (strcmpiW(override, szAll)==0)
+            msi_feature_set_state(package, feature, state);
         else
         {
             LPWSTR ptr = override;
@@ -1608,8 +1566,7 @@ static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
                 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
                     || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
                 {
-                    feature->ActionRequest= state;
-                    feature->Action = state;
+                    msi_feature_set_state(package, feature, state);
                     break;
                 }
                 if (ptr2)
@@ -1621,7 +1578,7 @@ static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
                     break;
             }
         }
-    } 
+    }
     msi_free(override);
 
     return TRUE;
@@ -1629,15 +1586,15 @@ static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property,
 
 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
 {
-    int install_level;
+    int level;
     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};
-    static const WCHAR szRemove[] =
-        {'R','E','M','O','V','E',0};
-    static const WCHAR szReinstall[] =
-        {'R','E','I','N','S','T','A','L','L',0};
+    static const WCHAR szAddSource[] =
+        {'A','D','D','S','O','U','R','C','E',0};
+    static const WCHAR szAdvertise[] =
+        {'A','D','V','E','R','T','I','S','E',0};
     BOOL override = FALSE;
     MSICOMPONENT* component;
     MSIFEATURE *feature;
@@ -1647,146 +1604,180 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
 
     TRACE("Checking Install Level\n");
 
-    install_level = msi_get_property_int( package, szlevel, 1 );
+    level = msi_get_property_int(package, szlevel, 1);
 
-    /* ok hereis the _real_ rub
+    /* ok here is the _real_ rub
      * all these activation/deactivation things happen in order and things
      * later on the list override things earlier on the list.
-     * 1) INSTALLLEVEL processing
-     * 2) ADDLOCAL
-     * 3) REMOVE
-     * 4) ADDSOURCE
-     * 5) ADDDEFAULT
-     * 6) REINSTALL
+     * 0) INSTALLLEVEL processing
+     * 1) ADDLOCAL
+     * 2) REMOVE
+     * 3) ADDSOURCE
+     * 4) ADDDEFAULT
+     * 5) REINSTALL
+     * 6) ADVERTISE
      * 7) COMPADDLOCAL
      * 8) COMPADDSOURCE
      * 9) FILEADDLOCAL
      * 10) FILEADDSOURCE
      * 11) FILEADDDEFAULT
-     * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
-     * ignored for all the features. seems strange, especially since it is not
-     * documented anywhere, but it is how it works. 
      *
      * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
      * REMOVE are the big ones, since we don't handle administrative installs
      * yet anyway.
      */
-    override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
-    override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
-    override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
+    override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
+    override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
+    override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
+    override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
+    override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
 
     if (!override)
     {
         LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
         {
             BOOL feature_state = ((feature->Level > 0) &&
-                             (feature->Level <= install_level));
+                                  (feature->Level <= level));
 
             if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
             {
                 if (feature->Attributes & msidbFeatureAttributesFavorSource)
-                {
-                    feature->ActionRequest = INSTALLSTATE_SOURCE;
-                    feature->Action = INSTALLSTATE_SOURCE;
-                }
+                    msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
                 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
-                {
-                    feature->ActionRequest = INSTALLSTATE_ADVERTISED;
-                    feature->Action = INSTALLSTATE_ADVERTISED;
-                }
+                    msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
                 else
-                {
-                    feature->ActionRequest = INSTALLSTATE_LOCAL;
-                    feature->Action = INSTALLSTATE_LOCAL;
-                }
+                    msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
             }
         }
-    }
-    else
-    {
-        /* set the Preselected Property */
-        static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
-        static const WCHAR szOne[] = { '1', 0 };
 
-        MSI_SetPropertyW(package,szPreselected,szOne);
+        /* disable child features of unselected parent features */
+        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+        {
+            FeatureList *fl;
+
+            if (feature->Level > 0 && feature->Level <= level)
+                continue;
+
+            LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
+                msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
+        }
     }
+    else
+        MSI_SetPropertyW(package, szPreselected, szOne);
 
     /*
-     * now we want to enable or disable components base on feature 
-    */
+     * now we want to enable or disable components base on feature
+     */
 
     LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
     {
         ComponentList *cl;
 
-        TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
-            debugstr_w(feature->Feature), feature->Installed, feature->Action,
-            feature->ActionRequest);
+        TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
+              debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
+
+        if (!feature->Level)
+            continue;
+
+        /* features with components that have compressed files are made local */
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            if (cl->component->Enabled &&
+                cl->component->ForceLocalState &&
+                feature->Action == INSTALLSTATE_SOURCE)
+            {
+                msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
+                break;
+            }
+        }
 
         LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
         {
             component = cl->component;
 
             if (!component->Enabled)
+                continue;
+
+            switch (feature->Action)
             {
-                component->Action = INSTALLSTATE_UNKNOWN;
-                component->ActionRequest = INSTALLSTATE_UNKNOWN;
+            case INSTALLSTATE_ABSENT:
+                component->anyAbsent = 1;
+                break;
+            case INSTALLSTATE_ADVERTISED:
+                component->hasAdvertiseFeature = 1;
+                break;
+            case INSTALLSTATE_SOURCE:
+                component->hasSourceFeature = 1;
+                break;
+            case INSTALLSTATE_LOCAL:
+                component->hasLocalFeature = 1;
+                break;
+            case INSTALLSTATE_DEFAULT:
+                if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
+                    component->hasAdvertiseFeature = 1;
+                else if (feature->Attributes & msidbFeatureAttributesFavorSource)
+                    component->hasSourceFeature = 1;
+                else
+                    component->hasLocalFeature = 1;
+                break;
+            default:
+                break;
             }
+        }
+    }
+
+    LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
+    {
+        /* if the component isn't enabled, leave it alone */
+        if (!component->Enabled)
+            continue;
+
+        /* check if it's local or source */
+        if (!(component->Attributes & msidbComponentAttributesOptional) &&
+             (component->hasLocalFeature || component->hasSourceFeature))
+        {
+            if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
+                 !component->ForceLocalState)
+                msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
             else
-            {
-                if (feature->Attributes == msidbFeatureAttributesFavorLocal)
-                {
-                    if (!(component->Attributes & msidbComponentAttributesSourceOnly))
-                    {
-                        component->Action = INSTALLSTATE_LOCAL;
-                        component->ActionRequest = INSTALLSTATE_LOCAL;
-                    }
-                }
-                else if (feature->Attributes == msidbFeatureAttributesFavorSource)
-                {
-                    if ((component->Action == INSTALLSTATE_UNKNOWN) ||
-                        (component->Action == INSTALLSTATE_ABSENT) ||
-                        (component->Action == INSTALLSTATE_ADVERTISED) ||
-                        (component->Action == INSTALLSTATE_DEFAULT))
-                           
-                    {
-                        component->Action = INSTALLSTATE_SOURCE;
-                        component->ActionRequest = INSTALLSTATE_SOURCE;
-                    }
-                }
-                else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED)
-                {
-                    if ((component->Action == INSTALLSTATE_UNKNOWN) ||
-                        (component->Action == INSTALLSTATE_ABSENT))
-                           
-                    {
-                        component->Action = INSTALLSTATE_ADVERTISED;
-                        component->ActionRequest = INSTALLSTATE_ADVERTISED;
-                    }
-                }
-                else if (feature->ActionRequest == INSTALLSTATE_ABSENT)
-                {
-                    if (component->Action == INSTALLSTATE_UNKNOWN)
-                    {
-                        component->Action = INSTALLSTATE_ABSENT;
-                        component->ActionRequest = INSTALLSTATE_ABSENT;
-                    }
-                }
-            }
+                msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
+            continue;
+        }
 
-            if (component->ForceLocalState)
-            {
-                feature->Action = INSTALLSTATE_LOCAL;
-                feature->ActionRequest = INSTALLSTATE_LOCAL;
-            }
+        /* if any feature is local, the component must be local too */
+        if (component->hasLocalFeature)
+        {
+            msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
+            continue;
+        }
+
+        if (component->hasSourceFeature)
+        {
+            msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
+            continue;
+        }
+
+        if (component->hasAdvertiseFeature)
+        {
+            msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
+            continue;
         }
-    } 
+
+        TRACE("nobody wants component %s\n", debugstr_w(component->Component));
+        if (component->anyAbsent)
+            msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
+    }
 
     LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
     {
-        TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
-            debugstr_w(component->Component), component->Installed, 
-            component->Action, component->ActionRequest);
+        if (component->Action == INSTALLSTATE_DEFAULT)
+        {
+            TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
+            msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
+        }
+
+        TRACE("Result: Component %s (Installed %i, Action %i)\n",
+            debugstr_w(component->Component), component->Installed, component->Action);
     }
 
 
@@ -1795,16 +1786,23 @@ UINT MSI_SetFeatureStates(MSIPACKAGE *package)
 
 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPCWSTR name;
     LPWSTR path;
+    MSIFOLDER *f;
 
     name = MSI_RecordGetString(row,1);
 
+    f = get_loaded_folder(package, name);
+    if (!f) return ERROR_SUCCESS;
+
+    /* reset the ResolvedTarget */
+    msi_free(f->ResolvedTarget);
+    f->ResolvedTarget = NULL;
+
     /* This helper function now does ALL the work */
     TRACE("Dir %s ...\n",debugstr_w(name));
-    load_folder(package,name);
-    path = resolve_folder(package,name,FALSE,TRUE,NULL);
+    path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
     TRACE("resolves to %s\n",debugstr_w(path));
     msi_free(path);
 
@@ -1813,7 +1811,7 @@ static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
 
 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPCWSTR name;
     MSIFEATURE *feature;
 
@@ -1830,53 +1828,55 @@ static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
         if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
         {
             int level = MSI_RecordGetInteger(row,2);
-            TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
+            TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
             feature->Level = level;
         }
     }
     return ERROR_SUCCESS;
 }
 
-
-/* 
- * A lot 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 LPWSTR msi_get_disk_file_version( LPCWSTR filename )
 {
-    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 szlevel[] =
-        {'I','N','S','T','A','L','L','L','E','V','E','L',0};
-    static const WCHAR szOne[] = { '1', 0 };
-    MSICOMPONENT *comp;
-    MSIFILE *file;
-    UINT rc;
-    MSIQUERY * view;
-    LPWSTR level;
+    static const WCHAR name_fmt[] =
+        {'%','u','.','%','u','.','%','u','.','%','u',0};
+    static const WCHAR name[] = {'\\',0};
+    VS_FIXEDFILEINFO *lpVer;
+    WCHAR filever[0x100];
+    LPVOID version;
+    DWORD versize;
+    DWORD handle;
+    UINT sz;
+
+    TRACE("%s\n", debugstr_w(filename));
+
+    versize = GetFileVersionInfoSizeW( filename, &handle );
+    if (!versize)
+        return NULL;
 
-    if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
-        return ERROR_SUCCESS;
-    
-    TRACE("Building Directory properties\n");
+    version = msi_alloc( versize );
+    GetFileVersionInfoW( filename, 0, versize, version );
 
-    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
-    if (rc == ERROR_SUCCESS)
+    if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
     {
-        rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
-                        package);
-        msiobj_release(&view->hdr);
+        msi_free( version );
+        return NULL;
     }
 
-    TRACE("File calculations\n");
+    sprintfW( filever, name_fmt,
+        HIWORD(lpVer->dwFileVersionMS),
+        LOWORD(lpVer->dwFileVersionMS),
+        HIWORD(lpVer->dwFileVersionLS),
+        LOWORD(lpVer->dwFileVersionLS));
+
+    msi_free( version );
+
+    return strdupW( filever );
+}
+
+static UINT msi_check_file_install_states( MSIPACKAGE *package )
+{
+    LPWSTR file_version;
+    MSIFILE *file;
 
     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
     {
@@ -1886,20 +1886,31 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
         if (!comp)
             continue;
 
+        if (file->IsCompressed)
+            comp->ForceLocalState = TRUE;
+
         /* calculate target */
-        p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
+        p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
 
         msi_free(file->TargetPath);
 
         TRACE("file %s is named %s\n",
-               debugstr_w(file->File),debugstr_w(file->FileName));       
+               debugstr_w(file->File), debugstr_w(file->FileName));
 
         file->TargetPath = build_directory_name(2, p, file->FileName);
 
         msi_free(p);
 
         TRACE("file %s resolves to %s\n",
-               debugstr_w(file->File),debugstr_w(file->TargetPath));       
+               debugstr_w(file->File), debugstr_w(file->TargetPath));
+
+        /* don't check files of components that aren't installed */
+        if (comp->Installed == INSTALLSTATE_UNKNOWN ||
+            comp->Installed == INSTALLSTATE_ABSENT)
+        {
+            file->state = msifs_missing;  /* assume files are missing */
+            continue;
+        }
 
         if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
         {
@@ -1908,47 +1919,70 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
             continue;
         }
 
-        if (file->Version)
+        if (file->Version &&
+            (file_version = msi_get_disk_file_version( file->TargetPath )))
         {
-            DWORD handle;
-            DWORD versize;
-            UINT sz;
-            LPVOID version;
-            static WCHAR name[] = {'\\',0};
-            static const WCHAR name_fmt[] = 
-                {'%','u','.','%','u','.','%','u','.','%','u',0};
-            WCHAR filever[0x100];
-            VS_FIXEDFILEINFO *lpVer;
-
-            TRACE("Version comparison..\n");
-            versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
-            version = msi_alloc(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)
+                  debugstr_w(file_version));
+            /* FIXME: seems like a bad way to compare version numbers */
+            if (lstrcmpiW(file_version, file->Version)<0)
             {
                 file->state = msifs_overwrite;
-                /* FIXME: cost should be diff in size */
                 comp->Cost += file->FileSize;
             }
             else
                 file->state = msifs_present;
-            msi_free(version);
+            msi_free( file_version );
         }
         else
             file->state = msifs_present;
     }
 
+    return ERROR_SUCCESS;
+}
+
+/*
+ * A lot 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 szlevel[] =
+        {'I','N','S','T','A','L','L','L','E','V','E','L',0};
+    static const WCHAR szOutOfDiskSpace[] =
+        {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
+    MSICOMPONENT *comp;
+    UINT rc;
+    MSIQUERY * view;
+    LPWSTR level;
+
+    TRACE("Building Directory properties\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
+                        package);
+        msiobj_release(&view->hdr);
+    }
+
+    /* read components states from the registry */
+    ACTION_GetComponentInstallStates(package);
+    ACTION_GetFeatureInstallStates(package);
+
+    TRACE("File calculations\n");
+    msi_check_file_install_states( package );
+
     TRACE("Evaluating Condition Table\n");
 
     rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
@@ -1962,15 +1996,13 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
     TRACE("Enabling or Disabling Components\n");
     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
     {
-        if (comp->Condition)
+        if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
         {
-            if (MSI_EvaluateConditionW(package,
-                comp->Condition) == MSICONDITION_FALSE)
-            {
-                TRACE("Disabling component %s\n", debugstr_w(comp->Component));
-                comp->Enabled = FALSE;
-            }
+            TRACE("Disabling component %s\n", debugstr_w(comp->Component));
+            comp->Enabled = FALSE;
         }
+        else
+            comp->Enabled = TRUE;
     }
 
     MSI_SetPropertyW(package,szCosting,szOne);
@@ -1980,7 +2012,8 @@ static UINT ACTION_CostFinalize(MSIPACKAGE *package)
         MSI_SetPropertyW(package,szlevel, szOne);
     msi_free(level);
 
-    ACTION_UpdateInstallStates(package);
+    /* FIXME: check volume disk space */
+    MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
 
     return MSI_SetFeatureStates(package);
 }
@@ -1991,6 +2024,7 @@ static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
                          DWORD *size)
 {
     LPSTR data = NULL;
+
     if (value[0]=='#' && value[1]!='#' && value[1]!='%')
     {
         if (value[1]=='x')
@@ -2037,7 +2071,7 @@ static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
             }
             msi_free(deformated);
 
-            TRACE("Data %li bytes(%i)\n",*size,count);
+            TRACE("Data %i bytes(%i)\n",*size,count);
         }
         else
         {
@@ -2063,7 +2097,7 @@ static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
             if (deformated[0] == '-')
                 d = -d;
             *(LPDWORD)data = d;
-            TRACE("DWORD %li\n",*(LPDWORD)data);
+            TRACE("DWORD %i\n",*(LPDWORD)data);
 
             msi_free(deformated);
         }
@@ -2090,14 +2124,25 @@ static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
         if (strstrW(value,szMulti))
             *type = REG_MULTI_SZ;
 
+        /* remove initial delimiter */
+        if (!strncmpW(value, szMulti, 3))
+            ptr = value + 3;
+
         *size = deformat_string(package, ptr,(LPWSTR*)&data);
+
+        /* add double NULL terminator */
+        if (*type == REG_MULTI_SZ)
+        {
+            *size += 2 * sizeof(WCHAR); /* two NULL terminators */
+            data = msi_realloc_zero(data, *size);
+        }
     }
     return data;
 }
 
 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     static const WCHAR szHCR[] = 
         {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
          'R','O','O','T','\\',0};
@@ -2166,8 +2211,7 @@ static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
     {
         case -1: 
             {
-                static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
-                LPWSTR all_users = msi_dup_property( package, szALLUSER );
+                LPWSTR all_users = msi_dup_property( package, szAllUsers );
                 if (all_users && all_users[0] == '1')
                 {
                     root_key = HKEY_LOCAL_MACHINE;
@@ -2222,18 +2266,13 @@ static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
         value_data = parse_value(package, value, &type, &size); 
     else
     {
-        static const WCHAR szEmpty[] = {0};
         value_data = (LPSTR)strdupW(szEmpty);
-        size = 0;
+        size = sizeof(szEmpty);
         type = REG_SZ;
     }
 
     deformat_string(package, name, &deformated);
 
-    /* get the double nulls to terminate SZ_MULTI */
-    if (type == REG_MULTI_SZ)
-        size +=sizeof(WCHAR);
-
     if (!check_first)
     {
         TRACE("Setting value %s of %s\n",debugstr_w(deformated),
@@ -2350,22 +2389,29 @@ static UINT ACTION_InstallValidate(MSIPACKAGE *package)
 
 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIPACKAGE* package = param;
     LPCWSTR cond = NULL; 
     LPCWSTR message = NULL;
+    UINT r;
+
     static const WCHAR title[]=
         {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
 
     cond = MSI_RecordGetString(row,1);
 
-    if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
+    r = MSI_EvaluateConditionW(package,cond);
+    if (r == MSICONDITION_FALSE)
     {
-        LPWSTR deformated;
-        message = MSI_RecordGetString(row,2);
-        deformat_string(package,message,&deformated); 
-        MessageBoxW(NULL,deformated,title,MB_OK);
-        msi_free(deformated);
-        return ERROR_FUNCTION_FAILED;
+        if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
+        {
+            LPWSTR deformated;
+            message = MSI_RecordGetString(row,2);
+            deformat_string(package,message,&deformated);
+            MessageBoxW(NULL,deformated,title,MB_OK);
+            msi_free(deformated);
+        }
+
+        return ERROR_INSTALL_FAILURE;
     }
 
     return ERROR_SUCCESS;
@@ -2395,7 +2441,7 @@ static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
 {
 
     if (!cmp->KeyPath)
-        return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
+        return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
 
     if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
     {
@@ -2562,7 +2608,7 @@ static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
         }
     }
     
-    /* add a count for permenent */
+    /* add a count for permanent */
     if (comp->Attributes & msidbComponentAttributesPermanent)
         count ++;
     
@@ -2572,26 +2618,15 @@ static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
         ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
 }
 
-/*
- * 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 squished_pc[GUID_SIZE];
     WCHAR squished_cc[GUID_SIZE];
     UINT rc;
     MSICOMPONENT *comp;
-    HKEY hkey=0,hkey2=0;
-
-    /* writes the Component and Features values to the registry */
+    HKEY hkey;
 
-    rc = MSIREG_OpenComponents(&hkey);
-    if (rc != ERROR_SUCCESS)
-        return rc;
+    TRACE("\n");
 
     squash_guid(package->ProductCode,squished_pc);
     ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
@@ -2605,11 +2640,10 @@ static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
             continue;
 
         squash_guid(comp->ComponentId,squished_cc);
-           
+
         msi_free(comp->FullKeypath);
         comp->FullKeypath = resolve_keypath( package, comp );
 
-        /* do the refcounting */
         ACTION_RefCountComponent( package, comp );
 
         TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
@@ -2617,20 +2651,22 @@ static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
                             debugstr_w(squished_cc),
                             debugstr_w(comp->FullKeypath),
                             comp->RefCount);
-        /*
-         * Write the keypath out if the component is to be registered
-         * and delete the key if the component is to be deregistered
-         */
-        if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
-        {
-            rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
-            if (rc != ERROR_SUCCESS)
-                continue;
 
+        if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
+            ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
+        {
             if (!comp->FullKeypath)
                 continue;
 
-            msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
+            if (package->Context == MSIINSTALLCONTEXT_MACHINE)
+                rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
+                                                     &hkey, TRUE);
+            else
+                rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
+                                                     &hkey, TRUE);
+
+            if (rc != ERROR_SUCCESS)
+                continue;
 
             if (comp->Attributes & msidbComponentAttributesPermanent)
             {
@@ -2639,45 +2675,68 @@ static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
                       '0','0','0','0','0','0','0','0','0','0','0','0',
                       '0','0','0','0','0','0','0','0',0 };
 
-                msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
+                msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
             }
 
-            RegCloseKey(hkey2);
-
-            /* UI stuff */
-            uirow = MSI_CreateRecord(3);
-            MSI_RecordSetStringW(uirow,1,package->ProductCode);
-            MSI_RecordSetStringW(uirow,2,comp->ComponentId);
-            MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
-            ui_actiondata(package,szProcessComponents,uirow);
-            msiobj_release( &uirow->hdr );
-        }
-        else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
-        {
-            DWORD res;
+            if (comp->Action == INSTALLSTATE_LOCAL)
+                msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
+            else
+            {
+                MSIFILE *file;
+                MSIRECORD *row;
+                LPWSTR ptr, ptr2;
+                WCHAR source[MAX_PATH];
+                WCHAR base[MAX_PATH];
+                LPWSTR sourcepath;
+
+                static const WCHAR fmt[] = {'%','0','2','d','\\',0};
+                static const WCHAR query[] = {
+                    'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+                    '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
+                    '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
+                    '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
+                    '`','D','i','s','k','I','d','`',0};
+
+                file = get_loaded_file(package, comp->KeyPath);
+                if (!file)
+                    continue;
 
-            rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
-            if (rc != ERROR_SUCCESS)
-                continue;
+                row = MSI_QueryGetRecord(package->db, query, file->Sequence);
+                sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
+                ptr2 = strrchrW(source, '\\') + 1;
+                msiobj_release(&row->hdr);
 
-            RegDeleteValueW(hkey2,squished_pc);
+                lstrcpyW(base, package->PackagePath);
+                ptr = strrchrW(base, '\\');
+                *(ptr + 1) = '\0';
 
-            /* if the key is empty delete it */
-            res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
-            RegCloseKey(hkey2);
-            if (res == ERROR_NO_MORE_ITEMS)
-                RegDeleteKeyW(hkey,squished_cc);
+                sourcepath = resolve_file_source(package, file);
+                ptr = sourcepath + lstrlenW(base);
+                lstrcpyW(ptr2, ptr);
+                msi_free(sourcepath);
 
-            /* UI stuff */
-            uirow = MSI_CreateRecord(2);
-            MSI_RecordSetStringW(uirow,1,package->ProductCode);
-            MSI_RecordSetStringW(uirow,2,comp->ComponentId);
-            ui_actiondata(package,szProcessComponents,uirow);
-            msiobj_release( &uirow->hdr );
+                msi_reg_set_val_str(hkey, squished_pc, source);
+            }
+            RegCloseKey(hkey);
         }
-    } 
-    RegCloseKey(hkey);
-    return rc;
+        else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
+        {
+            if (package->Context == MSIINSTALLCONTEXT_MACHINE)
+                MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
+            else
+                MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
+        }
+
+        /* UI stuff */
+        uirow = MSI_CreateRecord(3);
+        MSI_RecordSetStringW(uirow,1,package->ProductCode);
+        MSI_RecordSetStringW(uirow,2,comp->ComponentId);
+        MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
+        ui_actiondata(package,szProcessComponents,uirow);
+        msiobj_release( &uirow->hdr );
+    }
+
+    return ERROR_SUCCESS;
 }
 
 typedef struct {
@@ -2716,7 +2775,7 @@ static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
 
     TRACE("trying %s\n", debugstr_w(tl_struct->path));
     res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
-    if (!SUCCEEDED(res))
+    if (FAILED(res))
     {
         msi_free(tl_struct->path);
         tl_struct->path = NULL;
@@ -2742,12 +2801,15 @@ static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
 
 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIPACKAGE* package = param;
     LPCWSTR component;
     MSICOMPONENT *comp;
     MSIFILE *file;
     typelib_struct tl_struct;
+    ITypeLib *tlib;
     HMODULE module;
+    HRESULT hr;
+
     static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
 
     component = MSI_RecordGetString(row,3);
@@ -2791,11 +2853,11 @@ static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
             helpid = MSI_RecordGetString(row,6);
 
             if (helpid)
-                help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
+                help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
             res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
             msi_free(help);
 
-            if (!SUCCEEDED(res))
+            if (FAILED(res))
                 ERR("Failed to register type library %s\n",
                         debugstr_w(tl_struct.path));
             else
@@ -2816,7 +2878,16 @@ static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
         msi_free(tl_struct.source);
     }
     else
-        ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
+    {
+        hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
+        if (FAILED(hr))
+        {
+            ERR("Failed to load type library: %08x\n", hr);
+            return ERROR_FUNCTION_FAILED;
+        }
+
+        ITypeLib_Release(tlib);
+    }
 
     return ERROR_SUCCESS;
 }
@@ -2846,7 +2917,7 @@ static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
 
 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPWSTR target_file, target_folder, filename;
     LPCWSTR buffer, extension;
     MSICOMPONENT *comp;
@@ -2890,9 +2961,9 @@ static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
     }
 
     buffer = MSI_RecordGetString(row,2);
-    target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
+    target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
 
-    /* may be needed because of a bug somehwere else */
+    /* may be needed because of a bug somewhere else */
     create_full_pathW(target_folder);
 
     filename = msi_dup_record_field( row, 3 );
@@ -2966,7 +3037,7 @@ static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
     {
         LPWSTR Path;
         buffer = MSI_RecordGetString(row,12);
-        Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
+        Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
         if (Path)
             IShellLinkW_SetWorkingDirectory(sl,Path);
         msi_free(Path);
@@ -3000,23 +3071,19 @@ static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
         return ERROR_SUCCESS;
 
     res = CoInitialize( NULL );
-    if (FAILED (res))
-    {
-        ERR("CoInitialize failed\n");
-        return ERROR_FUNCTION_FAILED;
-    }
 
     rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
     msiobj_release(&view->hdr);
 
-    CoUninitialize();
+    if (SUCCEEDED(res))
+        CoUninitialize();
 
     return rc;
 }
 
-static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
+static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIPACKAGE* package = param;
     HANDLE the_file;
     LPWSTR FilePath;
     LPCWSTR FileName;
@@ -3073,128 +3140,316 @@ static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
     return ERROR_SUCCESS;
 }
 
-/*
- * 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)
+static UINT msi_publish_icons(MSIPACKAGE *package)
 {
-    UINT rc;
-    MSIQUERY * view;
-    static const WCHAR Query[]=
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-         '`','I','c','o','n','`',0};
-    /* for registry stuff */
-    HKEY hkey=0;
-    HKEY hukey=0;
-    static const WCHAR szProductLanguage[] =
-        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
-    static const WCHAR szARPProductIcon[] =
-        {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
-    static const WCHAR szProductVersion[] =
-        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
-    DWORD langid;
-    LPWSTR buffer;
-    DWORD size;
-    MSIHANDLE hDb, hSumInfo;
+    UINT r;
+    MSIQUERY *view;
 
-    /* write out icon files */
+    static const WCHAR query[]= {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','`','I','c','o','n','`',0};
 
-    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
-    if (rc == ERROR_SUCCESS)
+    r = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (r == ERROR_SUCCESS)
     {
-        MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
+        MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
         msiobj_release(&view->hdr);
     }
 
-    /* ok there is a lot more done here but i need to figure out what */
+    return ERROR_SUCCESS;
+}
 
-    rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
-    if (rc != ERROR_SUCCESS)
-        goto end;
+static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
+{
+    UINT r;
+    HKEY source;
+    LPWSTR buffer;
+    MSIMEDIADISK *disk;
+    MSISOURCELISTINFO *info;
 
-    rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
-    if (rc != ERROR_SUCCESS)
-        goto end;
+    r = RegCreateKeyW(hkey, szSourceList, &source);
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    RegCloseKey(source);
+
+    buffer = strrchrW(package->PackagePath, '\\') + 1;
+    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
+                              package->Context, MSICODE_PRODUCT,
+                              INSTALLPROPERTY_PACKAGENAMEW, buffer);
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
+                              package->Context, MSICODE_PRODUCT,
+                              INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
+                              package->Context, MSICODE_PRODUCT,
+                              INSTALLPROPERTY_DISKPROMPTW, szEmpty);
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
+    {
+        if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
+            msi_set_last_used_source(package->ProductCode, NULL, info->context,
+                                     info->options, info->value);
+        else
+            MsiSourceListSetInfoW(package->ProductCode, NULL,
+                                  info->context, info->options,
+                                  info->property, info->value);
+    }
+
+    LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
+    {
+        MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
+                                   disk->context, disk->options,
+                                   disk->disk_id, disk->volume_label, disk->disk_prompt);
+    }
+
+    return ERROR_SUCCESS;
+}
 
+static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
+{
+    MSIHANDLE hdb, suminfo;
+    WCHAR guids[MAX_PATH];
+    WCHAR packcode[SQUISH_GUID_SIZE];
+    LPWSTR buffer;
+    LPWSTR ptr;
+    DWORD langid;
+    DWORD size;
+    UINT r;
 
-    buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
-    msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+    static const WCHAR szARPProductIcon[] =
+        {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
+    static const WCHAR szProductVersion[] =
+        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+    static const WCHAR szAssignment[] =
+        {'A','s','s','i','g','n','m','e','n','t',0};
+    static const WCHAR szAdvertiseFlags[] =
+        {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
+    static const WCHAR szClients[] =
+        {'C','l','i','e','n','t','s',0};
+    static const WCHAR szColon[] = {':',0};
+
+    buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
+    msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
     msi_free(buffer);
 
-    langid = msi_get_property_int( package, szProductLanguage, 0 );
-    msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
+    langid = msi_get_property_int(package, szProductLanguage, 0);
+    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
 
-    buffer = msi_dup_property( package, szARPProductIcon );
+    /* FIXME */
+    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
+
+    buffer = msi_dup_property(package, szARPProductIcon);
     if (buffer)
     {
         LPWSTR path = build_icon_path(package,buffer);
-        msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
-        msi_free( path );
+        msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
+        msi_free(path);
+        msi_free(buffer);
     }
-    msi_free(buffer);
 
-    buffer = msi_dup_property( package, szProductVersion );
+    buffer = msi_dup_property(package, szProductVersion);
     if (buffer)
     {
         DWORD verdword = msi_version_str_to_dword(buffer);
-        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
-    }
-    msi_free(buffer);
-    
-    /* FIXME: Need to write more keys to the user registry */
-  
-    hDb= alloc_msihandle( &package->db->hdr );
-    if (!hDb) {
-        rc = ERROR_NOT_ENOUGH_MEMORY;
-        goto end;
+        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
+        msi_free(buffer);
     }
-    rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
-    MsiCloseHandle(hDb);
-    if (rc == ERROR_SUCCESS)
+
+    msi_reg_set_val_dword(hkey, szAssignment, 0);
+    msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
+    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
+    msi_reg_set_val_str(hkey, szClients, szColon);
+
+    hdb = alloc_msihandle(&package->db->hdr);
+    if (!hdb)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
+    MsiCloseHandle(hdb);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    size = MAX_PATH;
+    r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
+                                   NULL, guids, &size);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    ptr = strchrW(guids, ';');
+    if (ptr) *ptr = 0;
+    squash_guid(guids, packcode);
+    msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
+
+done:
+    MsiCloseHandle(suminfo);
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
+{
+    UINT r;
+    HKEY hkey;
+    LPWSTR upgrade;
+    WCHAR squashed_pc[SQUISH_GUID_SIZE];
+
+    static const WCHAR szUpgradeCode[] =
+        {'U','p','g','r','a','d','e','C','o','d','e',0};
+
+    upgrade = msi_dup_property(package, szUpgradeCode);
+    if (!upgrade)
+        return ERROR_SUCCESS;
+
+    if (package->Context == MSIINSTALLCONTEXT_MACHINE)
     {
-        WCHAR guidbuffer[0x200];
-        size = 0x200;
-        rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
-                                        guidbuffer, &size);
-        if (rc == ERROR_SUCCESS)
-        {
-            WCHAR squashed[GUID_SIZE];
-            /* for now we only care about the first guid */
-            LPWSTR ptr = strchrW(guidbuffer,';');
-            if (ptr) *ptr = 0;
-            squash_guid(guidbuffer,squashed);
-            msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
-        }
-        else
-        {
-            ERR("Unable to query Revision_Number...\n");
-            rc = ERROR_SUCCESS;
-        }
-        MsiCloseHandle(hSumInfo);
+        r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
+        if (r != ERROR_SUCCESS)
+            goto done;
     }
     else
     {
-        ERR("Unable to open Summary Information\n");
-        rc = ERROR_SUCCESS;
+        r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
+        if (r != ERROR_SUCCESS)
+            goto done;
     }
 
-end:
+    squash_guid(package->ProductCode, squashed_pc);
+    msi_reg_set_val_str(hkey, squashed_pc, NULL);
 
     RegCloseKey(hkey);
+
+done:
+    msi_free(upgrade);
+    return r;
+}
+
+static BOOL msi_check_publish(MSIPACKAGE *package)
+{
+    MSIFEATURE *feature;
+
+    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
+    {
+        if (feature->ActionRequest == INSTALLSTATE_LOCAL)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL msi_check_unpublish(MSIPACKAGE *package)
+{
+    MSIFEATURE *feature;
+
+    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
+    {
+        if (feature->ActionRequest != INSTALLSTATE_ABSENT)
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
+{
+    WCHAR patch_squashed[GUID_SIZE];
+    HKEY patches;
+    LONG res;
+    UINT r = ERROR_FUNCTION_FAILED;
+
+    res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
+                          &patches, NULL);
+    if (res != ERROR_SUCCESS)
+        return ERROR_FUNCTION_FAILED;
+
+    squash_guid(package->patch->patchcode, patch_squashed);
+
+    res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
+                         (const BYTE *)patch_squashed,
+                         (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
+    if (res != ERROR_SUCCESS)
+        goto done;
+
+    res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
+                         (const BYTE *)package->patch->transforms,
+                         (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
+    if (res == ERROR_SUCCESS)
+        r = ERROR_SUCCESS;
+
+done:
+    RegCloseKey(patches);
+    return r;
+}
+
+/*
+ * 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;
+    HKEY hukey=0;
+    HKEY hudkey=0;
+
+    /* FIXME: also need to publish if the product is in advertise mode */
+    if (!msi_check_publish(package))
+        return ERROR_SUCCESS;
+
+    rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
+                               &hukey, TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
+                                       NULL, &hudkey, TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = msi_publish_upgrade_code(package);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    if (package->patch)
+    {
+        rc = msi_publish_patch(package, hukey, hudkey);
+        if (rc != ERROR_SUCCESS)
+            goto end;
+    }
+
+    rc = msi_publish_product_properties(package, hukey);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = msi_publish_sourcelist(package, hukey);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = msi_publish_icons(package);
+
+end:
     RegCloseKey(hukey);
+    RegCloseKey(hudkey);
 
     return rc;
 }
 
 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
-    LPCWSTR component,section,key,value,identifier,filename,dirproperty;
+    MSIPACKAGE *package = param;
+    LPCWSTR component, section, key, value, identifier, dirproperty;
     LPWSTR deformated_section, deformated_key, deformated_value;
-    LPWSTR folder, fullname = NULL;
+    LPWSTR folder, filename, fullname = NULL;
+    LPCWSTR filenameptr;
     MSIRECORD * uirow;
     INT action;
     MSICOMPONENT *comp;
@@ -3217,7 +3472,6 @@ static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
     comp->Action = INSTALLSTATE_LOCAL;
 
     identifier = MSI_RecordGetString(row,1); 
-    filename = MSI_RecordGetString(row,2);
     dirproperty = MSI_RecordGetString(row,3);
     section = MSI_RecordGetString(row,4);
     key = MSI_RecordGetString(row,5);
@@ -3228,9 +3482,15 @@ static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
     deformat_string(package,key,&deformated_key);
     deformat_string(package,value,&deformated_value);
 
+    filename = msi_dup_record_field(row, 2);
+    if (filename && (filenameptr = strchrW(filename, '|')))
+        filenameptr++;
+    else
+        filenameptr = filename;
+
     if (dirproperty)
     {
-        folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
+        folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
         if (!folder)
             folder = msi_dup_property( package, dirproperty );
     }
@@ -3243,7 +3503,7 @@ static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
         goto cleanup;
     }
 
-    fullname = build_directory_name(2, folder, filename);
+    fullname = build_directory_name(2, folder, filenameptr);
 
     if (action == 0)
     {
@@ -3278,7 +3538,9 @@ static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
     MSI_RecordSetStringW(uirow,4,deformated_value);
     ui_actiondata(package,szWriteIniValues,uirow);
     msiobj_release( &uirow->hdr );
+
 cleanup:
+    msi_free(filename);
     msi_free(fullname);
     msi_free(folder);
     msi_free(deformated_key);
@@ -3309,7 +3571,7 @@ static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
 
 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPCWSTR filename;
     LPWSTR FullName;
     MSIFILE *file;
@@ -3346,7 +3608,11 @@ static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
                     &si, &info);
 
     if (brc)
+    {
+        CloseHandle(info.hThread);
         msi_dialog_check_messages(info.hProcess);
+        CloseHandle(info.hProcess);
+    }
 
     msi_free(FullName);
 
@@ -3355,8 +3621,8 @@ static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
     uipath = strdupW( file->TargetPath );
     p = strrchrW(uipath,'\\');
     if (p)
-        p[1]=0;
-    MSI_RecordSetStringW( uirow, 1, &p[2] );
+        p[0]=0;
+    MSI_RecordSetStringW( uirow, 1, &p[1] );
     MSI_RecordSetStringW( uirow, 2, uipath);
     ui_actiondata( package, szSelfRegModules, uirow);
     msiobj_release( &uirow->hdr );
@@ -3391,14 +3657,19 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
 {
     MSIFEATURE *feature;
     UINT rc;
-    HKEY hkey=0;
-    HKEY hukey=0;
-    
-    rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
+    HKEY hkey;
+    HKEY userdata = NULL;
+
+    if (!msi_check_publish(package))
+        return ERROR_SUCCESS;
+
+    rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
+                                &hkey, TRUE);
     if (rc != ERROR_SUCCESS)
         goto end;
 
-    rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
+    rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
+                                        &userdata, TRUE);
     if (rc != ERROR_SUCCESS)
         goto end;
 
@@ -3443,6 +3714,7 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
                 strcatW(data,buf);
             }
         }
+
         if (feature->Feature_Parent)
         {
             static const WCHAR sep[] = {'\2',0};
@@ -3450,7 +3722,7 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
             strcatW(data,feature->Feature_Parent);
         }
 
-        msi_reg_set_val_str( hkey, feature->Feature, data );
+        msi_reg_set_val_str( userdata, feature->Feature, data );
         msi_free(data);
 
         size = 0;
@@ -3458,8 +3730,9 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
             size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
         if (!absent)
         {
-            RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
-                       (LPBYTE)feature->Feature_Parent,size);
+            size += sizeof(WCHAR);
+            RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
+                           (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
         }
         else
         {
@@ -3469,7 +3742,7 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
             data[1] = 0;
             if (feature->Feature_Parent)
                 strcpyW( &data[1], feature->Feature_Parent );
-            RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
+            RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
                        (LPBYTE)data,size);
             msi_free(data);
         }
@@ -3484,72 +3757,84 @@ static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
 
 end:
     RegCloseKey(hkey);
-    RegCloseKey(hukey);
+    RegCloseKey(userdata);
     return rc;
 }
 
-static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
+static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
 {
-    static const WCHAR installerPathFmt[] = {
-        '%','s','\\','I','n','s','t','a','l','l','e','r','\\',0};
-    static const WCHAR fmt[] = {
-        '%','s','\\',
-        'I','n','s','t','a','l','l','e','r','\\',
-        '%','x','.','m','s','i',0};
-    static const WCHAR szOriginalDatabase[] =
-        {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
-    WCHAR windir[MAX_PATH], path[MAX_PATH], packagefile[MAX_PATH];
-    INT num, start;
-    LPWSTR msiFilePath;
-    BOOL r;
-
-    /* copy the package locally */
-    num = GetTickCount() & 0xffff;
-    if (!num) 
-        num = 1;
-    start = num;
-    GetWindowsDirectoryW( windir, MAX_PATH );
-    snprintfW( packagefile, MAX_PATH, fmt, windir, num );
-    do 
+    UINT r;
+    HKEY hkey;
+
+    TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
+
+    r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
+                               &hkey, FALSE);
+    if (r == ERROR_SUCCESS)
     {
-        HANDLE handle = CreateFileW(packagefile,GENERIC_WRITE, 0, NULL,
-                                  CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
-        if (handle != INVALID_HANDLE_VALUE)
-        {
-            CloseHandle(handle);
-            break;
-        }
-        if (GetLastError() != ERROR_FILE_EXISTS &&
-            GetLastError() != ERROR_SHARING_VIOLATION)
-            break;
-        if (!(++num & 0xffff)) num = 1;
-        sprintfW(packagefile,fmt,num);
-    } while (num != start);
+        RegDeleteValueW(hkey, feature->Feature);
+        RegCloseKey(hkey);
+    }
+
+    r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
+                                       &hkey, FALSE);
+    if (r == ERROR_SUCCESS)
+    {
+        RegDeleteValueW(hkey, feature->Feature);
+        RegCloseKey(hkey);
+    }
 
-    snprintfW( path, MAX_PATH, installerPathFmt, windir );
-    create_full_pathW(path);
+    return ERROR_SUCCESS;
+}
 
-    TRACE("Copying to local package %s\n",debugstr_w(packagefile));
+static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
+{
+    MSIFEATURE *feature;
 
-    msiFilePath = msi_dup_property( package, szOriginalDatabase );
-    r = CopyFileW( msiFilePath, packagefile, FALSE);
-    msi_free( msiFilePath );
+    if (!msi_check_unpublish(package))
+        return ERROR_SUCCESS;
 
-    if (!r)
+    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
     {
-        ERR("Unable to copy package (%s -> %s) (error %ld)\n",
-            debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
-        return ERROR_FUNCTION_FAILED;
+        msi_unpublish_feature(package, feature);
     }
 
-    /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
-    msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
     return ERROR_SUCCESS;
 }
 
-static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
+static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
 {
     LPWSTR prop, val, key;
+    SYSTEMTIME systime;
+    DWORD size, langid;
+    WCHAR date[9];
+    LPWSTR buffer;
+
+    static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
+    static const WCHAR szWindowsInstaller[] =
+        {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
+    static const WCHAR modpath_fmt[] =
+        {'M','s','i','E','x','e','c','.','e','x','e',' ',
+         '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
+    static const WCHAR szModifyPath[] =
+        {'M','o','d','i','f','y','P','a','t','h',0};
+    static const WCHAR szUninstallString[] =
+        {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
+    static const WCHAR szEstimatedSize[] =
+        {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+    static const WCHAR szProductVersion[] =
+        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+    static const WCHAR szProductName[] =
+        {'P','r','o','d','u','c','t','N','a','m','e',0};
+    static const WCHAR szDisplayName[] =
+        {'D','i','s','p','l','a','y','N','a','m','e',0};
+    static const WCHAR szDisplayVersion[] =
+        {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
+    static const WCHAR szManufacturer[] =
+        {'M','a','n','u','f','a','c','t','u','r','e','r',0};
+
     static const LPCSTR propval[] = {
         "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
         "ARPCONTACT",             "Contact",
@@ -3569,127 +3854,196 @@ static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
     };
     const LPCSTR *p = propval;
 
-    while( *p )
+    while (*p)
     {
-        prop = strdupAtoW( *p++ );
-        key = strdupAtoW( *p++ );
-        val = msi_dup_property( package, prop );
-        msi_reg_set_val_str( hkey, key, val );
+        prop = strdupAtoW(*p++);
+        key = strdupAtoW(*p++);
+        val = msi_dup_property(package, prop);
+        msi_reg_set_val_str(hkey, key, val);
         msi_free(val);
         msi_free(key);
         msi_free(prop);
     }
-    return ERROR_SUCCESS;
-}
-
-static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
-{
-    HKEY hkey=0;
-    LPWSTR buffer = NULL;
-    UINT rc;
-    DWORD size, langid;
-    static const WCHAR szWindowsInstaller[] = 
-        {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
-    static const WCHAR szUpgradeCode[] = 
-        {'U','p','g','r','a','d','e','C','o','d','e',0};
-    static const WCHAR modpath_fmt[] = 
-        {'M','s','i','E','x','e','c','.','e','x','e',' ',
-         '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
-    static const WCHAR szModifyPath[] = 
-        {'M','o','d','i','f','y','P','a','t','h',0};
-    static const WCHAR szUninstallString[] = 
-        {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
-    static const WCHAR szEstimatedSize[] = 
-        {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
-    static const WCHAR szProductLanguage[] =
-        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
-    static const WCHAR szProductVersion[] =
-        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
 
-    SYSTEMTIME systime;
-    static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
-    LPWSTR upgrade_code;
-    WCHAR szDate[9]; 
+    msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
 
-    rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
-    if (rc != ERROR_SUCCESS)
-        return rc;
+    size = deformat_string(package, modpath_fmt, &buffer);
+    RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
+    RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
+    msi_free(buffer);
 
-    /* dump all the info i can grab */
-    /* FIXME: Flesh out more information */
+    /* FIXME: Write real Estimated Size when we have it */
+    msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
 
-    msi_write_uninstall_property_vals( package, hkey );
+    buffer = msi_dup_property(package, szProductName);
+    msi_reg_set_val_str(hkey, szDisplayName, buffer);
+    msi_free(buffer);
 
-    msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
-    
-    msi_make_package_local( package, hkey );
+    buffer = msi_dup_property(package, cszSourceDir);
+    msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
+    msi_free(buffer);
 
-    /* do ModifyPath and UninstallString */
-    size = deformat_string(package,modpath_fmt,&buffer);
-    RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
-    RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
+    buffer = msi_dup_property(package, szManufacturer);
+    msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
     msi_free(buffer);
 
-    /* FIXME: Write real Estimated Size when we have it */
-    msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
-   
     GetLocalTime(&systime);
-    sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
-    msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
-   
-    langid = msi_get_property_int( package, szProductLanguage, 0 );
-    msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
+    sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
+    msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
+
+    langid = msi_get_property_int(package, szProductLanguage, 0);
+    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
 
-    buffer = msi_dup_property( package, szProductVersion );
+    buffer = msi_dup_property(package, szProductVersion);
+    msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
     if (buffer)
     {
         DWORD verdword = msi_version_str_to_dword(buffer);
 
-        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
-        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
-        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
-    }
-    msi_free(buffer);
-    
-    /* Handle Upgrade Codes */
-    upgrade_code = msi_dup_property( package, szUpgradeCode );
-    if (upgrade_code)
-    {
-        HKEY hkey2;
-        WCHAR squashed[33];
-        MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
-        squash_guid(package->ProductCode,squashed);
-        msi_reg_set_val_str( hkey2, squashed, NULL );
-        RegCloseKey(hkey2);
-        MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
-        squash_guid(package->ProductCode,squashed);
-        msi_reg_set_val_str( hkey2, squashed, NULL );
-        RegCloseKey(hkey2);
-
-        msi_free(upgrade_code);
+        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
+        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
+        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
+        msi_free(buffer);
     }
-    
-    RegCloseKey(hkey);
-
-    /* FIXME: call ui_actiondata */
 
     return ERROR_SUCCESS;
 }
 
-static UINT ACTION_InstallExecute(MSIPACKAGE *package)
-{
-    return execute_script(package,INSTALL_SCRIPT);
-}
-
-static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
+static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
 {
+    WCHAR squashed_pc[SQUISH_GUID_SIZE];
+    LPWSTR upgrade_code;
+    HKEY hkey, props;
+    HKEY upgrade;
     UINT rc;
 
-    /* turn off scheduleing */
-    package->script->CurrentlyScripting= FALSE;
+    static const WCHAR szUpgradeCode[] = {
+        'U','p','g','r','a','d','e','C','o','d','e',0};
 
-    /* first do the same as an InstallExecute */
-    rc = ACTION_InstallExecute(package);
+    /* FIXME: also need to publish if the product is in advertise mode */
+    if (!msi_check_publish(package))
+        return ERROR_SUCCESS;
+
+    rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
+                                 NULL, &props, TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto done;
+
+    msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->db->localfile );
+    msi_free( package->db->localfile );
+    package->db->localfile = NULL;
+
+    rc = msi_publish_install_properties(package, hkey);
+    if (rc != ERROR_SUCCESS)
+        goto done;
+
+    rc = msi_publish_install_properties(package, props);
+    if (rc != ERROR_SUCCESS)
+        goto done;
+
+    upgrade_code = msi_dup_property(package, szUpgradeCode);
+    if (upgrade_code)
+    {
+        MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
+        squash_guid(package->ProductCode, squashed_pc);
+        msi_reg_set_val_str(upgrade, squashed_pc, NULL);
+        RegCloseKey(upgrade);
+        msi_free(upgrade_code);
+    }
+
+done:
+    RegCloseKey(hkey);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_InstallExecute(MSIPACKAGE *package)
+{
+    return execute_script(package,INSTALL_SCRIPT);
+}
+
+static UINT msi_unpublish_product(MSIPACKAGE *package)
+{
+    LPWSTR upgrade;
+    LPWSTR remove = NULL;
+    LPWSTR *features = NULL;
+    BOOL full_uninstall = TRUE;
+    MSIFEATURE *feature;
+
+    static const WCHAR szUpgradeCode[] =
+        {'U','p','g','r','a','d','e','C','o','d','e',0};
+
+    remove = msi_dup_property(package, szRemove);
+    if (!remove)
+        return ERROR_SUCCESS;
+
+    features = msi_split_string(remove, ',');
+    if (!features)
+    {
+        msi_free(remove);
+        ERR("REMOVE feature list is empty!\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    if (!lstrcmpW(features[0], szAll))
+        full_uninstall = TRUE;
+    else
+    {
+        LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
+        {
+            if (feature->Action != INSTALLSTATE_ABSENT)
+                full_uninstall = FALSE;
+        }
+    }
+
+    if (!full_uninstall)
+        goto done;
+
+    MSIREG_DeleteProductKey(package->ProductCode);
+    MSIREG_DeleteUserDataProductKey(package->ProductCode);
+    MSIREG_DeleteUninstallKey(package->ProductCode);
+
+    if (package->Context == MSIINSTALLCONTEXT_MACHINE)
+    {
+        MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
+        MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
+    }
+    else
+    {
+        MSIREG_DeleteUserProductKey(package->ProductCode);
+        MSIREG_DeleteUserFeaturesKey(package->ProductCode);
+    }
+
+    upgrade = msi_dup_property(package, szUpgradeCode);
+    if (upgrade)
+    {
+        MSIREG_DeleteUserUpgradeCodesKey(upgrade);
+        msi_free(upgrade);
+    }
+
+done:
+    msi_free(remove);
+    msi_free(features);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
+{
+    UINT rc;
+
+    rc = msi_unpublish_product(package);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    /* turn off scheduling */
+    package->script->CurrentlyScripting= FALSE;
+
+    /* first do the same as an InstallExecute */
+    rc = ACTION_InstallExecute(package);
     if (rc != ERROR_SUCCESS)
         return rc;
 
@@ -3699,7 +4053,7 @@ static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
     return rc;
 }
 
-static UINT ACTION_ForceReboot(MSIPACKAGE *package)
+UINT ACTION_ForceReboot(MSIPACKAGE *package)
 {
     static const WCHAR RunOnce[] = {
     'S','o','f','t','w','a','r','e','\\',
@@ -3748,18 +4102,21 @@ static UINT ACTION_ForceReboot(MSIPACKAGE *package)
     return ERROR_INSTALL_SUSPEND;
 }
 
-UINT ACTION_ResolveSource(MSIPACKAGE* package)
+static UINT ACTION_ResolveSource(MSIPACKAGE* package)
 {
     DWORD attrib;
     UINT rc;
+
     /*
-     * we are currently doing what should be done here in the top level Install
-     * however for Adminastrative and uninstalls this step will be needed
+     * We are currently doing what should be done here in the top level Install
+     * however for Administrative and uninstalls this step will be needed
      */
     if (!package->PackagePath)
         return ERROR_SUCCESS;
 
-    attrib = GetFileAttributesW(package->PackagePath);
+    msi_set_sourcedir_props(package, TRUE);
+
+    attrib = GetFileAttributesW(package->db->path);
     if (attrib == INVALID_FILE_ATTRIBUTES)
     {
         LPWSTR prompt;
@@ -3767,17 +4124,17 @@ UINT ACTION_ResolveSource(MSIPACKAGE* package)
         DWORD size = 0;
 
         rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
-                MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
+                package->Context, MSICODE_PRODUCT,
                 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
         if (rc == ERROR_MORE_DATA)
         {
             prompt = msi_alloc(size * sizeof(WCHAR));
             MsiSourceListGetInfoW(package->ProductCode, NULL, 
-                    MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
+                    package->Context, MSICODE_PRODUCT,
                     INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
         }
         else
-            prompt = strdupW(package->PackagePath);
+            prompt = strdupW(package->db->path);
 
         msg = generate_error_string(package,1302,1,prompt);
         while(attrib == INVALID_FILE_ATTRIBUTES)
@@ -3788,7 +4145,7 @@ UINT ACTION_ResolveSource(MSIPACKAGE* package)
                 rc = ERROR_INSTALL_USEREXIT;
                 break;
             }
-            attrib = GetFileAttributesW(package->PackagePath);
+            attrib = GetFileAttributesW(package->db->path);
         }
         msi_free(prompt);
         rc = ERROR_SUCCESS;
@@ -3822,11 +4179,18 @@ static UINT ACTION_RegisterUser(MSIPACKAGE *package)
         {0},
     };
 
+    if (msi_check_unpublish(package))
+    {
+        MSIREG_DeleteUserDataProductKey(package->ProductCode);
+        return ERROR_SUCCESS;
+    }
+
     productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
     if (!productid)
         return ERROR_SUCCESS;
 
-    rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
+    rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
+                                 NULL, &hkey, TRUE);
     if (rc != ERROR_SUCCESS)
         goto end;
 
@@ -3843,7 +4207,7 @@ end:
 
     /* FIXME: call ui_actiondata */
 
-    return ERROR_SUCCESS;
+    return rc;
 }
 
 
@@ -3857,232 +4221,9 @@ static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
 }
 
 
-/*
- * Code based off of code located here
- * http://www.codeproject.com/gdi/fontnamefromfile.asp
- *
- * Using string index 4 (full font name) instead of 1 (family name)
- */
-static LPWSTR load_ttfname_from(LPCWSTR filename)
-{
-    HANDLE handle;
-    LPWSTR ret = NULL;
-    int i;
-
-    typedef struct _tagTT_OFFSET_TABLE{
-        USHORT uMajorVersion;
-        USHORT uMinorVersion;
-        USHORT uNumOfTables;
-        USHORT uSearchRange;
-        USHORT uEntrySelector;
-        USHORT uRangeShift;
-    }TT_OFFSET_TABLE;
-
-    typedef struct _tagTT_TABLE_DIRECTORY{
-        char szTag[4]; /* table name */
-        ULONG uCheckSum; /* Check sum */
-        ULONG uOffset; /* Offset from beginning of file */
-        ULONG uLength; /* length of the table in bytes */
-    }TT_TABLE_DIRECTORY;
-
-    typedef struct _tagTT_NAME_TABLE_HEADER{
-    USHORT uFSelector; /* format selector. Always 0 */
-    USHORT uNRCount; /* Name Records count */
-    USHORT uStorageOffset; /* Offset for strings storage, 
-                            * from start of the table */
-    }TT_NAME_TABLE_HEADER;
-   
-    typedef struct _tagTT_NAME_RECORD{
-        USHORT uPlatformID;
-        USHORT uEncodingID;
-        USHORT uLanguageID;
-        USHORT uNameID;
-        USHORT uStringLength;
-        USHORT uStringOffset; /* from start of storage area */
-    }TT_NAME_RECORD;
-
-#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
-#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
-
-    handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
-                    FILE_ATTRIBUTE_NORMAL, 0 );
-    if (handle != INVALID_HANDLE_VALUE)
-    {
-        TT_TABLE_DIRECTORY tblDir;
-        BOOL bFound = FALSE;
-        TT_OFFSET_TABLE ttOffsetTable;
-        DWORD dwRead;
-
-        ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL);
-        ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
-        ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
-        ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
-        
-        if (ttOffsetTable.uMajorVersion != 1 || 
-                        ttOffsetTable.uMinorVersion != 0)
-            return NULL;
-
-        for (i=0; i< ttOffsetTable.uNumOfTables; i++)
-        {
-            ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL);
-            if (strncmp(tblDir.szTag,"name",4)==0)
-            {
-                bFound = TRUE;
-                tblDir.uLength = SWAPLONG(tblDir.uLength);
-                tblDir.uOffset = SWAPLONG(tblDir.uOffset);
-                break;
-            }
-        }
-
-        if (bFound)
-        {
-            TT_NAME_TABLE_HEADER ttNTHeader;
-            TT_NAME_RECORD ttRecord;
-
-            SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
-            ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER),
-                            &dwRead,NULL);
-
-            ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
-            ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
-            bFound = FALSE;
-            for(i=0; i<ttNTHeader.uNRCount; i++)
-            {
-                ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL);
-                ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
-                /* 4 is the Full Font Name */
-                if(ttRecord.uNameID == 4)
-                {
-                    int nPos;
-                    LPSTR buf;
-                    static LPCSTR tt = " (TrueType)";
-
-                    ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
-                    ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
-                    nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
-                    SetFilePointer(handle, tblDir.uOffset + 
-                                    ttRecord.uStringOffset + 
-                                    ttNTHeader.uStorageOffset,
-                                    NULL, FILE_BEGIN);
-                    buf = msi_alloc_zero( ttRecord.uStringLength + 1 + strlen(tt) );
-                    ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
-                    if (strlen(buf) > 0)
-                    {
-                        strcat(buf,tt);
-                        ret = strdupAtoW(buf);
-                        msi_free(buf);
-                        break;
-                    }
-
-                    msi_free(buf);
-                    SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
-                }
-            }
-        }
-        CloseHandle(handle);
-    }
-    else
-        ERR("Unable to open font file %s\n", debugstr_w(filename));
-
-    TRACE("Returning fontname %s\n",debugstr_w(ret));
-    return ret;
-}
-
-static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
-{
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
-    LPWSTR name;
-    LPCWSTR filename;
-    MSIFILE *file;
-    static const WCHAR regfont1[] =
-        {'S','o','f','t','w','a','r','e','\\',
-         'M','i','c','r','o','s','o','f','t','\\',
-         'W','i','n','d','o','w','s',' ','N','T','\\',
-         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-         'F','o','n','t','s',0};
-    static const WCHAR regfont2[] =
-        {'S','o','f','t','w','a','r','e','\\',
-         'M','i','c','r','o','s','o','f','t','\\',
-         'W','i','n','d','o','w','s','\\',
-         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-         'F','o','n','t','s',0};
-    HKEY hkey1;
-    HKEY hkey2;
-    MSIRECORD *uirow;
-    LPWSTR uipath, p;
-
-    filename = MSI_RecordGetString( row, 1 );
-    file = get_loaded_file( package, filename );
-    if (!file)
-    {
-        ERR("Unable to load file\n");
-        return ERROR_SUCCESS;
-    }
-
-    /* check to make sure that component is installed */
-    if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
-    {
-        TRACE("Skipping: Component not scheduled for install\n");
-        return ERROR_SUCCESS;
-    }
-
-    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
-    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
-
-    if (MSI_RecordIsNull(row,2))
-        name = load_ttfname_from( file->TargetPath );
-    else
-        name = msi_dup_record_field(row,2);
-
-    if (name)
-    {
-        msi_reg_set_val_str( hkey1, name, file->FileName );
-        msi_reg_set_val_str( hkey2, name, file->FileName );
-    }
-
-    msi_free(name);
-    RegCloseKey(hkey1);
-    RegCloseKey(hkey2);
-
-    /* the UI chunk */
-    uirow = MSI_CreateRecord( 1 );
-    uipath = strdupW( file->TargetPath );
-    p = strrchrW(uipath,'\\');
-    if (p) p++;
-    else p = uipath;
-    MSI_RecordSetStringW( uirow, 1, p );
-    ui_actiondata( package, szRegisterFonts, uirow);
-    msiobj_release( &uirow->hdr );
-    msi_free( uipath );
-    /* FIXME: call ui_progress? */
-
-    return ERROR_SUCCESS;
-}
-
-static UINT ACTION_RegisterFonts(MSIPACKAGE *package)
-{
-    UINT rc;
-    MSIQUERY * view;
-    static const WCHAR ExecSeqQuery[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
-         '`','F','o','n','t','`',0};
-
-    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
-    if (rc != ERROR_SUCCESS)
-    {
-        TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
-        return ERROR_SUCCESS;
-    }
-
-    MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
-    msiobj_release(&view->hdr);
-
-    return ERROR_SUCCESS;
-}
-
 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
 {
-    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    MSIPACKAGE *package = param;
     LPCWSTR compgroupid=NULL;
     LPCWSTR feature=NULL;
     LPCWSTR text = NULL;
@@ -4176,99 +4317,1651 @@ static UINT ACTION_PublishComponents(MSIPACKAGE *package)
     return rc;
 }
 
-static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
-                                           LPCSTR action, LPCWSTR table )
+static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
 {
-    static const WCHAR query[] = {
-        'S','E','L','E','C','T',' ','*',' ',
-        'F','R','O','M',' ','`','%','s','`',0 };
-    MSIQUERY *view = NULL;
-    DWORD count = 0;
-    UINT r;
-    
-    r = MSI_OpenQuery( package->db, &view, query, table );
-    if (r == ERROR_SUCCESS)
+    MSIPACKAGE *package = param;
+    MSIRECORD *row;
+    MSIFILE *file;
+    SC_HANDLE hscm, service = NULL;
+    LPCWSTR comp, depends, pass;
+    LPWSTR name = NULL, disp = NULL;
+    LPCWSTR load_order, serv_name, key;
+    DWORD serv_type, start_type;
+    DWORD err_control;
+
+    static const WCHAR query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
+         '`','C','o','m','p','o','n','e','n','t','`',' ',
+         'W','H','E','R','E',' ',
+         '`','C','o','m','p','o','n','e','n','t','`',' ',
+         '=','\'','%','s','\'',0};
+
+    hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
+    if (!hscm)
     {
-        r = MSI_IterateRecords(view, &count, NULL, package);
-        msiobj_release(&view->hdr);
+        ERR("Failed to open the SC Manager!\n");
+        goto done;
     }
 
-    if (count)
-        FIXME("%s -> %lu ignored %s table values\n",
-              action, count, debugstr_w(table));
+    start_type = MSI_RecordGetInteger(rec, 5);
+    if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
+        goto done;
+
+    depends = MSI_RecordGetString(rec, 8);
+    if (depends && *depends)
+        FIXME("Dependency list unhandled!\n");
+
+    deformat_string(package, MSI_RecordGetString(rec, 2), &name);
+    deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
+    serv_type = MSI_RecordGetInteger(rec, 4);
+    err_control = MSI_RecordGetInteger(rec, 6);
+    load_order = MSI_RecordGetString(rec, 7);
+    serv_name = MSI_RecordGetString(rec, 9);
+    pass = MSI_RecordGetString(rec, 10);
+    comp = MSI_RecordGetString(rec, 12);
+
+    /* fetch the service path */
+    row = MSI_QueryGetRecord(package->db, query, comp);
+    if (!row)
+    {
+        ERR("Control query failed!\n");
+        goto done;
+    }
 
-    return ERROR_SUCCESS;
-}
+    key = MSI_RecordGetString(row, 6);
 
-static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
-{
-    TRACE("%p\n", package);
-    return ERROR_SUCCESS;
-}
+    file = get_loaded_file(package, key);
+    msiobj_release(&row->hdr);
+    if (!file)
+    {
+        ERR("Failed to load the service file\n");
+        goto done;
+    }
 
-static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
-{
-    static const WCHAR table[] =
-         {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
-    return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
-}
+    service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
+                             start_type, err_control, file->TargetPath,
+                             load_order, NULL, NULL, serv_name, pass);
+    if (!service)
+    {
+        if (GetLastError() != ERROR_SERVICE_EXISTS)
+            ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
+    }
 
-static UINT ACTION_MoveFiles( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
-    return msi_unimplemented_action_stub( package, "MoveFiles", table );
-}
+done:
+    CloseServiceHandle(service);
+    CloseServiceHandle(hscm);
+    msi_free(name);
+    msi_free(disp);
 
-static UINT ACTION_PatchFiles( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = { 'P','a','t','c','h',0 };
-    return msi_unimplemented_action_stub( package, "PatchFiles", table );
+    return ERROR_SUCCESS;
 }
 
-static UINT ACTION_BindImage( MSIPACKAGE *package )
+static UINT ACTION_InstallServices( MSIPACKAGE *package )
 {
-    static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
-    return msi_unimplemented_action_stub( package, "BindImage", table );
-}
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
+    
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
 
-static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = {
-        'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
-    return msi_unimplemented_action_stub( package, "IsolateComponents", table );
-}
+    rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
+    msiobj_release(&view->hdr);
 
-static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
-    return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
+    return rc;
 }
 
-static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
+/* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
+static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
 {
-    static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
-    return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
-}
+    LPCWSTR *vector, *temp_vector;
+    LPWSTR p, q;
+    DWORD sep_len;
 
-static UINT ACTION_InstallServices( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = {
-        'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 };
-    return msi_unimplemented_action_stub( package, "InstallServices", table );
-}
+    static const WCHAR separator[] = {'[','~',']',0};
 
-static UINT ACTION_StartServices( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = {
-        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
-    return msi_unimplemented_action_stub( package, "StartServices", table );
-}
+    *numargs = 0;
+    sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
+
+    if (!args)
+        return NULL;
+
+    vector = msi_alloc(sizeof(LPWSTR));
+    if (!vector)
+        return NULL;
+
+    p = args;
+    do
+    {
+        (*numargs)++;
+        vector[*numargs - 1] = p;
+
+        if ((q = strstrW(p, separator)))
+        {
+            *q = '\0';
+
+            temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
+            if (!temp_vector)
+            {
+                msi_free(vector);
+                return NULL;
+            }
+            vector = temp_vector;
+
+            p = q + sep_len;
+        }
+    } while (q);
+
+    return vector;
+}
+
+static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
+{
+    MSIPACKAGE *package = param;
+    MSICOMPONENT *comp;
+    SC_HANDLE scm, service = NULL;
+    LPCWSTR name, *vector = NULL;
+    LPWSTR args;
+    DWORD event, numargs;
+    UINT r = ERROR_FUNCTION_FAILED;
+
+    comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
+    if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
+        return ERROR_SUCCESS;
+
+    name = MSI_RecordGetString(rec, 2);
+    event = MSI_RecordGetInteger(rec, 3);
+    args = strdupW(MSI_RecordGetString(rec, 4));
+
+    if (!(event & msidbServiceControlEventStart))
+        return ERROR_SUCCESS;
+
+    scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
+    if (!scm)
+    {
+        ERR("Failed to open the service control manager\n");
+        goto done;
+    }
+
+    service = OpenServiceW(scm, name, SERVICE_START);
+    if (!service)
+    {
+        ERR("Failed to open service %s\n", debugstr_w(name));
+        goto done;
+    }
+
+    vector = msi_service_args_to_vector(args, &numargs);
+
+    if (!StartServiceW(service, numargs, vector))
+    {
+        ERR("Failed to start service %s\n", debugstr_w(name));
+        goto done;
+    }
+
+    r = ERROR_SUCCESS;
+
+done:
+    CloseServiceHandle(service);
+    CloseServiceHandle(scm);
+
+    msi_free(args);
+    msi_free(vector);
+    return r;
+}
+
+static UINT ACTION_StartServices( MSIPACKAGE *package )
+{
+    UINT rc;
+    MSIQUERY *view;
+
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
+
+    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
+{
+    DWORD i, needed, count;
+    ENUM_SERVICE_STATUSW *dependencies;
+    SERVICE_STATUS ss;
+    SC_HANDLE depserv;
+
+    if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
+                               0, &needed, &count))
+        return TRUE;
+
+    if (GetLastError() != ERROR_MORE_DATA)
+        return FALSE;
+
+    dependencies = msi_alloc(needed);
+    if (!dependencies)
+        return FALSE;
+
+    if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
+                                needed, &needed, &count))
+        goto error;
+
+    for (i = 0; i < count; i++)
+    {
+        depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
+                               SERVICE_STOP | SERVICE_QUERY_STATUS);
+        if (!depserv)
+            goto error;
+
+        if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
+            goto error;
+    }
+
+    return TRUE;
+
+error:
+    msi_free(dependencies);
+    return FALSE;
+}
+
+static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
+{
+    MSIPACKAGE *package = param;
+    MSICOMPONENT *comp;
+    SERVICE_STATUS status;
+    SERVICE_STATUS_PROCESS ssp;
+    SC_HANDLE scm = NULL, service = NULL;
+    LPWSTR name, args;
+    DWORD event, needed;
+
+    event = MSI_RecordGetInteger(rec, 3);
+    if (!(event & msidbServiceControlEventStop))
+        return ERROR_SUCCESS;
+
+    comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
+    if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
+        return ERROR_SUCCESS;
+
+    deformat_string(package, MSI_RecordGetString(rec, 2), &name);
+    deformat_string(package, MSI_RecordGetString(rec, 4), &args);
+    args = strdupW(MSI_RecordGetString(rec, 4));
+
+    scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+    if (!scm)
+    {
+        WARN("Failed to open the SCM: %d\n", GetLastError());
+        goto done;
+    }
+
+    service = OpenServiceW(scm, name,
+                           SERVICE_STOP |
+                           SERVICE_QUERY_STATUS |
+                           SERVICE_ENUMERATE_DEPENDENTS);
+    if (!service)
+    {
+        WARN("Failed to open service (%s): %d\n",
+              debugstr_w(name), GetLastError());
+        goto done;
+    }
+
+    if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
+                              sizeof(SERVICE_STATUS_PROCESS), &needed))
+    {
+        WARN("Failed to query service status (%s): %d\n",
+             debugstr_w(name), GetLastError());
+        goto done;
+    }
+
+    if (ssp.dwCurrentState == SERVICE_STOPPED)
+        goto done;
+
+    stop_service_dependents(scm, service);
+
+    if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
+        WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
+
+done:
+    CloseServiceHandle(service);
+    CloseServiceHandle(scm);
+    msi_free(name);
+    msi_free(args);
+
+    return ERROR_SUCCESS;
+}
 
 static UINT ACTION_StopServices( MSIPACKAGE *package )
 {
-    static const WCHAR table[] = {
-        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
-    return msi_unimplemented_action_stub( package, "StopServices", table );
+    UINT rc;
+    MSIQUERY *view;
+
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
+
+    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
+{
+    MSIFILE *file;
+
+    LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
+    {
+        if (!lstrcmpW(file->File, filename))
+            return file;
+    }
+
+    return NULL;
+}
+
+static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    LPWSTR driver, driver_path, ptr;
+    WCHAR outpath[MAX_PATH];
+    MSIFILE *driver_file, *setup_file;
+    LPCWSTR desc;
+    DWORD len, usage;
+    UINT r = ERROR_SUCCESS;
+
+    static const WCHAR driver_fmt[] = {
+        'D','r','i','v','e','r','=','%','s',0};
+    static const WCHAR setup_fmt[] = {
+        'S','e','t','u','p','=','%','s',0};
+    static const WCHAR usage_fmt[] = {
+        'F','i','l','e','U','s','a','g','e','=','1',0};
+
+    desc = MSI_RecordGetString(rec, 3);
+
+    driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
+    setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
+
+    if (!driver_file || !setup_file)
+    {
+        ERR("ODBC Driver entry not found!\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
+          lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
+          lstrlenW(usage_fmt) + 1;
+    driver = msi_alloc(len * sizeof(WCHAR));
+    if (!driver)
+        return ERROR_OUTOFMEMORY;
+
+    ptr = driver;
+    lstrcpyW(ptr, desc);
+    ptr += lstrlenW(ptr) + 1;
+
+    sprintfW(ptr, driver_fmt, driver_file->FileName);
+    ptr += lstrlenW(ptr) + 1;
+
+    sprintfW(ptr, setup_fmt, setup_file->FileName);
+    ptr += lstrlenW(ptr) + 1;
+
+    lstrcpyW(ptr, usage_fmt);
+    ptr += lstrlenW(ptr) + 1;
+    *ptr = '\0';
+
+    driver_path = strdupW(driver_file->TargetPath);
+    ptr = strrchrW(driver_path, '\\');
+    if (ptr) *ptr = '\0';
+
+    if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
+                             NULL, ODBC_INSTALL_COMPLETE, &usage))
+    {
+        ERR("Failed to install SQL driver!\n");
+        r = ERROR_FUNCTION_FAILED;
+    }
+
+    msi_free(driver);
+    msi_free(driver_path);
+
+    return r;
+}
+
+static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    LPWSTR translator, translator_path, ptr;
+    WCHAR outpath[MAX_PATH];
+    MSIFILE *translator_file, *setup_file;
+    LPCWSTR desc;
+    DWORD len, usage;
+    UINT r = ERROR_SUCCESS;
+
+    static const WCHAR translator_fmt[] = {
+        'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
+    static const WCHAR setup_fmt[] = {
+        'S','e','t','u','p','=','%','s',0};
+
+    desc = MSI_RecordGetString(rec, 3);
+
+    translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
+    setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
+
+    if (!translator_file || !setup_file)
+    {
+        ERR("ODBC Translator entry not found!\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
+          lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
+    translator = msi_alloc(len * sizeof(WCHAR));
+    if (!translator)
+        return ERROR_OUTOFMEMORY;
+
+    ptr = translator;
+    lstrcpyW(ptr, desc);
+    ptr += lstrlenW(ptr) + 1;
+
+    sprintfW(ptr, translator_fmt, translator_file->FileName);
+    ptr += lstrlenW(ptr) + 1;
+
+    sprintfW(ptr, setup_fmt, setup_file->FileName);
+    ptr += lstrlenW(ptr) + 1;
+    *ptr = '\0';
+
+    translator_path = strdupW(translator_file->TargetPath);
+    ptr = strrchrW(translator_path, '\\');
+    if (ptr) *ptr = '\0';
+
+    if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
+                                 NULL, ODBC_INSTALL_COMPLETE, &usage))
+    {
+        ERR("Failed to install SQL translator!\n");
+        r = ERROR_FUNCTION_FAILED;
+    }
+
+    msi_free(translator);
+    msi_free(translator_path);
+
+    return r;
+}
+
+static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
+{
+    LPWSTR attrs;
+    LPCWSTR desc, driver;
+    WORD request = ODBC_ADD_SYS_DSN;
+    INT registration;
+    DWORD len;
+    UINT r = ERROR_SUCCESS;
+
+    static const WCHAR attrs_fmt[] = {
+        'D','S','N','=','%','s',0 };
+
+    desc = MSI_RecordGetString(rec, 3);
+    driver = MSI_RecordGetString(rec, 4);
+    registration = MSI_RecordGetInteger(rec, 5);
+
+    if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
+    else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
+
+    len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
+    attrs = msi_alloc(len * sizeof(WCHAR));
+    if (!attrs)
+        return ERROR_OUTOFMEMORY;
+
+    sprintfW(attrs, attrs_fmt, desc);
+    attrs[len - 1] = '\0';
+
+    if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
+    {
+        ERR("Failed to install SQL data source!\n");
+        r = ERROR_FUNCTION_FAILED;
+    }
+
+    msi_free(attrs);
+
+    return r;
+}
+
+static UINT ACTION_InstallODBC( MSIPACKAGE *package )
+{
+    UINT rc;
+    MSIQUERY *view;
+
+    static const WCHAR driver_query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        'O','D','B','C','D','r','i','v','e','r',0 };
+
+    static const WCHAR translator_query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
+
+    static const WCHAR source_query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
+
+    rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
+    msiobj_release(&view->hdr);
+
+    rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
+    msiobj_release(&view->hdr);
+
+    rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+#define ENV_ACT_SETALWAYS   0x1
+#define ENV_ACT_SETABSENT   0x2
+#define ENV_ACT_REMOVE      0x4
+#define ENV_ACT_REMOVEMATCH 0x8
+
+#define ENV_MOD_MACHINE     0x20000000
+#define ENV_MOD_APPEND      0x40000000
+#define ENV_MOD_PREFIX      0x80000000
+#define ENV_MOD_MASK        0xC0000000
+
+#define check_flag_combo(x, y) ((x) & ~(y)) == (y)
+
+static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
+{
+    LPCWSTR cptr = *name;
+
+    static const WCHAR prefix[] = {'[','~',']',0};
+    static const int prefix_len = 3;
+
+    *flags = 0;
+    while (*cptr)
+    {
+        if (*cptr == '=')
+            *flags |= ENV_ACT_SETALWAYS;
+        else if (*cptr == '+')
+            *flags |= ENV_ACT_SETABSENT;
+        else if (*cptr == '-')
+            *flags |= ENV_ACT_REMOVE;
+        else if (*cptr == '!')
+            *flags |= ENV_ACT_REMOVEMATCH;
+        else if (*cptr == '*')
+            *flags |= ENV_MOD_MACHINE;
+        else
+            break;
+
+        cptr++;
+        (*name)++;
+    }
+
+    if (!*cptr)
+    {
+        ERR("Missing environment variable\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    if (*value)
+    {
+        LPCWSTR ptr = *value;
+    if (!strncmpW(ptr, prefix, prefix_len))
+    {
+        *flags |= ENV_MOD_APPEND;
+        *value += lstrlenW(prefix);
+    }
+    else if (lstrlenW(*value) >= prefix_len)
+    {
+        ptr += lstrlenW(ptr) - prefix_len;
+        if (!lstrcmpW(ptr, prefix))
+        {
+            *flags |= ENV_MOD_PREFIX;
+            /* the "[~]" will be removed by deformat_string */;
+        }
+    }
+    }
+
+    if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
+        check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
+        check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
+        check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
+    {
+        ERR("Invalid flags: %08x\n", *flags);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    if (!*flags)
+        *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    LPCWSTR name, value;
+    LPWSTR data = NULL, newval = NULL;
+    LPWSTR deformatted = NULL, ptr;
+    DWORD flags, type, size;
+    LONG res;
+    HKEY env = NULL, root;
+    LPCWSTR environment;
+
+    static const WCHAR user_env[] =
+        {'E','n','v','i','r','o','n','m','e','n','t',0};
+    static const WCHAR machine_env[] =
+        {'S','y','s','t','e','m','\\',
+         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+         'C','o','n','t','r','o','l','\\',
+         'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
+         'E','n','v','i','r','o','n','m','e','n','t',0};
+
+    name = MSI_RecordGetString(rec, 2);
+    value = MSI_RecordGetString(rec, 3);
+
+    TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
+
+    res = env_set_flags(&name, &value, &flags);
+    if (res != ERROR_SUCCESS)
+       goto done;
+
+    if (value && !deformat_string(package, value, &deformatted))
+    {
+        res = ERROR_OUTOFMEMORY;
+        goto done;
+    }
+
+    value = deformatted;
+
+    if (flags & ENV_MOD_MACHINE)
+    {
+        environment = machine_env;
+        root = HKEY_LOCAL_MACHINE;
+    }
+    else
+    {
+        environment = user_env;
+        root = HKEY_CURRENT_USER;
+    }
+
+    res = RegCreateKeyExW(root, environment, 0, NULL, 0,
+                          KEY_ALL_ACCESS, NULL, &env, NULL);
+    if (res != ERROR_SUCCESS)
+        goto done;
+
+    if (flags & ENV_ACT_REMOVE)
+        FIXME("Not removing environment variable on uninstall!\n");
+
+    size = 0;
+    res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
+    if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
+        (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
+        goto done;
+
+    if (res != ERROR_FILE_NOT_FOUND)
+    {
+        if (flags & ENV_ACT_SETABSENT)
+        {
+            res = ERROR_SUCCESS;
+            goto done;
+        }
+
+        data = msi_alloc(size);
+        if (!data)
+        {
+            RegCloseKey(env);
+            return ERROR_OUTOFMEMORY;
+        }
+
+        res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
+        if (res != ERROR_SUCCESS)
+            goto done;
+
+        if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
+        {
+            res = RegDeleteKeyW(env, name);
+            goto done;
+        }
+
+        size += (lstrlenW(value) + 1) * sizeof(WCHAR);
+        newval =  msi_alloc(size);
+        ptr = newval;
+        if (!newval)
+        {
+            res = ERROR_OUTOFMEMORY;
+            goto done;
+        }
+
+        if (!(flags & ENV_MOD_MASK))
+            lstrcpyW(newval, value);
+        else
+        {
+            if (flags & ENV_MOD_PREFIX)
+            {
+                lstrcpyW(newval, value);
+                lstrcatW(newval, szSemiColon);
+                ptr = newval + lstrlenW(value) + 1;
+            }
+
+            lstrcpyW(ptr, data);
+
+            if (flags & ENV_MOD_APPEND)
+            {
+                lstrcatW(newval, szSemiColon);
+                lstrcatW(newval, value);
+            }
+        }
+    }
+    else if (value)
+    {
+        size = (lstrlenW(value) + 1) * sizeof(WCHAR);
+        newval = msi_alloc(size);
+        if (!newval)
+        {
+            res = ERROR_OUTOFMEMORY;
+            goto done;
+        }
+
+        lstrcpyW(newval, value);
+    }
+
+    if (newval)
+    {
+    TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
+    res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
+    }
+    else
+        res = ERROR_SUCCESS;
+
+done:
+    if (env) RegCloseKey(env);
+    msi_free(deformatted);
+    msi_free(data);
+    msi_free(newval);
+    return res;
+}
+
+static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+#define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
+
+typedef struct
+{
+    struct list entry;
+    LPWSTR sourcename;
+    LPWSTR destname;
+    LPWSTR source;
+    LPWSTR dest;
+} FILE_LIST;
+
+static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
+{
+    BOOL ret;
+
+    if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
+        GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
+    {
+        WARN("Source or dest is directory, not moving\n");
+        return FALSE;
+    }
+
+    if (options == msidbMoveFileOptionsMove)
+    {
+        TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
+        ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
+        if (!ret)
+        {
+            WARN("MoveFile failed: %d\n", GetLastError());
+            return FALSE;
+        }
+    }
+    else
+    {
+        TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
+        ret = CopyFileW(source, dest, FALSE);
+        if (!ret)
+        {
+            WARN("CopyFile failed: %d\n", GetLastError());
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
+{
+    LPWSTR path, ptr;
+    DWORD dirlen, pathlen;
+
+    ptr = strrchrW(wildcard, '\\');
+    dirlen = ptr - wildcard + 1;
+
+    pathlen = dirlen + lstrlenW(filename) + 1;
+    path = msi_alloc(pathlen * sizeof(WCHAR));
+
+    lstrcpynW(path, wildcard, dirlen + 1);
+    lstrcatW(path, filename);
+
+    return path;
+}
+
+static void free_file_entry(FILE_LIST *file)
+{
+    msi_free(file->source);
+    msi_free(file->dest);
+    msi_free(file);
+}
+
+static void free_list(FILE_LIST *list)
+{
+    while (!list_empty(&list->entry))
+    {
+        FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
+
+        list_remove(&file->entry);
+        free_file_entry(file);
+    }
+}
+
+static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
+{
+    FILE_LIST *new, *file;
+    LPWSTR ptr, filename;
+    DWORD size;
+
+    new = msi_alloc_zero(sizeof(FILE_LIST));
+    if (!new)
+        return FALSE;
+
+    new->source = strdupW(source);
+    ptr = strrchrW(dest, '\\') + 1;
+    filename = strrchrW(new->source, '\\') + 1;
+
+    new->sourcename = filename;
+
+    if (*ptr)
+        new->destname = ptr;
+    else
+        new->destname = new->sourcename;
+
+    size = (ptr - dest) + lstrlenW(filename) + 1;
+    new->dest = msi_alloc(size * sizeof(WCHAR));
+    if (!new->dest)
+    {
+        free_file_entry(new);
+        return FALSE;
+    }
+
+    lstrcpynW(new->dest, dest, ptr - dest + 1);
+    lstrcatW(new->dest, filename);
+
+    if (list_empty(&files->entry))
+    {
+        list_add_head(&files->entry, &new->entry);
+        return TRUE;
+    }
+
+    LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
+    {
+        if (lstrcmpW(source, file->source) < 0)
+        {
+            list_add_before(&file->entry, &new->entry);
+            return TRUE;
+        }
+    }
+
+    list_add_after(&file->entry, &new->entry);
+    return TRUE;
+}
+
+static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
+{
+    WIN32_FIND_DATAW wfd;
+    HANDLE hfile;
+    LPWSTR path;
+    BOOL res;
+    FILE_LIST files, *file;
+    DWORD size;
+
+    hfile = FindFirstFileW(source, &wfd);
+    if (hfile == INVALID_HANDLE_VALUE) return FALSE;
+
+    list_init(&files.entry);
+
+    for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
+    {
+        if (is_dot_dir(wfd.cFileName)) continue;
+
+        path = wildcard_to_file(source, wfd.cFileName);
+        if (!path)
+        {
+            res = FALSE;
+            goto done;
+        }
+
+        add_wildcard(&files, path, dest);
+        msi_free(path);
+    }
+
+    /* no files match the wildcard */
+    if (list_empty(&files.entry))
+        goto done;
+
+    /* only the first wildcard match gets renamed to dest */
+    file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
+    size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
+    file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
+    if (!file->dest)
+    {
+        res = FALSE;
+        goto done;
+    }
+
+    lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
+
+    while (!list_empty(&files.entry))
+    {
+        file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
+
+        msi_move_file(file->source, file->dest, options);
+
+        list_remove(&file->entry);
+        free_file_entry(file);
+    }
+
+    res = TRUE;
+
+done:
+    free_list(&files);
+    FindClose(hfile);
+    return res;
+}
+
+static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
+{
+    MSIPACKAGE *package = param;
+    MSICOMPONENT *comp;
+    LPCWSTR sourcename;
+    LPWSTR destname = NULL;
+    LPWSTR sourcedir = NULL, destdir = NULL;
+    LPWSTR source = NULL, dest = NULL;
+    int options;
+    DWORD size;
+    BOOL ret, wildcards;
+
+    comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
+    if (!comp || !comp->Enabled ||
+        !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
+    {
+        TRACE("Component not set for install, not moving file\n");
+        return ERROR_SUCCESS;
+    }
+
+    sourcename = MSI_RecordGetString(rec, 3);
+    options = MSI_RecordGetInteger(rec, 7);
+
+    sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
+    if (!sourcedir)
+        goto done;
+
+    destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
+    if (!destdir)
+        goto done;
+
+    if (!sourcename)
+    {
+        if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
+            goto done;
+
+        source = strdupW(sourcedir);
+        if (!source)
+            goto done;
+    }
+    else
+    {
+        size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
+        source = msi_alloc(size * sizeof(WCHAR));
+        if (!source)
+            goto done;
+
+        lstrcpyW(source, sourcedir);
+        if (source[lstrlenW(source) - 1] != '\\')
+            lstrcatW(source, szBackSlash);
+        lstrcatW(source, sourcename);
+    }
+
+    wildcards = strchrW(source, '*') || strchrW(source, '?');
+
+    if (MSI_RecordIsNull(rec, 4))
+    {
+        if (!wildcards)
+        {
+            destname = strdupW(sourcename);
+            if (!destname)
+                goto done;
+        }
+    }
+    else
+    {
+        destname = strdupW(MSI_RecordGetString(rec, 4));
+        if (destname)
+            reduce_to_longfilename(destname);
+    }
+
+    size = 0;
+    if (destname)
+        size = lstrlenW(destname);
+
+    size += lstrlenW(destdir) + 2;
+    dest = msi_alloc(size * sizeof(WCHAR));
+    if (!dest)
+        goto done;
+
+    lstrcpyW(dest, destdir);
+    if (dest[lstrlenW(dest) - 1] != '\\')
+        lstrcatW(dest, szBackSlash);
+
+    if (destname)
+        lstrcatW(dest, destname);
+
+    if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
+    {
+        ret = CreateDirectoryW(destdir, NULL);
+        if (!ret)
+        {
+            WARN("CreateDirectory failed: %d\n", GetLastError());
+            return ERROR_SUCCESS;
+        }
+    }
+
+    if (!wildcards)
+        msi_move_file(source, dest, options);
+    else
+        move_files_wildcard(source, dest, options);
+
+done:
+    msi_free(sourcedir);
+    msi_free(destdir);
+    msi_free(destname);
+    msi_free(source);
+    msi_free(dest);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_MoveFiles( MSIPACKAGE *package )
+{
+    UINT rc;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','M','o','v','e','F','i','l','e','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+typedef struct tagMSIASSEMBLY
+{
+    struct list entry;
+    MSICOMPONENT *component;
+    MSIFEATURE *feature;
+    MSIFILE *file;
+    LPWSTR manifest;
+    LPWSTR application;
+    DWORD attributes;
+    BOOL installed;
+} MSIASSEMBLY;
+
+static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
+                                              DWORD dwReserved);
+static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
+                                          LPVOID pvReserved, HMODULE *phModDll);
+
+static BOOL init_functionpointers(void)
+{
+    HRESULT hr;
+    HMODULE hfusion;
+    HMODULE hmscoree;
+
+    static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
+
+    hmscoree = LoadLibraryA("mscoree.dll");
+    if (!hmscoree)
+    {
+        WARN("mscoree.dll not available\n");
+        return FALSE;
+    }
+
+    pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
+    if (!pLoadLibraryShim)
+    {
+        WARN("LoadLibraryShim not available\n");
+        FreeLibrary(hmscoree);
+        return FALSE;
+    }
+
+    hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
+    if (FAILED(hr))
+    {
+        WARN("fusion.dll not available\n");
+        FreeLibrary(hmscoree);
+        return FALSE;
+    }
+
+    pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
+
+    FreeLibrary(hmscoree);
+    return TRUE;
+}
+
+static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
+                             LPWSTR path)
+{
+    IAssemblyCache *cache;
+    HRESULT hr;
+    UINT r = ERROR_FUNCTION_FAILED;
+
+    TRACE("installing assembly: %s\n", debugstr_w(path));
+
+    if (assembly->feature)
+        msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
+
+    if (assembly->manifest)
+        FIXME("Manifest unhandled\n");
+
+    if (assembly->application)
+    {
+        FIXME("Assembly should be privately installed\n");
+        return ERROR_SUCCESS;
+    }
+
+    if (assembly->attributes == msidbAssemblyAttributesWin32)
+    {
+        FIXME("Win32 assemblies not handled\n");
+        return ERROR_SUCCESS;
+    }
+
+    hr = pCreateAssemblyCache(&cache, 0);
+    if (FAILED(hr))
+        goto done;
+
+    hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
+    if (FAILED(hr))
+        ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
+
+    r = ERROR_SUCCESS;
+
+done:
+    IAssemblyCache_Release(cache);
+    return r;
+}
+
+typedef struct tagASSEMBLY_LIST
+{
+    MSIPACKAGE *package;
+    IAssemblyCache *cache;
+    struct list *assemblies;
+} ASSEMBLY_LIST;
+
+typedef struct tagASSEMBLY_NAME
+{
+    LPWSTR name;
+    LPWSTR version;
+    LPWSTR culture;
+    LPWSTR pubkeytoken;
+} ASSEMBLY_NAME;
+
+static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
+{
+    ASSEMBLY_NAME *asmname = param;
+    LPCWSTR name = MSI_RecordGetString(rec, 2);
+    LPWSTR val = msi_dup_record_field(rec, 3);
+
+    static const WCHAR Name[] = {'N','a','m','e',0};
+    static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
+    static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
+    static const WCHAR PublicKeyToken[] = {
+        'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
+
+    if (!strcmpiW(name, Name))
+        asmname->name = val;
+    else if (!strcmpiW(name, Version))
+        asmname->version = val;
+    else if (!strcmpiW(name, Culture))
+        asmname->culture = val;
+    else if (!strcmpiW(name, PublicKeyToken))
+        asmname->pubkeytoken = val;
+    else
+        msi_free(val);
+
+    return ERROR_SUCCESS;
+}
+
+static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
+{
+    if (!*str)
+    {
+        *size = lstrlenW(append) + 1;
+        *str = msi_alloc((*size) * sizeof(WCHAR));
+        lstrcpyW(*str, append);
+        return;
+    }
+
+    (*size) += lstrlenW(append);
+    *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
+    lstrcatW(*str, append);
+}
+
+static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
+                                     MSICOMPONENT *comp)
+{
+    ASSEMBLY_INFO asminfo;
+    ASSEMBLY_NAME name;
+    MSIQUERY *view;
+    LPWSTR disp;
+    DWORD size;
+    BOOL found;
+    UINT r;
+
+    static const WCHAR separator[] = {',',' ',0};
+    static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
+    static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
+    static const WCHAR PublicKeyToken[] = {
+        'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
+        'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
+        '=','\'','%','s','\'',0};
+
+    disp = NULL;
+    found = FALSE;
+    ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
+    ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
+
+    r = MSI_OpenQuery(db, &view, query, comp->Component);
+    if (r != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
+    msiobj_release(&view->hdr);
+
+    if (!name.name)
+    {
+        ERR("No assembly name specified!\n");
+        goto done;
+    }
+
+    append_str(&disp, &size, name.name);
+
+    if (name.version)
+    {
+        append_str(&disp, &size, separator);
+        append_str(&disp, &size, Version);
+        append_str(&disp, &size, name.version);
+    }
+
+    if (name.culture)
+    {
+        append_str(&disp, &size, separator);
+        append_str(&disp, &size, Culture);
+        append_str(&disp, &size, name.culture);
+    }
+
+    if (name.pubkeytoken)
+    {
+        append_str(&disp, &size, separator);
+        append_str(&disp, &size, PublicKeyToken);
+        append_str(&disp, &size, name.pubkeytoken);
+    }
+
+    asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
+    IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
+                                     disp, &asminfo);
+    found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
+
+done:
+    msi_free(disp);
+    msi_free(name.name);
+    msi_free(name.version);
+    msi_free(name.culture);
+    msi_free(name.pubkeytoken);
+
+    return found;
+}
+
+static UINT load_assembly(MSIRECORD *rec, LPVOID param)
+{
+    ASSEMBLY_LIST *list = param;
+    MSIASSEMBLY *assembly;
+
+    assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
+    if (!assembly)
+        return ERROR_OUTOFMEMORY;
+
+    assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
+
+    if (!assembly->component || !assembly->component->Enabled ||
+        !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
+    {
+        TRACE("Component not set for install, not publishing assembly\n");
+        msi_free(assembly);
+        return ERROR_SUCCESS;
+    }
+
+    assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
+    assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
+
+    if (!assembly->file)
+    {
+        ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
+    assembly->application = strdupW(MSI_RecordGetString(rec, 4));
+    assembly->attributes = MSI_RecordGetInteger(rec, 5);
+
+    if (assembly->application)
+    {
+        WCHAR version[24];
+        DWORD size = sizeof(version)/sizeof(WCHAR);
+
+        /* FIXME: we should probably check the manifest file here */
+
+        if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
+            (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
+        {
+            assembly->installed = TRUE;
+        }
+    }
+    else
+        assembly->installed = check_assembly_installed(list->package->db,
+                                                       list->cache,
+                                                       assembly->component);
+
+    list_add_head(list->assemblies, &assembly->entry);
+    return ERROR_SUCCESS;
+}
+
+static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
+{
+    IAssemblyCache *cache = NULL;
+    ASSEMBLY_LIST list;
+    MSIQUERY *view;
+    HRESULT hr;
+    UINT r;
+
+    static const WCHAR query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
+
+    r = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (r != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    hr = pCreateAssemblyCache(&cache, 0);
+    if (FAILED(hr))
+        return ERROR_FUNCTION_FAILED;
+
+    list.package = package;
+    list.cache = cache;
+    list.assemblies = assemblies;
+
+    r = MSI_IterateRecords(view, NULL, load_assembly, &list);
+    msiobj_release(&view->hdr);
+
+    IAssemblyCache_Release(cache);
+
+    return r;
+}
+
+static void free_assemblies(struct list *assemblies)
+{
+    struct list *item, *cursor;
+
+    LIST_FOR_EACH_SAFE(item, cursor, assemblies)
+    {
+        MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
+
+        list_remove(&assembly->entry);
+        msi_free(assembly->application);
+        msi_free(assembly->manifest);
+        msi_free(assembly);
+    }
+}
+
+static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
+{
+    MSIASSEMBLY *assembly;
+
+    LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
+    {
+        if (!lstrcmpW(assembly->file->File, file))
+        {
+            *out = assembly;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
+                               LPWSTR *path, DWORD *attrs, PVOID user)
+{
+    MSIASSEMBLY *assembly;
+    WCHAR temppath[MAX_PATH];
+    struct list *assemblies = user;
+    UINT r;
+
+    if (!find_assembly(assemblies, file, &assembly))
+        return FALSE;
+
+    GetTempPathW(MAX_PATH, temppath);
+    PathAddBackslashW(temppath);
+    lstrcatW(temppath, assembly->file->FileName);
+
+    if (action == MSICABEXTRACT_BEGINEXTRACT)
+    {
+        if (assembly->installed)
+            return FALSE;
+
+        *path = strdupW(temppath);
+        *attrs = assembly->file->Attributes;
+    }
+    else if (action == MSICABEXTRACT_FILEEXTRACTED)
+    {
+        assembly->installed = TRUE;
+
+        r = install_assembly(package, assembly, temppath);
+        if (r != ERROR_SUCCESS)
+            ERR("Failed to install assembly\n");
+    }
+
+    return TRUE;
+}
+
+static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
+{
+    UINT r;
+    struct list assemblies = LIST_INIT(assemblies);
+    MSIASSEMBLY *assembly;
+    MSIMEDIAINFO *mi;
+
+    if (!init_functionpointers() || !pCreateAssemblyCache)
+        return ERROR_FUNCTION_FAILED;
+
+    r = load_assemblies(package, &assemblies);
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    if (list_empty(&assemblies))
+        goto done;
+
+    mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
+    if (!mi)
+    {
+        r = ERROR_OUTOFMEMORY;
+        goto done;
+    }
+
+    LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
+    {
+        if (assembly->installed && !mi->is_continuous)
+            continue;
+
+        if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
+            (assembly->file->IsCompressed && !mi->is_extracted))
+        {
+            MSICABDATA data;
+
+            r = ready_media(package, assembly->file, mi);
+            if (r != ERROR_SUCCESS)
+            {
+                ERR("Failed to ready media\n");
+                break;
+            }
+
+            data.mi = mi;
+            data.package = package;
+            data.cb = installassembly_cb;
+            data.user = &assemblies;
+
+            if (assembly->file->IsCompressed &&
+                !msi_cabextract(package, mi, &data))
+            {
+                ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
+                r = ERROR_FUNCTION_FAILED;
+                break;
+            }
+        }
+
+        if (!assembly->file->IsCompressed)
+        {
+            LPWSTR source = resolve_file_source(package, assembly->file);
+
+            r = install_assembly(package, assembly, source);
+            if (r != ERROR_SUCCESS)
+                ERR("Failed to install assembly\n");
+
+            msi_free(source);
+        }
+
+        /* FIXME: write Installer assembly reg values */
+    }
+
+done:
+    free_assemblies(&assemblies);
+    return r;
+}
+
+static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
+                                           LPCSTR action, LPCWSTR table )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','`','%','s','`',0 };
+    MSIQUERY *view = NULL;
+    DWORD count = 0;
+    UINT r;
+    
+    r = MSI_OpenQuery( package->db, &view, query, table );
+    if (r == ERROR_SUCCESS)
+    {
+        r = MSI_IterateRecords(view, &count, NULL, package);
+        msiobj_release(&view->hdr);
+    }
+
+    if (count)
+        FIXME("%s -> %u ignored %s table values\n",
+              action, count, debugstr_w(table));
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
+{
+    TRACE("%p\n", package);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
+{
+    static const WCHAR table[] =
+         {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
+    return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
+}
+
+static UINT ACTION_PatchFiles( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'P','a','t','c','h',0 };
+    return msi_unimplemented_action_stub( package, "PatchFiles", table );
+}
+
+static UINT ACTION_BindImage( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
+    return msi_unimplemented_action_stub( package, "BindImage", table );
+}
+
+static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
+    return msi_unimplemented_action_stub( package, "IsolateComponents", table );
+}
+
+static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
+    return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
+}
+
+static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
+    return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
 }
 
 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
@@ -4277,12 +5970,11 @@ static UINT ACTION_DeleteServices( MSIPACKAGE *package )
         'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
     return msi_unimplemented_action_stub( package, "DeleteServices", table );
 }
-
-static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
+static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
 {
-    static const WCHAR table[] = {
-        'E','n','v','i','r','o','n','m','e','n','t',0 };
-    return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
+       static const WCHAR table[] = {
+               'P','r','o','d','u','c','t','I','D',0 };
+       return msi_unimplemented_action_stub( package, "ValidateProductID", table );
 }
 
 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
@@ -4292,13 +5984,6 @@ static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
     return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
 }
 
-static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = {
-        'M','s','i','A','s','s','e','m','b','l','y',0 };
-    return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
-}
-
 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
 {
     static const WCHAR table[] = {
@@ -4312,12 +5997,6 @@ static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
     return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
 }
 
-static UINT ACTION_CCPSearch( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
-    return msi_unimplemented_action_stub( package, "CCPSearch", table );
-}
-
 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
 {
     static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
@@ -4336,29 +6015,115 @@ static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
     return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
 }
 
-static struct _actions StandardActions[] = {
+static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
+    return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
+}
+
+static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
+    return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
+}
+
+static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
+    return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
+}
+
+static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
+    return msi_unimplemented_action_stub( package, "RemoveFolders", table );
+}
+
+static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
+    return msi_unimplemented_action_stub( package, "RemoveODBC", table );
+}
+
+static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
+    return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
+}
+
+static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
+    return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
+}
+
+static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
+    return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
+}
+
+static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'A','p','p','I','d',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
+}
+
+static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
+}
+
+static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'M','I','M','E',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
+}
+
+static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
+}
+
+static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
+}
+
+typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
+
+static const struct
+{
+    const WCHAR *action;
+    UINT (*handler)(MSIPACKAGE *);
+}
+StandardActions[] =
+{
     { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
     { szAppSearch, ACTION_AppSearch },
     { szBindImage, ACTION_BindImage },
-    { szCCPSearch, ACTION_CCPSearch},
+    { szCCPSearch, ACTION_CCPSearch },
     { szCostFinalize, ACTION_CostFinalize },
     { szCostInitialize, ACTION_CostInitialize },
     { szCreateFolders, ACTION_CreateFolders },
     { szCreateShortcuts, ACTION_CreateShortcuts },
     { szDeleteServices, ACTION_DeleteServices },
-    { szDisableRollback, NULL},
+    { szDisableRollback, NULL },
     { szDuplicateFiles, ACTION_DuplicateFiles },
     { szExecuteAction, ACTION_ExecuteAction },
     { szFileCost, ACTION_FileCost },
     { szFindRelatedProducts, ACTION_FindRelatedProducts },
     { szForceReboot, ACTION_ForceReboot },
-    { szInstallAdminPackage, NULL},
+    { szInstallAdminPackage, NULL },
     { szInstallExecute, ACTION_InstallExecute },
     { szInstallExecuteAgain, ACTION_InstallExecute },
     { szInstallFiles, ACTION_InstallFiles},
     { szInstallFinalize, ACTION_InstallFinalize },
     { szInstallInitialize, ACTION_InstallInitialize },
-    { szInstallSFPCatalogFile, NULL},
+    { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
     { szInstallValidate, ACTION_InstallValidate },
     { szIsolateComponents, ACTION_IsolateComponents },
     { szLaunchConditions, ACTION_LaunchConditions },
@@ -4366,7 +6131,7 @@ static struct _actions StandardActions[] = {
     { szMoveFiles, ACTION_MoveFiles },
     { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
     { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
-    { szInstallODBC, NULL},
+    { szInstallODBC, ACTION_InstallODBC },
     { szInstallServices, ACTION_InstallServices },
     { szPatchFiles, ACTION_PatchFiles },
     { szProcessComponents, ACTION_ProcessComponents },
@@ -4381,36 +6146,290 @@ static struct _actions StandardActions[] = {
     { szRegisterProduct, ACTION_RegisterProduct },
     { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
     { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
-    { szRegisterUser, ACTION_RegisterUser},
-    { szRemoveDuplicateFiles, NULL},
+    { szRegisterUser, ACTION_RegisterUser },
+    { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
     { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
-    { szRemoveExistingProducts, NULL},
-    { szRemoveFiles, ACTION_RemoveFiles},
-    { szRemoveFolders, NULL},
+    { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
+    { szRemoveFiles, ACTION_RemoveFiles },
+    { szRemoveFolders, ACTION_RemoveFolders },
     { szRemoveIniValues, ACTION_RemoveIniValues },
-    { szRemoveODBC, NULL},
-    { szRemoveRegistryValues, NULL},
-    { szRemoveShortcuts, NULL},
-    { szResolveSource, ACTION_ResolveSource},
-    { szRMCCPSearch, ACTION_RMCCPSearch},
-    { szScheduleReboot, NULL},
+    { szRemoveODBC, ACTION_RemoveODBC },
+    { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
+    { szRemoveShortcuts, ACTION_RemoveShortcuts },
+    { szResolveSource, ACTION_ResolveSource },
+    { szRMCCPSearch, ACTION_RMCCPSearch },
+    { szScheduleReboot, NULL },
     { szSelfRegModules, ACTION_SelfRegModules },
     { szSelfUnregModules, ACTION_SelfUnregModules },
-    { szSetODBCFolders, NULL},
+    { szSetODBCFolders, NULL },
     { szStartServices, ACTION_StartServices },
     { szStopServices, ACTION_StopServices },
-    { szUnpublishComponents, NULL},
-    { szUnpublishFeatures, NULL},
-    { szUnregisterClassInfo, NULL},
-    { szUnregisterComPlus, ACTION_UnregisterComPlus},
-    { szUnregisterExtensionInfo, NULL},
+    { szUnpublishComponents, ACTION_UnpublishComponents },
+    { szUnpublishFeatures, ACTION_UnpublishFeatures },
+    { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
+    { szUnregisterComPlus, ACTION_UnregisterComPlus },
+    { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
     { szUnregisterFonts, ACTION_UnregisterFonts },
-    { szUnregisterMIMEInfo, NULL},
-    { szUnregisterProgIdInfo, NULL},
-    { szUnregisterTypeLibraries, NULL},
-    { szValidateProductID, NULL},
+    { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
+    { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
+    { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
+    { szValidateProductID, ACTION_ValidateProductID },
     { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
     { szWriteIniValues, ACTION_WriteIniValues },
-    { szWriteRegistryValues, ACTION_WriteRegistryValues},
-    { NULL, NULL},
+    { szWriteRegistryValues, ACTION_WriteRegistryValues },
+    { NULL, NULL },
 };
+
+static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
+                                        UINT* rc, BOOL force )
+{
+    BOOL ret = FALSE;
+    BOOL run = force;
+    int i;
+
+    if (!run && !package->script->CurrentlyScripting)
+        run = TRUE;
+
+    if (!run)
+    {
+        if (strcmpW(action,szInstallFinalize) == 0 ||
+            strcmpW(action,szInstallExecute) == 0 ||
+            strcmpW(action,szInstallExecuteAgain) == 0)
+                run = TRUE;
+    }
+
+    i = 0;
+    while (StandardActions[i].action != NULL)
+    {
+        if (strcmpW(StandardActions[i].action, action)==0)
+        {
+            if (!run)
+            {
+                ui_actioninfo(package, action, TRUE, 0);
+                *rc = schedule_action(package,INSTALL_SCRIPT,action);
+                ui_actioninfo(package, action, FALSE, *rc);
+            }
+            else
+            {
+                ui_actionstart(package, action);
+                if (StandardActions[i].handler)
+                {
+                    *rc = StandardActions[i].handler(package);
+                }
+                else
+                {
+                    FIXME("unhandled standard action %s\n",debugstr_w(action));
+                    *rc = ERROR_SUCCESS;
+                }
+            }
+            ret = TRUE;
+            break;
+        }
+        i++;
+    }
+    return ret;
+}
+
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
+{
+    UINT rc = ERROR_SUCCESS;
+    BOOL handled;
+
+    TRACE("Performing action (%s)\n", debugstr_w(action));
+
+    handled = ACTION_HandleStandardAction(package, action, &rc, force);
+
+    if (!handled)
+        handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
+
+    if (!handled)
+    {
+        WARN("unhandled msi action %s\n", debugstr_w(action));
+        rc = ERROR_FUNCTION_NOT_CALLED;
+    }
+
+    return rc;
+}
+
+UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
+{
+    UINT rc = ERROR_SUCCESS;
+    BOOL handled = FALSE;
+
+    TRACE("Performing action (%s)\n", debugstr_w(action));
+
+    handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
+
+    if (!handled)
+        handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
+
+    if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
+        handled = TRUE;
+
+    if (!handled)
+    {
+        WARN("unhandled msi action %s\n", debugstr_w(action));
+        rc = ERROR_FUNCTION_NOT_CALLED;
+    }
+
+    return rc;
+}
+
+static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIRECORD *row;
+
+    static const WCHAR 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};
+    static const WCHAR UISeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+     '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
+     '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
+        ' ', '=',' ','%','i',0};
+
+    if (needs_ui_sequence(package))
+        row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
+    else
+        row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
+
+    if (row)
+    {
+        LPCWSTR action, cond;
+
+        TRACE("Running the actions\n");
+
+        /* check conditions */
+        cond = MSI_RecordGetString(row, 2);
+
+        /* this is a hack to skip errors in the condition code */
+        if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
+        {
+            msiobj_release(&row->hdr);
+            return ERROR_SUCCESS;
+        }
+
+        action = MSI_RecordGetString(row, 1);
+        if (!action)
+        {
+            ERR("failed to fetch action\n");
+            msiobj_release(&row->hdr);
+            return ERROR_FUNCTION_FAILED;
+        }
+
+        if (needs_ui_sequence(package))
+            rc = ACTION_PerformUIAction(package, action, -1);
+        else
+            rc = ACTION_PerformAction(package, action, -1, FALSE);
+
+        msiobj_release(&row->hdr);
+    }
+
+    return rc;
+}
+
+/****************************************************
+ * TOP level entry points
+ *****************************************************/
+
+UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
+                         LPCWSTR szCommandLine )
+{
+    UINT rc;
+    BOOL ui_exists;
+
+    static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
+    static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
+
+    MSI_SetPropertyW(package, szAction, szInstall);
+
+    package->script->InWhatSequence = SEQUENCE_INSTALL;
+
+    if (szPackagePath)
+    {
+        LPWSTR p, dir;
+        LPCWSTR file;
+
+        dir = strdupW(szPackagePath);
+        p = strrchrW(dir, '\\');
+        if (p)
+        {
+            *(++p) = 0;
+            file = szPackagePath + (p - dir);
+        }
+        else
+        {
+            msi_free(dir);
+            dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
+            GetCurrentDirectoryW(MAX_PATH, dir);
+            lstrcatW(dir, szBackSlash);
+            file = szPackagePath;
+        }
+
+        msi_free( package->PackagePath );
+        package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
+        if (!package->PackagePath)
+        {
+            msi_free(dir);
+            return ERROR_OUTOFMEMORY;
+        }
+
+        lstrcpyW(package->PackagePath, dir);
+        lstrcatW(package->PackagePath, file);
+        msi_free(dir);
+
+        msi_set_sourcedir_props(package, FALSE);
+    }
+
+    msi_parse_command_line( package, szCommandLine, FALSE );
+
+    msi_apply_transforms( package );
+    msi_apply_patches( package );
+
+    if (!szCommandLine && msi_get_property_int( package, szInstalled, 0 ))
+    {
+        TRACE("setting reinstall property\n");
+        MSI_SetPropertyW( package, szReinstall, szAll );
+    }
+
+    /* properties may have been added by a transform */
+    msi_clone_properties( package );
+    msi_set_context( package );
+
+    if (needs_ui_sequence( package))
+    {
+        package->script->InWhatSequence |= SEQUENCE_UI;
+        rc = ACTION_ProcessUISequence(package);
+        ui_exists = ui_sequence_exists(package);
+        if (rc == ERROR_SUCCESS || !ui_exists)
+        {
+            package->script->InWhatSequence |= SEQUENCE_EXEC;
+            rc = ACTION_ProcessExecSequence(package, ui_exists);
+        }
+    }
+    else
+        rc = ACTION_ProcessExecSequence(package, FALSE);
+
+    package->script->CurrentlyScripting = FALSE;
+
+    /* process the ending type action */
+    if (rc == ERROR_SUCCESS)
+        ACTION_PerformActionSequence(package, -1);
+    else if (rc == ERROR_INSTALL_USEREXIT)
+        ACTION_PerformActionSequence(package, -2);
+    else if (rc == ERROR_INSTALL_SUSPEND)
+        ACTION_PerformActionSequence(package, -4);
+    else  /* failed */
+        ACTION_PerformActionSequence(package, -3);
+
+    /* finish up running custom actions */
+    ACTION_FinishCustomActions(package);
+
+    if (rc == ERROR_SUCCESS && package->need_reboot)
+        return ERROR_SUCCESS_REBOOT_REQUIRED;
+
+    return rc;
+}