bool CHTTPPythonHandler::CanHandleRequest(const HTTPRequest &request) { ADDON::AddonPtr addon; std::string path; // try to resolve the addon as any python script must be part of a webinterface if (!CHTTPWebinterfaceHandler::ResolveAddon(request.pathUrl, addon, path) || addon == NULL || addon->Type() != ADDON::ADDON_WEB_INTERFACE) return false; // static webinterfaces aren't allowed to run python scripts ADDON::CWebinterface* webinterface = static_cast<ADDON::CWebinterface*>(addon.get()); if (webinterface->GetType() != ADDON::WebinterfaceTypeWsgi) return false; return true; }
void CHTTPPythonWsgiInvoker::executeScript(void *fp, const std::string &script, void *module, void *moduleDict) { if (m_request == NULL || m_addon == NULL || m_addon->Type() != ADDON::ADDON_WEB_INTERFACE || fp == NULL || script.empty() || module == NULL || moduleDict == NULL) return; ADDON::CWebinterface* webinterface = static_cast<ADDON::CWebinterface*>(m_addon.get()); if (webinterface->GetType() != ADDON::WebinterfaceTypeWsgi) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: trying to execute a non-WSGI script at %s", script.c_str()); return; } PyObject* pyScript = NULL; PyObject* pyModule = NULL; PyObject* pyEntryPoint = NULL; std::map<std::string, std::string> cgiEnvironment; PyObject* pyEnviron = NULL; PyObject* pyStart_response = NULL; PyObject* pyArgs = NULL; PyObject* pyResult = NULL; PyObject* pyResultIterator = NULL; PyObject* pyIterResult = NULL; // get the script std::string scriptName = URIUtils::GetFileName(script); URIUtils::RemoveExtension(scriptName); pyScript = PyString_FromStringAndSize(scriptName.c_str(), scriptName.size()); if (pyScript == NULL) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to convert script \"%s\" to python string", script.c_str()); return; } // load the script CLog::Log(LOGDEBUG, "CHTTPPythonWsgiInvoker: loading WSGI script \"%s\"", script.c_str()); pyModule = PyImport_Import(pyScript); Py_DECREF(pyScript); if (pyModule == NULL) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to load WSGI script \"%s\"", script.c_str()); return; } // get the entry point const std::string& entryPoint = webinterface->EntryPoint(); CLog::Log(LOGDEBUG, "CHTTPPythonWsgiInvoker: loading entry point \"%s\" from WSGI script \"%s\"", entryPoint.c_str(), script.c_str()); pyEntryPoint = PyObject_GetAttrString(pyModule, entryPoint.c_str()); if (pyEntryPoint == NULL) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to entry point \"%s\" from WSGI script \"%s\"", entryPoint.c_str(), script.c_str()); goto cleanup; } // check if the loaded entry point is a callable function if (!PyCallable_Check(pyEntryPoint)) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: defined entry point \"%s\" from WSGI script \"%s\" is not callable", entryPoint.c_str(), script.c_str()); goto cleanup; } // prepare the WsgiResponse object m_wsgiResponse = new XBMCAddon::xbmcwsgi::WsgiResponse(); if (m_wsgiResponse == NULL) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to create WsgiResponse object for WSGI script \"%s\"", script.c_str()); goto cleanup; } try { // prepare the start_response callable pyStart_response = PythonBindings::makePythonInstance(m_wsgiResponse, true); // create the (CGI) environment dictionary cgiEnvironment = createCgiEnvironment(m_request, m_addon); // and turn it into a python dictionary pyEnviron = PyDict_New(); for (std::map<std::string, std::string>::const_iterator cgiEnv = cgiEnvironment.begin(); cgiEnv != cgiEnvironment.end(); ++cgiEnv) { PyObject* pyEnvEntry = PyString_FromStringAndSize(cgiEnv->second.c_str(), cgiEnv->second.size()); PyDict_SetItemString(pyEnviron, cgiEnv->first.c_str(), pyEnvEntry); Py_DECREF(pyEnvEntry); } // add the WSGI-specific environment variables addWsgiEnvironment(m_request, pyEnviron); } catch (const XBMCAddon::WrongTypeException& e) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to prepare WsgiResponse object for WSGI script \"%s\" with wrong type exception: %s", script.c_str(), e.GetMessage()); goto cleanup; } catch (const XbmcCommons::Exception& e) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to prepare WsgiResponse object for WSGI script \"%s\" with exception: %s", script.c_str(), e.GetMessage()); goto cleanup; } catch (...) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to prepare WsgiResponse object for WSGI script \"%s\" with unknown exception", script.c_str()); goto cleanup; } // put together the arguments pyArgs = PyTuple_Pack(2, pyEnviron, pyStart_response); Py_DECREF(pyEnviron); Py_DECREF(pyStart_response); // call the given handler with the prepared arguments pyResult = PyObject_CallObject(pyEntryPoint, pyArgs); Py_DECREF(pyArgs); if (pyResult == NULL) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: no result for WSGI script \"%s\"", script.c_str()); goto cleanup; } // try to get an iterator from the result object pyResultIterator = PyObject_GetIter(pyResult); if (pyResultIterator == NULL || !PyIter_Check(pyResultIterator)) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: result of WSGI script \"%s\" is not iterable", script.c_str()); goto cleanup; } // go through all the iterables in the result and turn them into strings while ((pyIterResult = PyIter_Next(pyResultIterator)) != NULL) { std::string result; try { PythonBindings::PyXBMCGetUnicodeString(result, pyIterResult, false, "result", "handle_request"); } catch (const XBMCAddon::WrongTypeException& e) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to parse result iterable object for WSGI script \"%s\" with wrong type exception: %s", script.c_str(), e.GetMessage()); goto cleanup; } catch (const XbmcCommons::Exception& e) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to parse result iterable object for WSGI script \"%s\" with exception: %s", script.c_str(), e.GetMessage()); goto cleanup; } catch (...) { CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to parse result iterable object for WSGI script \"%s\" with unknown exception", script.c_str()); goto cleanup; } // append the result string to the response m_wsgiResponse->Append(result); } cleanup: if (pyIterResult != NULL) { Py_DECREF(pyIterResult); } if (pyResultIterator != NULL) { // Call optional close method on iterator if (PyObject_HasAttrString(pyResultIterator, (char*)"close") == 1) { if (PyObject_CallMethod(pyResultIterator, (char*)"close", NULL) == NULL) CLog::Log(LOGERROR, "CHTTPPythonWsgiInvoker: failed to close iterator object for WSGI script \"%s\"", script.c_str()); } Py_DECREF(pyResultIterator); } if (pyResult != NULL) { Py_DECREF(pyResult); } if (pyEntryPoint != NULL) { Py_DECREF(pyEntryPoint); } if (pyModule != NULL) { Py_DECREF(pyModule); } }