# # Author: Thanos Vassilakis thanos@0x01.com # # Copyright (c) thanos vassilakis 2000,2001, 2002 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Lesser General Public License for more details. # # See terms of license at gnu.org. # # $Id: request.py,v 1.19 2004/07/12 04:00:22 thanos Exp $ # __version__="$Revision: 1.19 $" from tempfile import mktemp from types import StringType from Cookie import SimpleCookie as CookiePot import time, sys from session import Session, CookieFileImpl from resultcodes import HTTP_MOVED_PERMANENTLY, HTTP_MOVED_TEMPORARILY class SERVER_RETURN(Exception):pass from urlparse import urlparse, urlunparse try: from urllib import urlencode except: pass try: from url import Url except: Url = None class RequestIO: """ RequestIO is a proxy ouput stream. Everything writen to it will be buffered until the headers are sent or complete. Then the contents is flused to the real out stream. """ def read(self, n = -1): return "" def readline(self, length = None): return "" def readlines(self): return [] def writelines(self, list): self.write(''.join(list)) def isatty(self): return 0 def seek(self, pos, mode = 0): pass def __init__(self): self.pos = 0 self.headers_sent = 0 self.out = "" self.header_out = None def write(self, s): if not s: return if not self.headers_sent: self.out += s else: self.getOutStream().write(s) self.pos += len(s) def flush(self): self.getOutStream().write(self.out) self.out="" def getHeadersOut(self): if self.header_out is None: self.header_out = self.impl.getHeadersOut() return self.header_out def tell(self): return self.pos def close(self): #if not self.headers_sent: self.send_http_header() self.flush() def headersSent(self): self.headers_sent=1 def __del__(self): self.close() def setHeaderOut(self, key, value): """ set a header entry. If this entry already exits overwrite it. """ if type(value) != StringType: value = str(value) self.getHeadersOut()[key] = value def addHeaderOut(self, key, value): """ add a header entry. """ self.getHeadersOut().add(key, value) def removeHeaderOut(self, key): del self.getHeadersOut()[key] class ServiceRequest(RequestIO): """ Bridge class for a http service Request """ inputs = None inputSeq = None inputDict = None cookies = None environ = None _session = None def __init__(self, implClass, req=None): RequestIO.__init__(self) self.impl = implClass() self.impl.setup(self, req) self.response="" self.status = self.getStatusCode(200) def __call__(self): return self def pso(self): " returns proxy to pso object " return self def getOutStream(self): return self.impl.getOutStream() def getInStream(self): return self.impl.getInStream() def setup(self, serviceHandler, reqHandler): pass def close(self): RequestIO.close(self) if self._session is not None: self._session.save(self) # # basics # def getStatusCode(self, code): self.impl.getStatusCode(code) # # environ # def getEnviron(self, key=None, default = None): """ if key is not given returns a dict of the server environment. Otherwise returns the entry for key. If no entry is found returns None or default if given. """ if self.environ is None: self.environ = self.impl.getEnviron(self) if key is None: return self.environ return self.environ.get(key, default) def setEnviron(self, key, value): self.getEnviron()[key] = value # # # # # # Session handling # def getSession(self, sessionImplClass = CookieFileImpl, **parameters): """ returns the current session. The session implentation class may be passed to sessionImplClass, which if None defaults to CookieFileImpl. The method can be passed keyword arguments which will be treated as HTTP directives """ if sessionImplClass: for k,v in parameters.items(): self.setEnviron(k, v) self._session = Session(self, sessionImplClass()) if self._session.isNew(): self.setSession(self._session) return self._session def setSession(self, session): self.impl.setSession(self, session) session = {} #property(getSession,setSession) # # Cookie handling # def getCookieKey(self): return self.impl.getCookieKey() def getCookies(self): """ req.getCookies()-> dict returns a dictionary of cookies. """ if self.cookies is None: cookies = self.getEnviron(self.impl.getCookieKey(), '') if cookies: self.cookies = CookiePot() self.cookies.load(cookies) else: self.cookies = CookiePot() return self.cookies def getCookie(self, key, default=None): """ returns the cookie requested by key otherwise returns default, if default is not given returns None. """ return self.getCookies().get(key, default) def setCookie(self, key, value, **attrs): """ sets cookie, key, to value. Also will set any attributes given. e.g. request.setCookie("login",name, comment="user id") cookiefmt = "%s=%s;" cookie = cookiefmt % (key, value) for k,v in attrs.items(): cookie += cookiefmt % (k, v) """ cookies = self.getCookies() cookies[key] = value for k,v in attrs.items(): cookies[key][k] = v self.addHeaderOut('set-cookie', cookies[key].output(header='')) def send_http_header(self, content_type='text/html'): """ send to stdout the content headers. Each on a seperate line. contenttype has not been set it will default to 'text/html'. Then send an extra newline """ #print if not self.getHeadersOut().has_key('content-type'): if not self.getHeadersOut().has_key('location'): if not self.getHeadersOut().has_key('status'): self.getHeadersOut()['content-type'] = content_type self.impl.syncHeadersOut(self.getHeadersOut()) self.headersSent() self.impl.send_http_header(self) self.flush() # # Control # def redirect(self, url, permanent=0): """ force an imediate redirect to given url. """ self.impl.redirect(self, url, permanent) def setStatus(self, status): """ set the HTTP return status. This normally defaults to 200. """ self.setHeaderOut('status', status) def sendStatus(self, status): """ set the HTTP return status. This normally defaults to 200. """ self.setStatus(status) self.impl.sendStatus(status) # # Input # def hasInput(self, key): """req.hasInput(key) -> 1 | 0 tests if a field in a form was filled. """ return self.getInputs().has_key(key) def hasInputs(self, *keys): return [key for key in keys if key in self.getInputs().keys()] def getInputs(self, key=None): """req.getInput(key) -> FiledStorage| List of Fields if key is given will return a list of fields values for that key. if there are no values an empty list is returned. if no key is given returns the cgi.FieldStorage object. """ if not self.inputs: self.inputs = self.impl.getInputs(self) if not key: return self.inputs if hasattr(self.inputs, 'getlist'): return self.inputs.getlist(key) else: if self.inputs.has_key(key): value = self.inputs.getvalue(key) if value and type(value) is type([]): return value else: return [value] return [] def getInputSeq(self): if not self.inputSeq: self.inputSeq=[] for key in self.getInputs().keys(): values = self.getInputs(key) for value in values: self.inputSeq.append((key, value)) return self.inputSeq def getInputDict(self): if not self.inputDict: self.inputDict={} for key in self.getInputs().keys(): self.inputDict[key] = self.getInputs(key) return self.inputDict def getInput(self, key, default=None, index=None): """req.getInput(key) -> String | default returns the given form field value as a String. If there are multiple values under the same key, it will return the first in the list, unless index is given. If no value is found will return "", unless default is given. """ if not index: index =0 try: return self.getInputs(key)[index] except: # import traceback # traceback.print_exc() return default def getFile(self, key, default=None): """req.getFile(fieldname)-> Field returns an uploaded file. The filed has the usual cgi.Field members plus filename - the given file name file - the actual file uploaded tempname - is None until keep() is called. the methods: keep() - The file object is a temporay file that will be deleted when the cgi terminates. keep asigns the file a a temp file name. save(name) - This method can be used to save the tempfile under the given name. """ if self.hasInput(key): file = self.getInputs()[key] if file.filename: file.__class__.tempname= None file.__class__.keep= keep file.__class__.save= save return file return default # # Utilities # _url = None def getUrl(self): if self._url is None: self._url= Url("http://%(HTTP_HOST)s%(REQUEST_URI)s" % self.getEnviron()) return self._url #url = property(getUrl) def uriParts(self): if not Url: raise 'under mod_python urlparse has problems' url = "http://%(HTTP_HOST)s%(REQUEST_URI)s" % self.getEnviron() parts = list(urlparse(url)) info_path = self.getEnviron('PATH_INFO','') path=parts[2] if info_path: path,dummy = path.split(info_path) indx = path.rfind('/') if indx > -1: script = path[indx:] path = path[:indx] else: script = path path='' parts[2] = path parts.insert(3, script) parts.insert(4, info_path) return parts def buildUri(self, parts, clean, **kws): query={} i = 0 for key in ('scheme', 'netloc', 'path', 'script','pathinfo', 'param', 'fragment'): if kws.has_key(key): parts[i] = kws[key] del kws[key] i += 1 if clean: parts[6] ='' qs = parts[6] if qs: if type((qs)) ==type(''): # now for QS key, values: # querySeq = parts[6].split('&') querySeq= map(lambda x: x.split('='), querySeq) for k,v in querySeq: if query.has_key(k): query[k].append(v) else: query[k] = [v] querySeq=[] query.update(kws) if query: for key,values in query.items(): if type(values) == type([]): for value in values: querySeq.append((key, value)) else: querySeq.append((key, values)) query = urlencode(querySeq, doseq=1) else: query = parts[6] if parts[4]: parts[2]= "%s%s%s" % tuple(parts[2:5]) else: parts[2]= "%s%s" % tuple(parts[2:4]) del parts[3:5] parts[4]= query return urlunparse(parts) def serviceUri(self, clean=1, **kws): parts = list(self.uriParts()) url = self.buildUri(parts, clean, **kws) return url def baseUri(self): parts = list(self.uriParts()) return "%s://%s%s/" % tuple(parts[:3]) def pageUri(self, page): return self.baseUri() + page def log(self, *args): args = map(str, args) post = "\n%s: %s" % ( time.ctime(), ' '.join(args)) try: sys.stderr.write(post) except: import traceback traceback.print_exc(file=self.stderr) def keep(fileField): """ req.getFile(key).keep() -> file This method is added to file form fields. Files that are uploaded are stored as nameless temporary files. This method allows you to store the file, so it can be processed at a later stage. Calling it replaces the nameless temp file with the new named temp file. """ if not fileField.tempname: fileField.tempname = mktemp() fileField.file.seek(0) fp = open(fileField.tempname,'wb') fp.write(fileField.file.read()) fp.close() fileField.file= open(fileField.tempname) def save(fileField, newName): """ req.getFile(key).saveAs(somename) -> None This method is added to file form fields. Files that are uploaded are stored as nameless temporary files. This method alows you to store the file with a given name. """ if not fileField.tempname: fileField.keep() fileField.file.flush() fileField.file.close() import os os.rename(fileField.tempname, newName) def psoTest(req): if not req: req = ServiceRequest() req.pso().send_http_header() try: session['reload'] +=1 except: session['reload'] = 0 req.pso().write("\n try reload ", req.pso().session) def simpleTest(): req = ServiceRequest() import sys sys.stdout = req print 1,"hello world" req.pso().send_http_header() req.pso().write(" 2. hello world") print 3, " third time" def redtest1(): import sys sys.stdout = req = ServiceRequest() print "hello", req.send_http_header() print "world" def redtest2(): req = ServiceRequest() print "hello", req.send_http_header() print "world" if __name__ == '__main__': if 0: import pdb pdb.run("simpleTest()") else: redtest2()