|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
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 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 import base64 |
|
18 import http.client |
|
19 import http.cookiejar |
|
20 import socket |
|
21 import urllib.request |
|
22 import xmlrpc.client |
|
23 |
|
24 # import interfaces |
|
25 |
|
26 # import packages |
|
27 |
|
28 |
|
29 class XMLRPCCookieAuthTransport(xmlrpc.client.Transport): |
|
30 """An XML-RPC transport handling authentication via cookies""" |
|
31 |
|
32 _http_connection = http.client.HTTPConnection |
|
33 |
|
34 def __init__(self, user_agent, credentials=(), cookies=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
|
35 xmlrpc.client.Transport.__init__(self) |
|
36 self.user_agent = user_agent |
|
37 self.credentials = credentials |
|
38 self.cookies = cookies |
|
39 self.timeout = timeout |
|
40 self.headers = headers |
|
41 |
|
42 def make_connection(self, host): |
|
43 # This is the make_connection that runs under Python 2.7 and newer. |
|
44 # The code is pulled straight from 2.7 xmlrpclib, except replacing |
|
45 # HTTPConnection with self._http_connection |
|
46 if self._connection and host == self._connection[0]: |
|
47 return self._connection[1] |
|
48 chost, self._extra_headers, _x509 = self.get_host_info(host) |
|
49 self._connection = host, self._http_connection(chost, timeout=self.timeout) |
|
50 return self._connection[1] |
|
51 |
|
52 # override the send_host hook to also send authentication info |
|
53 def send_host(self, connection, host): |
|
54 connection.putheader('Host', host) |
|
55 if (self.cookies is not None) and (len(self.cookies) > 0): |
|
56 for cookie in self.cookies: |
|
57 connection.putheader('Cookie', '%s=%s' % (cookie.name, cookie.value)) |
|
58 elif self.credentials: |
|
59 auth = 'Basic %s' % base64.encodebytes("%s:%s" % self.credentials).strip() |
|
60 connection.putheader('Authorization', auth) |
|
61 |
|
62 # send user agent |
|
63 def send_user_agent(self, connection): |
|
64 connection.putheader('User-Agent', self.user_agent) |
|
65 |
|
66 # send custom headers |
|
67 def send_headers(self, connection, headers): |
|
68 xmlrpc.client.Transport.send_headers(self, connection, headers) |
|
69 for k, v in (self.headers or {}).iteritems(): |
|
70 connection.putheader(k, v) |
|
71 |
|
72 # dummy request class for extracting cookies |
|
73 class CookieRequest(urllib.request.Request): |
|
74 pass |
|
75 |
|
76 # dummy response info headers helper |
|
77 class CookieResponseHelper: |
|
78 def __init__(self, response): |
|
79 self.response = response |
|
80 def getheaders(self, header): |
|
81 return self.response.msg.getallmatchingheaders(header) |
|
82 |
|
83 # dummy response class for extracting cookies |
|
84 class CookieResponse: |
|
85 def __init__(self, response): |
|
86 self.response = response |
|
87 def info(self): |
|
88 return XMLRPCCookieAuthTransport.CookieResponseHelper(self.response) |
|
89 |
|
90 def request(self, host, handler, request_body, verbose=False): |
|
91 # issue XML-RPC request |
|
92 connection = self.make_connection(host) |
|
93 self.verbose = verbose |
|
94 if verbose: |
|
95 connection.set_debuglevel(1) |
|
96 self.send_request(connection, handler, request_body) |
|
97 self.send_host(connection, host) |
|
98 self.send_user_agent(connection) |
|
99 self.send_headers(connection) |
|
100 self.send_content(connection, request_body) |
|
101 # get response |
|
102 return self.get_response(connection, host, handler) |
|
103 |
|
104 def get_response(self, connection, host, handler): |
|
105 response = connection.getresponse() |
|
106 # extract cookies from response headers |
|
107 if self.cookies is not None: |
|
108 crequest = XMLRPCCookieAuthTransport.CookieRequest('http://%s/' % host) |
|
109 cresponse = XMLRPCCookieAuthTransport.CookieResponse(response) |
|
110 self.cookies.extract_cookies(cresponse, crequest) |
|
111 if response.status != 200: |
|
112 raise xmlrpc.client.ProtocolError(host + handler, response.status, response.reason, response.getheaders()) |
|
113 return self.parse_response(response) |
|
114 |
|
115 |
|
116 class SecureXMLRPCCookieAuthTransport(XMLRPCCookieAuthTransport): |
|
117 """Secure XML-RPC transport""" |
|
118 |
|
119 _http_connection = http.client.HTTPSConnection |
|
120 |
|
121 |
|
122 def get_client(uri, credentials=(), verbose=False, allow_none=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
|
123 """Get an XML-RPC client which supports basic authentication""" |
|
124 if uri.startswith('https:'): |
|
125 transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS secure transport)', credentials, |
|
126 timeout=timeout, headers=headers) |
|
127 else: |
|
128 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS basic transport)', credentials, |
|
129 timeout=timeout, headers=headers) |
|
130 return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none) |
|
131 |
|
132 |
|
133 def get_client_with_cookies(uri, credentials=(), verbose=False, allow_none=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, |
|
134 headers=None, cookies=None): |
|
135 """Get an XML-RPC client which supports authentication through cookies""" |
|
136 if cookies is None: |
|
137 cookies = http.cookiejar.CookieJar() |
|
138 if uri.startswith('https:'): |
|
139 transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS secure cookie transport)', |
|
140 credentials, cookies, timeout, headers) |
|
141 else: |
|
142 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS basic cookie transport)', |
|
143 credentials, cookies, timeout, headers) |
|
144 return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none) |