""" * ==================================================================== * Copyright (c) 2001 Jon Travis. All rights reserved. * See the file COPYING for information on usage and redistribution. * ==================================================================== """ import emb_html import mod_snake from request_file import RequestFile import os import stat import string import sys import time import traceback import threading from posix import chdir try: from cStringIO import StringIO except: from StringIO import StringIO EPY_MIMETYPE = 'application/x-httpd-epy' EPY_FAKEREQ_NAME = 'EPY' # per-server keys PER_SVR_CACHE = 0 PER_SVR_LOGFD = 1 PER_SVR_LOGFILE = 2 PER_SVR_SERVER = 3 # per-dir keys PER_DIR_ERRSEND = 0 PER_DIR_ENABLED = 1 Global_Lock = threading.Lock() class EPy_Loader: """EPy_Loader: A caching object for .epy files loaded from disk""" def __init__(self, filename): self.filename = filename self.mtime = 0 self.persist = 0 # Persistant namespace? def is_outdated(self, mtime): if mtime != self.mtime: return 1 def load(self, mtime, req): x = emb_html.TextProcessor() x.register_processor(emb_html.LoadTimeProcessor) x.register_processor(emb_html.ExecProcessor) x.register_processor(emb_html.EvalProcessor) data = open(self.filename).read() use_namespace = {EPY_FAKEREQ_NAME : req, '__file__' : self.filename } x.gobble(use_namespace, data, 0, len(data)) if not self.persist: self.namespace = {} else: del use_namespace[EPY_FAKEREQ_NAME] self.namespace = use_namespace self.processor = x self.mtime = mtime def run(self, req): use_namespace = self.namespace.copy() use_namespace.update({ EPY_FAKEREQ_NAME : req, '__file__' : self.filename }) self.processor.process(use_namespace, req.write) if not self.persist: self.namespace = {} else: del use_namespace[EPY_FAKEREQ_NAME] self.namespace = use_namespace class FakeRequest(RequestFile): """FakeRequest: Object for handling a fake request_req. This object provides the link between embedded Python and some of the mod_snake functions, such as setting headers, setting up CGI environment, etc.""" def __init__(self, req, epy): RequestFile.__init__(self, req) self.__req = req self.__rwrite = req.rwrite self.__epy = epy self.can_send_headers = 1 self.environ = {} def set_persist(self, value): self.__epy.persist = value def set_header(self, header, value): if string.upper(header) == 'CONTENT-TYPE': self.__req.content_type = value return self.__req.headers_out[header] = value def setup_cgi(self): """Setup CGI variables and files, so cgi.py can be used""" res = self.__req.setup_client_block(mod_snake.REQUEST_CHUNKED_ERROR) if res: raise "Failed to setup client block" self.__req.should_client_block() self.__req.add_common_vars() self.__req.add_cgi_vars() # Setup environ that CGI's can use based on subprocess environment, # though don't touch the real os.env for key, val in self.__req.subprocess_env.items(): self.environ[key] = val # Ensure that environ has a query_string so that cgi.py doesn't barf # when trying to look at argv[] if not self.environ.has_key('QUERY_STRING'): self.environ['QUERY_STRING'] = '' self.environ['GATEWAY_INTERFACE'] = mod_snake.get_version() def write(self, data): if self.can_send_headers: self.__req.send_http_header() self.can_send_headers = 0 RequestFile.write(self, data) class SnakeEPy: """SnakeEPy: Embedded Python processor""" def __init__(self, module): hooks = { "open_logs" : self.open_logs, "content_handler" : self.content_handler, "create_svr_config" : self.create_svr_config, "create_dir_config" : self.create_dir_config, } for hook in hooks.keys(): module.add_hook(hook, hooks[hook]) directives = { "SnakeEPyErrSend" :(mod_snake.OR_ALL, mod_snake.FLAG, self.cmd_SnakeEPyErrSend), "SnakeEPy" :(mod_snake.OR_ALL, mod_snake.FLAG, self.cmd_SnakeEPy), "SnakeEPyLogFile" :(mod_snake.RSRC_CONF,mod_snake.TAKE1, self.cmd_SnakeEPyLogFile), } module.add_directives(directives) def open_logs(self, per_svr, module): if not per_svr[PER_SVR_LOGFILE]: return mod_snake.OK logfile = mod_snake.ap_server_root_relative(per_svr[PER_SVR_LOGFILE]) per_svr[PER_SVR_LOGFD] = open(logfile, 'a') return mod_snake.OK def create_svr_config(self, server): return { PER_SVR_CACHE : {}, PER_SVR_LOGFD : None, PER_SVR_LOGFILE : None, PER_SVR_SERVER : server } def create_dir_config(self, path): return { PER_DIR_ERRSEND : 0, PER_DIR_ENABLED : 0, } def cmd_SnakeEPy(self, per_dir, per_svr, on_or_off): per_dir[PER_DIR_ENABLED] = on_or_off def cmd_SnakeEPyErrSend(self, per_dir, per_svr, on_or_off): per_dir[PER_DIR_ERRSEND] = on_or_off def cmd_SnakeEPyLogFile(self, per_dir, per_svr, filename): per_svr[PER_SVR_LOGFILE] = filename def log_error(self, per_svr, error_lines): logstr = '' for line in error_lines: logstr = logstr + ('%s: %s' % (time.ctime(time.time()), line)) if per_svr[PER_SVR_LOGFD]: per_svr[PER_SVR_LOGFD].write(logstr) else: mod_snake.ap_log_error(mod_snake.APLOG_CRIT, per_svr[PER_SVR_SERVER], logstr) def content_handler(self, per_dir, per_svr, r): if (r.header_only or not per_dir[PER_DIR_ENABLED] or r.content_type != EPY_MIMETYPE): return mod_snake.DECLINED file_cache = per_svr[PER_SVR_CACHE] filename = r.filename mtime = r.finfo[stat.ST_MTIME] if not file_cache.has_key(filename): epy = EPy_Loader(filename) file_cache[filename] = epy else: epy = file_cache[filename] saveout = savein = None try: r.content_type = 'text/html' fake_req = FakeRequest(r, epy) Global_Lock.acquire() start_dir = os.getcwd() chdir(os.path.dirname(filename)) saveout, savein = sys.stdout, sys.stdin sys.stdout, sys.stdin = fake_req, fake_req if epy.is_outdated(mtime): epy.load(mtime, fake_req) epy.run(fake_req) chdir(start_dir) Global_Lock.release() return mod_snake.OK except: chdir(start_dir) if saveout: sys.stdout, sys.stdin = saveout, savein Global_Lock.release() etype, value, tb = sys.exc_info() tback = traceback.format_exception(etype, value, tb, None) self.log_error(per_svr, tback) if not per_dir[PER_DIR_ERRSEND]: return mod_snake.HTTP_INTERNAL_SERVER_ERROR r.content_type = 'text/html' r.send_http_header() tot = string.join(tback,'') r.rwrite('

mod_snake_epy internal error: %s

' % r.filename) r.rwrite('
' + tot + '
') return mod_snake.OK return mod_snake.OK