/* * vboxweb.h: * header file for "real" web server code. * * Copyright (C) 2006-2011 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. */ /**************************************************************************** * * debug macro * ****************************************************************************/ void WebLog(const char *pszFormat, ...); #define WEBDEBUG(a) if (g_fVerbose) { WebLog a; } #ifdef DEBUG #define LOG_GROUP LOG_GROUP_WEBSERVICE #include #endif #include #include #include #include #include #include /**************************************************************************** * * typedefs * ****************************************************************************/ // type used by gSOAP-generated code typedef std::string WSDLT_ID; // combined managed object ref (session ID plus object ID) typedef std::string vbox__uuid; /**************************************************************************** * * global variables * ****************************************************************************/ extern ComPtr g_pVirtualBox; extern bool g_fVerbose; extern PRTSTREAM g_pstrLog; extern util::WriteLockHandle *g_pAuthLibLockHandle; extern util::WriteLockHandle *g_pSessionsLockHandle; extern const WSDLT_ID g_EmptyWSDLID; /**************************************************************************** * * SOAP exceptions * ****************************************************************************/ void RaiseSoapInvalidObjectFault(struct soap *soap, WSDLT_ID obj); void RaiseSoapRuntimeFault2(struct soap *soap, HRESULT apirc, IUnknown *pObj, const com::Guid &iid); /** * Template function called everywhere from methodmaps.cpp which calls * RaiseSoapRuntimeFault2() with the correct COM interface ID. * @param soap * @param apirc * @param pObj */ template void RaiseSoapRuntimeFault(struct soap *soap, HRESULT apirc, const ComPtr &pObj) { RaiseSoapRuntimeFault2(soap, apirc, pObj, COM_IIDOF(T)); } /**************************************************************************** * * conversion helpers * ****************************************************************************/ std::string ConvertComString(const com::Bstr &bstr); std::string ConvertComString(const com::Guid &bstr); /**************************************************************************** * * 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 session. */ class WebServiceSession { friend class ManagedObjectRef; private: uint64_t _uSessionID; WebServiceSessionPrivate *_pp; // opaque data struct (defined in vboxweb.cpp) bool _fDestructing; ManagedObjectRef *_pISession; time_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 _uSessionID; } const WSDLT_ID& getSessionWSDLID() const; void touch(); time_t getLastObjectLookup() const { return _tLastObjectLookup; } static WebServiceSession* findSessionFromRef(const WSDLT_ID &id); void DumpRefs(); }; /** * ManagedObjectRef is used to map COM pointers to object IDs * within a session. 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 session ID combined with an object ID. * */ class ManagedObjectRef { protected: // owning session: WebServiceSession &_session; 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 &session, 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 * @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); static ManagedObjectRef* findFromPtr(ComPtr pcu); static ManagedObjectRef* create(const WSDLT_ID &idParent, ComPtr pcu); }; /** * 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_pSessionsLockHandle 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 for the given COM pointer. If a reference already exists * for the given pointer, then that reference's ID is returned instead. * * 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 session ID * @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, 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_pSessionsLockHandle COMMA_LOCKVAL_SRC_POS); WebServiceSession *pSession; if ((pSession = WebServiceSession::findSessionFromRef(idParent))) { ManagedObjectRef *pRef; // we need an IUnknown pointer for the MOR ComPtr pobjUnknown = pc; if ( ((pRef = pSession->findRefFromPtr(pobjUnknown))) || ((pRef = new ManagedObjectRef(*pSession, pobjUnknown, // IUnknown *pobjUnknown pc, // void *pobjInterface COM_IIDOF(T), pcszInterface))) ) return pRef->getWSDLID(); } // session has expired, return an empty MOR instead of allocating a // new reference which couldn't be used anyway. return g_EmptyWSDLID; }