# # Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you # may not use this file except in compliance with the License. You # may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. See the License for the specific language governing # permissions and limitations under the License. # # Originally developed by Gregory Trubetskoy. # from . import apache import importlib.util import os import sys # if threads are not available # create a functionless lock object try: import threading _lock = threading.Lock() except (ImportError, AttributeError): class DummyLock: def acquire(self): pass def release(self): pass _lock = DummyLock() # the next statement deserves some explaining. # it seems that the standard os.environ object looses # memory if the environment is manipulated frequently. Since for # CGI you have to rebuild it for every request, your httpd will # grow rather fast. I am not exactly sure why it happens and if there # is a more sensible remedy, but this seems to work OK. os.environ = {} original = list(sys.modules.keys()) # find out the standard library location stdlib, x = os.path.split(os.__file__) def handler(req): ### if you don't need indirect modules reloaded, comment out ### code unitl ### end # if there are any new modules since the import of this module, # delete them. for m in list(sys.modules.keys()): if m not in original: # unless they are part of standard library mod = sys.modules[m] if hasattr(mod, "__file__"): path, x = os.path.split(mod.__file__) if path != stdlib: del sys.modules[m] ### end # get the filename of the script if "script_filename" in req.subprocess_env: dir, file = os.path.split(req.subprocess_env["script_filename"]) else: dir, file = os.path.split(req.filename) module_name, ext = os.path.splitext(file) _lock.acquire() try: try: # The CGI spec requires us to set current working # directory to that of the script. This is not # thread safe, this is why we must obtain the lock. cwd = os.getcwd() os.chdir(dir) # simulate cgi environment env, si, so = apache.setup_cgi(req) scriptPath = os.path.join(dir, file) if not os.path.exists(scriptPath): raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) # avoid loading modules outside dir # (e.g. shenaningans like ../../../../etc/passwd) scriptPath = os.path.abspath(scriptPath) if not scriptPath.startswith(dir): raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) try: # we do not search the pythonpath (security reasons) spec = importlib.util.spec_from_file_location(module_name, scriptPath) except (ModuleNotFoundError, ValueError): raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) if spec is None: raise apache.SERVER_RETURN(apache.HTTP_NOT_FOUND) module = importlib.util.module_from_spec(spec) sys.modules[module_name] = module spec.loader.exec_module(module) return apache.OK finally: # unsimulate the cgi environment apache.restore_nocgi(env, si, so) os.chdir(cwd) finally: _lock.release()