[MSIEXEC] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / base / system / msiexec / msiexec.c
index 7f2dc2a..8dcd216 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-#define COM_NO_WINDOWS_H
-#include <stdarg.h>
-#include <windef.h>
-#include <winbase.h>
-#include <winreg.h>
-#include <winsvc.h>
-#include <winuser.h>
+#define WIN32_LEAN_AND_MEAN
+
+#include <windows.h>
 #include <msi.h>
+#include <winsvc.h>
 #include <objbase.h>
 #include <stdio.h>
 
-#include <wine/debug.h>
-#include <wine/unicode.h>
+#include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
 
@@ -114,10 +109,8 @@ static BOOL IsProductCode(LPWSTR str)
 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
 {
        struct string_list *entry;
-       DWORD size;
 
-       size = sizeof *entry + lstrlenW(str) * sizeof (WCHAR);
-       entry = HeapAlloc(GetProcessHeap(), 0, size);
+       entry = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list, str[lstrlenW(str) + 1]));
        if(!entry)
        {
                WINE_ERR("Out of memory!\n");
@@ -223,14 +216,6 @@ static DWORD msi_atou(LPCWSTR str)
        return ret;
 }
 
-static LPWSTR msi_strdup(LPCWSTR str)
-{
-       DWORD len = lstrlenW(str)+1;
-       LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
-       lstrcpyW(ret, str);
-       return ret;
-}
-
 /* str1 is the same as str2, ignoring case */
 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
 {
@@ -351,26 +336,25 @@ static DWORD DoDllUnregisterServer(LPCWSTR DllName)
 
 static DWORD DoRegServer(void)
 {
+    static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
+    static const WCHAR msiexecW[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
     SC_HANDLE scm, service;
-    CHAR path[MAX_PATH+12];
-    DWORD ret = 0;
+    WCHAR path[MAX_PATH+12];
+    DWORD len, ret = 0;
 
-    scm = OpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CREATE_SERVICE);
-    if (!scm)
+    if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
     {
         fprintf(stderr, "Failed to open the service control manager.\n");
         return 1;
     }
-
-    GetSystemDirectoryA(path, MAX_PATH);
-    lstrcatA(path, "\\msiexec.exe /V");
-
-    service = CreateServiceA(scm, "MSIServer", "MSIServer", GENERIC_ALL,
-                             SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
-                             SERVICE_ERROR_NORMAL, path, NULL, NULL,
-                             NULL, NULL, NULL);
-
-    if (service) CloseServiceHandle(service);
+    len = GetSystemDirectoryW(path, MAX_PATH);
+    lstrcpyW(path + len, msiexecW);
+    if ((service = CreateServiceW(scm, msiserverW, msiserverW, GENERIC_ALL,
+                                  SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
+                                  SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
+    {
+        CloseServiceHandle(service);
+    }
     else if (GetLastError() != ERROR_SERVICE_EXISTS)
     {
         fprintf(stderr, "Failed to create MSI service\n");
@@ -380,6 +364,35 @@ static DWORD DoRegServer(void)
     return ret;
 }
 
+static DWORD DoUnregServer(void)
+{
+    static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
+    SC_HANDLE scm, service;
+    DWORD ret = 0;
+
+    if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
+    {
+        fprintf(stderr, "Failed to open service control manager\n");
+        return 1;
+    }
+    if ((service = OpenServiceW(scm, msiserverW, DELETE)))
+    {
+        if (!DeleteService(service))
+        {
+            fprintf(stderr, "Failed to delete MSI service\n");
+            ret = 1;
+        }
+        CloseServiceHandle(service);
+    }
+    else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
+    {
+        fprintf(stderr, "Failed to open MSI service\n");
+        ret = 1;
+    }
+    CloseServiceHandle(scm);
+    return ret;
+}
+
 static INT DoEmbedding( LPWSTR key )
 {
        printf("Remote custom actions are not supported yet\n");
@@ -392,97 +405,111 @@ static INT DoEmbedding( LPWSTR key )
 
 enum chomp_state
 {
-       cs_whitespace,
-       cs_token,
-       cs_quote
+    CS_WHITESPACE,
+    CS_TOKEN,
+    CS_QUOTE
 };
 
-static int chomp( WCHAR *str )
+static int chomp( const WCHAR *in, WCHAR *out )
 {
-       enum chomp_state state = cs_token;
-       WCHAR *p, *out;
-       int count = 1, ignore;
+    enum chomp_state state = CS_TOKEN;
+    const WCHAR *p;
+    int count = 1;
+    BOOL ignore;
 
-       for( p = str, out = str; *p; p++ )
-       {
-               ignore = 1;
-               switch( state )
-               {
-               case cs_whitespace:
-                       switch( *p )
-                       {
-                       case ' ':
-                               break;
-                       case '"':
-                               state = cs_quote;
-                               count++;
-                               break;
-                       default:
-                               count++;
-                               ignore = 0;
-                               state = cs_token;
-                       }
-                       break;
-
-               case cs_token:
-                       switch( *p )
-                       {
-                       case '"':
-                               state = cs_quote;
-                               break;
-                       case ' ':
-                               state = cs_whitespace;
-                               *out++ = 0;
-                               break;
-                       default:
-                               ignore = 0;
-                       }
-                       break;
-
-               case cs_quote:
-                       switch( *p )
-                       {
-                       case '"':
-                               state = cs_token;
-                               break;
-                       default:
-                               ignore = 0;
-                       }
-                       break;
-               }
-               if( !ignore )
-                       *out++ = *p;
-       }
-
-       *out = 0;
-
-       return count;
+    for (p = in; *p; p++)
+    {
+        ignore = TRUE;
+        switch (state)
+        {
+        case CS_WHITESPACE:
+            switch (*p)
+            {
+            case ' ':
+                break;
+            case '"':
+                state = CS_QUOTE;
+                count++;
+                break;
+            default:
+                count++;
+                ignore = FALSE;
+                state = CS_TOKEN;
+            }
+            break;
+
+        case CS_TOKEN:
+            switch (*p)
+            {
+            case '"':
+                state = CS_QUOTE;
+                break;
+            case ' ':
+                state = CS_WHITESPACE;
+                if (out) *out++ = 0;
+                break;
+            default:
+                if (p > in && p[-1] == '"')
+                {
+                    if (out) *out++ = 0;
+                    count++;
+                }
+                ignore = FALSE;
+            }
+            break;
+
+        case CS_QUOTE:
+            switch (*p)
+            {
+            case '"':
+                state = CS_TOKEN;
+                break;
+            default:
+                ignore = FALSE;
+            }
+            break;
+        }
+        if (!ignore && out) *out++ = *p;
+    }
+    if (out) *out = 0;
+    return count;
 }
 
 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
 {
-       WCHAR **argv, *p = msi_strdup(cmdline);
-       int i, n;
+    WCHAR **argv, *p;
+    int i, count;
 
-       n = chomp( p );
-       argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
-       for( i=0; i<n; i++ )
-       {
-               argv[i] = p;
-               p += lstrlenW(p) + 1;
-       }
-       argv[i] = NULL;
+    *pargc = 0;
+    *pargv = NULL;
+
+    count = chomp( cmdline, NULL );
+    if (!(p = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline) + count + 1) * sizeof(WCHAR) )))
+        return;
+
+    count = chomp( cmdline, p );
+    if (!(argv = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR *) )))
+    {
+        HeapFree( GetProcessHeap(), 0, p );
+        return;
+    }
+    for (i = 0; i < count; i++)
+    {
+        argv[i] = p;
+        p += lstrlenW( p ) + 1;
+    }
+    argv[i] = NULL;
 
-       *pargc = n;
-       *pargv = argv;
+    *pargc = count;
+    *pargv = argv;
 }
 
-static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
+static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
 {
        LONG r;
-       HKEY hkey = 0, hkeyArgs = 0;
+       HKEY hkey;
        DWORD sz = 0, type = 0;
-       LPWSTR buf = NULL;
+       WCHAR *buf;
        BOOL ret = FALSE;
 
        r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
@@ -491,8 +518,15 @@ static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
        r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
        if(r == ERROR_SUCCESS && type == REG_SZ)
        {
-               buf = HeapAlloc(GetProcessHeap(), 0, sz);
-               r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)buf, &sz);
+               int len = lstrlenW( *pargv[0] );
+               if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
+               {
+                       RegCloseKey( hkey );
+                       return FALSE;
+               }
+               memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
+               buf[len++] = ' ';
+               r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
                if( r == ERROR_SUCCESS )
                {
                        process_args(buf, pargc, pargv);
@@ -500,7 +534,7 @@ static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
                }
                HeapFree(GetProcessHeap(), 0, buf);
        }
-       RegCloseKey(hkeyArgs);
+       RegCloseKey(hkey);
        return ret;
 }
 
@@ -569,7 +603,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                {
                        FunctionRegServer = TRUE;
                }
-               else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister"))
+               else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
+                       ||  msi_option_equal(argvW[i], "unreg"))
                {
                        FunctionUnregServer = TRUE;
                }
@@ -601,6 +636,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                        WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
                        PackageName = argvW[i];
                        StringListAppend(&property_list, ActionAdmin);
+                       WINE_FIXME("Administrative installs are not currently supported\n");
                }
                else if(msi_option_prefix(argvW[i], "f"))
                {
@@ -839,7 +875,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                                ExitProcess(1);
                        }
                }
-               else if(msi_option_equal(argvW[i], "p"))
+               else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
                {
                        FunctionPatch = TRUE;
                        i++;
@@ -855,10 +891,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                        {
                                InstallUILevel = INSTALLUILEVEL_NONE;
                        }
-                       else if(msi_strequal(argvW[i]+2, "b"))
-                       {
-                               InstallUILevel = INSTALLUILEVEL_BASIC;
-                       }
                        else if(msi_strequal(argvW[i]+2, "r"))
                        {
                                InstallUILevel = INSTALLUILEVEL_REDUCED;
@@ -871,18 +903,25 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                        {
                                InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
                        }
-                       else if(msi_strequal(argvW[i]+2, "b+"))
-                       {
-                               InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
-                       }
-                       else if(msi_strequal(argvW[i]+2, "b-"))
-                       {
-                               InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
-                       }
-                       else if(msi_strequal(argvW[i]+2, "b+!"))
+                       else if(msi_strprefix(argvW[i]+2, "b"))
                        {
-                               InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
-                               WINE_FIXME("Unknown modifier: !\n");
+                const WCHAR *ptr = argvW[i] + 3;
+
+                InstallUILevel = INSTALLUILEVEL_BASIC;
+
+                while (*ptr)
+                {
+                    if (msi_strprefix(ptr, "+"))
+                        InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
+                    if (msi_strprefix(ptr, "-"))
+                        InstallUILevel |= INSTALLUILEVEL_PROGRESSONLY;
+                    if (msi_strprefix(ptr, "!"))
+                    {
+                        WINE_FIXME("Unhandled modifier: !\n");
+                        InstallUILevel |= INSTALLUILEVEL_HIDECANCEL;
+                    }
+                    ptr++;
+                }
                        }
                        else
                        {
@@ -890,6 +929,14 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
                                         wine_dbgstr_w(argvW[i]+2));
                        }
                }
+                else if(msi_option_equal(argvW[i], "passive"))
+                {
+                    static const WCHAR rebootpromptW[] =
+                        {'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
+
+                    InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
+                    StringListAppend(&property_list, rebootpromptW);
+                }
                else if(msi_option_equal(argvW[i], "y"))
                {
                        FunctionDllRegisterServer = TRUE;
@@ -976,7 +1023,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
        }
        else if (FunctionUnregServer)
        {
-               WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
+               ReturnCode = DoUnregServer();
        }
        else if (FunctionServer)
        {