Beispiel #1
0
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);
  }
}