[Repoze-dev] Repoze CVS: converters.py dependencies.txt mapply.py request.py response.py tainted.py xmlrpc.py

Chris McDonough chrism at agendaless.com
Tue Sep 11 06:37:20 UTC 2007


Update of /home/repoze/cvs/repoze/repoze/zope2/keep/publisher
In directory laguna.palladion.com:/tmp/cvs-serv14697/repoze/zope2/keep/publisher

Added Files:
	converters.py dependencies.txt mapply.py request.py 
	response.py tainted.py xmlrpc.py 
Log Message:
Restructure.  After this checkin, I am going to break existing checkouts
by moving this entire module to repoze.zope2 on the server.  Sorry.


--- NEW FILE: request.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Basic ZPublisher request management.

$Id: request.py,v 1.1 2007-09-11 06:37:18 chrism Exp $
"""
from urllib import quote as urllib_quote
import xmlrpc
from zExceptions import Forbidden, Unauthorized, NotFound
[...2281 lines suppressed...]
        if 'passw' in k.lower():
            v = '<password obscured>'

        result.append((k, v))

    return result


# The trusted_proxies configuration setting contains a sequence
# of front-end proxies that are trusted to supply an accurate
# X_FORWARDED_FOR header. If REMOTE_ADDR is one of the values in this list
# and it has set an X_FORWARDED_FOR header, ZPublisher copies REMOTE_ADDR
# into X_FORWARDED_BY, and the last element of the X_FORWARDED_FOR list
# into REMOTE_ADDR. X_FORWARDED_FOR is left unchanged.
# The ZConfig machinery may sets this attribute on initialization
# if any trusted-proxies are defined in the configuration file.

trusted_proxies = []



--- NEW FILE: converters.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.1 $'[11:-2]

import re
from types import ListType, TupleType, UnicodeType
from DateTime import DateTime
from cgi import escape

# This may get overwritten during configuration
default_encoding = 'iso-8859-15'

def field2string(v):
    if isinstance(v, UnicodeType):
        return v.encode(default_encoding)
    else:
        return str(v)

def field2text(v, nl=re.compile('\r\n|\n\r').search):
    v = field2string(v)
    mo = nl(v)
    if mo is None: return v
    l = mo.start(0)
    r=[]
    s=0
    while l >= s:
        r.append(v[s:l])
        s=l+2
        mo=nl(v,s)
        if mo is None: l=-1
        else:          l=mo.start(0)

    r.append(v[s:])

    return '\n'.join(r)

def field2required(v):
    v = field2string(v)
    if v.strip(): return v
    raise ValueError, 'No input for required field<p>'

def field2int(v):
    if isinstance(v, (ListType, TupleType)):
        return map(field2int, v)
    v = field2string(v)
    if v:
        try: return int(v)
        except ValueError:
            raise ValueError, (
                "An integer was expected in the value %s" % escape(`v`)
                )
    raise ValueError, 'Empty entry when <strong>integer</strong> expected'

def field2float(v):
    if isinstance(v, (ListType, TupleType)):
        return map(field2float, v)
    v = field2string(v)
    if v:
        try: return float(v)
        except ValueError:
            raise ValueError, (
                "A floating-point number was expected in the value %s" %
                escape(`v`)
                )
    raise ValueError, (
        'Empty entry when <strong>floating-point number</strong> expected')

def field2long(v):
    if isinstance(v, (ListType, TupleType)):
        return map(field2long, v)
    v = field2string(v)
    # handle trailing 'L' if present.
    if v[-1:] in ('L', 'l'):
        v = v[:-1]
    if v:
        try: return long(v)
        except ValueError:
            raise ValueError, (
                "A long integer was expected in the value %s" % escape(`v`)
                )
    raise ValueError, 'Empty entry when <strong>integer</strong> expected'

def field2tokens(v):
    v = field2string(v)
    return v.split()

def field2lines(v):
    if isinstance(v, (ListType, TupleType)):
        result=[]
        for item in v:
            result.append(str(item))
        return result
    return field2text(v).splitlines()

def field2date(v):
    v = field2string(v)
    try:
        v = DateTime(v)
    except DateTime.SyntaxError, e:
        raise DateTime.SyntaxError, "Invalid DateTime "+escape(`v`)
    return v

def field2date_international(v):
    v = field2string(v)
    try:
        v = DateTime(v, datefmt="international")
    except DateTime.SyntaxError, e:
        raise DateTime.SyntaxError, "Invalid DateTime "+escape(`v`)
    return v

def field2boolean(v):
    if v == 'False':
        return not 1
    return not not v

class _unicode_converter:
    def __call__(self,v):
        # Convert a regular python string. This probably doesnt do what you want,
        # whatever that might be. If you are getting exceptions below, you
        # probably missed the encoding tag from a form field name. Use:
        #       <input name="description:utf8:ustring" .....
        # rather than
        #       <input name="description:ustring" .....
        v = unicode(v)
        return self.convert_unicode(v)

    def convert_unicode(self,v):
        raise NotImplementedError('convert_unicode')

class field2ustring(_unicode_converter):
    def convert_unicode(self,v):
        return v
field2ustring = field2ustring()

class field2utokens(_unicode_converter):
    def convert_unicode(self,v):
        return v.split()
field2utokens = field2utokens()

class field2utext(_unicode_converter):
    def convert_unicode(self,v):
        return unicode(field2text(v.encode('utf8')),'utf8')
field2utext = field2utext()

class field2ulines:
    def __call__(self, v):
        if hasattr(v,'read'):
            v=v.read()
        if isinstance(v, (ListType, TupleType)): 
            return [field2ustring(x) for x in v]
        v = unicode(v)
        return self.convert_unicode(v)

    def convert_unicode(self, v):
        return field2utext.convert_unicode(v).splitlines()

field2ulines = field2ulines()

type_converters = {
    'float':              field2float,
    'int':                field2int,
    'long':               field2long,
    'string':             field2string,
    'date':               field2date,
    'date_international': field2date_international,
    'required':           field2required,
    'tokens':             field2tokens,
    'lines':              field2lines,
    'text':               field2text,
    'boolean':            field2boolean,
    'ustring':            field2ustring,
    'utokens':            field2utokens,
    'ulines':             field2ulines,
    'utext':              field2utext,
    }

get_converter=type_converters.get

--- NEW FILE: tainted.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

__version__='$Revision: 1.1 $'[11:-2]

from cgi import escape

# TaintedStrings hold potentially dangerous untrusted data; anything that could
# possibly hold HTML is considered dangerous. DTML code will use the quoted
# value of this tring, and raised exceptions in Zope will use the repr()
# conversion.
class TaintedString:
    def __init__(self, value):
        self._value = value

    def __str__(self):
        return self._value

    def __repr__(self):
        return repr(self.quoted())

    def __cmp__(self, o):
        return cmp(self._value, o)

    def __hash__(self):
        return hash(self._value)

    def __len__(self):
        return len(self._value)

    def __getitem__(self, index):
        v = self._value[index]
        if '<' in v:
            v = self.__class__(v)
        return v

    def __getslice__(self, i, j):
        i = max(i, 0)
        j = max(j, 0)
        v = self._value[i:j]
        if '<' in v:
            v = self.__class__(v)
        return v

    def __add__(self, o):
        return self.__class__(self._value + o)

    def __radd__(self, o):
        return self.__class__(o + self._value)

    def __mul__(self, o):
        return self.__class__(self._value * o)

    def __rmul__(self, o):
        return self.__class__(o * self._value)

    def __mod__(self, o):
        return self.__class__(self._value % o)

    def __int__(self):
        return int(self._value)

    def __float__(self):
        return float(self._value)

    def __long__(self):
        return long(self._value)

    def __getstate__(self):
        # If an object tries to store a TaintedString, it obviously wasn't aware
        # that it was playing with untrusted data. Complain acordingly.
        raise SystemError("A TaintedString cannot be pickled. Code that "
            "caused this TaintedString to be stored should be more careful "
            "with untrusted data from the REQUEST.")

    def __getattr__(self, a):
        # for string methods support other than those defined below
        return getattr(self._value, a)

    # Python 2.2 only.
    def decode(self, *args):
        return self.__class__(self._value.decode(*args))

    def encode(self, *args):
        return self.__class__(self._value.encode(*args))

    def expandtabs(self, *args):
        return self.__class__(self._value.expandtabs(*args))

    def replace(self, *args):
        v = self._value.replace(*args)
        if '<' in v:
            v = self.__class__(v)
        return v

    def split(self, *args):
        r = self._value.split(*args)
        return map(lambda v, c=self.__class__: '<' in v and c(v) or v, r)

    def splitlines(self, *args):
        r = self._value.splitlines(*args)
        return map(lambda v, c=self.__class__: '<' in v and c(v) or v, r)

    def translate(self, *args):
        v = self._value.translate(*args)
        if '<' in v:
            v = self.__class__(v)
        return v

    def quoted(self):
        return escape(self._value, 1)

    # As called by cDocumentTemplate
    __untaint__ = quoted


def createSimpleWrapper(func):
    return lambda s, f=func: s.__class__(getattr(s._value, f)())

def createOneArgWrapper(func):
    return lambda s, a, f=func: s.__class__(getattr(s._value, f)(a))

def createOneOptArgWrapper(func):
    return lambda s, a=None, f=func: s.__class__(getattr(s._value, f)(a))
    
simpleWrappedMethods = \
    "capitalize lower swapcase title upper".split()

oneArgWrappedMethods = "center join ljust rjust".split()

oneOptArgWrappedMethods = "lstrip rstrip strip".split()

for f in simpleWrappedMethods:
    setattr(TaintedString, f, createSimpleWrapper(f))

for f in oneArgWrappedMethods:
    setattr(TaintedString, f, createOneArgWrapper(f))

for f in oneOptArgWrappedMethods:
    setattr(TaintedString, f, createOneOptArgWrapper(f))

--- NEW FILE: mapply.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Provide an apply-like facility that works with any mapping object
"""

def default_call_object(object, args, context):
    result=object(*args) # Type s<cr> to step into published object.
    return result

def default_missing_name(name, context):
    raise TypeError, 'argument %s was ommitted' % name

def default_handle_class(klass, context):
    if hasattr(klass,'__init__'):
        f=klass.__init__.im_func
        c=f.func_code
        names=c.co_varnames[1:c.co_argcount]
        return klass, names, f.func_defaults
    else:
        return klass, (), ()

def mapply(object, positional=(), keyword={},
           debug=None, maybe=None,
           missing_name=default_missing_name,
           handle_class=default_handle_class,
           context=None, bind=0,
           ):

    if hasattr(object,'__bases__'):
        f, names, defaults = handle_class(object, context)
    else:
        f=object
        im=0
        if hasattr(f, 'im_func'):
            im=1
        elif not hasattr(f,'func_defaults'):
            if hasattr(f, '__call__'):
                f=f.__call__
                if hasattr(f, 'im_func'):
                    im=1
                elif not hasattr(f,'func_defaults') and maybe: return object
            elif maybe: return object

        if im:
            f=f.im_func
            c=f.func_code
            defaults=f.func_defaults
            names=c.co_varnames[1:c.co_argcount]
        else:
            defaults=f.func_defaults
            c=f.func_code
            names=c.co_varnames[:c.co_argcount]

    nargs=len(names)
    if positional:
        positional=list(positional)
        if bind and nargs and names[0]=='self':
            positional.insert(0, missing_name('self', context))
        if len(positional) > nargs: raise TypeError, 'too many arguments'
        args=positional
    else:
        if bind and nargs and names[0]=='self':
            args=[missing_name('self', context)]
        else:
            args=[]

    get=keyword.get
    nrequired=len(names) - (len(defaults or ()))
    for index in range(len(args), len(names)):
        name=names[index]
        v=get(name, args)
        if v is args:
            if index < nrequired: v=missing_name(name, context)
            else: v=defaults[index-nrequired]
        args.append(v)

    args=tuple(args)
    if debug is not None: return debug(object,args,context)
    else: return object(*args)

--- NEW FILE: dependencies.txt ---
stdlib

DateTime
zExceptions
Acquisition
zope.interface
zope.component
zope.event
zope.app.publication
zope.app.publisher
zope.publisher
zope.traversing
ZODB
transaction
paste
zope.security
AccessControl
ExtensionClass

--- NEW FILE: response.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''CGI Response Output formatter

$Id: response.py,v 1.1 2007-09-11 06:37:18 chrism Exp $'''
__version__ = '$Revision: 1.1 $'[11:-2]

from zExceptions import Unauthorized, Forbidden, NotFound, BadRequest

[...1039 lines suppressed...]
        """\
        Return data as a stream

        HTML data may be returned using a stream-oriented interface.
        This allows the browser to display partial results while
        computation of a response to proceed.

        The published object should first set any output headers or
        cookies on the response object.

        Note that published objects must not generate any errors
        after beginning stream-oriented output.

        """
        if not self._wrote:
            self.outputBody()
            self._wrote = 1
            self.stdout.flush()

        self.stdout.write(data)

--- NEW FILE: xmlrpc.py ---
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""XML-RPC support module

Written by Eric Kidd at UserLand software, with much help from Jim Fulton
at DC. This code hooks Zope up to Fredrik Lundh's Python XML-RPC library.

See http://www.xmlrpc.com/ and http://linux.userland.com/ for more
information about XML-RPC and Zope.
"""

import re
import sys, types
from HTTPResponse import HTTPResponse
import xmlrpclib

from zExceptions import Unauthorized
from ZODB.POSException import ConflictError

# Make DateTime.DateTime marshallable via XML-RPC
# http://www.zope.org/Collectors/Zope/2109
from DateTime import DateTime
WRAPPERS = xmlrpclib.WRAPPERS + (DateTime,)

def dump_instance(self, value, write):
    # Check for special wrappers
    if value.__class__ in WRAPPERS:
        self.write = write
        value.encode(self)
        del self.write
    else:
        # Store instance attributes as a struct (really?).
        # We want to avoid disclosing private attributes.
        # Private attributes are by convention named with
        # a leading underscore character.
        value = dict([(k, v) for (k, v) in value.__dict__.items()
                      if k[:1] != '_'])
        self.dump_struct(value, write)

xmlrpclib.Marshaller.dispatch[types.InstanceType] = dump_instance

def parse_input(data):
    """Parse input data and return a method path and argument tuple

    The data is a string.
    """
    #
    # For example, with the input:
    #
    #   <?xml version="1.0"?>
    #   <methodCall>
    #      <methodName>examples.getStateName</methodName>
    #      <params>
    #         <param>
    #            <value><i4>41</i4></value>
    #            </param>
    #         </params>
    #      </methodCall>
    #
    # the function should return:
    #
    #     ('examples.getStateName', (41,))
    params, method = xmlrpclib.loads(data)
    # Translate '.' to '/' in meth to represent object traversal.
    method = method.replace('.', '/')
    return method, params

# See below
#
# def response(anHTTPResponse):
#     """Return a valid ZPublisher response object
#
#     Use data already gathered by the existing response.
#     The new response will replace the existing response.
#     """
#     # As a first cut, lets just clone the response and
#     # put all of the logic in our refined response class below.
#     r=Response()
#     r.__dict__.update(anHTTPResponse.__dict__)
#     return r

########################################################################
# Possible implementation helpers:

class Response:
    """Customized Response that handles XML-RPC-specific details.

    We override setBody to marhsall Python objects into XML-RPC. We
    also override exception to convert errors to XML-RPC faults.

    If these methods stop getting called, make sure that ZPublisher is
    using the xmlrpc.Response object created above and not the original
    HTTPResponse object from which it was cloned.

    It's probably possible to improve the 'exception' method quite a bit.
    The current implementation, however, should suffice for now.
    """
    
    _error_format = 'text/plain' # No html in error values

    # Because we can't predict what kind of thing we're customizing,
    # we have to use delegation, rather than inheritance to do the
    # customization.

    def __init__(self, real): self.__dict__['_real']=real

    def __getattr__(self, name): return getattr(self._real, name)
    def __setattr__(self, name, v): return setattr(self._real, name, v)
    def __delattr__(self, name): return delattr(self._real, name)

    def setBody(self, body, title='', is_error=0, bogus_str_search=None):
        if isinstance(body, xmlrpclib.Fault):
            # Convert Fault object to XML-RPC response.
            body=xmlrpclib.dumps(body, methodresponse=1, allow_none=True)
        else:
            # Marshall our body as an XML-RPC response. Strings will be sent
            # strings, integers as integers, etc. We do *not* convert
            # everything to a string first.
            # Previously this had special handling if the response
            # was a Python None. This is now patched in xmlrpclib to
            # allow Nones nested inside data structures too.
            try:
                body = xmlrpclib.dumps(
                    (body,), methodresponse=1, allow_none=True)
            except ConflictError:
                raise
            except:
                self.exception()
                return
        # Set our body to the XML-RPC message, and fix our MIME type.
        self._real.setBody(body)
        self._real.setHeader('content-type', 'text/xml')
        return self

    def exception(self, fatal=0, info=None,
                  absuri_match=None, tag_search=None):
        # Fetch our exception info. t is type, v is value and tb is the
        # traceback object.
        if type(info) is type(()) and len(info)==3: t,v,tb = info
        else: t,v,tb = sys.exc_info()

        # Don't mask 404 respnses, as some XML-RPC libraries rely on the HTTP
        # mechanisms for detecting when authentication is required. Fixes Zope
        # Collector issue 525.
        if t == 'Unauthorized' or (
            isinstance(t, types.ClassType) and issubclass(t, Unauthorized)
            ):

            return self._real.exception(fatal=fatal, info=info)

        # Create an appropriate Fault object. Containing error information
        Fault=xmlrpclib.Fault
        f=None
        try:
            # Strip HTML tags from the error value
            vstr = str(v)
            remove = [r"<[^<>]*>", r"&[A-Za-z]+;"]
            for pat in remove:
                vstr = re.sub(pat, " ", vstr)
            from Globals import DevelopmentMode
            if DevelopmentMode:
                from traceback import format_exception
                value = '\n' + ''.join(format_exception(t, vstr, tb))
            else:
                value = '%s - %s' % (t, vstr)
                
            if isinstance(v, Fault):
                f=v
            elif isinstance(v, Exception):
                f=Fault(-1, 'Unexpected Zope exception: %s' % value)
            else:
                f=Fault(-2, 'Unexpected Zope error value: %s' % value)
        except ConflictError:
            raise
        except:
            f=Fault(-3, "Unknown Zope fault type")

        # Do the damage.
        self.setBody(f)
        self._real.setStatus(200)

        return tb

response=Response

_______________________________________________
Repoze-dev mailing list
Repoze-dev at lists.repoze.org
http://lists.repoze.org/mailman/listinfo/repoze-dev



More information about the Repoze-dev mailing list