• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.45
/src/ZPublisher/xmlrpc.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
"""XML-RPC support module
1✔
14

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

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

22
import re
1✔
23
import sys
1✔
24
import xmlrpc.client
1✔
25

26
from AccessControl import getSecurityManager
1✔
27
from AccessControl.Permissions import view
1✔
28
from App.config import getConfiguration
1✔
29
from DateTime.DateTime import DateTime
1✔
30
from ExtensionClass import Base
1✔
31
from zExceptions import Unauthorized
1✔
32
from ZODB.POSException import ConflictError
1✔
33

34

35
# Make DateTime.DateTime marshallable via XML-RPC
36
WRAPPERS = xmlrpc.client.WRAPPERS + (DateTime, )
1✔
37

38

39
def dump_instance(self, value, write):
1✔
40
    # Check for special wrappers
41
    if value.__class__ in WRAPPERS:
1✔
42
        self.write = write
1✔
43
        value.encode(self)
1✔
44
        del self.write
1✔
45
    else:
46
        # Store instance attributes as a struct (really?).
47
        # We want to avoid disclosing private attributes.
48
        # Private attributes are by convention named with
49
        # a leading underscore character.
50
        ob_dict = {k: v for (k, v) in value.__dict__.items() if k[:1] != '_'}
1✔
51

52
        # If the instance attribute is a Zope object we also want to prevent
53
        # disclosing it to users without at least View permission.
54
        zope_objects = [(k, v) for (k, v) in ob_dict.items()
1✔
55
                        if isinstance(v, Base)]
56
        if zope_objects:
1✔
57
            sm = getSecurityManager()
1✔
58
            for ob_id, ob in zope_objects:
1✔
59
                if not sm.checkPermission(view, getattr(value, ob_id)):
1✔
60
                    del ob_dict[ob_id]
1✔
61

62
        self.dump_struct(ob_dict, write)
1✔
63

64

65
# Override the standard marshaller for object instances
66
# to skip private attributes.
67
xmlrpc.client.Marshaller.dispatch['_arbitrary_instance'] = dump_instance
1✔
68
xmlrpc.client.Marshaller.dispatch[DateTime] = dump_instance
1✔
69

70

71
def parse_input(data):
1✔
72
    """Parse input data and return a method path and argument tuple
73

74
    The data is a string.
75
    """
76
    #
77
    # For example, with the input:
78
    #
79
    #   <?xml version="1.0"?>
80
    #   <methodCall>
81
    #      <methodName>examples.getStateName</methodName>
82
    #      <params>
83
    #         <param>
84
    #            <value><i4>41</i4></value>
85
    #            </param>
86
    #         </params>
87
    #      </methodCall>
88
    #
89
    # the function should return:
90
    #
91
    #     ('examples.getStateName', (41,))
92
    params, method = xmlrpc.client.loads(data)
1✔
93
    # Translate '.' to '/' in meth to represent object traversal.
94
    method = method.replace('.', '/')
1✔
95
    return method, params
1✔
96

97
# See below
98
#
99
# def response(anHTTPResponse):
100
#     """Return a valid ZPublisher response object
101
#
102
#     Use data already gathered by the existing response.
103
#     The new response will replace the existing response.
104
#     """
105
#     # As a first cut, lets just clone the response and
106
#     # put all of the logic in our refined response class below.
107
#     r=Response()
108
#     r.__dict__.update(anHTTPResponse.__dict__)
109
#     return r
110

111
########################################################################
112
# Possible implementation helpers:
113

114

115
def is_xmlrpc_response(response):
1✔
116
    return isinstance(response, Response)
1✔
117

118

119
class Response:
1✔
120
    """Customized Response that handles XML-RPC-specific details.
121

122
    We override setBody to marshall Python objects into XML-RPC. We
123
    also override exception to convert errors to XML-RPC faults.
124

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

129
    It's probably possible to improve the 'exception' method quite a bit.
130
    The current implementation, however, should suffice for now.
131
    """
132

133
    _error_format = 'text/plain'  # No html in error values
1✔
134

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

139
    def __init__(self, real):
1✔
140
        self.__dict__['_real'] = real
1✔
141

142
    def __getattr__(self, name):
1✔
143
        return getattr(self._real, name)
×
144

145
    def __setattr__(self, name, v):
1✔
146
        return setattr(self._real, name, v)
×
147

148
    def __delattr__(self, name):
1✔
149
        return delattr(self._real, name)
×
150

151
    def setBody(self, body, title='', is_error=0, bogus_str_search=None):
1✔
152
        if isinstance(body, xmlrpc.client.Fault):
1!
153
            # Convert Fault object to XML-RPC response.
154
            body = xmlrpc.client.dumps(body, methodresponse=1, allow_none=True)
×
155
        else:
156
            # Marshall our body as an XML-RPC response. Strings will be sent
157
            # strings, integers as integers, etc. We do *not* convert
158
            # everything to a string first.
159
            # Previously this had special handling if the response
160
            # was a Python None. This is now patched in xmlrpc.client to
161
            # allow Nones nested inside data structures too.
162
            try:
1✔
163
                body = xmlrpc.client.dumps(
1✔
164
                    (body,), methodresponse=1, allow_none=True)
165
            except ConflictError:
×
166
                raise
×
167
            except Exception:
×
168
                self.exception()
×
169
                return
×
170
        # Set our body to the XML-RPC message, and fix our MIME type.
171
        self._real.setBody(body)
1✔
172
        self._real.setHeader('content-type', 'text/xml')
1✔
173
        return self
1✔
174

175
    def exception(self, fatal=0, info=None,
1✔
176
                  absuri_match=None, tag_search=None):
177
        # Fetch our exception info. t is type, v is value and tb is the
178
        # traceback object.
179
        if isinstance(info, tuple) and len(info) == 3:
×
180
            t, v, tb = info
×
181
        else:
182
            t, v, tb = sys.exc_info()
×
183

184
        # Don't mask 404 responses, as some XML-RPC libraries rely on the HTTP
185
        # mechanisms for detecting when authentication is required. Fixes Zope
186
        # Collector issue 525.
187
        if issubclass(t, Unauthorized):
×
188
            return self._real.exception(fatal=fatal, info=info)
×
189

190
        # Create an appropriate Fault object. Containing error information
191
        Fault = xmlrpc.client.Fault
×
192
        f = None
×
193
        try:
×
194
            # Strip HTML tags from the error value
195
            vstr = str(v)
×
196
            remove = [r"<[^<>]*>", r"&[A-Za-z]+;"]
×
197
            for pat in remove:
×
198
                vstr = re.sub(pat, " ", vstr)
×
199
            if getConfiguration().debug_mode:
×
200
                from traceback import format_exception
×
201
                value = '\n' + ''.join(format_exception(t, vstr, tb))
×
202
            else:
203
                value = f'{t} - {vstr}'
×
204
            if isinstance(v, Fault):
×
205
                f = v
×
206
            elif isinstance(v, Exception):
×
207
                f = Fault(-1, 'Unexpected Zope exception: %s' % value)
×
208
            else:
209
                f = Fault(-2, 'Unexpected Zope error value: %s' % value)
×
210
        except ConflictError:
×
211
            raise
×
212
        except Exception:
×
213
            f = Fault(-3, "Unknown Zope fault type")
×
214

215
        # Do the damage.
216
        self.setBody(f)
×
217
        self._real.setStatus(200)
×
218

219
        return tb
×
220

221

222
response = Response
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc