/* $Id: $ */ /** @file * vboxweb.h - header file for "real" web server code. */ /* * * Copyright (C) 2006-2016 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #define LOG_GROUP LOG_GROUP_WEBSERVICE #include #include #include #include #include #include #include /**************************************************************************** * * debug macro * ****************************************************************************/ #define WEBDEBUG(a) do { if (g_fVerbose) { LogRel(a); } } while (0) /**************************************************************************** * * typedefs * ****************************************************************************/ // type used by gSOAP-generated code typedef std::string WSDLT_ID; // combined managed object ref (websession ID plus object ID) typedef std::string vbox__uuid; /**************************************************************************** * * global variables * ****************************************************************************/ extern bool g_fVerbose; extern util::WriteLockHandle *g_pWebsessionsLockHandle; extern const WSDLT_ID g_EmptyWSDLID; /**************************************************************************** * * SOAP exceptions * ****************************************************************************/ extern void RaiseSoapInvalidObjectFault(struct soap *soap, WSDLT_ID obj); extern void RaiseSoapRuntimeFault(struct soap *soap, const WSDLT_ID &idThis, const char *pcszMethodName, HRESULT apirc, IUnknown *pObj, const com::Guid &iid); /**************************************************************************** * * conversion helpers * ****************************************************************************/ extern std::string ConvertComString(const com::Bstr &bstr); extern std::string ConvertComString(const com::Guid &bstr); extern std::string Base64EncodeByteArray(ComSafeArrayIn(BYTE, aData)); extern void Base64DecodeByteArray(struct soap *soap, const std::string& aStr, ComSafeArrayOut(BYTE, aData), const WSDLT_ID &idThis, const char *pszMethodName, IUnknown *pObj, const com::Guid &iid); /**************************************************************************** * * managed object reference classes * ****************************************************************************/ class WebServiceSessionPrivate; class ManagedObjectRef; /** * An instance of this gets created for every client that logs onto the * webservice (via the special IWebsessionManager::logon() SOAP API) and * maintains the managed object references for that websession. */ class WebServiceSession { friend class ManagedObjectRef; private: uint64_t _uWebsessionID; uint64_t _uNextObjectID; WebServiceSessionPrivate *_pp; // opaque data struct (defined in vboxweb.cpp) bool _fDestructing; uint32_t _tLastObjectLookup; // hide the copy constructor because we're not copyable WebServiceSession(const WebServiceSession ©From); public: WebServiceSession(); ~WebServiceSession(); int authenticate(const char *pcszUsername, const char *pcszPassword, IVirtualBox **ppVirtualBox); ManagedObjectRef* findRefFromPtr(const IUnknown *pObject); uint64_t getID() const { return _uWebsessionID; } uint64_t createObjectID() { uint64_t id = ASMAtomicIncU64(&_uNextObjectID); return id - 1; } void touch(); uint32_t getLastObjectLookup() const { return _tLastObjectLookup; } static WebServiceSession* findWebsessionFromRef(const WSDLT_ID &id); size_t CountRefs(); }; /** * ManagedObjectRef is used to map COM pointers to object IDs * within a websession. Such object IDs are 64-bit integers. * * When a webservice method call is invoked on an object, it * has an opaque string called a "managed object reference". Such * a string consists of a websession ID combined with an object ID. * */ class ManagedObjectRef { protected: // owning websession: WebServiceSession &_websession; IUnknown *_pobjUnknown; // pointer to IUnknown interface for this MOR void *_pobjInterface; // pointer to COM interface represented by _guidInterface, for which this MOR // was created; this may be an IUnknown or something more specific com::Guid _guidInterface; // the interface which _pvObj represents const char *_pcszInterface; // string representation of that interface (e.g. "IMachine") // keys: uint64_t _id; uintptr_t _ulp; // long ID as string WSDLT_ID _strID; public: ManagedObjectRef(WebServiceSession &websession, IUnknown *pobjUnknown, void *pobjInterface, const com::Guid &guidInterface, const char *pcszInterface); ~ManagedObjectRef(); uint64_t getID() { return _id; } /** * Returns the contained COM pointer and the UUID of the COM interface * which it supports. * @param ppobjInterface * @param ppobjUnknown * @return */ const com::Guid& getPtr(void **ppobjInterface, IUnknown **ppobjUnknown) { *ppobjInterface = _pobjInterface; *ppobjUnknown = _pobjUnknown; return _guidInterface; } /** * Returns the ID of this managed object reference to string * form, for returning with SOAP data or similar. * * @return The ID in string form. */ const WSDLT_ID& getWSDLID() const { return _strID; } const char* getInterfaceName() const { return _pcszInterface; } static int findRefFromId(const WSDLT_ID &id, ManagedObjectRef **pRef, bool fNullAllowed); }; /** * Template function that resolves a managed object reference to a COM pointer * of the template class T. * * This gets called only from tons of generated code in methodmaps.cpp to * resolve objects in *input* parameters to COM methods (i.e. translate * MOR strings to COM objects which should exist already). * * This is a template function so that we can support ComPtr's for arbitrary * interfaces and automatically verify that the managed object reference on * the internal stack actually is of the expected interface. We also now avoid * calling QueryInterface for the case that the interface desired by the caller * is the same as the interface for which the MOR was originally created. In * that case, the lookup is very fast. * * @param soap * @param id in: integer managed object reference, as passed in by web service client * @param pComPtr out: reference to COM pointer object that receives the com pointer, * if SOAP_OK is returned * @param fNullAllowed in: if true, then this func returns a NULL COM pointer if an * empty MOR is passed in (i.e. NULL pointers are allowed). If false, * then this fails; this will be false when called for the "this" * argument of method calls, which really shouldn't be NULL. * @return error code or SOAP_OK if no error */ template int findComPtrFromId(struct soap *soap, const WSDLT_ID &id, ComPtr &pComPtr, bool fNullAllowed) { // findRefFromId requires thelock util::AutoWriteLock lock(g_pWebsessionsLockHandle COMMA_LOCKVAL_SRC_POS); int rc; ManagedObjectRef *pRef; if ((rc = ManagedObjectRef::findRefFromId(id, &pRef, fNullAllowed))) // error: RaiseSoapInvalidObjectFault(soap, id); else { if (fNullAllowed && pRef == NULL) { WEBDEBUG((" %s(): returning NULL object as permitted\n", __FUNCTION__)); pComPtr.setNull(); return 0; } const com::Guid &guidCaller = COM_IIDOF(T); // pRef->getPtr returns both a void* for its specific interface pointer as well as a generic IUnknown* void *pobjInterface; IUnknown *pobjUnknown; const com::Guid &guidInterface = pRef->getPtr(&pobjInterface, &pobjUnknown); if (guidInterface == guidCaller) { // same interface: then no QueryInterface needed WEBDEBUG((" %s(): returning original %s*=0x%lX (IUnknown*=0x%lX)\n", __FUNCTION__, pRef->getInterfaceName(), pobjInterface, pobjUnknown)); pComPtr = (T*)pobjInterface; // this calls AddRef() once return 0; } // QueryInterface tests whether p actually supports the templated T interface desired by caller T *pT; pobjUnknown->QueryInterface(guidCaller.ref(), (void**)&pT); // this adds a reference count if (pT) { // assign to caller's ComPtr; use asOutParam() to avoid adding another reference, QueryInterface() already added one WEBDEBUG((" %s(): returning pointer 0x%lX for queried interface %RTuuid (IUnknown*=0x%lX)\n", __FUNCTION__, pT, guidCaller.raw(), pobjUnknown)); *(pComPtr.asOutParam()) = pT; return 0; } WEBDEBUG((" Interface not supported for object reference %s, which is of class %s\n", id.c_str(), pRef->getInterfaceName())); rc = VERR_WEB_UNSUPPORTED_INTERFACE; RaiseSoapInvalidObjectFault(soap, id); // @todo better message } return rc; } /** * Creates a new managed object reference for the given COM pointer. If one * already exists for the given pointer, then that reference's ID is returned. * * This gets called from tons of generated code in methodmaps.cpp to resolve * objects *returned* from COM methods (i.e. create MOR strings from COM * objects which might have been newly created). * * @param idParent managed object reference of calling object; used to extract * websession ID * @param pcszInterface * @param pc COM object for which to create a reference * @return existing or new managed object reference */ template const WSDLT_ID& createOrFindRefFromComPtr(const WSDLT_ID &idParent, const char *pcszInterface, const ComPtr &pc) { // NULL comptr should return NULL MOR if (pc.isNull()) { WEBDEBUG((" createOrFindRefFromComPtr(): returning empty MOR for NULL COM pointer\n")); return g_EmptyWSDLID; } util::AutoWriteLock lock(g_pWebsessionsLockHandle COMMA_LOCKVAL_SRC_POS); WebServiceSession *pWebsession; if ((pWebsession = WebServiceSession::findWebsessionFromRef(idParent))) { ManagedObjectRef *pRef; // we need an IUnknown pointer for the MOR ComPtr pobjUnknown = pc; if ( ((pRef = pWebsession->findRefFromPtr(pobjUnknown))) || ((pRef = new ManagedObjectRef(*pWebsession, pobjUnknown, // IUnknown *pobjUnknown pc, // void *pobjInterface COM_IIDOF(T), pcszInterface))) ) return pRef->getWSDLID(); } // websession has expired, return an empty MOR instead of allocating a // new reference which couldn't be used anyway. return g_EmptyWSDLID; }