This class implements the controller interface for Windows/mingw. --- SpiceXPI/src/plugin/Makefile.am | 2 + SpiceXPI/src/plugin/controller-win.cpp | 266 +++++++++++++++++++++++++++++++++ SpiceXPI/src/plugin/controller-win.h | 93 ++++++++++++ SpiceXPI/src/plugin/controller.cpp | 15 ++ SpiceXPI/src/plugin/plugin.cpp | 11 ++ configure.ac | 6 +- 6 files changed, 390 insertions(+), 3 deletions(-) create mode 100644 SpiceXPI/src/plugin/controller-win.cpp create mode 100644 SpiceXPI/src/plugin/controller-win.h diff --git a/SpiceXPI/src/plugin/Makefile.am b/SpiceXPI/src/plugin/Makefile.am index bb50d21..93f4c2c 100644 --- a/SpiceXPI/src/plugin/Makefile.am +++ b/SpiceXPI/src/plugin/Makefile.am @@ -66,6 +66,8 @@ if OS_WINDOWS $(LIBTOOL) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(WINDRES) $(RCFLAGS) -i $< -o $@ npSpiceConsole_la_SOURCES += \ + controller-win.cpp \ + controller-win.h \ resource.rc \ $(NULL) diff --git a/SpiceXPI/src/plugin/controller-win.cpp b/SpiceXPI/src/plugin/controller-win.cpp new file mode 100644 index 0000000..7158624 --- /dev/null +++ b/SpiceXPI/src/plugin/controller-win.cpp @@ -0,0 +1,266 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Copyright 2009-2011, Red Hat Inc. + * Copyright 2013, Red Hat Inc. + * Based on mozilla.org's scriptable plugin example + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Uri Lublin + * Martin Stransky + * Peter Hatina + * Christophe Fergeau + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "config.h" + +#include <aclapi.h> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <cerrno> +#include <glib.h> +#include <gio/gwin32outputstream.h> + +#include "rederrorcodes.h" +#include "controller-win.h" +#include "plugin.h" + +SpiceControllerWin::SpiceControllerWin(nsPluginInstance *aPlugin): + SpiceController(aPlugin) +{ + g_random_set_seed(GPOINTER_TO_INT(aPlugin)); +} + +SpiceControllerWin::~SpiceControllerWin() +{ +} + +int SpiceControllerWin::Connect() +{ + HANDLE hClientPipe; + + hClientPipe = CreateFile(m_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, + NULL); + if (hClientPipe != INVALID_HANDLE_VALUE) { + g_warning("Connection OK"); + m_pipe = g_win32_output_stream_new(hClientPipe, TRUE); + return 0; + } else { + g_warning("Connection failed"); + return -1; + } + g_return_val_if_reached(-1); +} + +static int get_sid(HANDLE handle, PSID* ppsid, PSECURITY_DESCRIPTOR* ppsec_desc) +{ + DWORD ret = GetSecurityInfo(handle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, + ppsid, NULL, NULL, NULL, ppsec_desc); + if (ret != ERROR_SUCCESS) { + return ret; + } + if (!IsValidSid(*ppsid)) { + return -1; + } + return 0; +} + +//checks whether the handle owner is the current user. +static bool is_same_user(HANDLE handle) +{ + PSECURITY_DESCRIPTOR psec_desc_handle = NULL; + PSECURITY_DESCRIPTOR psec_desc_user = NULL; + PSID psid_handle; + PSID psid_user; + bool ret; + + ret = !get_sid(handle, &psid_handle, &psec_desc_handle) && + !get_sid(GetCurrentProcess(), &psid_user, &psec_desc_user) && + EqualSid(psid_handle, psid_user); + LocalFree(psec_desc_handle); + LocalFree(psec_desc_user); + return ret; +} + +bool SpiceControllerWin::CheckPipe() +{ + void *hClientPipe; + + if (!G_IS_WIN32_OUTPUT_STREAM(m_pipe)) + return false; + + hClientPipe = g_win32_output_stream_get_handle(G_WIN32_OUTPUT_STREAM(m_pipe)); + // Verify the named-pipe-server owner is the current user. + // Do it here so upon failure use next condition cleanup code. + if (hClientPipe != INVALID_HANDLE_VALUE) { + if (!is_same_user(hClientPipe)) { + g_critical("Closing pipe to spicec -- it is not safe"); + g_object_unref(G_OBJECT(m_pipe)); + m_pipe = NULL; + return false; + } + } + + return true; +} + +#ifdef _UNICODE +#define _T L +#else +#define _T +#endif + +#define SPICE_CLIENT_REGISTRY_KEY _T("Software\\spice-space.org\\spicex") +#define SPICE_XPI_DLL _T("npSpiceConsole.dll") +#define RED_CLIENT_FILE_NAME _T("spicec.exe") +#define CMDLINE_LENGTH 32768 + +GStrv SpiceControllerWin::GetClientPath() +{ + LONG lret; + HKEY hkey; + + lret = RegOpenKeyEx(HKEY_CURRENT_USER, SPICE_CLIENT_REGISTRY_KEY, + 0, KEY_READ, &hkey); + if (lret == ERROR_SUCCESS) { + DWORD dwType = REG_SZ; + TCHAR lpCommandLine[CMDLINE_LENGTH] = {0}; + DWORD dwSize = sizeof(lpCommandLine); + GArray *argv_garray; + char *it; + + lret = RegQueryValueEx(hkey, "client", NULL, &dwType, + (LPBYTE)lpCommandLine, &dwSize); + RegCloseKey(hkey); + + /* Some convoluted code here, the registry key contains the + * command to run as a string, the GSpawn API expects an array + * of strings. The awkward part is that the GSpawn API will + * then rebuild a commandline string from this array :-/ */ + argv_garray = g_array_new(TRUE, FALSE, sizeof(char *)); + /* Look for .exe from the end as the path in which the SPICE + * client is installed may contain '.exe' */ + it = g_strrstr_len(lpCommandLine, dwSize, ".exe "); + if (it != NULL) { + gchar **args; + gchar *val; + it += strlen(".exe"); + *it = '\0'; + it++; + val = g_strdup(lpCommandLine); + g_array_prepend_val(argv_garray, val); + args = g_strsplit(it, " ", -1); + g_array_append_vals(argv_garray, args, g_strv_length(args)); + /* We don't want to free the strings stored in args, just + * the container */ + g_free(args); + return (GStrv)g_array_free(argv_garray, FALSE); + } + } + + g_warn_if_reached(); + + return NULL; +} + +GStrv SpiceControllerWin::GetFallbackClientPath() +{ + HMODULE hModule; + // we assume the Spice client binary is located in the same dir as the + // Firefox plugin + if ((hModule = GetModuleHandle(SPICE_XPI_DLL))) { + gchar *module_path; + GStrv fallback_argv; + + module_path = g_win32_get_package_installation_directory_of_module(hModule); + if (module_path != NULL) { + fallback_argv = g_new0(char *, 3); + fallback_argv[0] = g_build_filename(module_path, RED_CLIENT_FILE_NAME, NULL); + fallback_argv[1] = g_strdup("--controller"); + g_free(module_path); + return fallback_argv; + } + } + + g_warn_if_reached(); + + return NULL; +} + +#define RED_CLIENT_PIPE_NAME TEXT("\\\\.\\pipe\\SpiceController-%lu") +void SpiceControllerWin::SetupControllerPipe(GStrv &env) +{ + char *pipe_name; + pipe_name = g_strdup_printf(RED_CLIENT_PIPE_NAME, (unsigned long)g_random_int()); + this->SetFilename(pipe_name); + /* FIXME set SPICE_XPI_NAMEDPIPE */ + env = g_environ_setenv(env, "SPICE_XPI_NAMEDPIPE", pipe_name, TRUE); + g_free(pipe_name); +} + +void SpiceControllerWin::StopClient() +{ + if (m_pid_controller != NULL) { + //WaitForPid will take care of closing the handle + TerminateProcess(m_pid_controller, 0); + m_pid_controller = NULL; + } +} + + +uint32_t SpiceControllerWin::Write(const void *lpBuffer, uint32_t nBytesToWrite) +{ + GError *error = NULL; + gsize bytes_written; + + g_return_val_if_fail(G_IS_OUTPUT_STREAM(m_pipe), 0); + + g_output_stream_write_all(m_pipe, lpBuffer, nBytesToWrite, + &bytes_written, NULL, &error); + if (error != NULL) { + g_warning("Error writing to controller pipe: %s", error->message); + g_clear_error(&error); + return -1; + } + if (bytes_written != nBytesToWrite) { + g_warning("Partial write (%"G_GSIZE_MODIFIER"u instead of %u", + bytes_written, (unsigned int)nBytesToWrite); + } + + return bytes_written; +} diff --git a/SpiceXPI/src/plugin/controller-win.h b/SpiceXPI/src/plugin/controller-win.h new file mode 100644 index 0000000..fe09900 --- /dev/null +++ b/SpiceXPI/src/plugin/controller-win.h @@ -0,0 +1,93 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Copyright 2009-2011, Red Hat Inc. + * Copyright 2013, Red Hat Inc. + * Based on mozilla.org's scriptable plugin example + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Uri Lublin + * Martin Stransky + * Peter Hatina + * Christophe Fergeau + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef SPICE_CONTROLLER_WIN_H +#define SPICE_CONTROLLER_WIN_H + +/* + Basic assumption: + ------------------ + Cross platform compatible. + Easy to transform into remote process communication + Secured + + chosen: + Unix - Unix Domain Sockets (easy to change into regular sockets for remote communication) + Windows - Named pipe (which allows remote access and is duplex + (rather than anonymous pipe which is local only and one way) +*/ + +#include <glib.h> +#include <glib-object.h> /* for GStrv */ +#include <gio/gio.h> +#include <string> +extern "C" { +# include <stdint.h> +# include <limits.h> +} + +#include <spice/controller_prot.h> +#include "controller.h" + +class nsPluginInstance; + +class SpiceControllerWin: public SpiceController +{ +public: + SpiceControllerWin(nsPluginInstance *aPlugin); + virtual ~SpiceControllerWin(); + + virtual void StopClient(); + virtual uint32_t Write(const void *lpBuffer, uint32_t nBytesToWrite); + int Connect(int nRetries) { return SpiceController::Connect(nRetries); }; + +private: + virtual int Connect(); + virtual void SetupControllerPipe(GStrv &env); + virtual bool CheckPipe(); + virtual GStrv GetClientPath(void); + virtual GStrv GetFallbackClientPath(void); +}; + +#endif // SPICE_CONTROLLER_WIN_H diff --git a/SpiceXPI/src/plugin/controller.cpp b/SpiceXPI/src/plugin/controller.cpp index ccef1d4..ac97ce1 100644 --- a/SpiceXPI/src/plugin/controller.cpp +++ b/SpiceXPI/src/plugin/controller.cpp @@ -97,6 +97,21 @@ int SpiceController::Connect(const int nRetries) g_usleep(sleep_time * G_USEC_PER_SEC); ++sleep_time; } + if (rc != 0) { + g_warning("error connecting"); + g_assert(m_pipe == NULL); + } + if (!CheckPipe()) { + g_warning("Pipe validation failure"); + g_warn_if_fail(m_pipe == NULL); + } + if (m_pipe == NULL) { + g_warning("failed to create pipe"); +#ifdef XP_WIN + rc = MAKE_HRESULT(1, FACILITY_CREATE_RED_PIPE, GetLastError()); +#endif + this->StopClient(); + } return rc; } diff --git a/SpiceXPI/src/plugin/plugin.cpp b/SpiceXPI/src/plugin/plugin.cpp index cd17620..2e59bfd 100644 --- a/SpiceXPI/src/plugin/plugin.cpp +++ b/SpiceXPI/src/plugin/plugin.cpp @@ -66,7 +66,12 @@ extern "C" { #include <fstream> #include <set> +#if defined(XP_UNIX) #include "controller-unix.h" +#endif +#if defined(XP_WIN) +#include "controller-win.h" +#endif #include "plugin.h" #include "nsScriptablePeer.h" @@ -187,7 +192,13 @@ nsPluginInstance::nsPluginInstance(NPP aInstance): g_type_init(); #endif +#if defined(XP_WIN) + m_external_controller = new SpiceControllerWin(this); +#elif defined(XP_UNIX) m_external_controller = new SpiceControllerUnix(this); +#else +#error "Unknown OS, no controller implementation" +#endif } nsPluginInstance::~nsPluginInstance() diff --git a/configure.ac b/configure.ac index 7f401ab..39a1f7e 100644 --- a/configure.ac +++ b/configure.ac @@ -41,9 +41,9 @@ AC_CONFIG_SUBDIRS([spice-protocol]) SPICE_PROTOCOL_CFLAGS='-I ${top_srcdir}/spice-protocol' AC_SUBST(SPICE_PROTOCOL_CFLAGS) -PKG_CHECK_MODULES(GLIB, glib-2.0 gio-2.0 gthread-2.0) -AC_SUBST(GLIB_CFLAGS) -AC_SUBST(GLIB_LIBS) +SPICE_XPI_REQUIRES="glib-2.0 gio-2.0 gthread-2.0" +AS_IF([test "x$backend" = "xwindows"], [SPICE_XPI_REQUIRES="$SPICE_XPI_REQUIRES gio-windows-2.0"]) +PKG_CHECK_MODULES([GLIB],[$SPICE_XPI_REQUIRES]) AC_ARG_ENABLE([xpi], [AS_HELP_STRING([--enable-xpi], -- 1.8.1.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel