| # -*- coding: iso-8859-1 -*- |
| """ |
| MoinMoin - Data associated with a single Request |
| |
| @copyright: 2001-2003 by JΓΌrgen Hermann <jh@web.de> |
| @copyright: 2003-2004 by Thomas Waldmann |
| @license: GNU GPL, see COPYING for details. |
| """ |
| |
| import os, time, sys |
| from MoinMoin import config, wikiutil |
| from MoinMoin.util import MoinMoinNoFooter |
| |
| ############################################################################# |
| ### Timing |
| ############################################################################# |
| |
| class Clock: |
| """ Helper class for code profiling |
| we do not use time.clock() as this does not work across threads |
| """ |
| |
| def __init__(self): |
| self.timings = {'total': time.time()} |
| |
| def start(self, timer): |
| self.timings[timer] = time.time() - self.timings.get(timer, 0) |
| |
| def stop(self, timer): |
| self.timings[timer] = time.time() - self.timings[timer] |
| |
| def value(self, timer): |
| return "%.3f" % (self.timings[timer],) |
| |
| def dump(self): |
| outlist = [] |
| for timing in self.timings.items(): |
| outlist.append("%s = %.3fs" % timing) |
| return outlist |
| |
| |
| ############################################################################# |
| ### Request Data |
| ############################################################################# |
| class RequestBase: |
| """ A collection for all data associated with ONE request. """ |
| |
| # Header set to force misbehaved proxies and browsers to keep their |
| # hands off a page |
| # Details: http://support.microsoft.com/support/kb/articles/Q234/0/67.ASP |
| nocache = [ |
| "Pragma: no-cache", |
| "Cache-Control: no-cache", |
| "Expires: -1", |
| ] |
| |
| def __init__(self, properties={}): |
| self.writestack = [] |
| self.clock = Clock() |
| # order is important here! |
| from MoinMoin import user |
| self.user = user.User(self) |
| self.dicts = self.initdicts() |
| |
| from MoinMoin import i18n |
| |
| if config.theme_force: |
| theme_name = config.theme_default |
| else: |
| theme_name = self.user.theme_name |
| try: |
| self.theme = wikiutil.importPlugin('theme', theme_name)(self) |
| except TypeError: |
| theme_name = config.theme_default |
| self.theme = wikiutil.importPlugin('theme', theme_name)(self) |
| |
| self.args = None |
| self.form = None |
| self.logger = None |
| self.pragma = {} |
| self.mode_getpagelinks = 0 |
| self.no_closing_html_code = 0 |
| |
| self.sent_headers = 0 |
| self.user_headers = [] |
| |
| self.__dict__.update(properties) |
| |
| self.i18n = i18n |
| self.lang = i18n.requestLanguage(self) |
| self.getText = lambda text, i18n=self.i18n, request=self, lang=self.lang: i18n.getText(text, request, lang) |
| |
| # XXX Removed call to i18n.adaptcharset() |
| |
| self.opened_logs = 0 # XXX for what do we need that??? |
| |
| self.reset() |
| |
| def _setup_vars_from_std_env(self, env): |
| """ Sets the common Request members by parsing a standard |
| HTTPD environment (as created as environment by most common |
| webservers. To be used by derived classes. |
| |
| @param env: the environment to use |
| """ |
| self.http_accept_language = env.get('HTTP_ACCEPT_LANGUAGE', 'en') |
| self.server_name = env.get('SERVER_NAME', 'localhost') |
| self.server_port = env.get('SERVER_PORT', '80') |
| self.http_host = env.get('HTTP_HOST','localhost') |
| self.http_referer = env.get('HTTP_REFERER', '') |
| self.saved_cookie = env.get('HTTP_COOKIE', '') |
| self.script_name = env.get('SCRIPT_NAME', '') |
| self.path_info = env.get('PATH_INFO', '') |
| self.query_string = env.get('QUERY_STRING', '') |
| self.request_method = env.get('REQUEST_METHOD', None) |
| self.remote_addr = env.get('REMOTE_ADDR', '') |
| self.http_user_agent = env.get('HTTP_USER_AGENT', '') |
| self.is_ssl = env.get('SSL_PROTOCOL', '') != '' \ |
| or env.get('SSL_PROTOCOL_VERSION', '') != '' \ |
| or env.get('HTTPS', 'off') == 'on' |
| |
| self.auth_username = None |
| if config.auth_http_enabled and env.get('AUTH_TYPE','') == 'Basic': |
| self.auth_username = env.get('REMOTE_USER','') |
| |
| ## f=open('/tmp/env.log','a') |
| ## f.write('---ENV\n') |
| ## f.write('script_name = %s\n'%(self.script_name)) |
| ## f.write('path_info = %s\n'%(self.path_info)) |
| ## f.write('server_name = %s\n'%(self.server_name)) |
| ## f.write('server_port = %s\n'%(self.server_port)) |
| ## f.write('http_host = %s\n'%(self.http_host)) |
| ## f.write('------\n') |
| ## f.write('%s\n'%(repr(env))) |
| ## f.write('------\n') |
| ## f.close() |
| |
| def reset(self): |
| """ Reset request state. |
| |
| Called after saving a page, before serving the updated |
| page. Solves some practical problems with request state |
| modified during saving. |
| |
| """ |
| # This is the content language and has nothing to do with |
| # The user interface language. The content language can change |
| # during the rendering of a page by lang macros |
| self.current_lang = config.default_lang |
| self._footer_fragments = {} |
| self._all_pages = None |
| |
| if hasattr(self, "_fmt_hd_counters"): |
| del self._fmt_hd_counters |
| |
| |
| def add2footer(self, key, htmlcode): |
| """ Add a named HTML fragment to the footer, after the default links |
| """ |
| self._footer_fragments[key] = htmlcode |
| |
| |
| def getPragma(self, key, defval=None): |
| """ Query a pragma value (#pragma processing instruction) |
| |
| Keys are not case-sensitive. |
| """ |
| return self.pragma.get(key.lower(), defval) |
| |
| |
| def setPragma(self, key, value): |
| """ Set a pragma value (#pragma processing instruction) |
| |
| Keys are not case-sensitive. |
| """ |
| self.pragma[key.lower()] = value |
| |
| |
| def getPageList(self): |
| """ A cached version of wikiutil.getPageList(). |
| Also, this list is always sorted. |
| """ |
| if self._all_pages is None: |
| self._all_pages = wikiutil.getPageList(config.text_dir) |
| self._all_pages.sort() |
| |
| return self._all_pages |
| |
| def redirect(self, file=None): |
| if file: # redirect output to "file" |
| self.writestack.append(self.write) |
| self.write = file.write |
| else: # restore saved output file |
| self.write = self.writestack.pop() |
| |
| def reset_output(self): |
| """ restore default output method |
| destroy output stack |
| (useful for error messages) |
| """ |
| if self.writestack: |
| self.write = self.writestack[0] |
| self.writestack = [] |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| raise "NotImplementedError" |
| |
| def read(self, n): |
| """ Read n bytes from input stream. |
| """ |
| raise "NotImplementedError" |
| |
| def flush(self): |
| """ Flush output stream. |
| """ |
| raise "NotImplementedError" |
| |
| def initdicts(self): |
| from MoinMoin import wikidicts |
| dicts = wikidicts.GroupDict() |
| dicts.scandicts() |
| return dicts |
| |
| def isForbidden(self): |
| """ check for web spiders and refuse anything except viewing """ |
| forbidden = 0 |
| if ((self.query_string != '' or self.request_method != 'GET') |
| and self.query_string != 'action=rss_rc'): |
| from MoinMoin.util import web |
| forbidden = web.isSpiderAgent(request=self) |
| |
| if not forbidden and config.hosts_deny: |
| ip = self.remote_addr |
| for host in config.hosts_deny: |
| if ip == host or host[-1] == '.' and ip.startswith(host): |
| forbidden = 1 |
| break |
| return forbidden |
| |
| |
| def setup_args(self, form=None): |
| return {} |
| |
| def _setup_args_from_cgi_form(self, form=None): |
| """ A method to create the args from a standart cgi.FieldStorage |
| to be used be derived classes. |
| |
| @keyword form: a cgi.FieldStorage list. default is to call |
| cgi.FieldStorage(). |
| """ |
| import types, cgi |
| |
| if form is None: |
| form = cgi.FieldStorage() |
| |
| args = {} |
| for key in form.keys(): |
| values = form[key] |
| if not isinstance(values, types.ListType): |
| values = [values] |
| fixedResult = [] |
| for i in values: |
| if isinstance(i, cgi.MiniFieldStorage): |
| fixedResult.append(i.value) |
| elif isinstance(i, cgi.FieldStorage): |
| fixedResult.append(i.value) |
| # multiple uploads to same form field are stupid! |
| if i.filename: |
| args[key+'__filename__']=i.filename |
| |
| args[key] = fixedResult |
| return args |
| |
| def recodePageName(self, pagename): |
| # check for non-URI characters and then handle them according to |
| # http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1 |
| if pagename: |
| try: |
| dummy = unicode(pagename, 'ascii') |
| except UnicodeError: |
| # we have something else than plain ASCII, try |
| # converting from UTF-8 to local charset, or just give |
| # up and use URI value literally and see what happens |
| pagename = self.i18n.recode(pagename, 'utf-8', config.charset) or pagename |
| return pagename |
| # XXX UNICODE - use unicode for pagenames internally? |
| |
| def getBaseURL(self): |
| """ Return a fully qualified URL to this script. """ |
| return self.getQualifiedURL(self.getScriptname()) |
| |
| |
| def getQualifiedURL(self, uri=None): |
| """ Return a full URL starting with schema, servername and port. |
| |
| *uri* -- append this server-rooted uri (must start with a slash) |
| """ |
| if uri and uri[:4] == "http": |
| return uri |
| |
| schema, stdport = (('http', '80'), ('https', '443'))[self.is_ssl] |
| host = self.http_host |
| if not host: |
| host = self.server_name |
| port = self.server_port |
| if port != stdport: |
| host = "%s:%s" % (host, port) |
| |
| result = "%s://%s" % (schema, host) |
| if uri: |
| result = result + uri |
| |
| return result |
| |
| |
| def getUserAgent(self): |
| """ Get the user agent. """ |
| return self.http_user_agent |
| |
| |
| def run(self): |
| _ = self.getText |
| self.clock.start('run') |
| self.open_logs() |
| if self.isForbidden(): |
| self.http_headers([ |
| 'Status: 403 FORBIDDEN', |
| 'Content-Type: text/plain' |
| ]) |
| self.write('You are not allowed to access this!\n') |
| return self.finish() |
| |
| # Imports |
| from MoinMoin.Page import Page |
| |
| if self.query_string == 'action=xmlrpc': |
| from MoinMoin.wikirpc import xmlrpc |
| xmlrpc(self) |
| return self.finish() |
| |
| if self.query_string == 'action=xmlrpc2': |
| from MoinMoin.wikirpc import xmlrpc2 |
| xmlrpc2(self) |
| return self.finish() |
| |
| # parse request data |
| try: |
| self.args = self.setup_args() |
| self.form = self.args |
| path_info = self.getPathinfo() |
| |
| #from pprint import pformat |
| #sys.stderr.write(pformat(self.__dict__)) |
| |
| action = self.form.get('action',[None])[0] |
| |
| pagename = None |
| if len(path_info) and path_info[0] == '/': |
| pagename = wikiutil.unquoteWikiname(path_info[1:]) |
| except: # catch and print any exception |
| self.reset_output() |
| self.http_headers() |
| self.print_exception() |
| return self.finish() |
| |
| try: |
| # possibly jump to page where user left off |
| if not pagename and not action and self.user.remember_last_visit: |
| pagetrail = self.user.getTrail() |
| if pagetrail: |
| self.http_redirect(Page(pagetrail[-1]).url(self)) |
| return self.finish() |
| |
| # handle request |
| from MoinMoin import wikiaction |
| |
| pagename = self.recodePageName(pagename) |
| |
| if self.form.has_key('filepath') and self.form.has_key('noredirect'): |
| # looks like user wants to save a drawing |
| from MoinMoin.action.AttachFile import execute |
| execute(pagename, self) |
| raise MoinMoinNoFooter |
| |
| if action: |
| handler = wikiaction.getHandler(action) |
| if handler: |
| handler(pagename or |
| wikiutil.getSysPage(self, config.page_front_page).page_name, self) |
| else: |
| self.http_headers() |
| self.write("<p>" + _("Unknown action")) |
| else: |
| if self.form.has_key('goto'): |
| query = self.form['goto'][0].strip() |
| elif pagename: |
| query = pagename |
| else: |
| query = wikiutil.unquoteWikiname(self.query_string) or \ |
| wikiutil.getSysPage(self, config.page_front_page).page_name |
| |
| if config.allow_extended_names: |
| Page(query).send_page(self, count_hit=1) |
| else: |
| from MoinMoin.parser.wiki import Parser |
| import re |
| word_match = re.match(Parser.word_rule, query) |
| if word_match: |
| word = word_match.group(0) |
| Page(word).send_page(self, count_hit=1) |
| else: |
| self.http_headers() |
| self.write('<p>' + _("Can't work out query") + ' "<pre>' + query + '</pre>"') |
| |
| # generate page footer |
| # (actions that do not want this footer use raise util.MoinMoinNoFooter to break out |
| # of the default execution path, see the "except MoinMoinNoFooter" below) |
| |
| self.clock.stop('run') |
| self.clock.stop('total') |
| |
| if not self.no_closing_html_code: |
| if (config.show_timings and |
| self.form.get('action', [None])[0] != 'print'): |
| self.write('<ul id="timings">\n') |
| for t in self.clock.dump(): |
| self.write('<li>%s</li>\n' % t) |
| self.write('</ul>\n') |
| |
| if 0: # temporarily disabled - do we need that? |
| import socket |
| from MoinMoin import version |
| self.write('<!-- MoinMoin %s on %s served this page in %s secs -->' % ( |
| version.revision, socket.gethostname(), self.clock.value('total')) + |
| '</body></html>') |
| else: |
| self.write('</body>\n</html>\n\n') |
| |
| except MoinMoinNoFooter: |
| pass |
| |
| except: # catch and print any exception |
| saved_exc = sys.exc_info() |
| self.reset_output() |
| self.http_headers() |
| self.write("\n<!-- ERROR REPORT FOLLOWS -->\n") |
| try: |
| from MoinMoin.support import cgitb |
| except: |
| # no cgitb, for whatever reason |
| self.print_exception(*saved_exc) |
| else: |
| try: |
| cgitb.Hook(file=self).handle(saved_exc) |
| # was: cgitb.handler() |
| except: |
| self.print_exception(*saved_exc) |
| self.write("\n\n<hr>\n") |
| self.write("<p><strong>Additionally, cgitb raised this exception:</strong></p>\n") |
| self.print_exception() |
| del saved_exc |
| |
| return self.finish() |
| |
| |
| def http_redirect(self, url): |
| """ Redirect to a fully qualified, or server-rooted URL """ |
| if url.find("://") == -1: |
| url = self.getQualifiedURL(url) |
| |
| self.http_headers(["Status: 302", "Location: %s" % url]) |
| |
| |
| def print_exception(self, type=None, value=None, tb=None, limit=None): |
| if type is None: |
| type, value, tb = sys.exc_info() |
| import traceback |
| self.write("<h2>request.print_exception handler</h2>\n") |
| self.write("<h3>Traceback (most recent call last):</h3>\n") |
| list = traceback.format_tb(tb, limit) + \ |
| traceback.format_exception_only(type, value) |
| self.write("<pre>%s<strong>%s</strong></pre>\n" % ( |
| wikiutil.escape("".join(list[:-1])), |
| wikiutil.escape(list[-1]),)) |
| del tb |
| |
| |
| def open_logs(self): |
| pass |
| |
| # CGI --------------------------------------------------------------- |
| |
| class RequestCGI(RequestBase): |
| """ specialized on CGI requests """ |
| |
| def __init__(self, properties={}): |
| self._setup_vars_from_std_env(os.environ) |
| #sys.stderr.write("----\n") |
| #for key in os.environ.keys(): |
| # sys.stderr.write(" %s = '%s'\n" % (key, os.environ[key])) |
| RequestBase.__init__(self, properties) |
| |
| # force input/output to binary |
| if sys.platform == "win32": |
| import msvcrt |
| msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) |
| msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) |
| |
| def open_logs(self): |
| # create CGI log file, and one for catching stderr output |
| import cgi |
| if not self.opened_logs: |
| cgi.logfile = os.path.join(config.data_dir, 'cgi.log') |
| sys.stderr = open(os.path.join(config.data_dir, 'error.log'), 'at') |
| self.opened_logs = 1 |
| |
| def setup_args(self, form=None): |
| return self._setup_args_from_cgi_form(form) |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| if n is None: |
| return sys.stdin.read() |
| else: |
| return sys.stdin.read(n) |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| for piece in data: |
| sys.stdout.write(piece) |
| |
| def flush(self): |
| sys.stdout.flush() |
| |
| def finish(self): |
| # flush the output, ignore errors caused by the user closing the socket |
| try: |
| sys.stdout.flush() |
| except IOError, ex: |
| import errno |
| if ex.errno != errno.EPIPE: raise |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ("/path/to/my.cgi"). """ |
| name = self.script_name |
| if name == '/': |
| return '' |
| return name |
| |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| pathinfo = self.path_info |
| |
| # Fix for bug in IIS/4.0 |
| if os.name == 'nt': |
| scriptname = self.getScriptname() |
| if pathinfo.startswith(scriptname): |
| pathinfo = pathinfo[len(scriptname):] |
| |
| return pathinfo |
| |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| self.user_headers.append(header) |
| |
| |
| def http_headers(self, more_headers=[]): |
| if self.sent_headers: |
| #self.write("Headers already sent!!!\n") |
| return |
| self.sent_headers = 1 |
| have_ct = 0 |
| |
| # send http headers |
| for header in more_headers + self.user_headers: |
| if header.lower().startswith("content-type:"): |
| # don't send content-type multiple times! |
| if have_ct: continue |
| have_ct = 1 |
| self.write("%s\r\n" % header) |
| |
| if not have_ct: |
| self.write("Content-type: text/html;charset=%s\r\n" % config.charset) |
| |
| self.write('\r\n') |
| |
| #from pprint import pformat |
| #sys.stderr.write(pformat(more_headers)) |
| #sys.stderr.write(pformat(self.user_headers)) |
| |
| |
| # Twisted ----------------------------------------------------------- |
| |
| class RequestTwisted(RequestBase): |
| """ specialized on Twisted requests """ |
| |
| def __init__(self, twistedRequest, pagename, reactor, properties={}): |
| self.twistd = twistedRequest |
| self.http_accept_language = self.twistd.getHeader('Accept-Language') |
| self.reactor = reactor |
| self.saved_cookie = self.twistd.getHeader('Cookie') |
| self.server_protocol = self.twistd.clientproto |
| self.server_name = self.twistd.getRequestHostname().split(':')[0] |
| self.server_port = str(self.twistd.getHost()[2]) |
| self.is_ssl = self.twistd.isSecure() |
| if self.server_port != ('80', '443')[self.is_ssl]: |
| self.http_host = self.server_name + ':' + self.server_port |
| else: |
| self.http_host = self.server_name |
| self.script_name = "/" + '/'.join(self.twistd.prepath[:-1]) # "" XXX |
| self.path_info = "/" + pagename |
| if self.twistd.postpath: |
| self.path_info += '/' + '/'.join(self.twistd.postpath) |
| self.request_method = self.twistd.method |
| self.remote_host = self.twistd.getClient() |
| self.remote_addr = self.twistd.getClientIP() |
| self.http_user_agent = self.twistd.getHeader('User-Agent') |
| self.request_uri = self.twistd.uri |
| |
| qindex = self.request_uri.find('?') |
| if qindex != -1: |
| self.query_string = self.request_uri[qindex+1:] |
| else: |
| self.query_string = '' |
| self.outputlist = [] |
| self.auth_username = None # TODO, see: self.twistd.user / .password (http auth) |
| RequestBase.__init__(self, properties) |
| #print "request.RequestTwisted.__init__: received_headers=\n" + str(self.twistd.received_headers) |
| |
| def setup_args(self, form=None): |
| return self.twistd.args |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| # XXX why is that wrong?: |
| #rd = self.reactor.callFromThread(self.twistd.read) |
| |
| # XXX do we need self.reactor.callFromThread with that? |
| # XXX if yes, why doesnt it work? |
| self.twistd.content.seek(0, 0) |
| if n is None: |
| rd = self.twistd.content.read() |
| else: |
| rd = self.twistd.content.read(n) |
| #print "request.RequestTwisted.read: data=\n" + str(rd) |
| return rd |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| wd = ''.join(data) |
| # XXX UNICODE - encode to config.charset |
| #wd = u''.join(data).encode(config.charset) |
| #print "request.RequestTwisted.write: data=\n" + wd |
| self.reactor.callFromThread(self.twistd.write, wd) |
| |
| def flush(self): |
| pass # XXX is there a flush in twisted? |
| |
| def finish(self): |
| #print "request.RequestTwisted.finish" |
| self.reactor.callFromThread(self.twistd.finish) |
| |
| def open_logs(self): |
| return |
| # create log file for catching stderr output |
| if not self.opened_logs: |
| sys.stderr = open(os.path.join(config.data_dir, 'error.log'), 'at') |
| self.opened_logs = 1 |
| |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ("/path/to/my.cgi"). """ |
| scriptname = self.script_name |
| if scriptname == '/': |
| scriptname = '' |
| return scriptname |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| return self.path_info |
| |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| self.user_headers.append(header) |
| |
| def __setHttpHeader(self, header): |
| key, value = header.split(':',1) |
| value = value.lstrip() |
| self.twistd.setHeader(key, value) |
| #print "request.RequestTwisted.setHttpHeader: %s" % header |
| |
| def http_headers(self, more_headers=[]): |
| if self.sent_headers: |
| #self.write("Headers already sent!!!\n") |
| return |
| self.sent_headers = 1 |
| have_ct = 0 |
| |
| # set http headers |
| for header in more_headers + self.user_headers: |
| if header.lower().startswith("content-type:"): |
| # don't send content-type multiple times! |
| if have_ct: continue |
| have_ct = 1 |
| self.__setHttpHeader(header) |
| |
| if not have_ct: |
| self.__setHttpHeader("Content-type: text/html;charset=%s" % config.charset) |
| |
| |
| def http_redirect(self, url): |
| """ Redirect to a fully qualified, or server-rooted URL """ |
| if url.count("://") == 0: |
| # no https method?? |
| url = "http://%s:%s%s" % (self.server_name, self.server_port, url) |
| |
| self.twistd.redirect(url) |
| # calling finish here will send the rest of the data to the next |
| # request. leave the finish call to run() |
| #self.twistd.finish() |
| raise MoinMoinNoFooter |
| |
| |
| # CLI ------------------------------------------ |
| class RequestCLI(RequestBase): |
| """ specialized on commandline interface requests """ |
| |
| def __init__(self, pagename='', properties={}): |
| self.http_accept_language = '' |
| self.saved_cookie = '' |
| self.path_info = '/' + pagename |
| self.query_string = '' |
| self.remote_addr = '127.0.0.127' |
| self.is_ssl = 0 |
| self.auth_username = None |
| RequestBase.__init__(self, properties) |
| self.http_user_agent = '' |
| self.outputlist = [] |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| if n is None: |
| return sys.stdin.read() |
| else: |
| return sys.stdin.read(n) |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| for piece in data: |
| sys.stdout.write(piece) |
| |
| def flush(self): |
| sys.stdout.flush() |
| |
| def finish(self): |
| # flush the output, ignore errors caused by the user closing the socket |
| try: |
| sys.stdout.flush() |
| except IOError, ex: |
| import errno |
| if ex.errno != errno.EPIPE: raise |
| |
| def isForbidden(self): |
| """ check for web spiders and refuse anything except viewing """ |
| return 0 |
| |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ("/path/to/my.cgi"). """ |
| return '.' |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| return self.path_info |
| |
| |
| def getQualifiedURL(self, uri = None): |
| """ Return a full URL starting with schema, servername and port. |
| |
| *uri* -- append this server-rooted uri (must start with a slash) |
| """ |
| return uri |
| |
| |
| def getBaseURL(self): |
| """ Return a fully qualified URL to this script. """ |
| return self.getQualifiedURL(self.getScriptname()) |
| |
| |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| pass |
| |
| def http_headers(self, more_headers=[]): |
| pass |
| |
| def http_redirect(self, url): |
| """ Redirect to a fully qualified, or server-rooted URL """ |
| raise Exception("Redirect not supported for command line tools!") |
| |
| |
| # StandAlone Server ------------------------------------------------- |
| class RequestStandAlone(RequestBase): |
| """ |
| specialized on StandAlone Server (httpdmain.py) requests |
| """ |
| |
| def __init__(self, sa, properties={}): |
| """ |
| @param sa: stand alone server object |
| @param properties: ... |
| """ |
| import urllib |
| self.wfile = sa.wfile |
| self.rfile = sa.rfile |
| self.headers = sa.headers |
| self.is_ssl = 0 |
| rest = sa.path |
| i = rest.rfind('?') |
| if i >= 0: |
| rest, query = rest[:i], rest[i+1:] |
| else: |
| query = '' |
| uqrest = urllib.unquote(rest) |
| |
| #HTTP headers |
| self.env = {} |
| for hline in sa.headers.headers: |
| key = sa.headers.isheader(hline) |
| if key: |
| hdr = sa.headers.getheader(key) |
| self.env[key] = hdr |
| |
| #accept = [] |
| #for line in sa.headers.getallmatchingheaders('accept'): |
| # if line[:1] in string.whitespace: |
| # accept.append(line.strip()) |
| # else: |
| # accept = accept + line[7:].split(',') |
| # |
| #env['HTTP_ACCEPT'] = ','.join(accept) |
| |
| co = filter(None, sa.headers.getheaders('cookie')) |
| |
| self.http_accept_language = sa.headers.getheader('Accept-Language') |
| self.server_name = sa.server.server_name |
| self.server_port = str(sa.server.server_port) |
| self.http_host = sa.headers.getheader('host') |
| self.http_referer = sa.headers.getheader('referer') |
| self.saved_cookie = ', '.join(co) or '' |
| self.script_name = '' |
| self.path_info = uqrest |
| self.query_string = query or '' |
| self.request_method = sa.command |
| self.remote_addr = sa.client_address[0] |
| self.http_user_agent = sa.headers.getheader('user-agent') or '' |
| # from standalone script: |
| # XXX AUTH_TYPE |
| # XXX REMOTE_USER |
| # XXX REMOTE_IDENT |
| self.auth_username = None |
| #env['PATH_TRANSLATED'] = uqrest #self.translate_path(uqrest) |
| #host = self.address_string() |
| #if host != self.client_address[0]: |
| # env['REMOTE_HOST'] = host |
| # env['SERVER_PROTOCOL'] = self.protocol_version |
| RequestBase.__init__(self, properties) |
| |
| def open_logs(self): |
| # create error log file for catching stderr output |
| if not self.opened_logs: |
| sys.stderr = open(os.path.join(config.data_dir, 'error.log'), 'at') |
| self.opened_logs = 1 |
| |
| def setup_args(self, form=None): |
| self.env['REQUEST_METHOD'] = self.request_method |
| self.env['QUERY_STRING'] = self.query_string |
| ct = self.headers.getheader('content-type') |
| if ct: |
| self.env['CONTENT_TYPE'] = ct |
| cl = self.headers.getheader('content-length') |
| if cl: |
| self.env['CONTENT_LENGTH'] = cl |
| |
| import cgi |
| #print "env = ", self.env |
| #form = cgi.FieldStorage(self, headers=self.env, environ=self.env) |
| if form is None: |
| form = cgi.FieldStorage(self, environ=self.env) |
| return self._setup_args_from_cgi_form(form) |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| if n is None: |
| return self.rfile.read() |
| else: |
| return self.rfile.read(n) |
| |
| def readline (self): |
| L = "" |
| while 1: |
| c = self.read(1) |
| L += c |
| if c == '\n': |
| break |
| return L |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| for piece in data: |
| self.wfile.write(piece) |
| |
| def flush(self): |
| self.wfile.flush() |
| |
| def finish(self): |
| # flush the output, ignore errors caused by the user closing the socket |
| try: |
| self.wfile.flush() |
| except IOError, ex: |
| import errno |
| if ex.errno != errno.EPIPE: raise |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ("/path/to/my.cgi"). """ |
| name = self.script_name |
| if name == '/': |
| return '' |
| return name |
| |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| return self.path_info |
| |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| self.user_headers.append(header) |
| |
| def http_headers(self, more_headers=[]): |
| if self.sent_headers: |
| #self.write("Headers already sent!!!\n") |
| return |
| self.sent_headers = 1 |
| have_ct = 0 |
| |
| # send http headers |
| for header in more_headers + self.user_headers: |
| if header.lower().startswith("content-type:"): |
| # don't send content-type multiple times! |
| if have_ct: continue |
| have_ct = 1 |
| self.write("%s\r\n" % header) |
| |
| if not have_ct: |
| self.write("Content-type: text/html;charset=%s\r\n" % config.charset) |
| |
| self.write('\r\n') |
| |
| #from pprint import pformat |
| #sys.stderr.write(pformat(more_headers)) |
| #sys.stderr.write(pformat(self.user_headers)) |
| |
| |
| # mod_python/Apache ------------------------------------------------- |
| class RequestModPy(RequestBase): |
| """ specialized on mod_python requests """ |
| |
| def __init__(self, req): |
| """ Saves mod_pythons request and sets basic variables using |
| the req.subprocess_env, cause this provides a standard |
| way to access the values we need here. |
| |
| @param req: the mod_python request instance |
| """ |
| req.add_common_vars() |
| self.mpyreq = req |
| # some mod_python 2.7.X has no get method for table objects, |
| # so we make a real dict out of it first. |
| if not hasattr(req.subprocess_env,'get'): |
| env=dict(req.subprocess_env) |
| else: |
| env=req.subprocess_env |
| self._setup_vars_from_std_env(env) |
| # flags if headers sent out contained content-type or status |
| self._have_ct = 0 |
| self._have_status = 0 |
| RequestBase.__init__(self) |
| |
| |
| def setup_args(self, form=None): |
| """ Sets up args by using mod_python.util.FieldStorage, which |
| is different to cgi.FieldStorage. So we need a seperate |
| method for this. |
| """ |
| import types |
| from mod_python import util |
| if form is None: |
| form = util.FieldStorage(self.mpyreq) |
| |
| args = {} |
| for key in form.keys(): |
| values = form[key] |
| if not isinstance(values, types.ListType): |
| values = [values] |
| fixedResult = [] |
| for i in values: |
| ## mod_python 2.7 might return strings instead |
| ## of Field objects |
| if hasattr(i,'value'): |
| fixedResult.append(i.value) |
| else: |
| fixedResult.append(i) |
| ## if object has a filename attribute, remember it |
| ## with a name hack |
| if hasattr(i,'filename') and i.filename: |
| args[key+'__filename__']=i.filename |
| args[key] = fixedResult |
| return args |
| |
| def run(self, req): |
| """ mod_python calls this with its request object. We don't |
| need it cause its already passed to __init__. So ignore |
| it and just return RequestBase.run. |
| |
| @param req: the mod_python request instance |
| """ |
| return RequestBase.run(self) |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| if n is None: |
| return self.mpyreq.read() |
| else: |
| return self.mpyreq.read(n) |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| for piece in data: |
| self.mpyreq.write(piece) |
| |
| def flush(self): |
| """ We can't flush it, so do nothing. |
| """ |
| pass |
| |
| def finish(self): |
| """ Just return apache.OK. Status is set in req.status. |
| """ |
| # is it possible that we need to return somethig else here? |
| from mod_python import apache |
| return apache.OK |
| |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ('/path/to/my.cgi'). """ |
| name = self.script_name |
| if name == '/': |
| return '' |
| return name |
| |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| return self.path_info |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| """ Filters out content-type and status to set them directly |
| in the mod_python request. Rest is put into the headers_out |
| member of the mod_python request. |
| |
| @param header: string, containing valid HTTP header. |
| """ |
| key, value = header.split(':',1) |
| value = value.lstrip() |
| if key.lower() == 'content-type': |
| # save content-type for http_headers |
| if not self._have_ct: |
| # we only use the first content-type! |
| self.mpyreq.content_type = value |
| self._have_ct = 1 |
| elif key.lower() == 'status': |
| # save status for finish |
| try: |
| self.mpyreq.status = int(value.split(' ',1)[0]) |
| except: |
| pass |
| else: |
| self._have_status = 1 |
| else: |
| # this is a header we sent out |
| self.mpyreq.headers_out[key]=value |
| |
| |
| def http_headers(self, more_headers=[]): |
| """ Sends out headers and possibly sets default content-type |
| and status. |
| |
| @keyword more_headers: list of strings, defaults to [] |
| """ |
| for header in more_headers: |
| self.setHttpHeader(header) |
| # if we don't had an content-type header, set text/html |
| if self._have_ct == 0: |
| self.mpyreq.content_type = "text/html;charset=%s" % config.charset |
| # if we don't had a status header, set 200 |
| if self._have_status == 0: |
| self.mpyreq.status = 200 |
| # this is for mod_python 2.7.X, for 3.X it's a NOP |
| self.mpyreq.send_http_header() |
| |
| # FastCGI ----------------------------------------------------------- |
| |
| class RequestFastCGI(RequestBase): |
| """ specialized on FastCGI requests """ |
| |
| def __init__(self, fcgRequest, env, form, properties={}): |
| """ Initializes variables from FastCGI environment and saves |
| FastCGI request and form for further use. |
| |
| @param fcgRequest: the FastCGI request instance. |
| @param env: environment passed by FastCGI. |
| @param form: FieldStorage passed by FastCGI. |
| """ |
| self.fcgreq = fcgRequest |
| self.fcgenv = env |
| self.fcgform = form |
| self._setup_vars_from_std_env(env) |
| RequestBase.__init__(self, properties) |
| |
| def setup_args(self, form=None): |
| """ Use the FastCGI form to setup arguments. """ |
| if form is None: |
| form = self.fcgform |
| return self._setup_args_from_cgi_form(form) |
| |
| def read(self, n=None): |
| """ Read from input stream. |
| """ |
| if n is None: |
| return self.fcgreq.stdin.read() |
| else: |
| return self.fcgreq.stdin.read(n) |
| |
| def write(self, *data): |
| """ Write to output stream. |
| """ |
| self.fcgreq.out.write("".join(data)) |
| |
| def flush(self): |
| """ Flush output stream. |
| """ |
| self.fcgreq.flush_out() |
| |
| def finish(self): |
| """ Call finish method of FastCGI request to finish handling |
| of this request. |
| """ |
| self.fcgreq.finish() |
| |
| |
| ############################################################################# |
| ### Accessors |
| ############################################################################# |
| |
| def getScriptname(self): |
| """ Return the scriptname part of the URL ('/path/to/my.cgi'). """ |
| name = self.script_name |
| if name == '/': |
| return '' |
| return name |
| |
| |
| def getPathinfo(self): |
| """ Return the remaining part of the URL. """ |
| pathinfo = self.path_info |
| |
| # Fix for bug in IIS/4.0 |
| if os.name == 'nt': |
| scriptname = self.getScriptname() |
| if pathinfo.startswith(scriptname): |
| pathinfo = pathinfo[len(scriptname):] |
| |
| return pathinfo |
| |
| |
| ############################################################################# |
| ### Headers |
| ############################################################################# |
| |
| def setHttpHeader(self, header): |
| """ Save header for later send. """ |
| self.user_headers.append(header) |
| |
| |
| def http_headers(self, more_headers=[]): |
| """ Send out HTTP headers. Possibly set a default content-type. |
| """ |
| if self.sent_headers: |
| #self.write("Headers already sent!!!\n") |
| return |
| self.sent_headers = 1 |
| have_ct = 0 |
| |
| # send http headers |
| for header in more_headers + self.user_headers: |
| if header.lower().startswith("content-type:"): |
| # don't send content-type multiple times! |
| if have_ct: continue |
| have_ct = 1 |
| self.write("%s\r\n" % header) |
| |
| if not have_ct: |
| self.write("Content-type: text/html;charset=%s\r\n" % config.charset) |
| |
| self.write('\r\n') |
| |
| #from pprint import pformat |
| #sys.stderr.write(pformat(more_headers)) |
| #sys.stderr.write(pformat(self.user_headers)) |
| |
| package test0577; |
| |
| public class X { |
| |
| public static void main(String[] args) { |
| |
| } |
| } |
| |