| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <python2.7/Python.h> | 
 | #include <dlfcn.h> | 
 |  | 
 | #include "base/files/file_path.h" | 
 | #include "base/files/scoped_temp_dir.h" | 
 | #include "base/i18n/icu_util.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "mojo/application/application_runner_chromium.h" | 
 | #include "mojo/application/content_handler_factory.h" | 
 | #include "mojo/common/common_type_converters.h" | 
 | #include "mojo/common/data_pipe_utils.h" | 
 | #include "mojo/public/c/system/main.h" | 
 | #include "mojo/public/cpp/application/application_delegate.h" | 
 | #include "mojo/public/cpp/application/application_impl.h" | 
 | #include "mojo/public/cpp/application/interface_factory_impl.h" | 
 | #include "mojo/public/python/src/common.h" | 
 | #include "third_party/zlib/google/zip_reader.h" | 
 | #include "url/gurl.h" | 
 |  | 
 | char kMojoSystem[] = "mojo_system"; | 
 | char kMojoSystemImpl[] = "mojo_system_impl"; | 
 | char kMojoMain[] = "MojoMain"; | 
 |  | 
 | extern "C" { | 
 |   void initmojo_system(); | 
 |   void initmojo_system_impl(); | 
 | } | 
 |  | 
 | namespace services { | 
 | namespace python { | 
 | namespace content_handler { | 
 |  | 
 | using mojo::Application; | 
 | using mojo::ApplicationConnection; | 
 | using mojo::ApplicationDelegate; | 
 | using mojo::ContentHandlerFactory; | 
 | using mojo::InterfaceRequest; | 
 | using mojo::ScopedDataPipeConsumerHandle; | 
 | using mojo::URLResponsePtr; | 
 | using mojo::python::ScopedPyRef; | 
 |  | 
 | class PythonContentHandler : public ContentHandlerFactory::Delegate { | 
 |  public: | 
 |   PythonContentHandler(bool debug) : debug_(debug) {} | 
 |  | 
 |  private: | 
 |   // Extracts the target application into a temporary directory. This directory | 
 |   // is deleted at the end of the life of the PythonContentHandler object. | 
 |   std::unique_ptr<base::ScopedTempDir> ExtractApplication( | 
 |       URLResponsePtr response) { | 
 |     std::unique_ptr<base::ScopedTempDir> temp_dir(new base::ScopedTempDir); | 
 |     CHECK(temp_dir->CreateUniqueTempDir()); | 
 |  | 
 |     zip::ZipReader reader; | 
 |     const std::string input_data = CopyToString(response->body.Pass()); | 
 |     CHECK(reader.OpenFromString(input_data)); | 
 |     base::FilePath temp_dir_path = temp_dir->path(); | 
 |     while (reader.HasMore()) { | 
 |       CHECK(reader.OpenCurrentEntryInZip()); | 
 |       CHECK(reader.ExtractCurrentEntryIntoDirectory(temp_dir_path)); | 
 |       CHECK(reader.AdvanceToNextEntry()); | 
 |     } | 
 |     return temp_dir; | 
 |   } | 
 |  | 
 |   ScopedPyRef RunString(std::string command, PyObject* globals, int mode) { | 
 |     ScopedPyRef result(PyRun_String(command.c_str(), mode, globals, nullptr)); | 
 |  | 
 |     if (!result) { | 
 |       LOG(ERROR) << "Error while running command: '" << command << "'"; | 
 |       PyErr_Print(); | 
 |     } | 
 |     return result; | 
 |   } | 
 |  | 
 |   bool Exec(std::string command, PyObject* globals) { | 
 |     return RunString(command, globals, Py_file_input); | 
 |   } | 
 |  | 
 |   ScopedPyRef Eval(std::string command, PyObject* globals) { | 
 |     return RunString(command, globals, Py_eval_input); | 
 |   } | 
 |  | 
 |   // Sets up the Python interpreter and loads mojo system modules. This method | 
 |   // returns the global dictionary for the python environment that should be | 
 |   // used for subsequent calls. Takes as input the path of the unpacked | 
 |   // application files. | 
 |   PyObject* SetupPythonEnvironment(const std::string& application_path) { | 
 |     // TODO(etiennej): Build python ourselves so we don't have to rely on | 
 |     // dynamically loading a system library. | 
 |     dlopen("libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); | 
 |  | 
 |     PyImport_AppendInittab(kMojoSystemImpl, &initmojo_system_impl); | 
 |     PyImport_AppendInittab(kMojoSystem, &initmojo_system); | 
 |  | 
 |     Py_Initialize(); | 
 |  | 
 |     PyObject *m, *d; | 
 |     m = PyImport_AddModule("__main__"); | 
 |     if (!m) | 
 |       return nullptr; | 
 |     d = PyModule_GetDict(m); | 
 |  | 
 |     // Enable debug information if requested. | 
 |     if (debug_) { | 
 |       std::string enable_logging = | 
 |           "import logging;" | 
 |           "logging.basicConfig();" | 
 |           "logging.getLogger().setLevel(logging.DEBUG)"; | 
 |       if (!Exec(enable_logging, d)) | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     // Inject the application path into the python search path so that imports | 
 |     // from the application work as expected. | 
 |     std::string search_path_py_command = | 
 |         "import sys; sys.path.append('" + application_path + "');"; | 
 |     if (!Exec(search_path_py_command, d)) | 
 |       return nullptr; | 
 |  | 
 |     return d; | 
 |   } | 
 |  | 
 |   // Overridden from ContentHandlerFactory::ManagedDelegate: | 
 |   void RunApplication(InterfaceRequest<Application> application_request, | 
 |                       URLResponsePtr response) override { | 
 |     std::unique_ptr<base::ScopedTempDir> temp_dir = | 
 |         ExtractApplication(response.Pass()); | 
 |     base::FilePath directory_path = temp_dir->path(); | 
 |  | 
 |     PyObject* d = SetupPythonEnvironment(directory_path.value()); | 
 |     DCHECK(d); | 
 |  | 
 |     base::FilePath entry_path = directory_path.Append("__mojo__.py"); | 
 |  | 
 |     FILE* entry_file = base::OpenFile(entry_path, "r"); | 
 |     DCHECK(entry_file); | 
 |  | 
 |     // Ensure that all created objects are destroyed before calling Py_Finalize. | 
 |     { | 
 |       // Load the __mojo__.py file into the interpreter. MojoMain hasn't run | 
 |       // yet. | 
 |       ScopedPyRef result(PyRun_File(entry_file, entry_path.value().c_str(), | 
 |                                     Py_file_input, d, d)); | 
 |       if (!result) { | 
 |         LOG(ERROR) << "Error while loading script"; | 
 |         PyErr_Print(); | 
 |         return; | 
 |       } | 
 |  | 
 |       // Find MojoMain. | 
 |       ScopedPyRef py_function(PyMapping_GetItemString(d, kMojoMain)); | 
 |  | 
 |       if (!py_function) { | 
 |         LOG(ERROR) << "Locals size: " << PyMapping_Size(d); | 
 |         LOG(ERROR) << "MojoMain not found"; | 
 |         PyErr_Print(); | 
 |         return; | 
 |       } | 
 |  | 
 |       if (PyCallable_Check(py_function)) { | 
 |         MojoHandle application_request_handle = | 
 |             application_request.PassMessagePipe().release().value(); | 
 |         std::string handle_command = base::StringPrintf( | 
 |             "mojo_system.Handle(%u)", application_request_handle); | 
 |         ScopedPyRef py_input(Eval(handle_command, d)); | 
 |         if (!py_input) { | 
 |           MojoClose(application_request_handle); | 
 |           return; | 
 |         } | 
 |         ScopedPyRef py_arguments(PyTuple_New(1)); | 
 |         // py_input reference is stolen by py_arguments | 
 |         PyTuple_SetItem(py_arguments, 0, py_input.Release()); | 
 |         // Run MojoMain with application_request_handle as the first and only | 
 |         // argument. | 
 |         ScopedPyRef py_output(PyObject_CallObject(py_function, py_arguments)); | 
 |  | 
 |         if (!py_output) { | 
 |           LOG(ERROR) << "Error while executing MojoMain"; | 
 |           PyErr_Print(); | 
 |           return; | 
 |         } | 
 |       } else { | 
 |         LOG(ERROR) << "MojoMain is not callable; it should be a function"; | 
 |       } | 
 |     } | 
 |     Py_Finalize(); | 
 |   } | 
 |  | 
 |   std::string CopyToString(ScopedDataPipeConsumerHandle body) { | 
 |     std::string body_str; | 
 |     bool result = mojo::common::BlockingCopyToString(body.Pass(), &body_str); | 
 |     DCHECK(result); | 
 |     return body_str; | 
 |   } | 
 |  | 
 |   bool debug_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(PythonContentHandler); | 
 | }; | 
 |  | 
 | class PythonContentHandlerApp : public ApplicationDelegate { | 
 |  public: | 
 |   PythonContentHandlerApp() | 
 |       : content_handler_(false), | 
 |         debug_content_handler_(true), | 
 |         content_handler_factory_(&content_handler_), | 
 |         debug_content_handler_factory_(&debug_content_handler_) {} | 
 |  | 
 |  private: | 
 |   // Overridden from ApplicationDelegate: | 
 |   bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | 
 |     if (IsDebug(connection->GetConnectionURL())) | 
 |       connection->AddService(&debug_content_handler_factory_); | 
 |     else | 
 |       connection->AddService(&content_handler_factory_); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool IsDebug(const std::string& requestedUrl) { | 
 |     GURL url(requestedUrl); | 
 |     if (url.has_query()) { | 
 |       std::vector<std::string> query_parameters; | 
 |       Tokenize(url.query(), "&", &query_parameters); | 
 |       return std::find(query_parameters.begin(), query_parameters.end(), | 
 |                        "debug=true") != query_parameters.end(); | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   PythonContentHandler content_handler_; | 
 |   PythonContentHandler debug_content_handler_; | 
 |   ContentHandlerFactory content_handler_factory_; | 
 |   ContentHandlerFactory debug_content_handler_factory_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(PythonContentHandlerApp); | 
 | }; | 
 |  | 
 | }  // namespace content_handler | 
 | }  // namespace python | 
 | }  // namespace services | 
 |  | 
 | MojoResult MojoMain(MojoHandle application_request) { | 
 |   mojo::ApplicationRunnerChromium runner( | 
 |       new services::python::content_handler::PythonContentHandlerApp()); | 
 |   return runner.Run(application_request); | 
 | } |