Merge lp:~jr.allen/unifield-server/py27 into lp:unifield-server
- py27
- Merge into trunk
Proposed by
Jeff Allen
Status: | Merged |
---|---|
Merged at revision: | 4396 |
Proposed branch: | lp:~jr.allen/unifield-server/py27 |
Merge into: | lp:unifield-server |
Diff against target: |
5052 lines (+136/-4339) 26 files modified
MANIFEST.in (+0/-20) bin/addons/base/publisher_warranty/publisher_warranty.py (+29/-30) bin/addons/patches/README (+0/-27) bin/addons/patches/export_many2many.diff (+0/-57) bin/addons/patches/generate_server_ssl_key_cert.sh (+0/-21) bin/addons/patches/server_patch_export_boolean.diff (+0/-13) bin/addons/patches/server_patch_gzip_xmlrpc.diff (+0/-223) bin/addons/patches/server_patch_netrpc_gzip.diff (+0/-156) bin/addons/patches/web_patch_choose_server_port.diff (+0/-30) bin/addons/sync_client/gzip_xmlrpclib.py (+0/-114) bin/addons/sync_client/rpc.py (+10/-69) bin/addons/sync_client/sync_client.py (+4/-7) bin/addons/sync_client/timeout_transport.py (+4/-23) bin/addons/update_client/wizard.py (+20/-26) bin/service/gzip_xmlrpclib.py (+0/-45) bin/service/http_server.py (+12/-37) bin/service/websrv_lib.py (+14/-14) bin/tools/misc.py (+1/-6) bin/updater.py (+1/-5) bin/zipfile266.py (+0/-1409) python25-compat/BaseHTTPServer.py (+0/-587) python25-compat/SimpleXMLRPCServer.py (+0/-611) python25-compat/SocketServer.py (+0/-681) setup.py (+38/-37) ssl-cert.cfg (+0/-89) win32/setup.py (+3/-2) |
To merge this branch: | bzr merge lp:~jr.allen/unifield-server/py27 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
jftempo | Pending | ||
Review via email: mp+324109@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'MANIFEST.in' |
2 | --- MANIFEST.in 2011-04-06 07:57:57 +0000 |
3 | +++ MANIFEST.in 1970-01-01 00:00:00 +0000 |
4 | @@ -1,20 +0,0 @@ |
5 | -include rpminstall_sh.txt |
6 | -include README |
7 | -include LICENSE |
8 | -include MANIFEST.in |
9 | -include setup.nsi |
10 | -include setup.cfg |
11 | -include bin/import_xml.rng |
12 | -include bin/server.cert |
13 | -include bin/server.pkey |
14 | -include bin/gpl.txt |
15 | -include man/openerp-server.1 |
16 | -include man/openerp_serverrc.5 |
17 | -recursive-include pixmaps * |
18 | -recursive-include win32 * |
19 | -recursive-include doc * |
20 | -recursive-include bin *xml *xsl *sql *rml *sxw *csv *rng |
21 | -graft debian |
22 | -graft bin/addons |
23 | -graft python25-compat |
24 | -global-exclude *pyc *~ |
25 | |
26 | === modified file 'bin/addons/base/publisher_warranty/publisher_warranty.py' |
27 | --- bin/addons/base/publisher_warranty/publisher_warranty.py 2011-01-19 01:38:10 +0000 |
28 | +++ bin/addons/base/publisher_warranty/publisher_warranty.py 2017-05-22 10:09:36 +0000 |
29 | @@ -25,7 +25,6 @@ |
30 | |
31 | import datetime |
32 | import logging |
33 | -import sys |
34 | import urllib |
35 | import urllib2 |
36 | |
37 | @@ -89,22 +88,22 @@ |
38 | email = user.email |
39 | |
40 | msg = {'contract_name': valid_contract.name, |
41 | - 'tb': tb, |
42 | - 'explanations': explanations, |
43 | - 'remarks': remarks, |
44 | - 'origin': origin, |
45 | - 'dbname': cr.dbname, |
46 | - 'dbuuid': dbuuid, |
47 | - 'db_create_date': db_create_date, |
48 | - 'issue_name': issue_name, |
49 | - 'email': email, |
50 | - 'user_name': user_name, |
51 | - } |
52 | - |
53 | - |
54 | - add_arg = {"timeout":30} if sys.version_info >= (2,6) else {} |
55 | + 'tb': tb, |
56 | + 'explanations': explanations, |
57 | + 'remarks': remarks, |
58 | + 'origin': origin, |
59 | + 'dbname': cr.dbname, |
60 | + 'dbuuid': dbuuid, |
61 | + 'db_create_date': db_create_date, |
62 | + 'issue_name': issue_name, |
63 | + 'email': email, |
64 | + 'user_name': user_name, |
65 | + } |
66 | + |
67 | + |
68 | + add_arg = {"timeout":30} |
69 | uo = urllib2.urlopen(config.get("publisher_warranty_url"), |
70 | - urllib.urlencode({'arg0': msg, "action": "send",}),**add_arg) |
71 | + urllib.urlencode({'arg0': msg, "action": "send",}),**add_arg) |
72 | try: |
73 | submit_result = uo.read() |
74 | finally: |
75 | @@ -189,19 +188,19 @@ |
76 | limit_date = (datetime.datetime.now() - _PREVIOUS_LOG_CHECK).strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) |
77 | for message in result["messages"]: |
78 | ids = self.pool.get("res.log").search(cr, uid, [("res_model", "=", "publisher_warranty.contract"), |
79 | - ("create_date", ">=", limit_date), |
80 | - ("name", "=", message)]) |
81 | + ("create_date", ">=", limit_date), |
82 | + ("name", "=", message)]) |
83 | if ids: |
84 | continue |
85 | self.pool.get('res.log').create(cr, uid, |
86 | - { |
87 | - 'name': message, |
88 | - 'res_model': "publisher_warranty.contract", |
89 | - "read": True, |
90 | - "user_id": False, |
91 | - }, |
92 | - context=context |
93 | - ) |
94 | + { |
95 | + 'name': message, |
96 | + 'res_model': "publisher_warranty.contract", |
97 | + "read": True, |
98 | + "user_id": False, |
99 | + }, |
100 | + context=context |
101 | + ) |
102 | except Exception: |
103 | if cron_mode: |
104 | return False # we don't want to see any stack trace in cron |
105 | @@ -216,7 +215,7 @@ |
106 | @rtype: list of tuples(int,string) |
107 | """ |
108 | ids = self.pool.get('res.log').search(cr, uid, [("res_model", "=", "publisher_warranty.contract")] |
109 | - , order="create_date desc", limit=limit) |
110 | + , order="create_date desc", limit=limit) |
111 | if not ids: |
112 | return [] |
113 | messages = [(x.id, x.name) for x in self.pool.get('res.log').browse(cr, uid, ids)] |
114 | @@ -236,7 +235,7 @@ |
115 | 'date_start' : fields.date('Starting Date', readonly=True), |
116 | 'date_stop' : fields.date('Ending Date', readonly=True), |
117 | 'state' : fields.selection([('unvalidated', 'Unvalidated'), ('valid', 'Valid') |
118 | - , ('terminated', 'Terminated'), ('canceled', 'Canceled')], string="State", readonly=True), |
119 | + , ('terminated', 'Terminated'), ('canceled', 'Canceled')], string="State", readonly=True), |
120 | 'kind' : fields.char('Kind', size=64, readonly=True), |
121 | "check_support": fields.boolean("Support Level 1", readonly=True), |
122 | "check_opw": fields.boolean("OPW", readonly=True, help="Checked if this is an OpenERP Publisher's Warranty contract (versus older contract types"), |
123 | @@ -262,7 +261,7 @@ |
124 | |
125 | def send(self, cr, uid, tb, explanations, remarks=None, issue_name=None): |
126 | return self.pool.get("publisher_warranty.contract").send(cr, uid, tb, |
127 | - explanations, remarks, issue_name) |
128 | + explanations, remarks, issue_name) |
129 | |
130 | maintenance_contract() |
131 | |
132 | @@ -331,7 +330,7 @@ |
133 | "language": user.context_lang, |
134 | } |
135 | |
136 | - add_arg = {"timeout":30} if sys.version_info >= (2,6) else {} |
137 | + add_arg = {"timeout":30} |
138 | arguments = {'arg0': msg, "action": "update",} |
139 | arguments_raw = urllib.urlencode(arguments) |
140 | url = config.get("publisher_warranty_url") |
141 | |
142 | === removed directory 'bin/addons/patches' |
143 | === removed file 'bin/addons/patches/README' |
144 | --- bin/addons/patches/README 2012-05-02 14:50:59 +0000 |
145 | +++ bin/addons/patches/README 1970-01-01 00:00:00 +0000 |
146 | @@ -1,27 +0,0 @@ |
147 | -Mandatory Patch |
148 | - |
149 | -=== export_many2many.diff |
150 | -Patch to apply on the server. |
151 | -This patch allow the synchro module to export many2many properly |
152 | - |
153 | -=== server_patch_export_boolean.diff |
154 | -Change the original way in the server to export boolean False value to print a string containing "False" instead |
155 | -bzr merge lp:openobject-server/6.0-msf-patch-export-boolean |
156 | - |
157 | - |
158 | -Needed to enable compressed communication |
159 | - |
160 | -=== server_patch_gzip_xmlrpc.diff |
161 | -Add a new protocol of communication with the server: gzipxmlrpc. It's all based on XML-RPC protocol but transfer its data compressed in gzip instead |
162 | -bzr merge lp:openobject-server/6.0-msf-patch-gzip-xmlrpc |
163 | - |
164 | -=== server_patch_netrpc_gzip.diff |
165 | -Add a new protocol of communication with the server: netrpc_gzip. It's all based on netrpc protocol but transfer its data compressed in gzip instead |
166 | -bzr branch lp:openobject-server/6.0-msf-patch-netrpc-gzip |
167 | - |
168 | - |
169 | -Needed when testing and running Openerp netrpc on a different port |
170 | - |
171 | -=== web_patch_choose_server_port.diff |
172 | -Enable OpenERP Web Client to use a different OpenERP Server host, port and protocol. |
173 | -bzr branch lp:openobject-client-web/6.0-msf-patch-choose-server-port |
174 | |
175 | === removed file 'bin/addons/patches/export_many2many.diff' |
176 | --- bin/addons/patches/export_many2many.diff 2012-04-07 17:42:54 +0000 |
177 | +++ bin/addons/patches/export_many2many.diff 1970-01-01 00:00:00 +0000 |
178 | @@ -1,57 +0,0 @@ |
179 | -=== modified file 'bin/osv/orm.py' |
180 | ---- bin/osv/orm.py 2011-12-11 11:55:32 +0000 |
181 | -+++ bin/osv/orm.py 2012-04-06 09:26:29 +0000 |
182 | -@@ -551,6 +551,8 @@ |
183 | - def __export_row(self, cr, uid, row, fields, context=None): |
184 | - if context is None: |
185 | - context = {} |
186 | -+ |
187 | -+ sync_context = context.get('sync_context') |
188 | - |
189 | - def check_type(field_type): |
190 | - if field_type == 'float': |
191 | -@@ -569,6 +571,31 @@ |
192 | - selection_field(col_obj._inherits) |
193 | - else: |
194 | - return False |
195 | -+ |
196 | -+ def _get_xml_id(self, cr, uid, r): |
197 | -+ model_data = self.pool.get('ir.model.data') |
198 | -+ data_ids = model_data.search(cr, uid, [('model', '=', r._table_name), ('res_id', '=', r['id'])]) |
199 | -+ if len(data_ids): |
200 | -+ d = model_data.read(cr, uid, data_ids, ['name', 'module'])[0] |
201 | -+ if d['module']: |
202 | -+ r = '%s.%s' % (d['module'], d['name']) |
203 | -+ else: |
204 | -+ r = d['name'] |
205 | -+ else: |
206 | -+ postfix = 0 |
207 | -+ while True: |
208 | -+ n = self._table+'_'+str(r['id']) + (postfix and ('_'+str(postfix)) or '' ) |
209 | -+ if not model_data.search(cr, uid, [('name', '=', n)]): |
210 | -+ break |
211 | -+ postfix += 1 |
212 | -+ model_data.create(cr, uid, { |
213 | -+ 'name': n, |
214 | -+ 'model': self._name, |
215 | -+ 'res_id': r['id'], |
216 | -+ 'module': '__export__', |
217 | -+ }) |
218 | -+ r = '__export__.'+n |
219 | -+ return r |
220 | - |
221 | - lines = [] |
222 | - data = map(lambda x: '', range(len(fields))) |
223 | -@@ -620,6 +647,11 @@ |
224 | - if [x for x in fields2 if x]: |
225 | - break |
226 | - done.append(fields2) |
227 | -+ |
228 | -+ if sync_context and cols and cols._type=='many2many' and len(fields[fpos])>(i+1) and (fields[fpos][i+1]=='id'): |
229 | -+ data[fpos] = ','.join([_get_xml_id(self, cr, uid, x) for x in r]) |
230 | -+ break |
231 | -+ |
232 | - for row2 in r: |
233 | - lines2 = self.__export_row(cr, uid, row2, fields2, |
234 | - context) |
235 | - |
236 | |
237 | === removed file 'bin/addons/patches/generate_server_ssl_key_cert.sh' |
238 | --- bin/addons/patches/generate_server_ssl_key_cert.sh 2011-09-09 13:20:20 +0000 |
239 | +++ bin/addons/patches/generate_server_ssl_key_cert.sh 1970-01-01 00:00:00 +0000 |
240 | @@ -1,21 +0,0 @@ |
241 | -#!/bin/sh |
242 | - |
243 | -echo "generating SSL keys" |
244 | - |
245 | -openssl genrsa -des3 -out server.pkey 2048 |
246 | - |
247 | -openssl req -new -key server.pkey -out server.csr |
248 | - |
249 | -openssl genrsa -des3 -out ca.pkey 2048 |
250 | - |
251 | -openssl req -new -x509 -days 365 -key ca.pkey -out ca.crt |
252 | - |
253 | -openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.pkey -CAcreateserial -CAserial ca.srl |
254 | - |
255 | -# decrypt private key to avoid typing passphrase at each client connection |
256 | -openssl rsa -in server.pkey -out server.pkey |
257 | - |
258 | -#start openerp server with --cert-file=YOUR .crt FILE PATH --pkey-file=YOUR pkey FILE PATH |
259 | - |
260 | -#./openerp-server --addons-path=../openobject-addons/ --cert-file=bin/ssl/server.crt --pkey-file=bin/ssl/server.pkey |
261 | - |
262 | |
263 | === removed file 'bin/addons/patches/server_patch_export_boolean.diff' |
264 | --- bin/addons/patches/server_patch_export_boolean.diff 2011-09-09 13:20:20 +0000 |
265 | +++ bin/addons/patches/server_patch_export_boolean.diff 1970-01-01 00:00:00 +0000 |
266 | @@ -1,13 +0,0 @@ |
267 | -=== modified file 'bin/osv/orm.py' |
268 | ---- bin/osv/orm.py 2011-06-20 11:12:35 +0000 |
269 | -+++ bin/osv/orm.py 2011-08-22 11:19:48 +0000 |
270 | -@@ -557,7 +557,7 @@ |
271 | - elif field_type == 'integer': |
272 | - return 0 |
273 | - elif field_type == 'boolean': |
274 | -- return False |
275 | -+ return 'False' |
276 | - return '' |
277 | - |
278 | - def selection_field(in_field): |
279 | - |
280 | |
281 | === removed file 'bin/addons/patches/server_patch_gzip_xmlrpc.diff' |
282 | --- bin/addons/patches/server_patch_gzip_xmlrpc.diff 2011-09-09 13:20:20 +0000 |
283 | +++ bin/addons/patches/server_patch_gzip_xmlrpc.diff 1970-01-01 00:00:00 +0000 |
284 | @@ -1,223 +0,0 @@ |
285 | -=== added file 'bin/service/gzip_xmlrpclib.py' |
286 | ---- bin/service/gzip_xmlrpclib.py 1970-01-01 00:00:00 +0000 |
287 | -+++ bin/service/gzip_xmlrpclib.py 2011-09-08 13:10:09 +0000 |
288 | -@@ -0,0 +1,109 @@ |
289 | -+import zlib |
290 | -+import xmlrpclib |
291 | -+from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler |
292 | -+import logging |
293 | -+ |
294 | -+from timeout_transport import TimeoutTransport |
295 | -+ |
296 | -+# xmlrpc server side |
297 | -+ |
298 | -+class GzipXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): |
299 | -+ |
300 | -+ def do_POST(self): |
301 | -+ if not(self.headers.has_key('accept-encoding') and self.headers['accept-encoding'] == 'gzip'): |
302 | -+ return SimpleXMLRPCRequestHandler.do_POST(self) |
303 | -+ else: |
304 | -+ if not self.is_rpc_path_valid(): |
305 | -+ self.report_404() |
306 | -+ return |
307 | -+ try: |
308 | -+ max_chunk_size = 10*1024*1024 |
309 | -+ size_remaining = int(self.headers["content-length"]) |
310 | -+ L = [] |
311 | -+ while size_remaining: |
312 | -+ chunk_size = min(size_remaining, max_chunk_size) |
313 | -+ L.append(self.rfile.read(chunk_size)) |
314 | -+ size_remaining -= len(L[-1]) |
315 | -+ data = ''.join(L) |
316 | -+ data = zlib.decompress(data) # is_gzip |
317 | -+ response = self.server._marshaled_dispatch( |
318 | -+ data, getattr(self, '_dispatch', None) |
319 | -+ ) |
320 | -+ except Exception, e: |
321 | -+ self.send_response(500) |
322 | -+ self.end_headers() |
323 | -+ else: |
324 | -+ response = zlib.compress(response, zlib.Z_BEST_COMPRESSION) # is_gzip |
325 | -+ self.send_response(200) |
326 | -+ self.send_header("Content-type", "text/xml") |
327 | -+ self.send_header("Content-length", str(len(response))) |
328 | -+ self.send_header("Accept-Encoding", "gzip") # is_gzip |
329 | -+ self.end_headers() |
330 | -+ self.wfile.write(response) |
331 | -+ self.wfile.flush() |
332 | -+ self.connection.shutdown(1) |
333 | -+ |
334 | -+# xmlrpc client side |
335 | -+ |
336 | -+class GzipTransport(TimeoutTransport): |
337 | -+ |
338 | -+ def __init__(self, timeout=None, *args, **kwargs): |
339 | -+ TimeoutTransport.__init__(self, timeout=timeout, *args, **kwargs) |
340 | -+ self.__logger = logging.getLogger('xmlrpc.transport') |
341 | -+ |
342 | -+ def request(self, host, handler, request_body, verbose=0): |
343 | -+ h = self.make_connection(host) |
344 | -+ if verbose: |
345 | -+ h.set_debuglevel(1) |
346 | -+ self.send_request(h, handler, request_body) |
347 | -+ self.send_host(h, host) |
348 | -+ self.send_user_agent(h) |
349 | -+ |
350 | -+ # is_gzip |
351 | -+ h.putheader('Accept-Encoding', 'gzip') |
352 | -+ raw_size = len(request_body) |
353 | -+ request_body = zlib.compress(request_body, zlib.Z_BEST_COMPRESSION) |
354 | -+ gzipped_size = len(request_body) |
355 | -+ saving = 100*(float(raw_size-gzipped_size))/gzipped_size if gzipped_size else 0 |
356 | -+ self.__logger.debug('payload size: raw %s, gzipped %s, saving %.2f%%', |
357 | -+ raw_size, gzipped_size, saving) |
358 | -+ |
359 | -+ self.send_content(h, request_body) |
360 | -+ errcode, errmsg, headers = h.getreply() |
361 | -+ if errcode != 200: |
362 | -+ raise xmlrpclib.ProtocolError( |
363 | -+ host + handler, |
364 | -+ errcode, errmsg, |
365 | -+ headers |
366 | -+ ) |
367 | -+ self.verbose = verbose |
368 | -+ try: |
369 | -+ sock = h._conn.sock |
370 | -+ except AttributeError: |
371 | -+ sock = None |
372 | -+ |
373 | -+ # is_gzip |
374 | -+ if headers.has_key('accept-encoding') and headers['accept-encoding'] == 'gzip': |
375 | -+ return self._parse_gzipped_response(h.getfile(), sock) |
376 | -+ |
377 | -+ return self._parse_response(h.getfile(), sock) |
378 | -+ |
379 | -+ def _parse_gzipped_response(self, file, sock): |
380 | -+ p, u = self.getparser() |
381 | -+ response_chunks = [] |
382 | -+ while 1: |
383 | -+ if sock: |
384 | -+ chunk = sock.recv(1024) |
385 | -+ else: |
386 | -+ chunk = file.read(1024) |
387 | -+ if not chunk: |
388 | -+ break |
389 | -+ response_chunks.append(chunk) |
390 | -+ response = zlib.decompress("".join(response_chunks)) |
391 | -+ if self.verbose: |
392 | -+ print "body:", repr(response) |
393 | -+ p.feed(response) |
394 | -+ file.close() |
395 | -+ p.close() |
396 | -+ return u.close() |
397 | -+ |
398 | - |
399 | -=== modified file 'bin/service/http_server.py' |
400 | ---- bin/service/http_server.py 2011-01-18 23:50:33 +0000 |
401 | -+++ bin/service/http_server.py 2011-09-08 15:43:15 +0000 |
402 | -@@ -43,7 +43,9 @@ |
403 | - import xmlrpclib |
404 | - import logging |
405 | - |
406 | -+import SimpleXMLRPCServer |
407 | - from SimpleXMLRPCServer import SimpleXMLRPCDispatcher |
408 | -+import gzip_xmlrpclib |
409 | - |
410 | - try: |
411 | - import fcntl |
412 | -@@ -227,7 +229,7 @@ |
413 | - |
414 | - def init_servers(): |
415 | - global httpd, httpsd |
416 | -- if tools.config.get('xmlrpc'): |
417 | -+ if tools.config.get('xmlrpc') or tools.config.get('gzipxmlrpc'): |
418 | - httpd = HttpDaemon(tools.config.get('xmlrpc_interface', ''), |
419 | - int(tools.config.get('xmlrpc_port', 8069))) |
420 | - |
421 | -@@ -260,7 +262,6 @@ |
422 | - else: |
423 | - raise Exception("Incorrect protocol or no http services") |
424 | - |
425 | --import SimpleXMLRPCServer |
426 | - class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,HttpLogHandler,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): |
427 | - rpc_paths = [] |
428 | - protocol_version = 'HTTP/1.1' |
429 | -@@ -283,6 +284,27 @@ |
430 | - self.connection = dummyconn() |
431 | - self.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys()) |
432 | - |
433 | -+class GzipXMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,HttpLogHandler, gzip_xmlrpclib.GzipXMLRPCRequestHandler): |
434 | -+ rpc_paths = [] |
435 | -+ protocol_version = 'HTTP/1.1' |
436 | -+ _logger = logging.getLogger('xmlrpc') |
437 | -+ |
438 | -+ def _dispatch(self, method, params): |
439 | -+ try: |
440 | -+ service_name = self.path.split("/")[-1] |
441 | -+ return self.dispatch(service_name, method, params) |
442 | -+ except netsvc.OpenERPDispatcherException, e: |
443 | -+ raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback) |
444 | -+ |
445 | -+ def handle(self): |
446 | -+ pass |
447 | -+ |
448 | -+ def finish(self): |
449 | -+ pass |
450 | -+ |
451 | -+ def setup(self): |
452 | -+ self.connection = dummyconn() |
453 | -+ self.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys()) |
454 | - |
455 | - def init_xmlrpc(): |
456 | - if tools.config.get('xmlrpc', False): |
457 | -@@ -291,6 +313,12 @@ |
458 | - reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler)) |
459 | - logging.getLogger("web-services").info("Registered XML-RPC over HTTP") |
460 | - |
461 | -+ if tools.config.get('gzipxmlrpc', False): |
462 | -+ # Example of http file serving: |
463 | -+ # reg_http_service(HTTPDir('/test/',HTTPHandler)) |
464 | -+ reg_http_service(HTTPDir('/xmlrpc/', GzipXMLRPCRequestHandler)) |
465 | -+ logging.getLogger("web-services").info("Registered gzipped XML-RPC over HTTP") |
466 | -+ |
467 | - if tools.config.get('xmlrpcs', False) \ |
468 | - and not tools.config.get('xmlrpc', False): |
469 | - # only register at the secure server |
470 | - |
471 | -=== modified file 'bin/tools/config.py' |
472 | ---- bin/tools/config.py 2011-05-17 05:54:43 +0000 |
473 | -+++ bin/tools/config.py 2011-09-08 15:36:04 +0000 |
474 | -@@ -55,6 +55,7 @@ |
475 | - 'reportgz': False, |
476 | - 'netrpc': True, |
477 | - 'xmlrpc': True, |
478 | -+ 'gzipxmlrpc': False, |
479 | - 'xmlrpcs': True, |
480 | - 'translate_in': None, |
481 | - 'translate_out': None, |
482 | -@@ -119,6 +120,7 @@ |
483 | - group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", help="specify the TCP IP address for the XML-RPC protocol") |
484 | - group.add_option("--xmlrpc-port", dest="xmlrpc_port", help="specify the TCP port for the XML-RPC protocol", type="int") |
485 | - group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", help="disable the XML-RPC protocol") |
486 | -+ group.add_option("--gzipxmlrpc", dest="gzipxmlrpc", action="store_true", help="enable gzipping XML-RPC protocol (disable standard XMLRPC)") |
487 | - parser.add_option_group(group) |
488 | - |
489 | - title = "XML-RPC Secure Configuration" |
490 | -@@ -290,11 +292,15 @@ |
491 | - if self.options['pidfile'] in ('None', 'False'): |
492 | - self.options['pidfile'] = False |
493 | - |
494 | -+ if opt.gzipxmlrpc: |
495 | -+ # gzip prevails over standard |
496 | -+ opt.xmlrpc = False |
497 | -+ |
498 | - keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host', |
499 | - 'db_port', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout', |
500 | - 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', |
501 | - 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path', |
502 | -- 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone', |
503 | -+ 'netrpc', 'xmlrpc', 'gzipxmlrpc', 'syslog', 'without_demo', 'timezone', |
504 | - 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs', |
505 | - 'secure_cert_file', 'secure_pkey_file', |
506 | - 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix' |
507 | - |
508 | |
509 | === removed file 'bin/addons/patches/server_patch_netrpc_gzip.diff' |
510 | --- bin/addons/patches/server_patch_netrpc_gzip.diff 2012-04-04 10:18:51 +0000 |
511 | +++ bin/addons/patches/server_patch_netrpc_gzip.diff 1970-01-01 00:00:00 +0000 |
512 | @@ -1,156 +0,0 @@ |
513 | -=== modified file 'bin/service/netrpc_server.py' |
514 | ---- bin/service/netrpc_server.py 2011-01-18 23:50:33 +0000 |
515 | -+++ bin/service/netrpc_server.py 2011-09-08 22:58:33 +0000 |
516 | -@@ -35,7 +35,7 @@ |
517 | - import tools |
518 | - |
519 | - class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher): |
520 | -- def __init__(self, sock, threads): |
521 | -+ def __init__(self, sock, threads, is_gzip): |
522 | - spn = sock and sock.getpeername() |
523 | - spn = 'netrpc-client-%s:%s' % spn[0:2] |
524 | - threading.Thread.__init__(self, name=spn) |
525 | -@@ -44,6 +44,7 @@ |
526 | - # clients connection when they're idle for 20min. |
527 | - self.sock.settimeout(1200) |
528 | - self.threads = threads |
529 | -+ self.is_gzip = is_gzip |
530 | - |
531 | - def __del__(self): |
532 | - if self.sock: |
533 | -@@ -58,7 +59,7 @@ |
534 | - def run(self): |
535 | - self.running = True |
536 | - try: |
537 | -- ts = tiny_socket.mysocket(self.sock) |
538 | -+ ts = tiny_socket.mysocket(self.sock, self.is_gzip) |
539 | - except Exception: |
540 | - self.threads.remove(self) |
541 | - self.running = False |
542 | -@@ -101,7 +102,7 @@ |
543 | - |
544 | - |
545 | - class TinySocketServerThread(threading.Thread,netsvc.Server): |
546 | -- def __init__(self, interface, port, secure=False): |
547 | -+ def __init__(self, interface, port, secure=False, is_gzip=False): |
548 | - threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port) |
549 | - netsvc.Server.__init__(self) |
550 | - self.__port = port |
551 | -@@ -113,6 +114,7 @@ |
552 | - self.threads = [] |
553 | - netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, |
554 | - "starting NET-RPC service at %s port %d" % (interface or '0.0.0.0', port,)) |
555 | -+ self.is_gzip = is_gzip |
556 | - |
557 | - def run(self): |
558 | - try: |
559 | -@@ -122,7 +124,7 @@ |
560 | - if not fd_sets[0]: |
561 | - continue |
562 | - (clientsocket, address) = self.socket.accept() |
563 | -- ct = TinySocketClientThread(clientsocket, self.threads) |
564 | -+ ct = TinySocketClientThread(clientsocket, self.threads, self.is_gzip) |
565 | - clientsocket = None |
566 | - self.threads.append(ct) |
567 | - ct.start() |
568 | -@@ -162,7 +164,9 @@ |
569 | - |
570 | - def init_servers(): |
571 | - global netrpcd |
572 | -- if tools.config.get('netrpc', False): |
573 | -+ if tools.config.get('netrpc', False) or tools.config.get('netrpc_gzip', False): |
574 | - netrpcd = TinySocketServerThread( |
575 | - tools.config.get('netrpc_interface', ''), |
576 | -- int(tools.config.get('netrpc_port', 8070))) |
577 | -+ int(tools.config.get('netrpc_port', 8070)), |
578 | -+ is_gzip=tools.config.get('netrpc_gzip'), |
579 | -+ ) |
580 | - |
581 | -=== modified file 'bin/tiny_socket.py' |
582 | ---- bin/tiny_socket.py 2011-01-18 23:52:38 +0000 |
583 | -+++ bin/tiny_socket.py 2011-09-08 23:10:19 +0000 |
584 | -@@ -22,7 +22,9 @@ |
585 | - import socket |
586 | - import cPickle |
587 | - import cStringIO |
588 | --import marshal |
589 | -+ |
590 | -+import zlib |
591 | -+GZIP_MAGIC = '\x78\xda' # magic when max compression used |
592 | - |
593 | - class Myexception(Exception): |
594 | - """ |
595 | -@@ -39,7 +41,7 @@ |
596 | - |
597 | - class mysocket: |
598 | - |
599 | -- def __init__(self, sock=None): |
600 | -+ def __init__(self, sock=None, is_gzip=False): |
601 | - if sock is None: |
602 | - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
603 | - else: |
604 | -@@ -48,6 +50,7 @@ |
605 | - # prepare this socket for long operations: it may block for infinite |
606 | - # time, but should exit as soon as the net is down |
607 | - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) |
608 | -+ self.is_gzip = is_gzip |
609 | - |
610 | - def connect(self, host, port=False): |
611 | - if not port: |
612 | -@@ -61,6 +64,8 @@ |
613 | - |
614 | - def mysend(self, msg, exception=False, traceback=None): |
615 | - msg = cPickle.dumps([msg,traceback]) |
616 | -+ if self.is_gzip: |
617 | -+ msg = zlib.compress(msg, zlib.Z_BEST_COMPRESSION) |
618 | - self.sock.sendall('%8d%s%s' % (len(msg), exception and "1" or "0", msg)) |
619 | - |
620 | - def myreceive(self): |
621 | -@@ -82,6 +87,8 @@ |
622 | - if not chunk: |
623 | - raise socket.timeout |
624 | - msg = msg + chunk |
625 | -+ if msg.startswith(GZIP_MAGIC): |
626 | -+ msg = zlib.decompress(msg) |
627 | - msgio = cStringIO.StringIO(msg) |
628 | - unpickler = cPickle.Unpickler(msgio) |
629 | - unpickler.find_global = None |
630 | - |
631 | -=== modified file 'bin/tools/config.py' |
632 | ---- bin/tools/config.py 2011-05-17 05:54:43 +0000 |
633 | -+++ bin/tools/config.py 2011-09-08 22:17:09 +0000 |
634 | -@@ -54,7 +54,8 @@ |
635 | - 'db_maxconn': 64, |
636 | - 'reportgz': False, |
637 | - 'netrpc': True, |
638 | -+ 'netrpc_gzip': False, |
639 | - 'xmlrpc': True, |
640 | - 'xmlrpcs': True, |
641 | - 'translate_in': None, |
642 | - 'translate_out': None, |
643 | -@@ -138,6 +141,7 @@ |
644 | - group.add_option("--netrpc-interface", dest="netrpc_interface", help="specify the TCP IP address for the NETRPC protocol") |
645 | - group.add_option("--netrpc-port", dest="netrpc_port", help="specify the TCP port for the NETRPC protocol", type="int") |
646 | - group.add_option("--no-netrpc", dest="netrpc", action="store_false", help="disable the NETRPC protocol") |
647 | -+ group.add_option("--netrpc-gzip", dest="netrpc_gzip", action="store_true", help="enable gzipping the NETRPC protocol") |
648 | - parser.add_option_group(group) |
649 | - |
650 | - # Static HTTP |
651 | -@@ -290,11 +294,15 @@ |
652 | - if self.options['pidfile'] in ('None', 'False'): |
653 | - self.options['pidfile'] = False |
654 | - |
655 | -+ if opt.netrpc_gzip: |
656 | -+ # gzip prevails over standard |
657 | -+ opt.netrpc = False |
658 | -+ |
659 | - keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host', |
660 | - 'db_port', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout', |
661 | - 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', |
662 | - 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path', |
663 | -- 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone', |
664 | -+ 'netrpc', 'netrpc_gzip', 'xmlrpc', 'syslog', 'without_demo', 'timezone', |
665 | - 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs', |
666 | - 'secure_cert_file', 'secure_pkey_file', |
667 | - 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix' |
668 | - |
669 | |
670 | === removed file 'bin/addons/patches/web_patch_choose_server_port.diff' |
671 | --- bin/addons/patches/web_patch_choose_server_port.diff 2011-09-09 13:20:20 +0000 |
672 | +++ bin/addons/patches/web_patch_choose_server_port.diff 1970-01-01 00:00:00 +0000 |
673 | @@ -1,30 +0,0 @@ |
674 | -=== modified file 'openobject/commands.py' |
675 | ---- openobject/commands.py 2011-03-03 16:36:17 +0000 |
676 | -+++ openobject/commands.py 2011-08-22 15:00:29 +0000 |
677 | -@@ -50,6 +50,9 @@ |
678 | - help="configuration file", default=get_config_file()) |
679 | - parser.add_option("-a", "--address", help="host address, overrides server.socket_host") |
680 | - parser.add_option("-p", "--port", help="port number, overrides server.socket_port") |
681 | -+ parser.add_option("--openerp-host", dest="openerp_host", help="overrides openerp.server.host") |
682 | -+ parser.add_option("--openerp-port", dest="openerp_port", help="overrides openerp.server.port") |
683 | -+ parser.add_option("--openerp-protocol", dest="openerp_protocol", help="overrides openerp.server.protocol") |
684 | - parser.add_option("--no-static", dest="static", |
685 | - action="store_false", default=True, |
686 | - help="Disables serving static files through CherryPy") |
687 | -@@ -72,6 +75,15 @@ |
688 | - cherrypy.config['server.socket_port'] = int(options.port) |
689 | - except: |
690 | - pass |
691 | -+ if options.openerp_host: |
692 | -+ cherrypy.config['openerp.server.host'] = options.openerp_host |
693 | -+ if options.openerp_port: |
694 | -+ try: |
695 | -+ cherrypy.config['openerp.server.port'] = int(options.openerp_port) |
696 | -+ except: |
697 | -+ pass |
698 | -+ if options.openerp_protocol in ['http', 'https', 'socket']: |
699 | -+ cherrypy.config['openerp.server.protocol'] = options.openerp_protocol |
700 | - |
701 | - configure_babel() |
702 | - |
703 | - |
704 | |
705 | === removed file 'bin/addons/sync_client/gzip_xmlrpclib.py' |
706 | --- bin/addons/sync_client/gzip_xmlrpclib.py 2014-09-04 13:12:41 +0000 |
707 | +++ bin/addons/sync_client/gzip_xmlrpclib.py 1970-01-01 00:00:00 +0000 |
708 | @@ -1,114 +0,0 @@ |
709 | -import zlib |
710 | -import xmlrpclib |
711 | -from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler |
712 | -import logging |
713 | - |
714 | -from timeout_transport import TimeoutTransport, TimeoutSafeTransport |
715 | - |
716 | -# xmlrpc server side |
717 | - |
718 | -class GzipXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): |
719 | - |
720 | - def do_POST(self): |
721 | - if not(self.headers.has_key('accept-encoding') and self.headers['accept-encoding'] == 'gzip'): |
722 | - return SimpleXMLRPCRequestHandler.do_POST(self) |
723 | - else: |
724 | - if not self.is_rpc_path_valid(): |
725 | - self.report_404() |
726 | - return |
727 | - try: |
728 | - max_chunk_size = 10*1024*1024 |
729 | - size_remaining = int(self.headers["content-length"]) |
730 | - L = [] |
731 | - while size_remaining: |
732 | - chunk_size = min(size_remaining, max_chunk_size) |
733 | - L.append(self.rfile.read(chunk_size)) |
734 | - size_remaining -= len(L[-1]) |
735 | - data = ''.join(L) |
736 | - data = zlib.decompress(data) # is_gzip |
737 | - response = self.server._marshaled_dispatch( |
738 | - data, getattr(self, '_dispatch', None) |
739 | - ) |
740 | - except Exception, e: |
741 | - self.send_response(500) |
742 | - self.end_headers() |
743 | - else: |
744 | - response = zlib.compress(response, zlib.Z_BEST_COMPRESSION) # is_gzip |
745 | - self.send_response(200) |
746 | - self.send_header("Content-type", "text/xml") |
747 | - self.send_header("Content-length", str(len(response))) |
748 | - self.send_header("Accept-Encoding", "gzip") # is_gzip |
749 | - self.end_headers() |
750 | - self.wfile.write(response) |
751 | - self.wfile.flush() |
752 | - self.connection.shutdown(1) |
753 | - |
754 | -# xmlrpc client side |
755 | -class GzipTransport(TimeoutTransport): |
756 | - |
757 | - def __init__(self, timeout=None, *args, **kwargs): |
758 | - TimeoutTransport.__init__(self, timeout=timeout, *args, **kwargs) |
759 | - self._logger = logging.getLogger('xmlrpc.transport') |
760 | - |
761 | - def request(self, host, handler, request_body, verbose=0): |
762 | - h = self.make_connection(host) |
763 | - if verbose: |
764 | - h.set_debuglevel(1) |
765 | - self.send_request(h, handler, request_body) |
766 | - self.send_host(h, host) |
767 | - self.send_user_agent(h) |
768 | - |
769 | - # is_gzip |
770 | - h.putheader('Accept-Encoding', 'gzip') |
771 | - raw_size = len(request_body) |
772 | - request_body = zlib.compress(request_body, zlib.Z_BEST_COMPRESSION) |
773 | - gzipped_size = len(request_body) |
774 | - saving = 100*(float(raw_size-gzipped_size))/gzipped_size if gzipped_size else 0 |
775 | - self._logger.debug('payload size: raw %s, gzipped %s, saving %.2f%%', |
776 | - raw_size, gzipped_size, saving) |
777 | - |
778 | - self.send_content(h, request_body) |
779 | - errcode, errmsg, headers = h.getreply() |
780 | - if errcode != 200: |
781 | - raise xmlrpclib.ProtocolError( |
782 | - host + handler, |
783 | - errcode, errmsg, |
784 | - headers |
785 | - ) |
786 | - self.verbose = verbose |
787 | - try: |
788 | - sock = h._conn.sock |
789 | - except AttributeError: |
790 | - sock = None |
791 | - |
792 | - # is_gzip |
793 | - if headers.has_key('accept-encoding') and headers['accept-encoding'] == 'gzip': |
794 | - return self._parse_gzipped_response(h.getfile(), sock) |
795 | - return self._parse_response(h.getfile(), sock) |
796 | - |
797 | - def _parse_gzipped_response(self, file, sock): |
798 | - p, u = self.getparser() |
799 | - response_chunks = [] |
800 | - while 1: |
801 | - if sock: |
802 | - chunk = sock.recv(1024) |
803 | - else: |
804 | - chunk = file.read(1024) |
805 | - if not chunk: |
806 | - break |
807 | - response_chunks.append(chunk) |
808 | - response = zlib.decompress("".join(response_chunks)) |
809 | - if self.verbose: |
810 | - print "body:", repr(response) |
811 | - p.feed(response) |
812 | - file.close() |
813 | - p.close() |
814 | - return u.close() |
815 | - |
816 | -class GzipSafeTransport(TimeoutSafeTransport, GzipTransport): |
817 | - def __init__(self, timeout=None, *args, **kwargs): |
818 | - TimeoutSafeTransport.__init__(self, timeout=timeout, *args, **kwargs) |
819 | - self._logger = logging.getLogger('xmlrpc.transport') |
820 | - |
821 | - def request(self, host, handler, request_body, verbose=0): |
822 | - return GzipTransport.request(self, host, handler, request_body, verbose) |
823 | |
824 | === modified file 'bin/addons/sync_client/rpc.py' |
825 | --- bin/addons/sync_client/rpc.py 2016-11-15 15:32:01 +0000 |
826 | +++ bin/addons/sync_client/rpc.py 2017-05-22 10:09:36 +0000 |
827 | @@ -10,10 +10,10 @@ |
828 | import zlib |
829 | import xmlrpclib |
830 | from timeout_transport import TimeoutTransport |
831 | -from gzip_xmlrpclib import GzipTransport, GzipSafeTransport |
832 | from osv import osv |
833 | from tools.translate import _ |
834 | import tools |
835 | +import ssl |
836 | |
837 | try: |
838 | import cPickle as pickle |
839 | @@ -26,9 +26,6 @@ |
840 | import StringIO |
841 | |
842 | import logging |
843 | -#import logging.config |
844 | - |
845 | -#logging.config.fileConfig('logging.cfg') |
846 | |
847 | GZIP_MAGIC = '\x78\xda' # magic when max compression used |
848 | NB_RETRY = 10 |
849 | @@ -93,6 +90,8 @@ |
850 | def send(self, service_name, method, *args): |
851 | url = '%s/%s' % (self.url, service_name) |
852 | transport = TimeoutTransport(timeout=self.timeout) |
853 | + # Enable gzip on all payloads |
854 | + transport.encode_threshold = 0 |
855 | service = xmlrpclib.ServerProxy(url, allow_none=1, transport=transport) |
856 | return self._send(service, method, *args) |
857 | |
858 | @@ -113,36 +112,6 @@ |
859 | raise osv.except_osv(_('Error!'), "Unable to proceed for the following reason:\n%s" % (e.faultCode if hasattr(e, 'faultCode') else tools.ustr(e))) |
860 | |
861 | |
862 | -"""Modified version of xmlrcpclib.Transport.request (same in Python 2.4, 2.5, 2.6) |
863 | - to workaround Python bug http://bugs.python.org/issue1223 |
864 | - for Python versions before 2.6 |
865 | - This patch is inspired by http://www.cherrypy.org/ticket/743. |
866 | - See LP bug https://bugs.launchpad.net/openobject-client/+bug/673775 |
867 | -""" |
868 | -def fixed_request(self, host, handler, request_body, verbose=0): |
869 | - h = self.make_connection(host) |
870 | - if verbose: |
871 | - h.set_debuglevel(1) |
872 | - self.send_request(h, handler, request_body) |
873 | - self.send_host(h, host) |
874 | - self.send_user_agent(h) |
875 | - self.send_content(h, request_body) |
876 | - errcode, errmsg, headers = h.getreply() |
877 | - if errcode != 200: |
878 | - raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, |
879 | - headers) |
880 | - self.verbose = verbose |
881 | - # below we make sure to call parse_response() and |
882 | - # not _parse_response(), and don't pass the socket, |
883 | - # so it will have to use the file instead, and avoid |
884 | - # the problem of the original code. |
885 | - return self.parse_response(h.getfile()) |
886 | - |
887 | -# Rude monkey-patch to fix the SSL connection error in Python 2.5-, |
888 | -# as last resort solution to fix it all at once. |
889 | -if sys.version_info < (2,6): |
890 | - xmlrpclib.SafeTransport.request = fixed_request |
891 | - |
892 | class SecuredXmlRPCConnector(XmlRPCConnector): |
893 | """ |
894 | This class supports the XmlRPC protocol over HTTPS |
895 | @@ -155,33 +124,13 @@ |
896 | |
897 | def send(self, service_name, method, *args): |
898 | url = '%s/%s' % (self.url, service_name) |
899 | - service = xmlrpclib.ServerProxy(url, allow_none=1) |
900 | - return self._send(service, method, *args) |
901 | - |
902 | -class GzipXmlRPCConnector(XmlRPCConnector): |
903 | - """ |
904 | - This class supports the XmlRPC protocol with gzipped payload |
905 | - """ |
906 | - PROTOCOL = 'gzipxmlrpc' |
907 | - |
908 | - def send(self, service_name, method, *args): |
909 | - url = '%s/%s' % (self.url, service_name) |
910 | - gzip_transport = GzipTransport(timeout=self.timeout) |
911 | - service = xmlrpclib.ServerProxy(url, allow_none=1, transport=gzip_transport) |
912 | - return self._send(service, method, *args) |
913 | - |
914 | -class GzipXmlRPCSConnector(GzipXmlRPCConnector): |
915 | - PROTOCOL = 'gzipxmlrpcs' |
916 | - |
917 | - def __init__(self, hostname, port=8069, *args, **kwargs): |
918 | - GzipXmlRPCConnector.__init__(self, hostname, port, *args, **kwargs) |
919 | - self.url = 'https://%s:%s/xmlrpc' % (self.hostname, self.port) |
920 | - |
921 | - def send(self, service_name, method, *args): |
922 | - url = '%s/%s' % (self.url, service_name) |
923 | - gzip_safe_transport = GzipSafeTransport(timeout=self.timeout) |
924 | - service = xmlrpclib.ServerProxy(url, allow_none=1, transport=gzip_safe_transport) |
925 | - return getattr(service, method)(*args) |
926 | + # Decide whether to accept self-signed certificates |
927 | + ctx = ssl.create_default_context() |
928 | + if not tools.config.get('secure_verify', True): |
929 | + ctx.check_hostname = False |
930 | + ctx.verify_mode = ssl.CERT_NONE |
931 | + service = xmlrpclib.ServerProxy(url, allow_none=1, context=ctx) |
932 | + return self._send(service, method, *args) |
933 | |
934 | class NetRPC_Exception(Exception): |
935 | def __init__(self, faultCode, faultString): |
936 | @@ -217,11 +166,7 @@ |
937 | #self._logger.debug("rpc message : %s", msg) |
938 | msg = pickle.dumps([msg,traceback]) |
939 | if self.is_gzip: |
940 | - #raw_size = len(msg) |
941 | msg = zlib.compress(msg, zlib.Z_BEST_COMPRESSION) |
942 | - #gzipped_size = len(msg) |
943 | - #saving = 100*(float(raw_size-gzipped_size))/gzipped_size if gzipped_size else 0 |
944 | - #self._logger.debug('payload size: raw %s, gzipped %s, saving %.2f%%', raw_size, gzipped_size, saving) |
945 | size = len(msg) |
946 | self.sock.send('%8d' % size) |
947 | self.sock.send(exception and "1" or "0") |
948 | @@ -252,11 +197,7 @@ |
949 | raise RuntimeError, "socket connection broken" |
950 | msg = msg + chunk |
951 | if msg.startswith(GZIP_MAGIC): |
952 | - #gzipped_size = len(msg) |
953 | msg = zlib.decompress(msg) |
954 | - #raw_size = len(msg) |
955 | - #saving = 100*(float(raw_size-gzipped_size))/gzipped_size if gzipped_size else 0 |
956 | - #self._logger.debug('payload size: raw %s, gzipped %s, saving %.2f%%', raw_size, gzipped_size, saving) |
957 | res = SafeUnpickler.loads(msg) |
958 | |
959 | if isinstance(res[0],Exception): |
960 | |
961 | === modified file 'bin/addons/sync_client/sync_client.py' |
962 | --- bin/addons/sync_client/sync_client.py 2017-04-28 07:21:12 +0000 |
963 | +++ bin/addons/sync_client/sync_client.py 2017-05-22 10:09:36 +0000 |
964 | @@ -557,7 +557,7 @@ |
965 | if obj[model_field_name] not in model_field_dict: |
966 | model_field_dict[obj[model_field_name]] = set() |
967 | model_field_dict[obj[model_field_name]].update(eval(obj['arguments'])) |
968 | - |
969 | + |
970 | model_set = set(model_field_dict.keys()) |
971 | |
972 | def get_field_obj(model, field_name): |
973 | @@ -1354,13 +1354,10 @@ |
974 | return self.browse(cr, uid, ids, context=context)[0] |
975 | |
976 | def connector_factory(self, con): |
977 | - if con.protocol == 'xmlrpc': |
978 | + # xmlrpc now does gzip by default |
979 | + if con.protocol == 'xmlrpc' or con.protocol == 'gzipxmlrpc': |
980 | connector = rpc.XmlRPCConnector(con.host, con.port, timeout=con.timeout, retry=con.xmlrpc_retry) |
981 | - elif con.protocol == 'gzipxmlrpc': |
982 | - connector = rpc.GzipXmlRPCConnector(con.host, con.port, timeout=con.timeout, retry=con.xmlrpc_retry) |
983 | - elif con.protocol == 'gzipxmlrpcs': |
984 | - connector = rpc.GzipXmlRPCSConnector(con.host, con.port, timeout=con.timeout, retry=con.xmlrpc_retry) |
985 | - elif con.protocol == 'xmlrpcs': |
986 | + elif con.protocol == 'xmlrpcs' or con.protocol == 'gzipxmlrpcs': |
987 | connector = rpc.SecuredXmlRPCConnector(con.host, con.port, timeout=con.timeout, retry=con.xmlrpc_retry) |
988 | elif con.protocol == 'netrpc': |
989 | connector = rpc.NetRPCConnector(con.host, con.port, timeout=con.timeout, retry=con.netrpc_retry) |
990 | |
991 | === modified file 'bin/addons/sync_client/timeout_transport.py' |
992 | --- bin/addons/sync_client/timeout_transport.py 2014-10-29 11:09:22 +0000 |
993 | +++ bin/addons/sync_client/timeout_transport.py 2017-05-22 10:09:36 +0000 |
994 | @@ -1,6 +1,5 @@ |
995 | import httplib |
996 | import xmlrpclib |
997 | -import sys |
998 | |
999 | class TimeoutHTTPConnection(httplib.HTTPConnection): |
1000 | |
1001 | @@ -12,13 +11,6 @@ |
1002 | def set_timeout(self, timeout): |
1003 | self.timeout = timeout |
1004 | |
1005 | -class TimeoutHTTP(httplib.HTTP): |
1006 | - |
1007 | - _connection_class = TimeoutHTTPConnection |
1008 | - |
1009 | - def set_timeout(self, timeout): |
1010 | - self._conn.timeout = timeout |
1011 | - |
1012 | class TimeoutTransport(xmlrpclib.Transport): |
1013 | |
1014 | def __init__(self, timeout=None, *args, **kwargs): |
1015 | @@ -27,10 +19,7 @@ |
1016 | |
1017 | def make_connection(self, host): |
1018 | chost, self._extra_headers, _ = self.get_host_info(host) |
1019 | - if sys.version_info < (2,7): |
1020 | - self._connection = host, TimeoutHTTP(host) |
1021 | - else: |
1022 | - self._connection = host, TimeoutHTTPConnection(chost) |
1023 | + self._connection = host, TimeoutHTTPConnection(chost) |
1024 | self._connection[1].set_timeout(self.timeout) |
1025 | return self._connection[1] |
1026 | |
1027 | @@ -41,13 +30,6 @@ |
1028 | if self.timeout is not None: |
1029 | self.sock.settimeout(self.timeout) |
1030 | |
1031 | -class TimeoutHTTPS(httplib.HTTPS): |
1032 | - |
1033 | - _connection_class = TimeoutHTTPSConnection |
1034 | - |
1035 | - def set_timeout(self, timeout): |
1036 | - self._conn.timeout = timeout |
1037 | - |
1038 | class TimeoutSafeTransport(xmlrpclib.SafeTransport): |
1039 | |
1040 | def __init__(self, timeout=None, *args, **kwargs): |
1041 | @@ -55,8 +37,7 @@ |
1042 | self.timeout = timeout |
1043 | |
1044 | def make_connection(self, host): |
1045 | - # TODO: check make_connection for python > 2.6 |
1046 | - conn = TimeoutHTTPS(host) |
1047 | - conn.set_timeout(self.timeout) |
1048 | - return conn |
1049 | + self._connection = host, TimeoutHTTPSConnection(host) |
1050 | + self._connection[1].set_timeout(self.timeout) |
1051 | + return self._connection[1] |
1052 | |
1053 | |
1054 | === modified file 'bin/addons/update_client/wizard.py' |
1055 | --- bin/addons/update_client/wizard.py 2016-06-06 12:37:19 +0000 |
1056 | +++ bin/addons/update_client/wizard.py 2017-05-22 10:09:36 +0000 |
1057 | @@ -1,26 +1,20 @@ |
1058 | from __future__ import with_statement |
1059 | -import sys |
1060 | import os |
1061 | import logging |
1062 | import urllib2 |
1063 | import threading |
1064 | from base64 import b64decode |
1065 | -import hashlib |
1066 | from StringIO import StringIO |
1067 | import tarfile |
1068 | |
1069 | from osv import osv, fields |
1070 | from tools.translate import _ |
1071 | -import tools |
1072 | import base64 |
1073 | from tools import config |
1074 | -from updater import * |
1075 | +import updater |
1076 | |
1077 | from version import IMPORT_PATCH_SUCCESS, IMPORT_PATCH_INVALID, IMPORT_PATCH_UNKNOWN, IMPORT_PATCH_IGNORED |
1078 | |
1079 | -assert sys.version_info >= (2, 6), \ |
1080 | - "timeout argument of urllib2.urlopen() needs Python >= 2.6" |
1081 | - |
1082 | th_local = threading.local() |
1083 | |
1084 | def is_online(): |
1085 | @@ -46,7 +40,7 @@ |
1086 | |
1087 | def restart(self, cr, uid, ids, context=None): |
1088 | os.chdir( config['root_path'] ) |
1089 | - restart_server() |
1090 | + updater.restart_server() |
1091 | return {'type': 'ir.actions.act_window_close'} |
1092 | |
1093 | def _get_error(self, cr, uid, context=None): |
1094 | @@ -87,7 +81,7 @@ |
1095 | """Actualy, prepare the upgrade to be done at server restart""" |
1096 | # backup before patching |
1097 | self.pool.get('backup.config').exp_dump_for_state(cr, uid, |
1098 | - 'beforepatching', context=context, force=True) |
1099 | + 'beforepatching', context=context, force=True) |
1100 | |
1101 | connection_module = self.pool.get("sync.client.sync_server_connection") |
1102 | proxy = connection_module.get_connection(cr, uid, "sync.server.sync_manager") |
1103 | @@ -95,7 +89,7 @@ |
1104 | # SYNC_SERVER connection credentials to be able to automatically |
1105 | # reconnect to the SYNC_SERVER after an upgrade |
1106 | automatic_patching = sync_type=='automatic' and\ |
1107 | - connection_module.is_automatic_patching_allowed(cr, uid) |
1108 | + connection_module.is_automatic_patching_allowed(cr, uid) |
1109 | if automatic_patching: |
1110 | password = connection_module._get_password(cr, uid, [proxy], None, None, None).values()[0] |
1111 | password = base64.encodestring(password) |
1112 | @@ -118,7 +112,7 @@ |
1113 | }, context=context) |
1114 | next_revisions = self.pool.get('sync_client.version')._get_next_revisions(cr, uid, context=context) |
1115 | ## Prepare |
1116 | - (status, message, values) = do_prepare(cr, next_revisions) |
1117 | + (status, message, values) = updater.do_prepare(cr, next_revisions) |
1118 | wiz_value = {'message':_(message)} |
1119 | if status in ('corrupt','missing'): |
1120 | wiz_value['state'] = 'need-download' |
1121 | @@ -150,7 +144,7 @@ |
1122 | tar = tarfile.open(fileobj=StringIO(b64decode(wiz.patch))) |
1123 | for content in (fh.read() for fh in (tar.extractfile(name) for name in tar.getnames()) if fh): |
1124 | status = revisions.import_patch(cr, uid, |
1125 | - decoded=content, context=context) |
1126 | + decoded=content, context=context) |
1127 | cr.commit() |
1128 | if status == IMPORT_PATCH_SUCCESS: count += 1 |
1129 | elif status == IMPORT_PATCH_INVALID: pass |
1130 | @@ -179,10 +173,10 @@ |
1131 | self.write(cr, uid, ids, {'message':text}, context=context) |
1132 | else: |
1133 | self.write(cr, uid, ids, { |
1134 | - 'message' : _("%d revision(s) has been imported.") % count + \ |
1135 | - "\n\n" + self._generate(cr, uid, context=context), |
1136 | - 'state' : self._get_state(cr, uid, context=context), |
1137 | - }, context=context) |
1138 | + 'message' : _("%d revision(s) has been imported.") % count + \ |
1139 | + "\n\n" + self._generate(cr, uid, context=context), |
1140 | + 'state' : self._get_state(cr, uid, context=context), |
1141 | + }, context=context) |
1142 | |
1143 | return {} |
1144 | |
1145 | @@ -204,8 +198,8 @@ |
1146 | text += _("No revision has been applied yet.") |
1147 | text += "\n" |
1148 | next_revisions = revisions.browse(cr, uid, |
1149 | - revisions._get_next_revisions(cr, uid, context=context), |
1150 | - context=context) |
1151 | + revisions._get_next_revisions(cr, uid, context=context), |
1152 | + context=context) |
1153 | if next_revisions: |
1154 | text += "\n" |
1155 | if len(next_revisions) == 1: |
1156 | @@ -246,20 +240,20 @@ |
1157 | entity = self.pool.get('sync.client.entity').get_entity(cr, uid) |
1158 | if entity.usb_instance_type == 'remote_warehouse': |
1159 | return True |
1160 | - except Exception, e: |
1161 | + except Exception: |
1162 | pass |
1163 | return False |
1164 | |
1165 | _columns = { |
1166 | 'message' : fields.text("Caption", readonly=True), |
1167 | 'state' : fields.selection([ |
1168 | - ('need-provide-manually','Need To Provide Manually The Files'), |
1169 | - ('need-download','Need Download'), |
1170 | - ('up-to-date','Up-To-Date'), |
1171 | - ('need-install','Need Install'), |
1172 | - ('need-restart','Need Restart'), |
1173 | - ('blocked','Blocked') |
1174 | - ], string="Status"), |
1175 | + ('need-provide-manually','Need To Provide Manually The Files'), |
1176 | + ('need-download','Need Download'), |
1177 | + ('up-to-date','Up-To-Date'), |
1178 | + ('need-install','Need Install'), |
1179 | + ('need-restart','Need Restart'), |
1180 | + ('blocked','Blocked') |
1181 | + ], string="Status"), |
1182 | 'patch' : fields.binary("Patch"), |
1183 | 'error': fields.text('Error', readonly="1"), |
1184 | } |
1185 | |
1186 | === removed file 'bin/service/gzip_xmlrpclib.py' |
1187 | --- bin/service/gzip_xmlrpclib.py 2014-09-04 13:14:34 +0000 |
1188 | +++ bin/service/gzip_xmlrpclib.py 1970-01-01 00:00:00 +0000 |
1189 | @@ -1,45 +0,0 @@ |
1190 | -import zlib |
1191 | -import xmlrpclib |
1192 | -from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler |
1193 | -import logging |
1194 | - |
1195 | -# xmlrpc server side |
1196 | - |
1197 | -class GzipXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): |
1198 | - |
1199 | - def do_POST(self): |
1200 | - if not(self.headers.has_key('accept-encoding') and self.headers['accept-encoding'] == 'gzip'): |
1201 | - return SimpleXMLRPCRequestHandler.do_POST(self) |
1202 | - else: |
1203 | - if not self.is_rpc_path_valid(): |
1204 | - self.report_404() |
1205 | - return |
1206 | - try: |
1207 | - max_chunk_size = 10*1024*1024 |
1208 | - size_remaining = int(self.headers["content-length"]) |
1209 | - L = [] |
1210 | - while size_remaining: |
1211 | - chunk_size = min(size_remaining, max_chunk_size) |
1212 | - L.append(self.rfile.read(chunk_size)) |
1213 | - size_remaining -= len(L[-1]) |
1214 | - data = ''.join(L) |
1215 | - data = zlib.decompress(data) # is_gzip |
1216 | - response = self.server._marshaled_dispatch( |
1217 | - data, getattr(self, '_dispatch', None) |
1218 | - ) |
1219 | - except Exception, e: |
1220 | - self.send_response(500) |
1221 | - self.end_headers() |
1222 | - else: |
1223 | - response = zlib.compress(response, zlib.Z_BEST_COMPRESSION) # is_gzip |
1224 | - self.send_response(200) |
1225 | - self.send_header("Content-type", "text/xml") |
1226 | - self.send_header("Content-length", str(len(response))) |
1227 | - self.send_header("Accept-Encoding", "gzip") # is_gzip |
1228 | - self.end_headers() |
1229 | - self.wfile.write(response) |
1230 | - self.wfile.flush() |
1231 | - self.connection.shutdown(1) |
1232 | - |
1233 | -# xmlrpc client side |
1234 | - |
1235 | |
1236 | === modified file 'bin/service/http_server.py' |
1237 | --- bin/service/http_server.py 2017-03-17 13:33:26 +0000 |
1238 | +++ bin/service/http_server.py 2017-05-22 10:09:36 +0000 |
1239 | @@ -46,8 +46,8 @@ |
1240 | import pooler |
1241 | |
1242 | import SimpleXMLRPCServer |
1243 | +import BaseHTTPServer |
1244 | from SimpleXMLRPCServer import SimpleXMLRPCDispatcher |
1245 | -import gzip_xmlrpclib |
1246 | |
1247 | try: |
1248 | import fcntl |
1249 | @@ -59,7 +59,7 @@ |
1250 | except ImportError: |
1251 | class SSLError(Exception): pass |
1252 | |
1253 | -class ThreadedHTTPServer(websrv_lib.ConnThreadingMixIn, SimpleXMLRPCDispatcher, websrv_lib.HTTPServer): |
1254 | +class ThreadedHTTPServer(websrv_lib.ConnThreadingMixIn, SimpleXMLRPCDispatcher, BaseHTTPServer.HTTPServer): |
1255 | """ A threaded httpd server, with all the necessary functionality for us. |
1256 | |
1257 | It also inherits the xml-rpc dispatcher, so that some xml-rpc functions |
1258 | @@ -76,7 +76,7 @@ |
1259 | self.logRequests = logRequests |
1260 | |
1261 | SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) |
1262 | - websrv_lib.HTTPServer.__init__(self, addr, requestHandler) |
1263 | + BaseHTTPServer.HTTPServer.__init__(self, addr, requestHandler) |
1264 | |
1265 | self.numThreads = 0 |
1266 | self.proto = proto |
1267 | @@ -231,11 +231,11 @@ |
1268 | |
1269 | def init_servers(): |
1270 | global httpd, httpsd |
1271 | - if tools.config.get('xmlrpc') or tools.config.get('gzipxmlrpc'): |
1272 | + if tools.config.get('xmlrpc'): |
1273 | httpd = HttpDaemon(tools.config.get('xmlrpc_interface', ''), |
1274 | int(tools.config.get('xmlrpc_port', 8069))) |
1275 | |
1276 | - if tools.config.get('xmlrpcs') or tools.config.get('gzipxmlrpcs'): |
1277 | + if tools.config.get('xmlrpcs'): |
1278 | httpsd = HttpSDaemon(tools.config.get('xmlrpcs_interface', ''), |
1279 | int(tools.config.get('xmlrpcs_port', 8071))) |
1280 | |
1281 | @@ -272,6 +272,11 @@ |
1282 | |
1283 | xmlrpc_uid_cache = {} |
1284 | |
1285 | + def __init__(self, conn, addr, svr): |
1286 | + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.__init__(self, conn, addr, svr) |
1287 | + # Always use gzip. |
1288 | + self.encode_threshold = 0 |
1289 | + |
1290 | def get_uid_list2log(self, db_name): |
1291 | ''' |
1292 | return a list of user id for which the xmlrpc requests should be logged |
1293 | @@ -319,29 +324,6 @@ |
1294 | self.connection = websrv_lib.dummyconn() |
1295 | self.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys()) |
1296 | |
1297 | - |
1298 | -class GzipXMLRPCRequestHandler(netsvc.OpenERPDispatcher,websrv_lib.FixSendError,HttpLogHandler, gzip_xmlrpclib.GzipXMLRPCRequestHandler): |
1299 | - rpc_paths = [] |
1300 | - protocol_version = 'HTTP/1.1' |
1301 | - _logger = logging.getLogger('xmlrpc') |
1302 | - |
1303 | - def _dispatch(self, method, params): |
1304 | - try: |
1305 | - service_name = self.path.split("/")[-1] |
1306 | - return self.dispatch(service_name, method, params) |
1307 | - except netsvc.OpenERPDispatcherException, e: |
1308 | - raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback) |
1309 | - |
1310 | - def handle(self): |
1311 | - pass |
1312 | - |
1313 | - def finish(self): |
1314 | - pass |
1315 | - |
1316 | - def setup(self): |
1317 | - self.connection = websrv_lib.dummyconn() |
1318 | - self.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys()) |
1319 | - |
1320 | def init_xmlrpc(): |
1321 | if tools.config.get('xmlrpc', False): |
1322 | # Example of http file serving: |
1323 | @@ -349,16 +331,9 @@ |
1324 | reg_http_service(websrv_lib.HTTPDir('/xmlrpc/', XMLRPCRequestHandler)) |
1325 | logging.getLogger("web-services").info("Registered XML-RPC over HTTP") |
1326 | |
1327 | - if tools.config.get('gzipxmlrpc', False): |
1328 | - # Example of http file serving: |
1329 | - # reg_http_service(HTTPDir('/test/',HTTPHandler)) |
1330 | - reg_http_service(websrv_lib.HTTPDir('/xmlrpc/', GzipXMLRPCRequestHandler)) |
1331 | - logging.getLogger("web-services").info("Registered gzipped XML-RPC over HTTP") |
1332 | - |
1333 | - if (tools.config.get('xmlrpcs', False) or tools.config.get('gzipxmlrpcs', False) ) \ |
1334 | - and not tools.config.get('xmlrpc', False): |
1335 | + if (tools.config.get('xmlrpcs', False), False) and not tools.config.get('xmlrpc', False): |
1336 | # only register at the secure server |
1337 | - reg_http_service(websrv_lib.HTTPDir('/xmlrpc/', GzipXMLRPCRequestHandler), True) |
1338 | + reg_http_service(websrv_lib.HTTPDir('/xmlrpc/', XMLRPCRequestHandler), True) |
1339 | logging.getLogger("web-services").info("Registered XML-RPC over HTTPS only") |
1340 | |
1341 | class StaticHTTPHandler(HttpLogHandler, websrv_lib.FixSendError, websrv_lib.HttpOptions, websrv_lib.HTTPHandler): |
1342 | |
1343 | === modified file 'bin/service/websrv_lib.py' |
1344 | --- bin/service/websrv_lib.py 2011-01-18 23:50:33 +0000 |
1345 | +++ bin/service/websrv_lib.py 2017-05-22 10:09:36 +0000 |
1346 | @@ -32,7 +32,7 @@ |
1347 | import base64 |
1348 | import errno |
1349 | import SocketServer |
1350 | -from BaseHTTPServer import * |
1351 | +import BaseHTTPServer |
1352 | from SimpleHTTPServer import SimpleHTTPRequestHandler |
1353 | |
1354 | class AuthRequiredExc(Exception): |
1355 | @@ -184,7 +184,7 @@ |
1356 | self.end_headers() |
1357 | if hasattr(self, '_flush'): |
1358 | self._flush() |
1359 | - |
1360 | + |
1361 | if self.command != 'HEAD' and code >= 200 and code not in (204, 304): |
1362 | self.wfile.write(content) |
1363 | |
1364 | @@ -217,16 +217,16 @@ |
1365 | |
1366 | def _prep_OPTIONS(self, opts): |
1367 | """Prepare the OPTIONS response, if needed |
1368 | - |
1369 | + |
1370 | Sometimes, like in special DAV folders, the OPTIONS may contain |
1371 | extra keywords, perhaps also dependant on the request url. |
1372 | @param the options already. MUST be copied before being altered |
1373 | @return the updated options. |
1374 | - |
1375 | + |
1376 | """ |
1377 | return opts |
1378 | |
1379 | -class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler): |
1380 | +class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPServer.BaseHTTPRequestHandler): |
1381 | """ this is a multiple handler, that will dispatch each request |
1382 | to a nested handler, iff it matches |
1383 | |
1384 | @@ -311,7 +311,7 @@ |
1385 | if hasattr(fore, '_flush'): |
1386 | fore._flush() |
1387 | return |
1388 | - |
1389 | + |
1390 | if fore.close_connection: |
1391 | # print "Closing connection because of handler" |
1392 | self.close_connection = fore.close_connection |
1393 | @@ -364,7 +364,7 @@ |
1394 | self.close_connection = 0 |
1395 | if version_number >= (2, 0): |
1396 | self.send_error(505, |
1397 | - "Invalid HTTP Version (%s)" % base_version_number) |
1398 | + "Invalid HTTP Version (%s)" % base_version_number) |
1399 | return False |
1400 | elif len(words) == 2: |
1401 | [command, path] = words |
1402 | @@ -397,14 +397,14 @@ |
1403 | self.log_message("Could not parse rawline.") |
1404 | return |
1405 | # self.parse_request(): # Do NOT parse here. the first line should be the only |
1406 | - |
1407 | + |
1408 | if self.path == '*' and self.command == 'OPTIONS': |
1409 | # special handling of path='*', must not use any vdir at all. |
1410 | if not self.parse_request(): |
1411 | return |
1412 | self.do_OPTIONS() |
1413 | return |
1414 | - |
1415 | + |
1416 | for vdir in self.server.vdirs: |
1417 | p = vdir.matches(self.path) |
1418 | if p == False: |
1419 | @@ -426,7 +426,7 @@ |
1420 | except IOError, e: |
1421 | if e.errno == errno.EPIPE: |
1422 | self.log_message("Could not complete request %s," \ |
1423 | - "client closed connection", self.rlpath.rstrip()) |
1424 | + "client closed connection", self.rlpath.rstrip()) |
1425 | else: |
1426 | raise |
1427 | return |
1428 | @@ -459,10 +459,10 @@ |
1429 | certfile, keyfile = self.getcert_fnames() |
1430 | try: |
1431 | self.connection = ssl.wrap_socket(self.request, |
1432 | - server_side=True, |
1433 | - certfile=certfile, |
1434 | - keyfile=keyfile, |
1435 | - ssl_version=ssl.PROTOCOL_SSLv23) |
1436 | + server_side=True, |
1437 | + certfile=certfile, |
1438 | + keyfile=keyfile, |
1439 | + ssl_version=ssl.PROTOCOL_TLSv1_2) |
1440 | self.rfile = self.connection.makefile('rb', self.rbufsize) |
1441 | self.wfile = self.connection.makefile('wb', self.wbufsize) |
1442 | self.log_message("Secure %s connection from %s",self.connection.cipher(),self.client_address) |
1443 | |
1444 | === modified file 'bin/tools/misc.py' |
1445 | --- bin/tools/misc.py 2017-02-21 13:51:44 +0000 |
1446 | +++ bin/tools/misc.py 2017-05-22 10:09:36 +0000 |
1447 | @@ -46,10 +46,7 @@ |
1448 | from itertools import islice, izip |
1449 | from lxml import etree |
1450 | from which import which |
1451 | -if sys.version_info[:2] < (2, 4): |
1452 | - from threadinglocal import local |
1453 | -else: |
1454 | - from threading import local |
1455 | +from threading import local |
1456 | try: |
1457 | from html2text import html2text |
1458 | except ImportError: |
1459 | @@ -1151,8 +1148,6 @@ |
1460 | |
1461 | |
1462 | def exception_to_unicode(e): |
1463 | - if (sys.version_info[:2] < (2,6)) and hasattr(e, 'message'): |
1464 | - return ustr(e.message) |
1465 | if hasattr(e, 'args'): |
1466 | return "\n".join((ustr(a) for a in e.args)) |
1467 | try: |
1468 | |
1469 | === modified file 'bin/updater.py' |
1470 | --- bin/updater.py 2017-04-12 13:47:39 +0000 |
1471 | +++ bin/updater.py 2017-05-22 10:09:36 +0000 |
1472 | @@ -12,11 +12,7 @@ |
1473 | from base64 import b64decode |
1474 | from StringIO import StringIO |
1475 | import logging |
1476 | - |
1477 | -if sys.version_info >= (2, 6, 6): |
1478 | - from zipfile import ZipFile |
1479 | -else: |
1480 | - from zipfile266 import ZipFile |
1481 | +from zipfile import ZipFile |
1482 | |
1483 | __all__ = ('isset_lock', 'server_version', 'base_version', 'do_prepare', 'base_module_upgrade', 'restart_server') |
1484 | |
1485 | |
1486 | === removed file 'bin/zipfile266.py' |
1487 | --- bin/zipfile266.py 2012-11-22 13:34:36 +0000 |
1488 | +++ bin/zipfile266.py 1970-01-01 00:00:00 +0000 |
1489 | @@ -1,1409 +0,0 @@ |
1490 | -""" |
1491 | -Read and write ZIP files. |
1492 | -""" |
1493 | -import struct, os, time, sys, shutil |
1494 | -import binascii, cStringIO, stat |
1495 | - |
1496 | -try: |
1497 | - import zlib # We may need its compression method |
1498 | - crc32 = zlib.crc32 |
1499 | -except ImportError: |
1500 | - zlib = None |
1501 | - crc32 = binascii.crc32 |
1502 | - |
1503 | -__all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile", |
1504 | - "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ] |
1505 | - |
1506 | -class BadZipfile(Exception): |
1507 | - pass |
1508 | - |
1509 | - |
1510 | -class LargeZipFile(Exception): |
1511 | - """ |
1512 | - Raised when writing a zipfile, the zipfile requires ZIP64 extensions |
1513 | - and those extensions are disabled. |
1514 | - """ |
1515 | - |
1516 | -error = BadZipfile # The exception raised by this module |
1517 | - |
1518 | -ZIP64_LIMIT = (1 << 31) - 1 |
1519 | -ZIP_FILECOUNT_LIMIT = 1 << 16 |
1520 | -ZIP_MAX_COMMENT = (1 << 16) - 1 |
1521 | - |
1522 | -# constants for Zip file compression methods |
1523 | -ZIP_STORED = 0 |
1524 | -ZIP_DEFLATED = 8 |
1525 | -# Other ZIP compression methods not supported |
1526 | - |
1527 | -# Below are some formats and associated data for reading/writing headers using |
1528 | -# the struct module. The names and structures of headers/records are those used |
1529 | -# in the PKWARE description of the ZIP file format: |
1530 | -# http://www.pkware.com/documents/casestudies/APPNOTE.TXT |
1531 | -# (URL valid as of January 2008) |
1532 | - |
1533 | -# The "end of central directory" structure, magic number, size, and indices |
1534 | -# (section V.I in the format document) |
1535 | -structEndArchive = "<4s4H2LH" |
1536 | -stringEndArchive = "PK\005\006" |
1537 | -sizeEndCentDir = struct.calcsize(structEndArchive) |
1538 | - |
1539 | -_ECD_SIGNATURE = 0 |
1540 | -_ECD_DISK_NUMBER = 1 |
1541 | -_ECD_DISK_START = 2 |
1542 | -_ECD_ENTRIES_THIS_DISK = 3 |
1543 | -_ECD_ENTRIES_TOTAL = 4 |
1544 | -_ECD_SIZE = 5 |
1545 | -_ECD_OFFSET = 6 |
1546 | -_ECD_COMMENT_SIZE = 7 |
1547 | -# These last two indices are not part of the structure as defined in the |
1548 | -# spec, but they are used internally by this module as a convenience |
1549 | -_ECD_COMMENT = 8 |
1550 | -_ECD_LOCATION = 9 |
1551 | - |
1552 | -# The "central directory" structure, magic number, size, and indices |
1553 | -# of entries in the structure (section V.F in the format document) |
1554 | -structCentralDir = "<4s4B4HL2L5H2L" |
1555 | -stringCentralDir = "PK\001\002" |
1556 | -sizeCentralDir = struct.calcsize(structCentralDir) |
1557 | - |
1558 | -# indexes of entries in the central directory structure |
1559 | -_CD_SIGNATURE = 0 |
1560 | -_CD_CREATE_VERSION = 1 |
1561 | -_CD_CREATE_SYSTEM = 2 |
1562 | -_CD_EXTRACT_VERSION = 3 |
1563 | -_CD_EXTRACT_SYSTEM = 4 |
1564 | -_CD_FLAG_BITS = 5 |
1565 | -_CD_COMPRESS_TYPE = 6 |
1566 | -_CD_TIME = 7 |
1567 | -_CD_DATE = 8 |
1568 | -_CD_CRC = 9 |
1569 | -_CD_COMPRESSED_SIZE = 10 |
1570 | -_CD_UNCOMPRESSED_SIZE = 11 |
1571 | -_CD_FILENAME_LENGTH = 12 |
1572 | -_CD_EXTRA_FIELD_LENGTH = 13 |
1573 | -_CD_COMMENT_LENGTH = 14 |
1574 | -_CD_DISK_NUMBER_START = 15 |
1575 | -_CD_INTERNAL_FILE_ATTRIBUTES = 16 |
1576 | -_CD_EXTERNAL_FILE_ATTRIBUTES = 17 |
1577 | -_CD_LOCAL_HEADER_OFFSET = 18 |
1578 | - |
1579 | -# The "local file header" structure, magic number, size, and indices |
1580 | -# (section V.A in the format document) |
1581 | -structFileHeader = "<4s2B4HL2L2H" |
1582 | -stringFileHeader = "PK\003\004" |
1583 | -sizeFileHeader = struct.calcsize(structFileHeader) |
1584 | - |
1585 | -_FH_SIGNATURE = 0 |
1586 | -_FH_EXTRACT_VERSION = 1 |
1587 | -_FH_EXTRACT_SYSTEM = 2 |
1588 | -_FH_GENERAL_PURPOSE_FLAG_BITS = 3 |
1589 | -_FH_COMPRESSION_METHOD = 4 |
1590 | -_FH_LAST_MOD_TIME = 5 |
1591 | -_FH_LAST_MOD_DATE = 6 |
1592 | -_FH_CRC = 7 |
1593 | -_FH_COMPRESSED_SIZE = 8 |
1594 | -_FH_UNCOMPRESSED_SIZE = 9 |
1595 | -_FH_FILENAME_LENGTH = 10 |
1596 | -_FH_EXTRA_FIELD_LENGTH = 11 |
1597 | - |
1598 | -# The "Zip64 end of central directory locator" structure, magic number, and size |
1599 | -structEndArchive64Locator = "<4sLQL" |
1600 | -stringEndArchive64Locator = "PK\x06\x07" |
1601 | -sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator) |
1602 | - |
1603 | -# The "Zip64 end of central directory" record, magic number, size, and indices |
1604 | -# (section V.G in the format document) |
1605 | -structEndArchive64 = "<4sQ2H2L4Q" |
1606 | -stringEndArchive64 = "PK\x06\x06" |
1607 | -sizeEndCentDir64 = struct.calcsize(structEndArchive64) |
1608 | - |
1609 | -_CD64_SIGNATURE = 0 |
1610 | -_CD64_DIRECTORY_RECSIZE = 1 |
1611 | -_CD64_CREATE_VERSION = 2 |
1612 | -_CD64_EXTRACT_VERSION = 3 |
1613 | -_CD64_DISK_NUMBER = 4 |
1614 | -_CD64_DISK_NUMBER_START = 5 |
1615 | -_CD64_NUMBER_ENTRIES_THIS_DISK = 6 |
1616 | -_CD64_NUMBER_ENTRIES_TOTAL = 7 |
1617 | -_CD64_DIRECTORY_SIZE = 8 |
1618 | -_CD64_OFFSET_START_CENTDIR = 9 |
1619 | - |
1620 | -def is_zipfile(filename): |
1621 | - """Quickly see if file is a ZIP file by checking the magic number.""" |
1622 | - try: |
1623 | - fpin = open(filename, "rb") |
1624 | - endrec = _EndRecData(fpin) |
1625 | - fpin.close() |
1626 | - if endrec: |
1627 | - return True # file has correct magic number |
1628 | - except IOError: |
1629 | - pass |
1630 | - return False |
1631 | - |
1632 | -def _EndRecData64(fpin, offset, endrec): |
1633 | - """ |
1634 | - Read the ZIP64 end-of-archive records and use that to update endrec |
1635 | - """ |
1636 | - fpin.seek(offset - sizeEndCentDir64Locator, 2) |
1637 | - data = fpin.read(sizeEndCentDir64Locator) |
1638 | - sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) |
1639 | - if sig != stringEndArchive64Locator: |
1640 | - return endrec |
1641 | - |
1642 | - if diskno != 0 or disks != 1: |
1643 | - raise BadZipfile("zipfiles that span multiple disks are not supported") |
1644 | - |
1645 | - # Assume no 'zip64 extensible data' |
1646 | - fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) |
1647 | - data = fpin.read(sizeEndCentDir64) |
1648 | - sig, sz, create_version, read_version, disk_num, disk_dir, \ |
1649 | - dircount, dircount2, dirsize, diroffset = \ |
1650 | - struct.unpack(structEndArchive64, data) |
1651 | - if sig != stringEndArchive64: |
1652 | - return endrec |
1653 | - |
1654 | - # Update the original endrec using data from the ZIP64 record |
1655 | - endrec[_ECD_SIGNATURE] = sig |
1656 | - endrec[_ECD_DISK_NUMBER] = disk_num |
1657 | - endrec[_ECD_DISK_START] = disk_dir |
1658 | - endrec[_ECD_ENTRIES_THIS_DISK] = dircount |
1659 | - endrec[_ECD_ENTRIES_TOTAL] = dircount2 |
1660 | - endrec[_ECD_SIZE] = dirsize |
1661 | - endrec[_ECD_OFFSET] = diroffset |
1662 | - return endrec |
1663 | - |
1664 | - |
1665 | -def _EndRecData(fpin): |
1666 | - """Return data from the "End of Central Directory" record, or None. |
1667 | - |
1668 | - The data is a list of the nine items in the ZIP "End of central dir" |
1669 | - record followed by a tenth item, the file seek offset of this record.""" |
1670 | - |
1671 | - # Determine file size |
1672 | - fpin.seek(0, 2) |
1673 | - filesize = fpin.tell() |
1674 | - |
1675 | - # Check to see if this is ZIP file with no archive comment (the |
1676 | - # "end of central directory" structure should be the last item in the |
1677 | - # file if this is the case). |
1678 | - try: |
1679 | - fpin.seek(-sizeEndCentDir, 2) |
1680 | - except IOError: |
1681 | - return None |
1682 | - data = fpin.read() |
1683 | - if data[0:4] == stringEndArchive and data[-2:] == "\000\000": |
1684 | - # the signature is correct and there's no comment, unpack structure |
1685 | - endrec = struct.unpack(structEndArchive, data) |
1686 | - endrec=list(endrec) |
1687 | - |
1688 | - # Append a blank comment and record start offset |
1689 | - endrec.append("") |
1690 | - endrec.append(filesize - sizeEndCentDir) |
1691 | - |
1692 | - # Try to read the "Zip64 end of central directory" structure |
1693 | - return _EndRecData64(fpin, -sizeEndCentDir, endrec) |
1694 | - |
1695 | - # Either this is not a ZIP file, or it is a ZIP file with an archive |
1696 | - # comment. Search the end of the file for the "end of central directory" |
1697 | - # record signature. The comment is the last item in the ZIP file and may be |
1698 | - # up to 64K long. It is assumed that the "end of central directory" magic |
1699 | - # number does not appear in the comment. |
1700 | - maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0) |
1701 | - fpin.seek(maxCommentStart, 0) |
1702 | - data = fpin.read() |
1703 | - start = data.rfind(stringEndArchive) |
1704 | - if start >= 0: |
1705 | - # found the magic number; attempt to unpack and interpret |
1706 | - recData = data[start:start+sizeEndCentDir] |
1707 | - endrec = list(struct.unpack(structEndArchive, recData)) |
1708 | - comment = data[start+sizeEndCentDir:] |
1709 | - # check that comment length is correct |
1710 | - if endrec[_ECD_COMMENT_SIZE] == len(comment): |
1711 | - # Append the archive comment and start offset |
1712 | - endrec.append(comment) |
1713 | - endrec.append(maxCommentStart + start) |
1714 | - |
1715 | - # Try to read the "Zip64 end of central directory" structure |
1716 | - return _EndRecData64(fpin, maxCommentStart + start - filesize, |
1717 | - endrec) |
1718 | - |
1719 | - # Unable to find a valid end of central directory structure |
1720 | - return |
1721 | - |
1722 | - |
1723 | -class ZipInfo (object): |
1724 | - """Class with attributes describing each file in the ZIP archive.""" |
1725 | - |
1726 | - __slots__ = ( |
1727 | - 'orig_filename', |
1728 | - 'filename', |
1729 | - 'date_time', |
1730 | - 'compress_type', |
1731 | - 'comment', |
1732 | - 'extra', |
1733 | - 'create_system', |
1734 | - 'create_version', |
1735 | - 'extract_version', |
1736 | - 'reserved', |
1737 | - 'flag_bits', |
1738 | - 'volume', |
1739 | - 'internal_attr', |
1740 | - 'external_attr', |
1741 | - 'header_offset', |
1742 | - 'CRC', |
1743 | - 'compress_size', |
1744 | - 'file_size', |
1745 | - '_raw_time', |
1746 | - ) |
1747 | - |
1748 | - def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): |
1749 | - self.orig_filename = filename # Original file name in archive |
1750 | - |
1751 | - # Terminate the file name at the first null byte. Null bytes in file |
1752 | - # names are used as tricks by viruses in archives. |
1753 | - null_byte = filename.find(chr(0)) |
1754 | - if null_byte >= 0: |
1755 | - filename = filename[0:null_byte] |
1756 | - # This is used to ensure paths in generated ZIP files always use |
1757 | - # forward slashes as the directory separator, as required by the |
1758 | - # ZIP format specification. |
1759 | - if os.sep != "/" and os.sep in filename: |
1760 | - filename = filename.replace(os.sep, "/") |
1761 | - |
1762 | - self.filename = filename # Normalized file name |
1763 | - self.date_time = date_time # year, month, day, hour, min, sec |
1764 | - # Standard values: |
1765 | - self.compress_type = ZIP_STORED # Type of compression for the file |
1766 | - self.comment = "" # Comment for each file |
1767 | - self.extra = "" # ZIP extra data |
1768 | - if sys.platform == 'win32': |
1769 | - self.create_system = 0 # System which created ZIP archive |
1770 | - else: |
1771 | - # Assume everything else is unix-y |
1772 | - self.create_system = 3 # System which created ZIP archive |
1773 | - self.create_version = 20 # Version which created ZIP archive |
1774 | - self.extract_version = 20 # Version needed to extract archive |
1775 | - self.reserved = 0 # Must be zero |
1776 | - self.flag_bits = 0 # ZIP flag bits |
1777 | - self.volume = 0 # Volume number of file header |
1778 | - self.internal_attr = 0 # Internal attributes |
1779 | - self.external_attr = 0 # External file attributes |
1780 | - # Other attributes are set by class ZipFile: |
1781 | - # header_offset Byte offset to the file header |
1782 | - # CRC CRC-32 of the uncompressed file |
1783 | - # compress_size Size of the compressed file |
1784 | - # file_size Size of the uncompressed file |
1785 | - |
1786 | - def FileHeader(self): |
1787 | - """Return the per-file header as a string.""" |
1788 | - dt = self.date_time |
1789 | - dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] |
1790 | - dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) |
1791 | - if self.flag_bits & 0x08: |
1792 | - # Set these to zero because we write them after the file data |
1793 | - CRC = compress_size = file_size = 0 |
1794 | - else: |
1795 | - CRC = self.CRC |
1796 | - compress_size = self.compress_size |
1797 | - file_size = self.file_size |
1798 | - |
1799 | - extra = self.extra |
1800 | - |
1801 | - if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT: |
1802 | - # File is larger than what fits into a 4 byte integer, |
1803 | - # fall back to the ZIP64 extension |
1804 | - fmt = '<HHQQ' |
1805 | - extra = extra + struct.pack(fmt, |
1806 | - 1, struct.calcsize(fmt)-4, file_size, compress_size) |
1807 | - file_size = 0xffffffff |
1808 | - compress_size = 0xffffffff |
1809 | - self.extract_version = max(45, self.extract_version) |
1810 | - self.create_version = max(45, self.extract_version) |
1811 | - |
1812 | - filename, flag_bits = self._encodeFilenameFlags() |
1813 | - header = struct.pack(structFileHeader, stringFileHeader, |
1814 | - self.extract_version, self.reserved, flag_bits, |
1815 | - self.compress_type, dostime, dosdate, CRC, |
1816 | - compress_size, file_size, |
1817 | - len(filename), len(extra)) |
1818 | - return header + filename + extra |
1819 | - |
1820 | - def _encodeFilenameFlags(self): |
1821 | - if isinstance(self.filename, unicode): |
1822 | - try: |
1823 | - return self.filename.encode('ascii'), self.flag_bits |
1824 | - except UnicodeEncodeError: |
1825 | - return self.filename.encode('utf-8'), self.flag_bits | 0x800 |
1826 | - else: |
1827 | - return self.filename, self.flag_bits |
1828 | - |
1829 | - def _decodeFilename(self): |
1830 | - if self.flag_bits & 0x800: |
1831 | - return self.filename.decode('utf-8') |
1832 | - else: |
1833 | - return self.filename |
1834 | - |
1835 | - def _decodeExtra(self): |
1836 | - # Try to decode the extra field. |
1837 | - extra = self.extra |
1838 | - unpack = struct.unpack |
1839 | - while extra: |
1840 | - tp, ln = unpack('<HH', extra[:4]) |
1841 | - if tp == 1: |
1842 | - if ln >= 24: |
1843 | - counts = unpack('<QQQ', extra[4:28]) |
1844 | - elif ln == 16: |
1845 | - counts = unpack('<QQ', extra[4:20]) |
1846 | - elif ln == 8: |
1847 | - counts = unpack('<Q', extra[4:12]) |
1848 | - elif ln == 0: |
1849 | - counts = () |
1850 | - else: |
1851 | - raise RuntimeError, "Corrupt extra field %s"%(ln,) |
1852 | - |
1853 | - idx = 0 |
1854 | - |
1855 | - # ZIP64 extension (large files and/or large archives) |
1856 | - if self.file_size in (0xffffffffffffffffL, 0xffffffffL): |
1857 | - self.file_size = counts[idx] |
1858 | - idx += 1 |
1859 | - |
1860 | - if self.compress_size == 0xFFFFFFFFL: |
1861 | - self.compress_size = counts[idx] |
1862 | - idx += 1 |
1863 | - |
1864 | - if self.header_offset == 0xffffffffL: |
1865 | - old = self.header_offset |
1866 | - self.header_offset = counts[idx] |
1867 | - idx+=1 |
1868 | - |
1869 | - extra = extra[ln+4:] |
1870 | - |
1871 | - |
1872 | -class _ZipDecrypter: |
1873 | - """Class to handle decryption of files stored within a ZIP archive. |
1874 | - |
1875 | - ZIP supports a password-based form of encryption. Even though known |
1876 | - plaintext attacks have been found against it, it is still useful |
1877 | - to be able to get data out of such a file. |
1878 | - |
1879 | - Usage: |
1880 | - zd = _ZipDecrypter(mypwd) |
1881 | - plain_char = zd(cypher_char) |
1882 | - plain_text = map(zd, cypher_text) |
1883 | - """ |
1884 | - |
1885 | - def _GenerateCRCTable(): |
1886 | - """Generate a CRC-32 table. |
1887 | - |
1888 | - ZIP encryption uses the CRC32 one-byte primitive for scrambling some |
1889 | - internal keys. We noticed that a direct implementation is faster than |
1890 | - relying on binascii.crc32(). |
1891 | - """ |
1892 | - poly = 0xedb88320 |
1893 | - table = [0] * 256 |
1894 | - for i in range(256): |
1895 | - crc = i |
1896 | - for j in range(8): |
1897 | - if crc & 1: |
1898 | - crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly |
1899 | - else: |
1900 | - crc = ((crc >> 1) & 0x7FFFFFFF) |
1901 | - table[i] = crc |
1902 | - return table |
1903 | - crctable = _GenerateCRCTable() |
1904 | - |
1905 | - def _crc32(self, ch, crc): |
1906 | - """Compute the CRC32 primitive on one byte.""" |
1907 | - return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ord(ch)) & 0xff] |
1908 | - |
1909 | - def __init__(self, pwd): |
1910 | - self.key0 = 305419896 |
1911 | - self.key1 = 591751049 |
1912 | - self.key2 = 878082192 |
1913 | - for p in pwd: |
1914 | - self._UpdateKeys(p) |
1915 | - |
1916 | - def _UpdateKeys(self, c): |
1917 | - self.key0 = self._crc32(c, self.key0) |
1918 | - self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295 |
1919 | - self.key1 = (self.key1 * 134775813 + 1) & 4294967295 |
1920 | - self.key2 = self._crc32(chr((self.key1 >> 24) & 255), self.key2) |
1921 | - |
1922 | - def __call__(self, c): |
1923 | - """Decrypt a single character.""" |
1924 | - c = ord(c) |
1925 | - k = self.key2 | 2 |
1926 | - c = c ^ (((k * (k^1)) >> 8) & 255) |
1927 | - c = chr(c) |
1928 | - self._UpdateKeys(c) |
1929 | - return c |
1930 | - |
1931 | -class ZipExtFile: |
1932 | - """File-like object for reading an archive member. |
1933 | - Is returned by ZipFile.open(). |
1934 | - """ |
1935 | - |
1936 | - def __init__(self, fileobj, zipinfo, decrypt=None): |
1937 | - self.fileobj = fileobj |
1938 | - self.decrypter = decrypt |
1939 | - self.bytes_read = 0L |
1940 | - self.rawbuffer = '' |
1941 | - self.readbuffer = '' |
1942 | - self.linebuffer = '' |
1943 | - self.eof = False |
1944 | - self.univ_newlines = False |
1945 | - self.nlSeps = ("\n", ) |
1946 | - self.lastdiscard = '' |
1947 | - |
1948 | - self.compress_type = zipinfo.compress_type |
1949 | - self.compress_size = zipinfo.compress_size |
1950 | - |
1951 | - self.closed = False |
1952 | - self.mode = "r" |
1953 | - self.name = zipinfo.filename |
1954 | - |
1955 | - # read from compressed files in 64k blocks |
1956 | - self.compreadsize = 64*1024 |
1957 | - if self.compress_type == ZIP_DEFLATED: |
1958 | - self.dc = zlib.decompressobj(-15) |
1959 | - |
1960 | - def set_univ_newlines(self, univ_newlines): |
1961 | - self.univ_newlines = univ_newlines |
1962 | - |
1963 | - # pick line separator char(s) based on universal newlines flag |
1964 | - self.nlSeps = ("\n", ) |
1965 | - if self.univ_newlines: |
1966 | - self.nlSeps = ("\r\n", "\r", "\n") |
1967 | - |
1968 | - def __iter__(self): |
1969 | - return self |
1970 | - |
1971 | - def next(self): |
1972 | - nextline = self.readline() |
1973 | - if not nextline: |
1974 | - raise StopIteration() |
1975 | - |
1976 | - return nextline |
1977 | - |
1978 | - def close(self): |
1979 | - self.closed = True |
1980 | - |
1981 | - def _checkfornewline(self): |
1982 | - nl, nllen = -1, -1 |
1983 | - if self.linebuffer: |
1984 | - # ugly check for cases where half of an \r\n pair was |
1985 | - # read on the last pass, and the \r was discarded. In this |
1986 | - # case we just throw away the \n at the start of the buffer. |
1987 | - if (self.lastdiscard, self.linebuffer[0]) == ('\r','\n'): |
1988 | - self.linebuffer = self.linebuffer[1:] |
1989 | - |
1990 | - for sep in self.nlSeps: |
1991 | - nl = self.linebuffer.find(sep) |
1992 | - if nl >= 0: |
1993 | - nllen = len(sep) |
1994 | - return nl, nllen |
1995 | - |
1996 | - return nl, nllen |
1997 | - |
1998 | - def readline(self, size = -1): |
1999 | - """Read a line with approx. size. If size is negative, |
2000 | - read a whole line. |
2001 | - """ |
2002 | - if size < 0: |
2003 | - size = sys.maxint |
2004 | - elif size == 0: |
2005 | - return '' |
2006 | - |
2007 | - # check for a newline already in buffer |
2008 | - nl, nllen = self._checkfornewline() |
2009 | - |
2010 | - if nl >= 0: |
2011 | - # the next line was already in the buffer |
2012 | - nl = min(nl, size) |
2013 | - else: |
2014 | - # no line break in buffer - try to read more |
2015 | - size -= len(self.linebuffer) |
2016 | - while nl < 0 and size > 0: |
2017 | - buf = self.read(min(size, 100)) |
2018 | - if not buf: |
2019 | - break |
2020 | - self.linebuffer += buf |
2021 | - size -= len(buf) |
2022 | - |
2023 | - # check for a newline in buffer |
2024 | - nl, nllen = self._checkfornewline() |
2025 | - |
2026 | - # we either ran out of bytes in the file, or |
2027 | - # met the specified size limit without finding a newline, |
2028 | - # so return current buffer |
2029 | - if nl < 0: |
2030 | - s = self.linebuffer |
2031 | - self.linebuffer = '' |
2032 | - return s |
2033 | - |
2034 | - buf = self.linebuffer[:nl] |
2035 | - self.lastdiscard = self.linebuffer[nl:nl + nllen] |
2036 | - self.linebuffer = self.linebuffer[nl + nllen:] |
2037 | - |
2038 | - # line is always returned with \n as newline char (except possibly |
2039 | - # for a final incomplete line in the file, which is handled above). |
2040 | - return buf + "\n" |
2041 | - |
2042 | - def readlines(self, sizehint = -1): |
2043 | - """Return a list with all (following) lines. The sizehint parameter |
2044 | - is ignored in this implementation. |
2045 | - """ |
2046 | - result = [] |
2047 | - while True: |
2048 | - line = self.readline() |
2049 | - if not line: break |
2050 | - result.append(line) |
2051 | - return result |
2052 | - |
2053 | - def read(self, size = None): |
2054 | - # act like file() obj and return empty string if size is 0 |
2055 | - if size == 0: |
2056 | - return '' |
2057 | - |
2058 | - # determine read size |
2059 | - bytesToRead = self.compress_size - self.bytes_read |
2060 | - |
2061 | - # adjust read size for encrypted files since the first 12 bytes |
2062 | - # are for the encryption/password information |
2063 | - if self.decrypter is not None: |
2064 | - bytesToRead -= 12 |
2065 | - |
2066 | - if size is not None and size >= 0: |
2067 | - if self.compress_type == ZIP_STORED: |
2068 | - lr = len(self.readbuffer) |
2069 | - bytesToRead = min(bytesToRead, size - lr) |
2070 | - elif self.compress_type == ZIP_DEFLATED: |
2071 | - if len(self.readbuffer) > size: |
2072 | - # the user has requested fewer bytes than we've already |
2073 | - # pulled through the decompressor; don't read any more |
2074 | - bytesToRead = 0 |
2075 | - else: |
2076 | - # user will use up the buffer, so read some more |
2077 | - lr = len(self.rawbuffer) |
2078 | - bytesToRead = min(bytesToRead, self.compreadsize - lr) |
2079 | - |
2080 | - # avoid reading past end of file contents |
2081 | - if bytesToRead + self.bytes_read > self.compress_size: |
2082 | - bytesToRead = self.compress_size - self.bytes_read |
2083 | - |
2084 | - # try to read from file (if necessary) |
2085 | - if bytesToRead > 0: |
2086 | - bytes = self.fileobj.read(bytesToRead) |
2087 | - self.bytes_read += len(bytes) |
2088 | - self.rawbuffer += bytes |
2089 | - |
2090 | - # handle contents of raw buffer |
2091 | - if self.rawbuffer: |
2092 | - newdata = self.rawbuffer |
2093 | - self.rawbuffer = '' |
2094 | - |
2095 | - # decrypt new data if we were given an object to handle that |
2096 | - if newdata and self.decrypter is not None: |
2097 | - newdata = ''.join(map(self.decrypter, newdata)) |
2098 | - |
2099 | - # decompress newly read data if necessary |
2100 | - if newdata and self.compress_type == ZIP_DEFLATED: |
2101 | - newdata = self.dc.decompress(newdata) |
2102 | - self.rawbuffer = self.dc.unconsumed_tail |
2103 | - if self.eof and len(self.rawbuffer) == 0: |
2104 | - # we're out of raw bytes (both from the file and |
2105 | - # the local buffer); flush just to make sure the |
2106 | - # decompressor is done |
2107 | - newdata += self.dc.flush() |
2108 | - # prevent decompressor from being used again |
2109 | - self.dc = None |
2110 | - |
2111 | - self.readbuffer += newdata |
2112 | - |
2113 | - |
2114 | - # return what the user asked for |
2115 | - if size is None or len(self.readbuffer) <= size: |
2116 | - bytes = self.readbuffer |
2117 | - self.readbuffer = '' |
2118 | - else: |
2119 | - bytes = self.readbuffer[:size] |
2120 | - self.readbuffer = self.readbuffer[size:] |
2121 | - |
2122 | - return bytes |
2123 | - |
2124 | - |
2125 | -class ZipFile: |
2126 | - """ Class with methods to open, read, write, close, list zip files. |
2127 | - |
2128 | - z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False) |
2129 | - |
2130 | - file: Either the path to the file, or a file-like object. |
2131 | - If it is a path, the file will be opened and closed by ZipFile. |
2132 | - mode: The mode can be either read "r", write "w" or append "a". |
2133 | - compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib). |
2134 | - allowZip64: if True ZipFile will create files with ZIP64 extensions when |
2135 | - needed, otherwise it will raise an exception when this would |
2136 | - be necessary. |
2137 | - |
2138 | - """ |
2139 | - |
2140 | - fp = None # Set here since __del__ checks it |
2141 | - |
2142 | - def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False): |
2143 | - """Open the ZIP file with mode read "r", write "w" or append "a".""" |
2144 | - if mode not in ("r", "w", "a"): |
2145 | - raise RuntimeError('ZipFile() requires mode "r", "w", or "a"') |
2146 | - |
2147 | - if compression == ZIP_STORED: |
2148 | - pass |
2149 | - elif compression == ZIP_DEFLATED: |
2150 | - if not zlib: |
2151 | - raise RuntimeError,\ |
2152 | - "Compression requires the (missing) zlib module" |
2153 | - else: |
2154 | - raise RuntimeError, "That compression method is not supported" |
2155 | - |
2156 | - self._allowZip64 = allowZip64 |
2157 | - self._didModify = False |
2158 | - self.debug = 0 # Level of printing: 0 through 3 |
2159 | - self.NameToInfo = {} # Find file info given name |
2160 | - self.filelist = [] # List of ZipInfo instances for archive |
2161 | - self.compression = compression # Method of compression |
2162 | - self.mode = key = mode.replace('b', '')[0] |
2163 | - self.pwd = None |
2164 | - self.comment = '' |
2165 | - |
2166 | - # Check if we were passed a file-like object |
2167 | - if isinstance(file, basestring): |
2168 | - self._filePassed = 0 |
2169 | - self.filename = file |
2170 | - modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'} |
2171 | - try: |
2172 | - self.fp = open(file, modeDict[mode]) |
2173 | - except IOError: |
2174 | - if mode == 'a': |
2175 | - mode = key = 'w' |
2176 | - self.fp = open(file, modeDict[mode]) |
2177 | - else: |
2178 | - raise |
2179 | - else: |
2180 | - self._filePassed = 1 |
2181 | - self.fp = file |
2182 | - self.filename = getattr(file, 'name', None) |
2183 | - |
2184 | - if key == 'r': |
2185 | - self._GetContents() |
2186 | - elif key == 'w': |
2187 | - pass |
2188 | - elif key == 'a': |
2189 | - try: # See if file is a zip file |
2190 | - self._RealGetContents() |
2191 | - # seek to start of directory and overwrite |
2192 | - self.fp.seek(self.start_dir, 0) |
2193 | - except BadZipfile: # file is not a zip file, just append |
2194 | - self.fp.seek(0, 2) |
2195 | - else: |
2196 | - if not self._filePassed: |
2197 | - self.fp.close() |
2198 | - self.fp = None |
2199 | - raise RuntimeError, 'Mode must be "r", "w" or "a"' |
2200 | - |
2201 | - def _GetContents(self): |
2202 | - """Read the directory, making sure we close the file if the format |
2203 | - is bad.""" |
2204 | - try: |
2205 | - self._RealGetContents() |
2206 | - except BadZipfile: |
2207 | - if not self._filePassed: |
2208 | - self.fp.close() |
2209 | - self.fp = None |
2210 | - raise |
2211 | - |
2212 | - def _RealGetContents(self): |
2213 | - """Read in the table of contents for the ZIP file.""" |
2214 | - fp = self.fp |
2215 | - endrec = _EndRecData(fp) |
2216 | - if not endrec: |
2217 | - raise BadZipfile, "File is not a zip file" |
2218 | - if self.debug > 1: |
2219 | - print endrec |
2220 | - size_cd = endrec[_ECD_SIZE] # bytes in central directory |
2221 | - offset_cd = endrec[_ECD_OFFSET] # offset of central directory |
2222 | - self.comment = endrec[_ECD_COMMENT] # archive comment |
2223 | - |
2224 | - # "concat" is zero, unless zip was concatenated to another file |
2225 | - concat = endrec[_ECD_LOCATION] - size_cd - offset_cd |
2226 | - if endrec[_ECD_SIGNATURE] == stringEndArchive64: |
2227 | - # If Zip64 extension structures are present, account for them |
2228 | - concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator) |
2229 | - |
2230 | - if self.debug > 2: |
2231 | - inferred = concat + offset_cd |
2232 | - print "given, inferred, offset", offset_cd, inferred, concat |
2233 | - # self.start_dir: Position of start of central directory |
2234 | - self.start_dir = offset_cd + concat |
2235 | - fp.seek(self.start_dir, 0) |
2236 | - data = fp.read(size_cd) |
2237 | - fp = cStringIO.StringIO(data) |
2238 | - total = 0 |
2239 | - while total < size_cd: |
2240 | - centdir = fp.read(sizeCentralDir) |
2241 | - if centdir[0:4] != stringCentralDir: |
2242 | - raise BadZipfile, "Bad magic number for central directory" |
2243 | - centdir = struct.unpack(structCentralDir, centdir) |
2244 | - if self.debug > 2: |
2245 | - print centdir |
2246 | - filename = fp.read(centdir[_CD_FILENAME_LENGTH]) |
2247 | - # Create ZipInfo instance to store file information |
2248 | - x = ZipInfo(filename) |
2249 | - x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH]) |
2250 | - x.comment = fp.read(centdir[_CD_COMMENT_LENGTH]) |
2251 | - x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] |
2252 | - (x.create_version, x.create_system, x.extract_version, x.reserved, |
2253 | - x.flag_bits, x.compress_type, t, d, |
2254 | - x.CRC, x.compress_size, x.file_size) = centdir[1:12] |
2255 | - x.volume, x.internal_attr, x.external_attr = centdir[15:18] |
2256 | - # Convert date/time code to (year, month, day, hour, min, sec) |
2257 | - x._raw_time = t |
2258 | - x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F, |
2259 | - t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) |
2260 | - |
2261 | - x._decodeExtra() |
2262 | - x.header_offset = x.header_offset + concat |
2263 | - x.filename = x._decodeFilename() |
2264 | - self.filelist.append(x) |
2265 | - self.NameToInfo[x.filename] = x |
2266 | - |
2267 | - # update total bytes read from central directory |
2268 | - total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH] |
2269 | - + centdir[_CD_EXTRA_FIELD_LENGTH] |
2270 | - + centdir[_CD_COMMENT_LENGTH]) |
2271 | - |
2272 | - if self.debug > 2: |
2273 | - print "total", total |
2274 | - |
2275 | - |
2276 | - def namelist(self): |
2277 | - """Return a list of file names in the archive.""" |
2278 | - l = [] |
2279 | - for data in self.filelist: |
2280 | - l.append(data.filename) |
2281 | - return l |
2282 | - |
2283 | - def infolist(self): |
2284 | - """Return a list of class ZipInfo instances for files in the |
2285 | - archive.""" |
2286 | - return self.filelist |
2287 | - |
2288 | - def printdir(self): |
2289 | - """Print a table of contents for the zip file.""" |
2290 | - print "%-46s %19s %12s" % ("File Name", "Modified ", "Size") |
2291 | - for zinfo in self.filelist: |
2292 | - date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] |
2293 | - print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size) |
2294 | - |
2295 | - def testzip(self): |
2296 | - """Read all the files and check the CRC.""" |
2297 | - chunk_size = 2 ** 20 |
2298 | - for zinfo in self.filelist: |
2299 | - try: |
2300 | - # Read by chunks, to avoid an OverflowError or a |
2301 | - # MemoryError with very large embedded files. |
2302 | - f = self.open(zinfo.filename, "r") |
2303 | - while f.read(chunk_size): # Check CRC-32 |
2304 | - pass |
2305 | - except BadZipfile: |
2306 | - return zinfo.filename |
2307 | - |
2308 | - def getinfo(self, name): |
2309 | - """Return the instance of ZipInfo given 'name'.""" |
2310 | - info = self.NameToInfo.get(name) |
2311 | - if info is None: |
2312 | - raise KeyError( |
2313 | - 'There is no item named %r in the archive' % name) |
2314 | - |
2315 | - return info |
2316 | - |
2317 | - def setpassword(self, pwd): |
2318 | - """Set default password for encrypted files.""" |
2319 | - self.pwd = pwd |
2320 | - |
2321 | - def read(self, name, pwd=None): |
2322 | - """Return file bytes (as a string) for name.""" |
2323 | - return self.open(name, "r", pwd).read() |
2324 | - |
2325 | - def open(self, name, mode="r", pwd=None): |
2326 | - """Return file-like object for 'name'.""" |
2327 | - if mode not in ("r", "U", "rU"): |
2328 | - raise RuntimeError, 'open() requires mode "r", "U", or "rU"' |
2329 | - if not self.fp: |
2330 | - raise RuntimeError, \ |
2331 | - "Attempt to read ZIP archive that was already closed" |
2332 | - |
2333 | - # Only open a new file for instances where we were not |
2334 | - # given a file object in the constructor |
2335 | - if self._filePassed: |
2336 | - zef_file = self.fp |
2337 | - else: |
2338 | - zef_file = open(self.filename, 'rb') |
2339 | - |
2340 | - # Make sure we have an info object |
2341 | - if isinstance(name, ZipInfo): |
2342 | - # 'name' is already an info object |
2343 | - zinfo = name |
2344 | - else: |
2345 | - # Get info object for name |
2346 | - zinfo = self.getinfo(name) |
2347 | - |
2348 | - zef_file.seek(zinfo.header_offset, 0) |
2349 | - |
2350 | - # Skip the file header: |
2351 | - fheader = zef_file.read(sizeFileHeader) |
2352 | - if fheader[0:4] != stringFileHeader: |
2353 | - raise BadZipfile, "Bad magic number for file header" |
2354 | - |
2355 | - fheader = struct.unpack(structFileHeader, fheader) |
2356 | - fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) |
2357 | - if fheader[_FH_EXTRA_FIELD_LENGTH]: |
2358 | - zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH]) |
2359 | - |
2360 | - if fname != zinfo.orig_filename: |
2361 | - raise BadZipfile, \ |
2362 | - 'File name in directory "%s" and header "%s" differ.' % ( |
2363 | - zinfo.orig_filename, fname) |
2364 | - |
2365 | - # check for encrypted flag & handle password |
2366 | - is_encrypted = zinfo.flag_bits & 0x1 |
2367 | - zd = None |
2368 | - if is_encrypted: |
2369 | - if not pwd: |
2370 | - pwd = self.pwd |
2371 | - if not pwd: |
2372 | - raise RuntimeError, "File %s is encrypted, " \ |
2373 | - "password required for extraction" % name |
2374 | - |
2375 | - zd = _ZipDecrypter(pwd) |
2376 | - # The first 12 bytes in the cypher stream is an encryption header |
2377 | - # used to strengthen the algorithm. The first 11 bytes are |
2378 | - # completely random, while the 12th contains the MSB of the CRC, |
2379 | - # or the MSB of the file time depending on the header type |
2380 | - # and is used to check the correctness of the password. |
2381 | - bytes = zef_file.read(12) |
2382 | - h = map(zd, bytes[0:12]) |
2383 | - if zinfo.flag_bits & 0x8: |
2384 | - # compare against the file type from extended local headers |
2385 | - check_byte = (zinfo._raw_time >> 8) & 0xff |
2386 | - else: |
2387 | - # compare against the CRC otherwise |
2388 | - check_byte = (zinfo.CRC >> 24) & 0xff |
2389 | - if ord(h[11]) != check_byte: |
2390 | - raise RuntimeError("Bad password for file", name) |
2391 | - |
2392 | - # build and return a ZipExtFile |
2393 | - if zd is None: |
2394 | - zef = ZipExtFile(zef_file, zinfo) |
2395 | - else: |
2396 | - zef = ZipExtFile(zef_file, zinfo, zd) |
2397 | - |
2398 | - # set universal newlines on ZipExtFile if necessary |
2399 | - if "U" in mode: |
2400 | - zef.set_univ_newlines(True) |
2401 | - return zef |
2402 | - |
2403 | - def extract(self, member, path=None, pwd=None): |
2404 | - """Extract a member from the archive to the current working directory, |
2405 | - using its full name. Its file information is extracted as accurately |
2406 | - as possible. `member' may be a filename or a ZipInfo object. You can |
2407 | - specify a different directory using `path'. |
2408 | - """ |
2409 | - if not isinstance(member, ZipInfo): |
2410 | - member = self.getinfo(member) |
2411 | - |
2412 | - if path is None: |
2413 | - path = os.getcwd() |
2414 | - |
2415 | - return self._extract_member(member, path, pwd) |
2416 | - |
2417 | - def extractall(self, path=None, members=None, pwd=None): |
2418 | - """Extract all members from the archive to the current working |
2419 | - directory. `path' specifies a different directory to extract to. |
2420 | - `members' is optional and must be a subset of the list returned |
2421 | - by namelist(). |
2422 | - """ |
2423 | - if members is None: |
2424 | - members = self.namelist() |
2425 | - |
2426 | - for zipinfo in members: |
2427 | - self.extract(zipinfo, path, pwd) |
2428 | - |
2429 | - def _extract_member(self, member, targetpath, pwd): |
2430 | - """Extract the ZipInfo object 'member' to a physical |
2431 | - file on the path targetpath. |
2432 | - """ |
2433 | - # build the destination pathname, replacing |
2434 | - # forward slashes to platform specific separators. |
2435 | - # Strip trailing path separator, unless it represents the root. |
2436 | - if (targetpath[-1:] in (os.path.sep, os.path.altsep) |
2437 | - and len(os.path.splitdrive(targetpath)[1]) > 1): |
2438 | - targetpath = targetpath[:-1] |
2439 | - |
2440 | - # don't include leading "/" from file name if present |
2441 | - if member.filename[0] == '/': |
2442 | - targetpath = os.path.join(targetpath, member.filename[1:]) |
2443 | - else: |
2444 | - targetpath = os.path.join(targetpath, member.filename) |
2445 | - |
2446 | - targetpath = os.path.normpath(targetpath) |
2447 | - |
2448 | - # Create all upper directories if necessary. |
2449 | - upperdirs = os.path.dirname(targetpath) |
2450 | - if upperdirs and not os.path.exists(upperdirs): |
2451 | - os.makedirs(upperdirs) |
2452 | - |
2453 | - if member.filename[-1] == '/': |
2454 | - if not os.path.isdir(targetpath): |
2455 | - os.mkdir(targetpath) |
2456 | - return targetpath |
2457 | - |
2458 | - source = self.open(member, pwd=pwd) |
2459 | - target = file(targetpath, "wb") |
2460 | - shutil.copyfileobj(source, target) |
2461 | - source.close() |
2462 | - target.close() |
2463 | - |
2464 | - return targetpath |
2465 | - |
2466 | - def _writecheck(self, zinfo): |
2467 | - """Check for errors before writing a file to the archive.""" |
2468 | - if zinfo.filename in self.NameToInfo: |
2469 | - if self.debug: # Warning for duplicate names |
2470 | - print "Duplicate name:", zinfo.filename |
2471 | - if self.mode not in ("w", "a"): |
2472 | - raise RuntimeError, 'write() requires mode "w" or "a"' |
2473 | - if not self.fp: |
2474 | - raise RuntimeError, \ |
2475 | - "Attempt to write ZIP archive that was already closed" |
2476 | - if zinfo.compress_type == ZIP_DEFLATED and not zlib: |
2477 | - raise RuntimeError, \ |
2478 | - "Compression requires the (missing) zlib module" |
2479 | - if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED): |
2480 | - raise RuntimeError, \ |
2481 | - "That compression method is not supported" |
2482 | - if zinfo.file_size > ZIP64_LIMIT: |
2483 | - if not self._allowZip64: |
2484 | - raise LargeZipFile("Filesize would require ZIP64 extensions") |
2485 | - if zinfo.header_offset > ZIP64_LIMIT: |
2486 | - if not self._allowZip64: |
2487 | - raise LargeZipFile("Zipfile size would require ZIP64 extensions") |
2488 | - |
2489 | - def write(self, filename, arcname=None, compress_type=None): |
2490 | - """Put the bytes from filename into the archive under the name |
2491 | - arcname.""" |
2492 | - if not self.fp: |
2493 | - raise RuntimeError( |
2494 | - "Attempt to write to ZIP archive that was already closed") |
2495 | - |
2496 | - st = os.stat(filename) |
2497 | - isdir = stat.S_ISDIR(st.st_mode) |
2498 | - mtime = time.localtime(st.st_mtime) |
2499 | - date_time = mtime[0:6] |
2500 | - # Create ZipInfo instance to store file information |
2501 | - if arcname is None: |
2502 | - arcname = filename |
2503 | - arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) |
2504 | - while arcname[0] in (os.sep, os.altsep): |
2505 | - arcname = arcname[1:] |
2506 | - if isdir: |
2507 | - arcname += '/' |
2508 | - zinfo = ZipInfo(arcname, date_time) |
2509 | - zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes |
2510 | - if compress_type is None: |
2511 | - zinfo.compress_type = self.compression |
2512 | - else: |
2513 | - zinfo.compress_type = compress_type |
2514 | - |
2515 | - zinfo.file_size = st.st_size |
2516 | - zinfo.flag_bits = 0x00 |
2517 | - zinfo.header_offset = self.fp.tell() # Start of header bytes |
2518 | - |
2519 | - self._writecheck(zinfo) |
2520 | - self._didModify = True |
2521 | - |
2522 | - if isdir: |
2523 | - zinfo.file_size = 0 |
2524 | - zinfo.compress_size = 0 |
2525 | - zinfo.CRC = 0 |
2526 | - self.filelist.append(zinfo) |
2527 | - self.NameToInfo[zinfo.filename] = zinfo |
2528 | - self.fp.write(zinfo.FileHeader()) |
2529 | - return |
2530 | - |
2531 | - fp = open(filename, "rb") |
2532 | - # Must overwrite CRC and sizes with correct data later |
2533 | - zinfo.CRC = CRC = 0 |
2534 | - zinfo.compress_size = compress_size = 0 |
2535 | - zinfo.file_size = file_size = 0 |
2536 | - self.fp.write(zinfo.FileHeader()) |
2537 | - if zinfo.compress_type == ZIP_DEFLATED: |
2538 | - cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, |
2539 | - zlib.DEFLATED, -15) |
2540 | - else: |
2541 | - cmpr = None |
2542 | - while 1: |
2543 | - buf = fp.read(1024 * 8) |
2544 | - if not buf: |
2545 | - break |
2546 | - file_size = file_size + len(buf) |
2547 | - CRC = crc32(buf, CRC) & 0xffffffff |
2548 | - if cmpr: |
2549 | - buf = cmpr.compress(buf) |
2550 | - compress_size = compress_size + len(buf) |
2551 | - self.fp.write(buf) |
2552 | - fp.close() |
2553 | - if cmpr: |
2554 | - buf = cmpr.flush() |
2555 | - compress_size = compress_size + len(buf) |
2556 | - self.fp.write(buf) |
2557 | - zinfo.compress_size = compress_size |
2558 | - else: |
2559 | - zinfo.compress_size = file_size |
2560 | - zinfo.CRC = CRC |
2561 | - zinfo.file_size = file_size |
2562 | - # Seek backwards and write CRC and file sizes |
2563 | - position = self.fp.tell() # Preserve current position in file |
2564 | - self.fp.seek(zinfo.header_offset + 14, 0) |
2565 | - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, |
2566 | - zinfo.file_size)) |
2567 | - self.fp.seek(position, 0) |
2568 | - self.filelist.append(zinfo) |
2569 | - self.NameToInfo[zinfo.filename] = zinfo |
2570 | - |
2571 | - def writestr(self, zinfo_or_arcname, bytes): |
2572 | - """Write a file into the archive. The contents is the string |
2573 | - 'bytes'. 'zinfo_or_arcname' is either a ZipInfo instance or |
2574 | - the name of the file in the archive.""" |
2575 | - if not isinstance(zinfo_or_arcname, ZipInfo): |
2576 | - zinfo = ZipInfo(filename=zinfo_or_arcname, |
2577 | - date_time=time.localtime(time.time())[:6]) |
2578 | - zinfo.compress_type = self.compression |
2579 | - zinfo.external_attr = 0600 << 16 |
2580 | - else: |
2581 | - zinfo = zinfo_or_arcname |
2582 | - |
2583 | - if not self.fp: |
2584 | - raise RuntimeError( |
2585 | - "Attempt to write to ZIP archive that was already closed") |
2586 | - |
2587 | - zinfo.file_size = len(bytes) # Uncompressed size |
2588 | - zinfo.header_offset = self.fp.tell() # Start of header bytes |
2589 | - self._writecheck(zinfo) |
2590 | - self._didModify = True |
2591 | - zinfo.CRC = crc32(bytes) & 0xffffffff # CRC-32 checksum |
2592 | - if zinfo.compress_type == ZIP_DEFLATED: |
2593 | - co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, |
2594 | - zlib.DEFLATED, -15) |
2595 | - bytes = co.compress(bytes) + co.flush() |
2596 | - zinfo.compress_size = len(bytes) # Compressed size |
2597 | - else: |
2598 | - zinfo.compress_size = zinfo.file_size |
2599 | - zinfo.header_offset = self.fp.tell() # Start of header bytes |
2600 | - self.fp.write(zinfo.FileHeader()) |
2601 | - self.fp.write(bytes) |
2602 | - self.fp.flush() |
2603 | - if zinfo.flag_bits & 0x08: |
2604 | - # Write CRC and file sizes after the file data |
2605 | - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, |
2606 | - zinfo.file_size)) |
2607 | - self.filelist.append(zinfo) |
2608 | - self.NameToInfo[zinfo.filename] = zinfo |
2609 | - |
2610 | - def __del__(self): |
2611 | - """Call the "close()" method in case the user forgot.""" |
2612 | - self.close() |
2613 | - |
2614 | - def close(self): |
2615 | - """Close the file, and for mode "w" and "a" write the ending |
2616 | - records.""" |
2617 | - if self.fp is None: |
2618 | - return |
2619 | - |
2620 | - if self.mode in ("w", "a") and self._didModify: # write ending records |
2621 | - count = 0 |
2622 | - pos1 = self.fp.tell() |
2623 | - for zinfo in self.filelist: # write central directory |
2624 | - count = count + 1 |
2625 | - dt = zinfo.date_time |
2626 | - dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] |
2627 | - dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) |
2628 | - extra = [] |
2629 | - if zinfo.file_size > ZIP64_LIMIT \ |
2630 | - or zinfo.compress_size > ZIP64_LIMIT: |
2631 | - extra.append(zinfo.file_size) |
2632 | - extra.append(zinfo.compress_size) |
2633 | - file_size = 0xffffffff |
2634 | - compress_size = 0xffffffff |
2635 | - else: |
2636 | - file_size = zinfo.file_size |
2637 | - compress_size = zinfo.compress_size |
2638 | - |
2639 | - if zinfo.header_offset > ZIP64_LIMIT: |
2640 | - extra.append(zinfo.header_offset) |
2641 | - header_offset = 0xffffffffL |
2642 | - else: |
2643 | - header_offset = zinfo.header_offset |
2644 | - |
2645 | - extra_data = zinfo.extra |
2646 | - if extra: |
2647 | - # Append a ZIP64 field to the extra's |
2648 | - extra_data = struct.pack( |
2649 | - '<HH' + 'Q'*len(extra), |
2650 | - 1, 8*len(extra), *extra) + extra_data |
2651 | - |
2652 | - extract_version = max(45, zinfo.extract_version) |
2653 | - create_version = max(45, zinfo.create_version) |
2654 | - else: |
2655 | - extract_version = zinfo.extract_version |
2656 | - create_version = zinfo.create_version |
2657 | - |
2658 | - try: |
2659 | - filename, flag_bits = zinfo._encodeFilenameFlags() |
2660 | - centdir = struct.pack(structCentralDir, |
2661 | - stringCentralDir, create_version, |
2662 | - zinfo.create_system, extract_version, zinfo.reserved, |
2663 | - flag_bits, zinfo.compress_type, dostime, dosdate, |
2664 | - zinfo.CRC, compress_size, file_size, |
2665 | - len(filename), len(extra_data), len(zinfo.comment), |
2666 | - 0, zinfo.internal_attr, zinfo.external_attr, |
2667 | - header_offset) |
2668 | - except DeprecationWarning: |
2669 | - print >>sys.stderr, (structCentralDir, |
2670 | - stringCentralDir, create_version, |
2671 | - zinfo.create_system, extract_version, zinfo.reserved, |
2672 | - zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, |
2673 | - zinfo.CRC, compress_size, file_size, |
2674 | - len(zinfo.filename), len(extra_data), len(zinfo.comment), |
2675 | - 0, zinfo.internal_attr, zinfo.external_attr, |
2676 | - header_offset) |
2677 | - raise |
2678 | - self.fp.write(centdir) |
2679 | - self.fp.write(filename) |
2680 | - self.fp.write(extra_data) |
2681 | - self.fp.write(zinfo.comment) |
2682 | - |
2683 | - pos2 = self.fp.tell() |
2684 | - # Write end-of-zip-archive record |
2685 | - centDirCount = count |
2686 | - centDirSize = pos2 - pos1 |
2687 | - centDirOffset = pos1 |
2688 | - if (centDirCount >= ZIP_FILECOUNT_LIMIT or |
2689 | - centDirOffset > ZIP64_LIMIT or |
2690 | - centDirSize > ZIP64_LIMIT): |
2691 | - # Need to write the ZIP64 end-of-archive records |
2692 | - zip64endrec = struct.pack( |
2693 | - structEndArchive64, stringEndArchive64, |
2694 | - 44, 45, 45, 0, 0, centDirCount, centDirCount, |
2695 | - centDirSize, centDirOffset) |
2696 | - self.fp.write(zip64endrec) |
2697 | - |
2698 | - zip64locrec = struct.pack( |
2699 | - structEndArchive64Locator, |
2700 | - stringEndArchive64Locator, 0, pos2, 1) |
2701 | - self.fp.write(zip64locrec) |
2702 | - centDirCount = min(centDirCount, 0xFFFF) |
2703 | - centDirSize = min(centDirSize, 0xFFFFFFFF) |
2704 | - centDirOffset = min(centDirOffset, 0xFFFFFFFF) |
2705 | - |
2706 | - # check for valid comment length |
2707 | - if len(self.comment) >= ZIP_MAX_COMMENT: |
2708 | - if self.debug > 0: |
2709 | - msg = 'Archive comment is too long; truncating to %d bytes' \ |
2710 | - % ZIP_MAX_COMMENT |
2711 | - self.comment = self.comment[:ZIP_MAX_COMMENT] |
2712 | - |
2713 | - endrec = struct.pack(structEndArchive, stringEndArchive, |
2714 | - 0, 0, centDirCount, centDirCount, |
2715 | - centDirSize, centDirOffset, len(self.comment)) |
2716 | - self.fp.write(endrec) |
2717 | - self.fp.write(self.comment) |
2718 | - self.fp.flush() |
2719 | - |
2720 | - if not self._filePassed: |
2721 | - self.fp.close() |
2722 | - self.fp = None |
2723 | - |
2724 | - |
2725 | -class PyZipFile(ZipFile): |
2726 | - """Class to create ZIP archives with Python library files and packages.""" |
2727 | - |
2728 | - def writepy(self, pathname, basename = ""): |
2729 | - """Add all files from "pathname" to the ZIP archive. |
2730 | - |
2731 | - If pathname is a package directory, search the directory and |
2732 | - all package subdirectories recursively for all *.py and enter |
2733 | - the modules into the archive. If pathname is a plain |
2734 | - directory, listdir *.py and enter all modules. Else, pathname |
2735 | - must be a Python *.py file and the module will be put into the |
2736 | - archive. Added modules are always module.pyo or module.pyc. |
2737 | - This method will compile the module.py into module.pyc if |
2738 | - necessary. |
2739 | - """ |
2740 | - dir, name = os.path.split(pathname) |
2741 | - if os.path.isdir(pathname): |
2742 | - initname = os.path.join(pathname, "__init__.py") |
2743 | - if os.path.isfile(initname): |
2744 | - # This is a package directory, add it |
2745 | - if basename: |
2746 | - basename = "%s/%s" % (basename, name) |
2747 | - else: |
2748 | - basename = name |
2749 | - if self.debug: |
2750 | - print "Adding package in", pathname, "as", basename |
2751 | - fname, arcname = self._get_codename(initname[0:-3], basename) |
2752 | - if self.debug: |
2753 | - print "Adding", arcname |
2754 | - self.write(fname, arcname) |
2755 | - dirlist = os.listdir(pathname) |
2756 | - dirlist.remove("__init__.py") |
2757 | - # Add all *.py files and package subdirectories |
2758 | - for filename in dirlist: |
2759 | - path = os.path.join(pathname, filename) |
2760 | - root, ext = os.path.splitext(filename) |
2761 | - if os.path.isdir(path): |
2762 | - if os.path.isfile(os.path.join(path, "__init__.py")): |
2763 | - # This is a package directory, add it |
2764 | - self.writepy(path, basename) # Recursive call |
2765 | - elif ext == ".py": |
2766 | - fname, arcname = self._get_codename(path[0:-3], |
2767 | - basename) |
2768 | - if self.debug: |
2769 | - print "Adding", arcname |
2770 | - self.write(fname, arcname) |
2771 | - else: |
2772 | - # This is NOT a package directory, add its files at top level |
2773 | - if self.debug: |
2774 | - print "Adding files from directory", pathname |
2775 | - for filename in os.listdir(pathname): |
2776 | - path = os.path.join(pathname, filename) |
2777 | - root, ext = os.path.splitext(filename) |
2778 | - if ext == ".py": |
2779 | - fname, arcname = self._get_codename(path[0:-3], |
2780 | - basename) |
2781 | - if self.debug: |
2782 | - print "Adding", arcname |
2783 | - self.write(fname, arcname) |
2784 | - else: |
2785 | - if pathname[-3:] != ".py": |
2786 | - raise RuntimeError, \ |
2787 | - 'Files added with writepy() must end with ".py"' |
2788 | - fname, arcname = self._get_codename(pathname[0:-3], basename) |
2789 | - if self.debug: |
2790 | - print "Adding file", arcname |
2791 | - self.write(fname, arcname) |
2792 | - |
2793 | - def _get_codename(self, pathname, basename): |
2794 | - """Return (filename, archivename) for the path. |
2795 | - |
2796 | - Given a module name path, return the correct file path and |
2797 | - archive name, compiling if necessary. For example, given |
2798 | - /python/lib/string, return (/python/lib/string.pyc, string). |
2799 | - """ |
2800 | - file_py = pathname + ".py" |
2801 | - file_pyc = pathname + ".pyc" |
2802 | - file_pyo = pathname + ".pyo" |
2803 | - if os.path.isfile(file_pyo) and \ |
2804 | - os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime: |
2805 | - fname = file_pyo # Use .pyo file |
2806 | - elif not os.path.isfile(file_pyc) or \ |
2807 | - os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime: |
2808 | - import py_compile |
2809 | - if self.debug: |
2810 | - print "Compiling", file_py |
2811 | - try: |
2812 | - py_compile.compile(file_py, file_pyc, None, True) |
2813 | - except py_compile.PyCompileError,err: |
2814 | - print err.msg |
2815 | - fname = file_pyc |
2816 | - else: |
2817 | - fname = file_pyc |
2818 | - archivename = os.path.split(fname)[1] |
2819 | - if basename: |
2820 | - archivename = "%s/%s" % (basename, archivename) |
2821 | - return (fname, archivename) |
2822 | - |
2823 | - |
2824 | -def main(args = None): |
2825 | - import textwrap |
2826 | - USAGE=textwrap.dedent("""\ |
2827 | - Usage: |
2828 | - zipfile.py -l zipfile.zip # Show listing of a zipfile |
2829 | - zipfile.py -t zipfile.zip # Test if a zipfile is valid |
2830 | - zipfile.py -e zipfile.zip target # Extract zipfile into target dir |
2831 | - zipfile.py -c zipfile.zip src ... # Create zipfile from sources |
2832 | - """) |
2833 | - if args is None: |
2834 | - args = sys.argv[1:] |
2835 | - |
2836 | - if not args or args[0] not in ('-l', '-c', '-e', '-t'): |
2837 | - print USAGE |
2838 | - sys.exit(1) |
2839 | - |
2840 | - if args[0] == '-l': |
2841 | - if len(args) != 2: |
2842 | - print USAGE |
2843 | - sys.exit(1) |
2844 | - zf = ZipFile(args[1], 'r') |
2845 | - zf.printdir() |
2846 | - zf.close() |
2847 | - |
2848 | - elif args[0] == '-t': |
2849 | - if len(args) != 2: |
2850 | - print USAGE |
2851 | - sys.exit(1) |
2852 | - zf = ZipFile(args[1], 'r') |
2853 | - zf.testzip() |
2854 | - print "Done testing" |
2855 | - |
2856 | - elif args[0] == '-e': |
2857 | - if len(args) != 3: |
2858 | - print USAGE |
2859 | - sys.exit(1) |
2860 | - |
2861 | - zf = ZipFile(args[1], 'r') |
2862 | - out = args[2] |
2863 | - for path in zf.namelist(): |
2864 | - if path.startswith('./'): |
2865 | - tgt = os.path.join(out, path[2:]) |
2866 | - else: |
2867 | - tgt = os.path.join(out, path) |
2868 | - |
2869 | - tgtdir = os.path.dirname(tgt) |
2870 | - if not os.path.exists(tgtdir): |
2871 | - os.makedirs(tgtdir) |
2872 | - fp = open(tgt, 'wb') |
2873 | - fp.write(zf.read(path)) |
2874 | - fp.close() |
2875 | - zf.close() |
2876 | - |
2877 | - elif args[0] == '-c': |
2878 | - if len(args) < 3: |
2879 | - print USAGE |
2880 | - sys.exit(1) |
2881 | - |
2882 | - def addToZip(zf, path, zippath): |
2883 | - if os.path.isfile(path): |
2884 | - zf.write(path, zippath, ZIP_DEFLATED) |
2885 | - elif os.path.isdir(path): |
2886 | - for nm in os.listdir(path): |
2887 | - addToZip(zf, |
2888 | - os.path.join(path, nm), os.path.join(zippath, nm)) |
2889 | - # else: ignore |
2890 | - |
2891 | - zf = ZipFile(args[1], 'w', allowZip64=True) |
2892 | - for src in args[2:]: |
2893 | - addToZip(zf, src, os.path.basename(src)) |
2894 | - |
2895 | - zf.close() |
2896 | - |
2897 | -if __name__ == "__main__": |
2898 | - main() |
2899 | |
2900 | === removed directory 'python25-compat' |
2901 | === removed file 'python25-compat/BaseHTTPServer.py' |
2902 | --- python25-compat/BaseHTTPServer.py 2009-09-10 00:02:41 +0000 |
2903 | +++ python25-compat/BaseHTTPServer.py 1970-01-01 00:00:00 +0000 |
2904 | @@ -1,587 +0,0 @@ |
2905 | -"""HTTP server base class. |
2906 | - |
2907 | -Note: the class in this module doesn't implement any HTTP request; see |
2908 | -SimpleHTTPServer for simple implementations of GET, HEAD and POST |
2909 | -(including CGI scripts). It does, however, optionally implement HTTP/1.1 |
2910 | -persistent connections, as of version 0.3. |
2911 | - |
2912 | -Contents: |
2913 | - |
2914 | -- BaseHTTPRequestHandler: HTTP request handler base class |
2915 | -- test: test function |
2916 | - |
2917 | -XXX To do: |
2918 | - |
2919 | -- log requests even later (to capture byte count) |
2920 | -- log user-agent header and other interesting goodies |
2921 | -- send error log to separate file |
2922 | -""" |
2923 | - |
2924 | - |
2925 | -# See also: |
2926 | -# |
2927 | -# HTTP Working Group T. Berners-Lee |
2928 | -# INTERNET-DRAFT R. T. Fielding |
2929 | -# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen |
2930 | -# Expires September 8, 1995 March 8, 1995 |
2931 | -# |
2932 | -# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt |
2933 | -# |
2934 | -# and |
2935 | -# |
2936 | -# Network Working Group R. Fielding |
2937 | -# Request for Comments: 2616 et al |
2938 | -# Obsoletes: 2068 June 1999 |
2939 | -# Category: Standards Track |
2940 | -# |
2941 | -# URL: http://www.faqs.org/rfcs/rfc2616.html |
2942 | - |
2943 | -# Log files |
2944 | -# --------- |
2945 | -# |
2946 | -# Here's a quote from the NCSA httpd docs about log file format. |
2947 | -# |
2948 | -# | The logfile format is as follows. Each line consists of: |
2949 | -# | |
2950 | -# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb |
2951 | -# | |
2952 | -# | host: Either the DNS name or the IP number of the remote client |
2953 | -# | rfc931: Any information returned by identd for this person, |
2954 | -# | - otherwise. |
2955 | -# | authuser: If user sent a userid for authentication, the user name, |
2956 | -# | - otherwise. |
2957 | -# | DD: Day |
2958 | -# | Mon: Month (calendar name) |
2959 | -# | YYYY: Year |
2960 | -# | hh: hour (24-hour format, the machine's timezone) |
2961 | -# | mm: minutes |
2962 | -# | ss: seconds |
2963 | -# | request: The first line of the HTTP request as sent by the client. |
2964 | -# | ddd: the status code returned by the server, - if not available. |
2965 | -# | bbbb: the total number of bytes sent, |
2966 | -# | *not including the HTTP/1.0 header*, - if not available |
2967 | -# | |
2968 | -# | You can determine the name of the file accessed through request. |
2969 | -# |
2970 | -# (Actually, the latter is only true if you know the server configuration |
2971 | -# at the time the request was made!) |
2972 | - |
2973 | -__version__ = "0.3" |
2974 | - |
2975 | -__all__ = ["HTTPServer", "BaseHTTPRequestHandler"] |
2976 | - |
2977 | -import sys |
2978 | -import time |
2979 | -import socket # For gethostbyaddr() |
2980 | -import mimetools |
2981 | -import SocketServer |
2982 | - |
2983 | -# Default error message template |
2984 | -DEFAULT_ERROR_MESSAGE = """\ |
2985 | -<head> |
2986 | -<title>Error response</title> |
2987 | -</head> |
2988 | -<body> |
2989 | -<h1>Error response</h1> |
2990 | -<p>Error code %(code)d. |
2991 | -<p>Message: %(message)s. |
2992 | -<p>Error code explanation: %(code)s = %(explain)s. |
2993 | -</body> |
2994 | -""" |
2995 | - |
2996 | -DEFAULT_ERROR_CONTENT_TYPE = "text/html" |
2997 | - |
2998 | -def _quote_html(html): |
2999 | - return html.replace("&", "&").replace("<", "<").replace(">", ">") |
3000 | - |
3001 | -class HTTPServer(SocketServer.TCPServer): |
3002 | - |
3003 | - allow_reuse_address = 1 # Seems to make sense in testing environment |
3004 | - |
3005 | - def server_bind(self): |
3006 | - """Override server_bind to store the server name.""" |
3007 | - SocketServer.TCPServer.server_bind(self) |
3008 | - host, port = self.socket.getsockname()[:2] |
3009 | - self.server_name = socket.getfqdn(host) |
3010 | - self.server_port = port |
3011 | - |
3012 | - |
3013 | -class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler): |
3014 | - |
3015 | - """HTTP request handler base class. |
3016 | - |
3017 | - The following explanation of HTTP serves to guide you through the |
3018 | - code as well as to expose any misunderstandings I may have about |
3019 | - HTTP (so you don't need to read the code to figure out I'm wrong |
3020 | - :-). |
3021 | - |
3022 | - HTTP (HyperText Transfer Protocol) is an extensible protocol on |
3023 | - top of a reliable stream transport (e.g. TCP/IP). The protocol |
3024 | - recognizes three parts to a request: |
3025 | - |
3026 | - 1. One line identifying the request type and path |
3027 | - 2. An optional set of RFC-822-style headers |
3028 | - 3. An optional data part |
3029 | - |
3030 | - The headers and data are separated by a blank line. |
3031 | - |
3032 | - The first line of the request has the form |
3033 | - |
3034 | - <command> <path> <version> |
3035 | - |
3036 | - where <command> is a (case-sensitive) keyword such as GET or POST, |
3037 | - <path> is a string containing path information for the request, |
3038 | - and <version> should be the string "HTTP/1.0" or "HTTP/1.1". |
3039 | - <path> is encoded using the URL encoding scheme (using %xx to signify |
3040 | - the ASCII character with hex code xx). |
3041 | - |
3042 | - The specification specifies that lines are separated by CRLF but |
3043 | - for compatibility with the widest range of clients recommends |
3044 | - servers also handle LF. Similarly, whitespace in the request line |
3045 | - is treated sensibly (allowing multiple spaces between components |
3046 | - and allowing trailing whitespace). |
3047 | - |
3048 | - Similarly, for output, lines ought to be separated by CRLF pairs |
3049 | - but most clients grok LF characters just fine. |
3050 | - |
3051 | - If the first line of the request has the form |
3052 | - |
3053 | - <command> <path> |
3054 | - |
3055 | - (i.e. <version> is left out) then this is assumed to be an HTTP |
3056 | - 0.9 request; this form has no optional headers and data part and |
3057 | - the reply consists of just the data. |
3058 | - |
3059 | - The reply form of the HTTP 1.x protocol again has three parts: |
3060 | - |
3061 | - 1. One line giving the response code |
3062 | - 2. An optional set of RFC-822-style headers |
3063 | - 3. The data |
3064 | - |
3065 | - Again, the headers and data are separated by a blank line. |
3066 | - |
3067 | - The response code line has the form |
3068 | - |
3069 | - <version> <responsecode> <responsestring> |
3070 | - |
3071 | - where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"), |
3072 | - <responsecode> is a 3-digit response code indicating success or |
3073 | - failure of the request, and <responsestring> is an optional |
3074 | - human-readable string explaining what the response code means. |
3075 | - |
3076 | - This server parses the request and the headers, and then calls a |
3077 | - function specific to the request type (<command>). Specifically, |
3078 | - a request SPAM will be handled by a method do_SPAM(). If no |
3079 | - such method exists the server sends an error response to the |
3080 | - client. If it exists, it is called with no arguments: |
3081 | - |
3082 | - do_SPAM() |
3083 | - |
3084 | - Note that the request name is case sensitive (i.e. SPAM and spam |
3085 | - are different requests). |
3086 | - |
3087 | - The various request details are stored in instance variables: |
3088 | - |
3089 | - - client_address is the client IP address in the form (host, |
3090 | - port); |
3091 | - |
3092 | - - command, path and version are the broken-down request line; |
3093 | - |
3094 | - - headers is an instance of mimetools.Message (or a derived |
3095 | - class) containing the header information; |
3096 | - |
3097 | - - rfile is a file object open for reading positioned at the |
3098 | - start of the optional input data part; |
3099 | - |
3100 | - - wfile is a file object open for writing. |
3101 | - |
3102 | - IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! |
3103 | - |
3104 | - The first thing to be written must be the response line. Then |
3105 | - follow 0 or more header lines, then a blank line, and then the |
3106 | - actual data (if any). The meaning of the header lines depends on |
3107 | - the command executed by the server; in most cases, when data is |
3108 | - returned, there should be at least one header line of the form |
3109 | - |
3110 | - Content-type: <type>/<subtype> |
3111 | - |
3112 | - where <type> and <subtype> should be registered MIME types, |
3113 | - e.g. "text/html" or "text/plain". |
3114 | - |
3115 | - """ |
3116 | - |
3117 | - # The Python system version, truncated to its first component. |
3118 | - sys_version = "Python/" + sys.version.split()[0] |
3119 | - |
3120 | - # The server software version. You may want to override this. |
3121 | - # The format is multiple whitespace-separated strings, |
3122 | - # where each string is of the form name[/version]. |
3123 | - server_version = "BaseHTTP/" + __version__ |
3124 | - |
3125 | - # The default request version. This only affects responses up until |
3126 | - # the point where the request line is parsed, so it mainly decides what |
3127 | - # the client gets back when sending a malformed request line. |
3128 | - # Most web servers default to HTTP 0.9, i.e. don't send a status line. |
3129 | - default_request_version = "HTTP/0.9" |
3130 | - |
3131 | - def parse_request(self): |
3132 | - """Parse a request (internal). |
3133 | - |
3134 | - The request should be stored in self.raw_requestline; the results |
3135 | - are in self.command, self.path, self.request_version and |
3136 | - self.headers. |
3137 | - |
3138 | - Return True for success, False for failure; on failure, an |
3139 | - error is sent back. |
3140 | - |
3141 | - """ |
3142 | - self.command = None # set in case of error on the first line |
3143 | - self.request_version = version = self.default_request_version |
3144 | - self.close_connection = 1 |
3145 | - requestline = self.raw_requestline |
3146 | - if requestline[-2:] == '\r\n': |
3147 | - requestline = requestline[:-2] |
3148 | - elif requestline[-1:] == '\n': |
3149 | - requestline = requestline[:-1] |
3150 | - self.requestline = requestline |
3151 | - words = requestline.split() |
3152 | - if len(words) == 3: |
3153 | - [command, path, version] = words |
3154 | - if version[:5] != 'HTTP/': |
3155 | - self.send_error(400, "Bad request version (%r)" % version) |
3156 | - return False |
3157 | - try: |
3158 | - base_version_number = version.split('/', 1)[1] |
3159 | - version_number = base_version_number.split(".") |
3160 | - # RFC 2145 section 3.1 says there can be only one "." and |
3161 | - # - major and minor numbers MUST be treated as |
3162 | - # separate integers; |
3163 | - # - HTTP/2.4 is a lower version than HTTP/2.13, which in |
3164 | - # turn is lower than HTTP/12.3; |
3165 | - # - Leading zeros MUST be ignored by recipients. |
3166 | - if len(version_number) != 2: |
3167 | - raise ValueError |
3168 | - version_number = int(version_number[0]), int(version_number[1]) |
3169 | - except (ValueError, IndexError): |
3170 | - self.send_error(400, "Bad request version (%r)" % version) |
3171 | - return False |
3172 | - if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": |
3173 | - self.close_connection = 0 |
3174 | - if version_number >= (2, 0): |
3175 | - self.send_error(505, |
3176 | - "Invalid HTTP Version (%s)" % base_version_number) |
3177 | - return False |
3178 | - elif len(words) == 2: |
3179 | - [command, path] = words |
3180 | - self.close_connection = 1 |
3181 | - if command != 'GET': |
3182 | - self.send_error(400, |
3183 | - "Bad HTTP/0.9 request type (%r)" % command) |
3184 | - return False |
3185 | - elif not words: |
3186 | - return False |
3187 | - else: |
3188 | - self.send_error(400, "Bad request syntax (%r)" % requestline) |
3189 | - return False |
3190 | - self.command, self.path, self.request_version = command, path, version |
3191 | - |
3192 | - # Examine the headers and look for a Connection directive |
3193 | - self.headers = self.MessageClass(self.rfile, 0) |
3194 | - |
3195 | - conntype = self.headers.get('Connection', "") |
3196 | - if conntype.lower() == 'close': |
3197 | - self.close_connection = 1 |
3198 | - elif (conntype.lower() == 'keep-alive' and |
3199 | - self.protocol_version >= "HTTP/1.1"): |
3200 | - self.close_connection = 0 |
3201 | - return True |
3202 | - |
3203 | - def handle_one_request(self): |
3204 | - """Handle a single HTTP request. |
3205 | - |
3206 | - You normally don't need to override this method; see the class |
3207 | - __doc__ string for information on how to handle specific HTTP |
3208 | - commands such as GET and POST. |
3209 | - |
3210 | - """ |
3211 | - self.raw_requestline = self.rfile.readline() |
3212 | - if not self.raw_requestline: |
3213 | - self.close_connection = 1 |
3214 | - return |
3215 | - if not self.parse_request(): # An error code has been sent, just exit |
3216 | - return |
3217 | - mname = 'do_' + self.command |
3218 | - if not hasattr(self, mname): |
3219 | - self.send_error(501, "Unsupported method (%r)" % self.command) |
3220 | - return |
3221 | - method = getattr(self, mname) |
3222 | - method() |
3223 | - |
3224 | - def handle(self): |
3225 | - """Handle multiple requests if necessary.""" |
3226 | - self.close_connection = 1 |
3227 | - |
3228 | - self.handle_one_request() |
3229 | - while not self.close_connection: |
3230 | - self.handle_one_request() |
3231 | - |
3232 | - def send_error(self, code, message=None): |
3233 | - """Send and log an error reply. |
3234 | - |
3235 | - Arguments are the error code, and a detailed message. |
3236 | - The detailed message defaults to the short entry matching the |
3237 | - response code. |
3238 | - |
3239 | - This sends an error response (so it must be called before any |
3240 | - output has been generated), logs the error, and finally sends |
3241 | - a piece of HTML explaining the error to the user. |
3242 | - |
3243 | - """ |
3244 | - |
3245 | - try: |
3246 | - short, long = self.responses[code] |
3247 | - except KeyError: |
3248 | - short, long = '???', '???' |
3249 | - if message is None: |
3250 | - message = short |
3251 | - explain = long |
3252 | - self.log_error("code %d, message %s", code, message) |
3253 | - # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201) |
3254 | - content = (self.error_message_format % |
3255 | - {'code': code, 'message': _quote_html(message), 'explain': explain}) |
3256 | - self.send_response(code, message) |
3257 | - self.send_header("Content-Type", self.error_content_type) |
3258 | - self.send_header('Connection', 'close') |
3259 | - self.end_headers() |
3260 | - if self.command != 'HEAD' and code >= 200 and code not in (204, 304): |
3261 | - self.wfile.write(content) |
3262 | - |
3263 | - error_message_format = DEFAULT_ERROR_MESSAGE |
3264 | - error_content_type = DEFAULT_ERROR_CONTENT_TYPE |
3265 | - |
3266 | - def send_response(self, code, message=None): |
3267 | - """Send the response header and log the response code. |
3268 | - |
3269 | - Also send two standard headers with the server software |
3270 | - version and the current date. |
3271 | - |
3272 | - """ |
3273 | - self.log_request(code) |
3274 | - if message is None: |
3275 | - if code in self.responses: |
3276 | - message = self.responses[code][0] |
3277 | - else: |
3278 | - message = '' |
3279 | - if self.request_version != 'HTTP/0.9': |
3280 | - self.wfile.write("%s %d %s\r\n" % |
3281 | - (self.protocol_version, code, message)) |
3282 | - # print (self.protocol_version, code, message) |
3283 | - self.send_header('Server', self.version_string()) |
3284 | - self.send_header('Date', self.date_time_string()) |
3285 | - |
3286 | - def send_header(self, keyword, value): |
3287 | - """Send a MIME header.""" |
3288 | - if self.request_version != 'HTTP/0.9': |
3289 | - self.wfile.write("%s: %s\r\n" % (keyword, value)) |
3290 | - |
3291 | - if keyword.lower() == 'connection': |
3292 | - if value.lower() == 'close': |
3293 | - self.close_connection = 1 |
3294 | - elif value.lower() == 'keep-alive': |
3295 | - self.close_connection = 0 |
3296 | - |
3297 | - def end_headers(self): |
3298 | - """Send the blank line ending the MIME headers.""" |
3299 | - if self.request_version != 'HTTP/0.9': |
3300 | - self.wfile.write("\r\n") |
3301 | - |
3302 | - def log_request(self, code='-', size='-'): |
3303 | - """Log an accepted request. |
3304 | - |
3305 | - This is called by send_response(). |
3306 | - |
3307 | - """ |
3308 | - |
3309 | - self.log_message('"%s" %s %s', |
3310 | - self.requestline, str(code), str(size)) |
3311 | - |
3312 | - def log_error(self, format, *args): |
3313 | - """Log an error. |
3314 | - |
3315 | - This is called when a request cannot be fulfilled. By |
3316 | - default it passes the message on to log_message(). |
3317 | - |
3318 | - Arguments are the same as for log_message(). |
3319 | - |
3320 | - XXX This should go to the separate error log. |
3321 | - |
3322 | - """ |
3323 | - |
3324 | - self.log_message(format, *args) |
3325 | - |
3326 | - def log_message(self, format, *args): |
3327 | - """Log an arbitrary message. |
3328 | - |
3329 | - This is used by all other logging functions. Override |
3330 | - it if you have specific logging wishes. |
3331 | - |
3332 | - The first argument, FORMAT, is a format string for the |
3333 | - message to be logged. If the format string contains |
3334 | - any % escapes requiring parameters, they should be |
3335 | - specified as subsequent arguments (it's just like |
3336 | - printf!). |
3337 | - |
3338 | - The client host and current date/time are prefixed to |
3339 | - every message. |
3340 | - |
3341 | - """ |
3342 | - |
3343 | - sys.stderr.write("%s - - [%s] %s\n" % |
3344 | - (self.address_string(), |
3345 | - self.log_date_time_string(), |
3346 | - format%args)) |
3347 | - |
3348 | - def version_string(self): |
3349 | - """Return the server software version string.""" |
3350 | - return self.server_version + ' ' + self.sys_version |
3351 | - |
3352 | - def date_time_string(self, timestamp=None): |
3353 | - """Return the current date and time formatted for a message header.""" |
3354 | - if timestamp is None: |
3355 | - timestamp = time.time() |
3356 | - year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) |
3357 | - s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( |
3358 | - self.weekdayname[wd], |
3359 | - day, self.monthname[month], year, |
3360 | - hh, mm, ss) |
3361 | - return s |
3362 | - |
3363 | - def log_date_time_string(self): |
3364 | - """Return the current time formatted for logging.""" |
3365 | - now = time.time() |
3366 | - year, month, day, hh, mm, ss, x, y, z = time.localtime(now) |
3367 | - s = "%02d/%3s/%04d %02d:%02d:%02d" % ( |
3368 | - day, self.monthname[month], year, hh, mm, ss) |
3369 | - return s |
3370 | - |
3371 | - weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
3372 | - |
3373 | - monthname = [None, |
3374 | - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', |
3375 | - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] |
3376 | - |
3377 | - def address_string(self): |
3378 | - """Return the client address formatted for logging. |
3379 | - |
3380 | - This version looks up the full hostname using gethostbyaddr(), |
3381 | - and tries to find a name that contains at least one dot. |
3382 | - |
3383 | - """ |
3384 | - |
3385 | - host, port = self.client_address[:2] |
3386 | - return socket.getfqdn(host) |
3387 | - |
3388 | - # Essentially static class variables |
3389 | - |
3390 | - # The version of the HTTP protocol we support. |
3391 | - # Set this to HTTP/1.1 to enable automatic keepalive |
3392 | - protocol_version = "HTTP/1.0" |
3393 | - |
3394 | - # The Message-like class used to parse headers |
3395 | - MessageClass = mimetools.Message |
3396 | - |
3397 | - # Table mapping response codes to messages; entries have the |
3398 | - # form {code: (shortmessage, longmessage)}. |
3399 | - # See RFC 2616. |
3400 | - responses = { |
3401 | - 100: ('Continue', 'Request received, please continue'), |
3402 | - 101: ('Switching Protocols', |
3403 | - 'Switching to new protocol; obey Upgrade header'), |
3404 | - |
3405 | - 200: ('OK', 'Request fulfilled, document follows'), |
3406 | - 201: ('Created', 'Document created, URL follows'), |
3407 | - 202: ('Accepted', |
3408 | - 'Request accepted, processing continues off-line'), |
3409 | - 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), |
3410 | - 204: ('No Content', 'Request fulfilled, nothing follows'), |
3411 | - 205: ('Reset Content', 'Clear input form for further input.'), |
3412 | - 206: ('Partial Content', 'Partial content follows.'), |
3413 | - |
3414 | - 300: ('Multiple Choices', |
3415 | - 'Object has several resources -- see URI list'), |
3416 | - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), |
3417 | - 302: ('Found', 'Object moved temporarily -- see URI list'), |
3418 | - 303: ('See Other', 'Object moved -- see Method and URL list'), |
3419 | - 304: ('Not Modified', |
3420 | - 'Document has not changed since given time'), |
3421 | - 305: ('Use Proxy', |
3422 | - 'You must use proxy specified in Location to access this ' |
3423 | - 'resource.'), |
3424 | - 307: ('Temporary Redirect', |
3425 | - 'Object moved temporarily -- see URI list'), |
3426 | - |
3427 | - 400: ('Bad Request', |
3428 | - 'Bad request syntax or unsupported method'), |
3429 | - 401: ('Unauthorized', |
3430 | - 'No permission -- see authorization schemes'), |
3431 | - 402: ('Payment Required', |
3432 | - 'No payment -- see charging schemes'), |
3433 | - 403: ('Forbidden', |
3434 | - 'Request forbidden -- authorization will not help'), |
3435 | - 404: ('Not Found', 'Nothing matches the given URI'), |
3436 | - 405: ('Method Not Allowed', |
3437 | - 'Specified method is invalid for this server.'), |
3438 | - 406: ('Not Acceptable', 'URI not available in preferred format.'), |
3439 | - 407: ('Proxy Authentication Required', 'You must authenticate with ' |
3440 | - 'this proxy before proceeding.'), |
3441 | - 408: ('Request Timeout', 'Request timed out; try again later.'), |
3442 | - 409: ('Conflict', 'Request conflict.'), |
3443 | - 410: ('Gone', |
3444 | - 'URI no longer exists and has been permanently removed.'), |
3445 | - 411: ('Length Required', 'Client must specify Content-Length.'), |
3446 | - 412: ('Precondition Failed', 'Precondition in headers is false.'), |
3447 | - 413: ('Request Entity Too Large', 'Entity is too large.'), |
3448 | - 414: ('Request-URI Too Long', 'URI is too long.'), |
3449 | - 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), |
3450 | - 416: ('Requested Range Not Satisfiable', |
3451 | - 'Cannot satisfy request range.'), |
3452 | - 417: ('Expectation Failed', |
3453 | - 'Expect condition could not be satisfied.'), |
3454 | - |
3455 | - 500: ('Internal Server Error', 'Server got itself in trouble'), |
3456 | - 501: ('Not Implemented', |
3457 | - 'Server does not support this operation'), |
3458 | - 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), |
3459 | - 503: ('Service Unavailable', |
3460 | - 'The server cannot process the request due to a high load'), |
3461 | - 504: ('Gateway Timeout', |
3462 | - 'The gateway server did not receive a timely response'), |
3463 | - 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), |
3464 | - } |
3465 | - |
3466 | - |
3467 | -def test(HandlerClass = BaseHTTPRequestHandler, |
3468 | - ServerClass = HTTPServer, protocol="HTTP/1.0"): |
3469 | - """Test the HTTP request handler class. |
3470 | - |
3471 | - This runs an HTTP server on port 8000 (or the first command line |
3472 | - argument). |
3473 | - |
3474 | - """ |
3475 | - |
3476 | - if sys.argv[1:]: |
3477 | - port = int(sys.argv[1]) |
3478 | - else: |
3479 | - port = 8000 |
3480 | - server_address = ('', port) |
3481 | - |
3482 | - HandlerClass.protocol_version = protocol |
3483 | - httpd = ServerClass(server_address, HandlerClass) |
3484 | - |
3485 | - sa = httpd.socket.getsockname() |
3486 | - print "Serving HTTP on", sa[0], "port", sa[1], "..." |
3487 | - httpd.serve_forever() |
3488 | - |
3489 | - |
3490 | -if __name__ == '__main__': |
3491 | - test() |
3492 | |
3493 | === removed file 'python25-compat/SimpleXMLRPCServer.py' |
3494 | --- python25-compat/SimpleXMLRPCServer.py 2009-09-10 00:02:41 +0000 |
3495 | +++ python25-compat/SimpleXMLRPCServer.py 1970-01-01 00:00:00 +0000 |
3496 | @@ -1,611 +0,0 @@ |
3497 | -"""Simple XML-RPC Server. |
3498 | - |
3499 | -This module can be used to create simple XML-RPC servers |
3500 | -by creating a server and either installing functions, a |
3501 | -class instance, or by extending the SimpleXMLRPCServer |
3502 | -class. |
3503 | - |
3504 | -It can also be used to handle XML-RPC requests in a CGI |
3505 | -environment using CGIXMLRPCRequestHandler. |
3506 | - |
3507 | -A list of possible usage patterns follows: |
3508 | - |
3509 | -1. Install functions: |
3510 | - |
3511 | -server = SimpleXMLRPCServer(("localhost", 8000)) |
3512 | -server.register_function(pow) |
3513 | -server.register_function(lambda x,y: x+y, 'add') |
3514 | -server.serve_forever() |
3515 | - |
3516 | -2. Install an instance: |
3517 | - |
3518 | -class MyFuncs: |
3519 | - def __init__(self): |
3520 | - # make all of the string functions available through |
3521 | - # string.func_name |
3522 | - import string |
3523 | - self.string = string |
3524 | - def _listMethods(self): |
3525 | - # implement this method so that system.listMethods |
3526 | - # knows to advertise the strings methods |
3527 | - return list_public_methods(self) + \ |
3528 | - ['string.' + method for method in list_public_methods(self.string)] |
3529 | - def pow(self, x, y): return pow(x, y) |
3530 | - def add(self, x, y) : return x + y |
3531 | - |
3532 | -server = SimpleXMLRPCServer(("localhost", 8000)) |
3533 | -server.register_introspection_functions() |
3534 | -server.register_instance(MyFuncs()) |
3535 | -server.serve_forever() |
3536 | - |
3537 | -3. Install an instance with custom dispatch method: |
3538 | - |
3539 | -class Math: |
3540 | - def _listMethods(self): |
3541 | - # this method must be present for system.listMethods |
3542 | - # to work |
3543 | - return ['add', 'pow'] |
3544 | - def _methodHelp(self, method): |
3545 | - # this method must be present for system.methodHelp |
3546 | - # to work |
3547 | - if method == 'add': |
3548 | - return "add(2,3) => 5" |
3549 | - elif method == 'pow': |
3550 | - return "pow(x, y[, z]) => number" |
3551 | - else: |
3552 | - # By convention, return empty |
3553 | - # string if no help is available |
3554 | - return "" |
3555 | - def _dispatch(self, method, params): |
3556 | - if method == 'pow': |
3557 | - return pow(*params) |
3558 | - elif method == 'add': |
3559 | - return params[0] + params[1] |
3560 | - else: |
3561 | - raise 'bad method' |
3562 | - |
3563 | -server = SimpleXMLRPCServer(("localhost", 8000)) |
3564 | -server.register_introspection_functions() |
3565 | -server.register_instance(Math()) |
3566 | -server.serve_forever() |
3567 | - |
3568 | -4. Subclass SimpleXMLRPCServer: |
3569 | - |
3570 | -class MathServer(SimpleXMLRPCServer): |
3571 | - def _dispatch(self, method, params): |
3572 | - try: |
3573 | - # We are forcing the 'export_' prefix on methods that are |
3574 | - # callable through XML-RPC to prevent potential security |
3575 | - # problems |
3576 | - func = getattr(self, 'export_' + method) |
3577 | - except AttributeError: |
3578 | - raise Exception('method "%s" is not supported' % method) |
3579 | - else: |
3580 | - return func(*params) |
3581 | - |
3582 | - def export_add(self, x, y): |
3583 | - return x + y |
3584 | - |
3585 | -server = MathServer(("localhost", 8000)) |
3586 | -server.serve_forever() |
3587 | - |
3588 | -5. CGI script: |
3589 | - |
3590 | -server = CGIXMLRPCRequestHandler() |
3591 | -server.register_function(pow) |
3592 | -server.handle_request() |
3593 | -""" |
3594 | - |
3595 | -# Written by Brian Quinlan (brian@sweetapp.com). |
3596 | -# Based on code written by Fredrik Lundh. |
3597 | - |
3598 | -import xmlrpclib |
3599 | -from xmlrpclib import Fault |
3600 | -import SocketServer |
3601 | -import BaseHTTPServer |
3602 | -import sys |
3603 | -import os |
3604 | -import traceback |
3605 | -try: |
3606 | - import fcntl |
3607 | -except ImportError: |
3608 | - fcntl = None |
3609 | - |
3610 | -def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): |
3611 | - """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d |
3612 | - |
3613 | - Resolves a dotted attribute name to an object. Raises |
3614 | - an AttributeError if any attribute in the chain starts with a '_'. |
3615 | - |
3616 | - If the optional allow_dotted_names argument is false, dots are not |
3617 | - supported and this function operates similar to getattr(obj, attr). |
3618 | - """ |
3619 | - |
3620 | - if allow_dotted_names: |
3621 | - attrs = attr.split('.') |
3622 | - else: |
3623 | - attrs = [attr] |
3624 | - |
3625 | - for i in attrs: |
3626 | - if i.startswith('_'): |
3627 | - raise AttributeError( |
3628 | - 'attempt to access private attribute "%s"' % i |
3629 | - ) |
3630 | - else: |
3631 | - obj = getattr(obj,i) |
3632 | - return obj |
3633 | - |
3634 | -def list_public_methods(obj): |
3635 | - """Returns a list of attribute strings, found in the specified |
3636 | - object, which represent callable attributes""" |
3637 | - |
3638 | - return [member for member in dir(obj) |
3639 | - if not member.startswith('_') and |
3640 | - hasattr(getattr(obj, member), '__call__')] |
3641 | - |
3642 | -def remove_duplicates(lst): |
3643 | - """remove_duplicates([2,2,2,1,3,3]) => [3,1,2] |
3644 | - |
3645 | - Returns a copy of a list without duplicates. Every list |
3646 | - item must be hashable and the order of the items in the |
3647 | - resulting list is not defined. |
3648 | - """ |
3649 | - u = {} |
3650 | - for x in lst: |
3651 | - u[x] = 1 |
3652 | - |
3653 | - return u.keys() |
3654 | - |
3655 | -class SimpleXMLRPCDispatcher: |
3656 | - """Mix-in class that dispatches XML-RPC requests. |
3657 | - |
3658 | - This class is used to register XML-RPC method handlers |
3659 | - and then to dispatch them. There should never be any |
3660 | - reason to instantiate this class directly. |
3661 | - """ |
3662 | - |
3663 | - def __init__(self, allow_none, encoding): |
3664 | - self.funcs = {} |
3665 | - self.instance = None |
3666 | - self.allow_none = allow_none |
3667 | - self.encoding = encoding |
3668 | - |
3669 | - def register_instance(self, instance, allow_dotted_names=False): |
3670 | - """Registers an instance to respond to XML-RPC requests. |
3671 | - |
3672 | - Only one instance can be installed at a time. |
3673 | - |
3674 | - If the registered instance has a _dispatch method then that |
3675 | - method will be called with the name of the XML-RPC method and |
3676 | - its parameters as a tuple |
3677 | - e.g. instance._dispatch('add',(2,3)) |
3678 | - |
3679 | - If the registered instance does not have a _dispatch method |
3680 | - then the instance will be searched to find a matching method |
3681 | - and, if found, will be called. Methods beginning with an '_' |
3682 | - are considered private and will not be called by |
3683 | - SimpleXMLRPCServer. |
3684 | - |
3685 | - If a registered function matches a XML-RPC request, then it |
3686 | - will be called instead of the registered instance. |
3687 | - |
3688 | - If the optional allow_dotted_names argument is true and the |
3689 | - instance does not have a _dispatch method, method names |
3690 | - containing dots are supported and resolved, as long as none of |
3691 | - the name segments start with an '_'. |
3692 | - |
3693 | - *** SECURITY WARNING: *** |
3694 | - |
3695 | - Enabling the allow_dotted_names options allows intruders |
3696 | - to access your module's global variables and may allow |
3697 | - intruders to execute arbitrary code on your machine. Only |
3698 | - use this option on a secure, closed network. |
3699 | - |
3700 | - """ |
3701 | - |
3702 | - self.instance = instance |
3703 | - self.allow_dotted_names = allow_dotted_names |
3704 | - |
3705 | - def register_function(self, function, name = None): |
3706 | - """Registers a function to respond to XML-RPC requests. |
3707 | - |
3708 | - The optional name argument can be used to set a Unicode name |
3709 | - for the function. |
3710 | - """ |
3711 | - |
3712 | - if name is None: |
3713 | - name = function.__name__ |
3714 | - self.funcs[name] = function |
3715 | - |
3716 | - def register_introspection_functions(self): |
3717 | - """Registers the XML-RPC introspection methods in the system |
3718 | - namespace. |
3719 | - |
3720 | - see http://xmlrpc.usefulinc.com/doc/reserved.html |
3721 | - """ |
3722 | - |
3723 | - self.funcs.update({'system.listMethods' : self.system_listMethods, |
3724 | - 'system.methodSignature' : self.system_methodSignature, |
3725 | - 'system.methodHelp' : self.system_methodHelp}) |
3726 | - |
3727 | - def register_multicall_functions(self): |
3728 | - """Registers the XML-RPC multicall method in the system |
3729 | - namespace. |
3730 | - |
3731 | - see http://www.xmlrpc.com/discuss/msgReader$1208""" |
3732 | - |
3733 | - self.funcs.update({'system.multicall' : self.system_multicall}) |
3734 | - |
3735 | - def _marshaled_dispatch(self, data, dispatch_method = None): |
3736 | - """Dispatches an XML-RPC method from marshalled (XML) data. |
3737 | - |
3738 | - XML-RPC methods are dispatched from the marshalled (XML) data |
3739 | - using the _dispatch method and the result is returned as |
3740 | - marshalled data. For backwards compatibility, a dispatch |
3741 | - function can be provided as an argument (see comment in |
3742 | - SimpleXMLRPCRequestHandler.do_POST) but overriding the |
3743 | - existing method through subclassing is the prefered means |
3744 | - of changing method dispatch behavior. |
3745 | - """ |
3746 | - |
3747 | - try: |
3748 | - params, method = xmlrpclib.loads(data) |
3749 | - |
3750 | - # generate response |
3751 | - if dispatch_method is not None: |
3752 | - response = dispatch_method(method, params) |
3753 | - else: |
3754 | - response = self._dispatch(method, params) |
3755 | - # wrap response in a singleton tuple |
3756 | - response = (response,) |
3757 | - response = xmlrpclib.dumps(response, methodresponse=1, |
3758 | - allow_none=self.allow_none, encoding=self.encoding) |
3759 | - except Fault, fault: |
3760 | - response = xmlrpclib.dumps(fault, allow_none=self.allow_none, |
3761 | - encoding=self.encoding) |
3762 | - except: |
3763 | - # report exception back to server |
3764 | - exc_type, exc_value, exc_tb = sys.exc_info() |
3765 | - response = xmlrpclib.dumps( |
3766 | - xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)), |
3767 | - encoding=self.encoding, allow_none=self.allow_none, |
3768 | - ) |
3769 | - |
3770 | - return response |
3771 | - |
3772 | - def system_listMethods(self): |
3773 | - """system.listMethods() => ['add', 'subtract', 'multiple'] |
3774 | - |
3775 | - Returns a list of the methods supported by the server.""" |
3776 | - |
3777 | - methods = self.funcs.keys() |
3778 | - if self.instance is not None: |
3779 | - # Instance can implement _listMethod to return a list of |
3780 | - # methods |
3781 | - if hasattr(self.instance, '_listMethods'): |
3782 | - methods = remove_duplicates( |
3783 | - methods + self.instance._listMethods() |
3784 | - ) |
3785 | - # if the instance has a _dispatch method then we |
3786 | - # don't have enough information to provide a list |
3787 | - # of methods |
3788 | - elif not hasattr(self.instance, '_dispatch'): |
3789 | - methods = remove_duplicates( |
3790 | - methods + list_public_methods(self.instance) |
3791 | - ) |
3792 | - methods.sort() |
3793 | - return methods |
3794 | - |
3795 | - def system_methodSignature(self, method_name): |
3796 | - """system.methodSignature('add') => [double, int, int] |
3797 | - |
3798 | - Returns a list describing the signature of the method. In the |
3799 | - above example, the add method takes two integers as arguments |
3800 | - and returns a double result. |
3801 | - |
3802 | - This server does NOT support system.methodSignature.""" |
3803 | - |
3804 | - # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html |
3805 | - |
3806 | - return 'signatures not supported' |
3807 | - |
3808 | - def system_methodHelp(self, method_name): |
3809 | - """system.methodHelp('add') => "Adds two integers together" |
3810 | - |
3811 | - Returns a string containing documentation for the specified method.""" |
3812 | - |
3813 | - method = None |
3814 | - if method_name in self.funcs: |
3815 | - method = self.funcs[method_name] |
3816 | - elif self.instance is not None: |
3817 | - # Instance can implement _methodHelp to return help for a method |
3818 | - if hasattr(self.instance, '_methodHelp'): |
3819 | - return self.instance._methodHelp(method_name) |
3820 | - # if the instance has a _dispatch method then we |
3821 | - # don't have enough information to provide help |
3822 | - elif not hasattr(self.instance, '_dispatch'): |
3823 | - try: |
3824 | - method = resolve_dotted_attribute( |
3825 | - self.instance, |
3826 | - method_name, |
3827 | - self.allow_dotted_names |
3828 | - ) |
3829 | - except AttributeError: |
3830 | - pass |
3831 | - |
3832 | - # Note that we aren't checking that the method actually |
3833 | - # be a callable object of some kind |
3834 | - if method is None: |
3835 | - return "" |
3836 | - else: |
3837 | - import pydoc |
3838 | - return pydoc.getdoc(method) |
3839 | - |
3840 | - def system_multicall(self, call_list): |
3841 | - """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ |
3842 | -[[4], ...] |
3843 | - |
3844 | - Allows the caller to package multiple XML-RPC calls into a single |
3845 | - request. |
3846 | - |
3847 | - See http://www.xmlrpc.com/discuss/msgReader$1208 |
3848 | - """ |
3849 | - |
3850 | - results = [] |
3851 | - for call in call_list: |
3852 | - method_name = call['methodName'] |
3853 | - params = call['params'] |
3854 | - |
3855 | - try: |
3856 | - # XXX A marshalling error in any response will fail the entire |
3857 | - # multicall. If someone cares they should fix this. |
3858 | - results.append([self._dispatch(method_name, params)]) |
3859 | - except Fault, fault: |
3860 | - results.append( |
3861 | - {'faultCode' : fault.faultCode, |
3862 | - 'faultString' : fault.faultString} |
3863 | - ) |
3864 | - except: |
3865 | - exc_type, exc_value, exc_tb = sys.exc_info() |
3866 | - results.append( |
3867 | - {'faultCode' : 1, |
3868 | - 'faultString' : "%s:%s" % (exc_type, exc_value)} |
3869 | - ) |
3870 | - return results |
3871 | - |
3872 | - def _dispatch(self, method, params): |
3873 | - """Dispatches the XML-RPC method. |
3874 | - |
3875 | - XML-RPC calls are forwarded to a registered function that |
3876 | - matches the called XML-RPC method name. If no such function |
3877 | - exists then the call is forwarded to the registered instance, |
3878 | - if available. |
3879 | - |
3880 | - If the registered instance has a _dispatch method then that |
3881 | - method will be called with the name of the XML-RPC method and |
3882 | - its parameters as a tuple |
3883 | - e.g. instance._dispatch('add',(2,3)) |
3884 | - |
3885 | - If the registered instance does not have a _dispatch method |
3886 | - then the instance will be searched to find a matching method |
3887 | - and, if found, will be called. |
3888 | - |
3889 | - Methods beginning with an '_' are considered private and will |
3890 | - not be called. |
3891 | - """ |
3892 | - |
3893 | - func = None |
3894 | - try: |
3895 | - # check to see if a matching function has been registered |
3896 | - func = self.funcs[method] |
3897 | - except KeyError: |
3898 | - if self.instance is not None: |
3899 | - # check for a _dispatch method |
3900 | - if hasattr(self.instance, '_dispatch'): |
3901 | - return self.instance._dispatch(method, params) |
3902 | - else: |
3903 | - # call instance method directly |
3904 | - try: |
3905 | - func = resolve_dotted_attribute( |
3906 | - self.instance, |
3907 | - method, |
3908 | - self.allow_dotted_names |
3909 | - ) |
3910 | - except AttributeError: |
3911 | - pass |
3912 | - |
3913 | - if func is not None: |
3914 | - return func(*params) |
3915 | - else: |
3916 | - raise Exception('method "%s" is not supported' % method) |
3917 | - |
3918 | -class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
3919 | - """Simple XML-RPC request handler class. |
3920 | - |
3921 | - Handles all HTTP POST requests and attempts to decode them as |
3922 | - XML-RPC requests. |
3923 | - """ |
3924 | - |
3925 | - # Class attribute listing the accessible path components; |
3926 | - # paths not on this list will result in a 404 error. |
3927 | - rpc_paths = ('/', '/RPC2') |
3928 | - |
3929 | - def is_rpc_path_valid(self): |
3930 | - if self.rpc_paths: |
3931 | - return self.path in self.rpc_paths |
3932 | - else: |
3933 | - # If .rpc_paths is empty, just assume all paths are legal |
3934 | - return True |
3935 | - |
3936 | - def do_POST(self): |
3937 | - """Handles the HTTP POST request. |
3938 | - |
3939 | - Attempts to interpret all HTTP POST requests as XML-RPC calls, |
3940 | - which are forwarded to the server's _dispatch method for handling. |
3941 | - """ |
3942 | - |
3943 | - # Check that the path is legal |
3944 | - if not self.is_rpc_path_valid(): |
3945 | - self.report_404() |
3946 | - return |
3947 | - |
3948 | - try: |
3949 | - # Get arguments by reading body of request. |
3950 | - # We read this in chunks to avoid straining |
3951 | - # socket.read(); around the 10 or 15Mb mark, some platforms |
3952 | - # begin to have problems (bug #792570). |
3953 | - max_chunk_size = 10*1024*1024 |
3954 | - size_remaining = int(self.headers["content-length"]) |
3955 | - L = [] |
3956 | - while size_remaining: |
3957 | - chunk_size = min(size_remaining, max_chunk_size) |
3958 | - L.append(self.rfile.read(chunk_size)) |
3959 | - size_remaining -= len(L[-1]) |
3960 | - data = ''.join(L) |
3961 | - |
3962 | - # In previous versions of SimpleXMLRPCServer, _dispatch |
3963 | - # could be overridden in this class, instead of in |
3964 | - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, |
3965 | - # check to see if a subclass implements _dispatch and dispatch |
3966 | - # using that method if present. |
3967 | - response = self.server._marshaled_dispatch( |
3968 | - data, getattr(self, '_dispatch', None) |
3969 | - ) |
3970 | - except Exception, e: # This should only happen if the module is buggy |
3971 | - # internal error, report as HTTP server error |
3972 | - self.send_response(500) |
3973 | - |
3974 | - # Send information about the exception if requested |
3975 | - if hasattr(self.server, '_send_traceback_header') and \ |
3976 | - self.server._send_traceback_header: |
3977 | - self.send_header("X-exception", str(e)) |
3978 | - self.send_header("X-traceback", traceback.format_exc()) |
3979 | - |
3980 | - self.end_headers() |
3981 | - else: |
3982 | - # got a valid XML RPC response |
3983 | - self.send_response(200) |
3984 | - self.send_header("Content-type", "text/xml") |
3985 | - self.send_header("Content-length", str(len(response))) |
3986 | - self.end_headers() |
3987 | - self.wfile.write(response) |
3988 | - |
3989 | - # shut down the connection |
3990 | - self.wfile.flush() |
3991 | - self.connection.shutdown(1) |
3992 | - |
3993 | - def report_404 (self): |
3994 | - # Report a 404 error |
3995 | - self.send_response(404) |
3996 | - response = 'No such page' |
3997 | - self.send_header("Content-type", "text/plain") |
3998 | - self.send_header("Content-length", str(len(response))) |
3999 | - self.end_headers() |
4000 | - self.wfile.write(response) |
4001 | - # shut down the connection |
4002 | - self.wfile.flush() |
4003 | - self.connection.shutdown(1) |
4004 | - |
4005 | - def log_request(self, code='-', size='-'): |
4006 | - """Selectively log an accepted request.""" |
4007 | - |
4008 | - if self.server.logRequests: |
4009 | - BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size) |
4010 | - |
4011 | -class SimpleXMLRPCServer(SocketServer.TCPServer, |
4012 | - SimpleXMLRPCDispatcher): |
4013 | - """Simple XML-RPC server. |
4014 | - |
4015 | - Simple XML-RPC server that allows functions and a single instance |
4016 | - to be installed to handle requests. The default implementation |
4017 | - attempts to dispatch XML-RPC calls to the functions or instance |
4018 | - installed in the server. Override the _dispatch method inhereted |
4019 | - from SimpleXMLRPCDispatcher to change this behavior. |
4020 | - """ |
4021 | - |
4022 | - allow_reuse_address = True |
4023 | - |
4024 | - # Warning: this is for debugging purposes only! Never set this to True in |
4025 | - # production code, as will be sending out sensitive information (exception |
4026 | - # and stack trace details) when exceptions are raised inside |
4027 | - # SimpleXMLRPCRequestHandler.do_POST |
4028 | - _send_traceback_header = False |
4029 | - |
4030 | - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, |
4031 | - logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): |
4032 | - self.logRequests = logRequests |
4033 | - |
4034 | - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) |
4035 | - SocketServer.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) |
4036 | - |
4037 | - # [Bug #1222790] If possible, set close-on-exec flag; if a |
4038 | - # method spawns a subprocess, the subprocess shouldn't have |
4039 | - # the listening socket open. |
4040 | - if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): |
4041 | - flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) |
4042 | - flags |= fcntl.FD_CLOEXEC |
4043 | - fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) |
4044 | - |
4045 | -class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): |
4046 | - """Simple handler for XML-RPC data passed through CGI.""" |
4047 | - |
4048 | - def __init__(self, allow_none=False, encoding=None): |
4049 | - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) |
4050 | - |
4051 | - def handle_xmlrpc(self, request_text): |
4052 | - """Handle a single XML-RPC request""" |
4053 | - |
4054 | - response = self._marshaled_dispatch(request_text) |
4055 | - |
4056 | - print 'Content-Type: text/xml' |
4057 | - print 'Content-Length: %d' % len(response) |
4058 | |
4059 | - sys.stdout.write(response) |
4060 | - |
4061 | - def handle_get(self): |
4062 | - """Handle a single HTTP GET request. |
4063 | - |
4064 | - Default implementation indicates an error because |
4065 | - XML-RPC uses the POST method. |
4066 | - """ |
4067 | - |
4068 | - code = 400 |
4069 | - message, explain = \ |
4070 | - BaseHTTPServer.BaseHTTPRequestHandler.responses[code] |
4071 | - |
4072 | - response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \ |
4073 | - { |
4074 | - 'code' : code, |
4075 | - 'message' : message, |
4076 | - 'explain' : explain |
4077 | - } |
4078 | - print 'Status: %d %s' % (code, message) |
4079 | - print 'Content-Type: text/html' |
4080 | - print 'Content-Length: %d' % len(response) |
4081 | |
4082 | - sys.stdout.write(response) |
4083 | - |
4084 | - def handle_request(self, request_text = None): |
4085 | - """Handle a single XML-RPC request passed through a CGI post method. |
4086 | - |
4087 | - If no XML data is given then it is read from stdin. The resulting |
4088 | - XML-RPC response is printed to stdout along with the correct HTTP |
4089 | - headers. |
4090 | - """ |
4091 | - |
4092 | - if request_text is None and \ |
4093 | - os.environ.get('REQUEST_METHOD', None) == 'GET': |
4094 | - self.handle_get() |
4095 | - else: |
4096 | - # POST data is normally available through stdin |
4097 | - if request_text is None: |
4098 | - request_text = sys.stdin.read() |
4099 | - |
4100 | - self.handle_xmlrpc(request_text) |
4101 | - |
4102 | -if __name__ == '__main__': |
4103 | - print 'Running XML-RPC server on port 8000' |
4104 | - server = SimpleXMLRPCServer(("localhost", 8000)) |
4105 | - server.register_function(pow) |
4106 | - server.register_function(lambda x,y: x+y, 'add') |
4107 | - server.serve_forever() |
4108 | |
4109 | === removed file 'python25-compat/SocketServer.py' |
4110 | --- python25-compat/SocketServer.py 2009-09-10 00:02:41 +0000 |
4111 | +++ python25-compat/SocketServer.py 1970-01-01 00:00:00 +0000 |
4112 | @@ -1,681 +0,0 @@ |
4113 | -"""Generic socket server classes. |
4114 | - |
4115 | -This module tries to capture the various aspects of defining a server: |
4116 | - |
4117 | -For socket-based servers: |
4118 | - |
4119 | -- address family: |
4120 | - - AF_INET{,6}: IP (Internet Protocol) sockets (default) |
4121 | - - AF_UNIX: Unix domain sockets |
4122 | - - others, e.g. AF_DECNET are conceivable (see <socket.h> |
4123 | -- socket type: |
4124 | - - SOCK_STREAM (reliable stream, e.g. TCP) |
4125 | - - SOCK_DGRAM (datagrams, e.g. UDP) |
4126 | - |
4127 | -For request-based servers (including socket-based): |
4128 | - |
4129 | -- client address verification before further looking at the request |
4130 | - (This is actually a hook for any processing that needs to look |
4131 | - at the request before anything else, e.g. logging) |
4132 | -- how to handle multiple requests: |
4133 | - - synchronous (one request is handled at a time) |
4134 | - - forking (each request is handled by a new process) |
4135 | - - threading (each request is handled by a new thread) |
4136 | - |
4137 | -The classes in this module favor the server type that is simplest to |
4138 | -write: a synchronous TCP/IP server. This is bad class design, but |
4139 | -save some typing. (There's also the issue that a deep class hierarchy |
4140 | -slows down method lookups.) |
4141 | - |
4142 | -There are five classes in an inheritance diagram, four of which represent |
4143 | -synchronous servers of four types: |
4144 | - |
4145 | - +------------+ |
4146 | - | BaseServer | |
4147 | - +------------+ |
4148 | - | |
4149 | - v |
4150 | - +-----------+ +------------------+ |
4151 | - | TCPServer |------->| UnixStreamServer | |
4152 | - +-----------+ +------------------+ |
4153 | - | |
4154 | - v |
4155 | - +-----------+ +--------------------+ |
4156 | - | UDPServer |------->| UnixDatagramServer | |
4157 | - +-----------+ +--------------------+ |
4158 | - |
4159 | -Note that UnixDatagramServer derives from UDPServer, not from |
4160 | -UnixStreamServer -- the only difference between an IP and a Unix |
4161 | -stream server is the address family, which is simply repeated in both |
4162 | -unix server classes. |
4163 | - |
4164 | -Forking and threading versions of each type of server can be created |
4165 | -using the ForkingMixIn and ThreadingMixIn mix-in classes. For |
4166 | -instance, a threading UDP server class is created as follows: |
4167 | - |
4168 | - class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass |
4169 | - |
4170 | -The Mix-in class must come first, since it overrides a method defined |
4171 | -in UDPServer! Setting the various member variables also changes |
4172 | -the behavior of the underlying server mechanism. |
4173 | - |
4174 | -To implement a service, you must derive a class from |
4175 | -BaseRequestHandler and redefine its handle() method. You can then run |
4176 | -various versions of the service by combining one of the server classes |
4177 | -with your request handler class. |
4178 | - |
4179 | -The request handler class must be different for datagram or stream |
4180 | -services. This can be hidden by using the request handler |
4181 | -subclasses StreamRequestHandler or DatagramRequestHandler. |
4182 | - |
4183 | -Of course, you still have to use your head! |
4184 | - |
4185 | -For instance, it makes no sense to use a forking server if the service |
4186 | -contains state in memory that can be modified by requests (since the |
4187 | -modifications in the child process would never reach the initial state |
4188 | -kept in the parent process and passed to each child). In this case, |
4189 | -you can use a threading server, but you will probably have to use |
4190 | -locks to avoid two requests that come in nearly simultaneous to apply |
4191 | -conflicting changes to the server state. |
4192 | - |
4193 | -On the other hand, if you are building e.g. an HTTP server, where all |
4194 | -data is stored externally (e.g. in the file system), a synchronous |
4195 | -class will essentially render the service "deaf" while one request is |
4196 | -being handled -- which may be for a very long time if a client is slow |
4197 | -to reqd all the data it has requested. Here a threading or forking |
4198 | -server is appropriate. |
4199 | - |
4200 | -In some cases, it may be appropriate to process part of a request |
4201 | -synchronously, but to finish processing in a forked child depending on |
4202 | -the request data. This can be implemented by using a synchronous |
4203 | -server and doing an explicit fork in the request handler class |
4204 | -handle() method. |
4205 | - |
4206 | -Another approach to handling multiple simultaneous requests in an |
4207 | -environment that supports neither threads nor fork (or where these are |
4208 | -too expensive or inappropriate for the service) is to maintain an |
4209 | -explicit table of partially finished requests and to use select() to |
4210 | -decide which request to work on next (or whether to handle a new |
4211 | -incoming request). This is particularly important for stream services |
4212 | -where each client can potentially be connected for a long time (if |
4213 | -threads or subprocesses cannot be used). |
4214 | - |
4215 | -Future work: |
4216 | -- Standard classes for Sun RPC (which uses either UDP or TCP) |
4217 | -- Standard mix-in classes to implement various authentication |
4218 | - and encryption schemes |
4219 | -- Standard framework for select-based multiplexing |
4220 | - |
4221 | -XXX Open problems: |
4222 | -- What to do with out-of-band data? |
4223 | - |
4224 | -BaseServer: |
4225 | -- split generic "request" functionality out into BaseServer class. |
4226 | - Copyright (C) 2000 Luke Kenneth Casson Leighton <lkcl@samba.org> |
4227 | - |
4228 | - example: read entries from a SQL database (requires overriding |
4229 | - get_request() to return a table entry from the database). |
4230 | - entry is processed by a RequestHandlerClass. |
4231 | - |
4232 | -""" |
4233 | - |
4234 | -# Author of the BaseServer patch: Luke Kenneth Casson Leighton |
4235 | - |
4236 | -# XXX Warning! |
4237 | -# There is a test suite for this module, but it cannot be run by the |
4238 | -# standard regression test. |
4239 | -# To run it manually, run Lib/test/test_socketserver.py. |
4240 | - |
4241 | -__version__ = "0.4" |
4242 | - |
4243 | - |
4244 | -import socket |
4245 | -import select |
4246 | -import sys |
4247 | -import os |
4248 | -try: |
4249 | - import threading |
4250 | -except ImportError: |
4251 | - import dummy_threading as threading |
4252 | - |
4253 | -__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", |
4254 | - "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", |
4255 | - "StreamRequestHandler","DatagramRequestHandler", |
4256 | - "ThreadingMixIn", "ForkingMixIn"] |
4257 | -if hasattr(socket, "AF_UNIX"): |
4258 | - __all__.extend(["UnixStreamServer","UnixDatagramServer", |
4259 | - "ThreadingUnixStreamServer", |
4260 | - "ThreadingUnixDatagramServer"]) |
4261 | - |
4262 | -class BaseServer: |
4263 | - |
4264 | - """Base class for server classes. |
4265 | - |
4266 | - Methods for the caller: |
4267 | - |
4268 | - - __init__(server_address, RequestHandlerClass) |
4269 | - - serve_forever(poll_interval=0.5) |
4270 | - - shutdown() |
4271 | - - handle_request() # if you do not use serve_forever() |
4272 | - - fileno() -> int # for select() |
4273 | - |
4274 | - Methods that may be overridden: |
4275 | - |
4276 | - - server_bind() |
4277 | - - server_activate() |
4278 | - - get_request() -> request, client_address |
4279 | - - handle_timeout() |
4280 | - - verify_request(request, client_address) |
4281 | - - server_close() |
4282 | - - process_request(request, client_address) |
4283 | - - close_request(request) |
4284 | - - handle_error() |
4285 | - |
4286 | - Methods for derived classes: |
4287 | - |
4288 | - - finish_request(request, client_address) |
4289 | - |
4290 | - Class variables that may be overridden by derived classes or |
4291 | - instances: |
4292 | - |
4293 | - - timeout |
4294 | - - address_family |
4295 | - - socket_type |
4296 | - - allow_reuse_address |
4297 | - |
4298 | - Instance variables: |
4299 | - |
4300 | - - RequestHandlerClass |
4301 | - - socket |
4302 | - |
4303 | - """ |
4304 | - |
4305 | - timeout = None |
4306 | - |
4307 | - def __init__(self, server_address, RequestHandlerClass): |
4308 | - """Constructor. May be extended, do not override.""" |
4309 | - self.server_address = server_address |
4310 | - self.RequestHandlerClass = RequestHandlerClass |
4311 | - self.__is_shut_down = threading.Event() |
4312 | - self.__serving = False |
4313 | - |
4314 | - def server_activate(self): |
4315 | - """Called by constructor to activate the server. |
4316 | - |
4317 | - May be overridden. |
4318 | - |
4319 | - """ |
4320 | - pass |
4321 | - |
4322 | - def serve_forever(self, poll_interval=0.5): |
4323 | - """Handle one request at a time until shutdown. |
4324 | - |
4325 | - Polls for shutdown every poll_interval seconds. Ignores |
4326 | - self.timeout. If you need to do periodic tasks, do them in |
4327 | - another thread. |
4328 | - """ |
4329 | - self.__serving = True |
4330 | - self.__is_shut_down.clear() |
4331 | - while self.__serving: |
4332 | - # XXX: Consider using another file descriptor or |
4333 | - # connecting to the socket to wake this up instead of |
4334 | - # polling. Polling reduces our responsiveness to a |
4335 | - # shutdown request and wastes cpu at all other times. |
4336 | - r, w, e = select.select([self], [], [], poll_interval) |
4337 | - if r: |
4338 | - self._handle_request_noblock() |
4339 | - self.__is_shut_down.set() |
4340 | - |
4341 | - def shutdown(self): |
4342 | - """Stops the serve_forever loop. |
4343 | - |
4344 | - Blocks until the loop has finished. This must be called while |
4345 | - serve_forever() is running in another thread, or it will |
4346 | - deadlock. |
4347 | - """ |
4348 | - self.__serving = False |
4349 | - self.__is_shut_down.wait() |
4350 | - |
4351 | - # The distinction between handling, getting, processing and |
4352 | - # finishing a request is fairly arbitrary. Remember: |
4353 | - # |
4354 | - # - handle_request() is the top-level call. It calls |
4355 | - # select, get_request(), verify_request() and process_request() |
4356 | - # - get_request() is different for stream or datagram sockets |
4357 | - # - process_request() is the place that may fork a new process |
4358 | - # or create a new thread to finish the request |
4359 | - # - finish_request() instantiates the request handler class; |
4360 | - # this constructor will handle the request all by itself |
4361 | - |
4362 | - def handle_request(self): |
4363 | - """Handle one request, possibly blocking. |
4364 | - |
4365 | - Respects self.timeout. |
4366 | - """ |
4367 | - # Support people who used socket.settimeout() to escape |
4368 | - # handle_request before self.timeout was available. |
4369 | - timeout = self.socket.gettimeout() |
4370 | - if timeout is None: |
4371 | - timeout = self.timeout |
4372 | - elif self.timeout is not None: |
4373 | - timeout = min(timeout, self.timeout) |
4374 | - fd_sets = select.select([self], [], [], timeout) |
4375 | - if not fd_sets[0]: |
4376 | - self.handle_timeout() |
4377 | - return |
4378 | - self._handle_request_noblock() |
4379 | - |
4380 | - def _handle_request_noblock(self): |
4381 | - """Handle one request, without blocking. |
4382 | - |
4383 | - I assume that select.select has returned that the socket is |
4384 | - readable before this function was called, so there should be |
4385 | - no risk of blocking in get_request(). |
4386 | - """ |
4387 | - try: |
4388 | - request, client_address = self.get_request() |
4389 | - except socket.error: |
4390 | - return |
4391 | - if self.verify_request(request, client_address): |
4392 | - try: |
4393 | - self.process_request(request, client_address) |
4394 | - except: |
4395 | - self.handle_error(request, client_address) |
4396 | - self.close_request(request) |
4397 | - |
4398 | - def handle_timeout(self): |
4399 | - """Called if no new request arrives within self.timeout. |
4400 | - |
4401 | - Overridden by ForkingMixIn. |
4402 | - """ |
4403 | - pass |
4404 | - |
4405 | - def verify_request(self, request, client_address): |
4406 | - """Verify the request. May be overridden. |
4407 | - |
4408 | - Return True if we should proceed with this request. |
4409 | - |
4410 | - """ |
4411 | - return True |
4412 | - |
4413 | - def process_request(self, request, client_address): |
4414 | - """Call finish_request. |
4415 | - |
4416 | - Overridden by ForkingMixIn and ThreadingMixIn. |
4417 | - |
4418 | - """ |
4419 | - self.finish_request(request, client_address) |
4420 | - self.close_request(request) |
4421 | - |
4422 | - def server_close(self): |
4423 | - """Called to clean-up the server. |
4424 | - |
4425 | - May be overridden. |
4426 | - |
4427 | - """ |
4428 | - pass |
4429 | - |
4430 | - def finish_request(self, request, client_address): |
4431 | - """Finish one request by instantiating RequestHandlerClass.""" |
4432 | - self.RequestHandlerClass(request, client_address, self) |
4433 | - |
4434 | - def close_request(self, request): |
4435 | - """Called to clean up an individual request.""" |
4436 | - pass |
4437 | - |
4438 | - def handle_error(self, request, client_address): |
4439 | - """Handle an error gracefully. May be overridden. |
4440 | - |
4441 | - The default is to print a traceback and continue. |
4442 | - |
4443 | - """ |
4444 | - print '-'*40 |
4445 | - print 'Exception happened during processing of request from', |
4446 | - print client_address |
4447 | - import traceback |
4448 | - traceback.print_exc() # XXX But this goes to stderr! |
4449 | - print '-'*40 |
4450 | - |
4451 | - |
4452 | -class TCPServer(BaseServer): |
4453 | - |
4454 | - """Base class for various socket-based server classes. |
4455 | - |
4456 | - Defaults to synchronous IP stream (i.e., TCP). |
4457 | - |
4458 | - Methods for the caller: |
4459 | - |
4460 | - - __init__(server_address, RequestHandlerClass, bind_and_activate=True) |
4461 | - - serve_forever(poll_interval=0.5) |
4462 | - - shutdown() |
4463 | - - handle_request() # if you don't use serve_forever() |
4464 | - - fileno() -> int # for select() |
4465 | - |
4466 | - Methods that may be overridden: |
4467 | - |
4468 | - - server_bind() |
4469 | - - server_activate() |
4470 | - - get_request() -> request, client_address |
4471 | - - handle_timeout() |
4472 | - - verify_request(request, client_address) |
4473 | - - process_request(request, client_address) |
4474 | - - close_request(request) |
4475 | - - handle_error() |
4476 | - |
4477 | - Methods for derived classes: |
4478 | - |
4479 | - - finish_request(request, client_address) |
4480 | - |
4481 | - Class variables that may be overridden by derived classes or |
4482 | - instances: |
4483 | - |
4484 | - - timeout |
4485 | - - address_family |
4486 | - - socket_type |
4487 | - - request_queue_size (only for stream sockets) |
4488 | - - allow_reuse_address |
4489 | - |
4490 | - Instance variables: |
4491 | - |
4492 | - - server_address |
4493 | - - RequestHandlerClass |
4494 | - - socket |
4495 | - |
4496 | - """ |
4497 | - |
4498 | - address_family = socket.AF_INET |
4499 | - |
4500 | - socket_type = socket.SOCK_STREAM |
4501 | - |
4502 | - request_queue_size = 5 |
4503 | - |
4504 | - allow_reuse_address = False |
4505 | - |
4506 | - def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): |
4507 | - """Constructor. May be extended, do not override.""" |
4508 | - BaseServer.__init__(self, server_address, RequestHandlerClass) |
4509 | - self.socket = socket.socket(self.address_family, |
4510 | - self.socket_type) |
4511 | - if bind_and_activate: |
4512 | - self.server_bind() |
4513 | - self.server_activate() |
4514 | - |
4515 | - def server_bind(self): |
4516 | - """Called by constructor to bind the socket. |
4517 | - |
4518 | - May be overridden. |
4519 | - |
4520 | - """ |
4521 | - if self.allow_reuse_address: |
4522 | - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
4523 | - self.socket.bind(self.server_address) |
4524 | - self.server_address = self.socket.getsockname() |
4525 | - |
4526 | - def server_activate(self): |
4527 | - """Called by constructor to activate the server. |
4528 | - |
4529 | - May be overridden. |
4530 | - |
4531 | - """ |
4532 | - self.socket.listen(self.request_queue_size) |
4533 | - |
4534 | - def server_close(self): |
4535 | - """Called to clean-up the server. |
4536 | - |
4537 | - May be overridden. |
4538 | - |
4539 | - """ |
4540 | - self.socket.close() |
4541 | - |
4542 | - def fileno(self): |
4543 | - """Return socket file number. |
4544 | - |
4545 | - Interface required by select(). |
4546 | - |
4547 | - """ |
4548 | - return self.socket.fileno() |
4549 | - |
4550 | - def get_request(self): |
4551 | - """Get the request and client address from the socket. |
4552 | - |
4553 | - May be overridden. |
4554 | - |
4555 | - """ |
4556 | - return self.socket.accept() |
4557 | - |
4558 | - def close_request(self, request): |
4559 | - """Called to clean up an individual request.""" |
4560 | - request.close() |
4561 | - |
4562 | - |
4563 | -class UDPServer(TCPServer): |
4564 | - |
4565 | - """UDP server class.""" |
4566 | - |
4567 | - allow_reuse_address = False |
4568 | - |
4569 | - socket_type = socket.SOCK_DGRAM |
4570 | - |
4571 | - max_packet_size = 8192 |
4572 | - |
4573 | - def get_request(self): |
4574 | - data, client_addr = self.socket.recvfrom(self.max_packet_size) |
4575 | - return (data, self.socket), client_addr |
4576 | - |
4577 | - def server_activate(self): |
4578 | - # No need to call listen() for UDP. |
4579 | - pass |
4580 | - |
4581 | - def close_request(self, request): |
4582 | - # No need to close anything. |
4583 | - pass |
4584 | - |
4585 | -class ForkingMixIn: |
4586 | - |
4587 | - """Mix-in class to handle each request in a new process.""" |
4588 | - |
4589 | - timeout = 300 |
4590 | - active_children = None |
4591 | - max_children = 40 |
4592 | - |
4593 | - def collect_children(self): |
4594 | - """Internal routine to wait for children that have exited.""" |
4595 | - if self.active_children is None: return |
4596 | - while len(self.active_children) >= self.max_children: |
4597 | - # XXX: This will wait for any child process, not just ones |
4598 | - # spawned by this library. This could confuse other |
4599 | - # libraries that expect to be able to wait for their own |
4600 | - # children. |
4601 | - try: |
4602 | - pid, status = os.waitpid(0, options=0) |
4603 | - except os.error: |
4604 | - pid = None |
4605 | - if pid not in self.active_children: continue |
4606 | - self.active_children.remove(pid) |
4607 | - |
4608 | - # XXX: This loop runs more system calls than it ought |
4609 | - # to. There should be a way to put the active_children into a |
4610 | - # process group and then use os.waitpid(-pgid) to wait for any |
4611 | - # of that set, but I couldn't find a way to allocate pgids |
4612 | - # that couldn't collide. |
4613 | - for child in self.active_children: |
4614 | - try: |
4615 | - pid, status = os.waitpid(child, os.WNOHANG) |
4616 | - except os.error: |
4617 | - pid = None |
4618 | - if not pid: continue |
4619 | - try: |
4620 | - self.active_children.remove(pid) |
4621 | - except ValueError, e: |
4622 | - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, |
4623 | - self.active_children)) |
4624 | - |
4625 | - def handle_timeout(self): |
4626 | - """Wait for zombies after self.timeout seconds of inactivity. |
4627 | - |
4628 | - May be extended, do not override. |
4629 | - """ |
4630 | - self.collect_children() |
4631 | - |
4632 | - def process_request(self, request, client_address): |
4633 | - """Fork a new subprocess to process the request.""" |
4634 | - self.collect_children() |
4635 | - pid = os.fork() |
4636 | - if pid: |
4637 | - # Parent process |
4638 | - if self.active_children is None: |
4639 | - self.active_children = [] |
4640 | - self.active_children.append(pid) |
4641 | - self.close_request(request) |
4642 | - return |
4643 | - else: |
4644 | - # Child process. |
4645 | - # This must never return, hence os._exit()! |
4646 | - try: |
4647 | - self.finish_request(request, client_address) |
4648 | - os._exit(0) |
4649 | - except: |
4650 | - try: |
4651 | - self.handle_error(request, client_address) |
4652 | - finally: |
4653 | - os._exit(1) |
4654 | - |
4655 | - |
4656 | -class ThreadingMixIn: |
4657 | - """Mix-in class to handle each request in a new thread.""" |
4658 | - |
4659 | - # Decides how threads will act upon termination of the |
4660 | - # main process |
4661 | - daemon_threads = False |
4662 | - |
4663 | - def process_request_thread(self, request, client_address): |
4664 | - """Same as in BaseServer but as a thread. |
4665 | - |
4666 | - In addition, exception handling is done here. |
4667 | - |
4668 | - """ |
4669 | - try: |
4670 | - self.finish_request(request, client_address) |
4671 | - self.close_request(request) |
4672 | - except: |
4673 | - self.handle_error(request, client_address) |
4674 | - self.close_request(request) |
4675 | - |
4676 | - def process_request(self, request, client_address): |
4677 | - """Start a new thread to process the request.""" |
4678 | - t = threading.Thread(target = self.process_request_thread, |
4679 | - args = (request, client_address)) |
4680 | - if self.daemon_threads: |
4681 | - t.setDaemon (1) |
4682 | - t.start() |
4683 | - |
4684 | - |
4685 | -class ForkingUDPServer(ForkingMixIn, UDPServer): pass |
4686 | -class ForkingTCPServer(ForkingMixIn, TCPServer): pass |
4687 | - |
4688 | -class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass |
4689 | -class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass |
4690 | - |
4691 | -if hasattr(socket, 'AF_UNIX'): |
4692 | - |
4693 | - class UnixStreamServer(TCPServer): |
4694 | - address_family = socket.AF_UNIX |
4695 | - |
4696 | - class UnixDatagramServer(UDPServer): |
4697 | - address_family = socket.AF_UNIX |
4698 | - |
4699 | - class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass |
4700 | - |
4701 | - class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass |
4702 | - |
4703 | -class BaseRequestHandler: |
4704 | - |
4705 | - """Base class for request handler classes. |
4706 | - |
4707 | - This class is instantiated for each request to be handled. The |
4708 | - constructor sets the instance variables request, client_address |
4709 | - and server, and then calls the handle() method. To implement a |
4710 | - specific service, all you need to do is to derive a class which |
4711 | - defines a handle() method. |
4712 | - |
4713 | - The handle() method can find the request as self.request, the |
4714 | - client address as self.client_address, and the server (in case it |
4715 | - needs access to per-server information) as self.server. Since a |
4716 | - separate instance is created for each request, the handle() method |
4717 | - can define arbitrary other instance variariables. |
4718 | - |
4719 | - """ |
4720 | - |
4721 | - def __init__(self, request, client_address, server): |
4722 | - self.request = request |
4723 | - self.client_address = client_address |
4724 | - self.server = server |
4725 | - try: |
4726 | - self.setup() |
4727 | - self.handle() |
4728 | - self.finish() |
4729 | - finally: |
4730 | - sys.exc_traceback = None # Help garbage collection |
4731 | - |
4732 | - def setup(self): |
4733 | - pass |
4734 | - |
4735 | - def handle(self): |
4736 | - pass |
4737 | - |
4738 | - def finish(self): |
4739 | - pass |
4740 | - |
4741 | - |
4742 | -# The following two classes make it possible to use the same service |
4743 | -# class for stream or datagram servers. |
4744 | -# Each class sets up these instance variables: |
4745 | -# - rfile: a file object from which receives the request is read |
4746 | -# - wfile: a file object to which the reply is written |
4747 | -# When the handle() method returns, wfile is flushed properly |
4748 | - |
4749 | - |
4750 | -class StreamRequestHandler(BaseRequestHandler): |
4751 | - |
4752 | - """Define self.rfile and self.wfile for stream sockets.""" |
4753 | - |
4754 | - # Default buffer sizes for rfile, wfile. |
4755 | - # We default rfile to buffered because otherwise it could be |
4756 | - # really slow for large data (a getc() call per byte); we make |
4757 | - # wfile unbuffered because (a) often after a write() we want to |
4758 | - # read and we need to flush the line; (b) big writes to unbuffered |
4759 | - # files are typically optimized by stdio even when big reads |
4760 | - # aren't. |
4761 | - rbufsize = -1 |
4762 | - wbufsize = 0 |
4763 | - |
4764 | - def setup(self): |
4765 | - self.connection = self.request |
4766 | - self.rfile = self.connection.makefile('rb', self.rbufsize) |
4767 | - self.wfile = self.connection.makefile('wb', self.wbufsize) |
4768 | - |
4769 | - def finish(self): |
4770 | - if not self.wfile.closed: |
4771 | - self.wfile.flush() |
4772 | - self.wfile.close() |
4773 | - self.rfile.close() |
4774 | - |
4775 | - |
4776 | -class DatagramRequestHandler(BaseRequestHandler): |
4777 | - |
4778 | - # XXX Regrettably, I cannot get this working on Linux; |
4779 | - # s.recvfrom() doesn't return a meaningful client address. |
4780 | - |
4781 | - """Define self.rfile and self.wfile for datagram sockets.""" |
4782 | - |
4783 | - def setup(self): |
4784 | - try: |
4785 | - from cStringIO import StringIO |
4786 | - except ImportError: |
4787 | - from StringIO import StringIO |
4788 | - self.packet, self.socket = self.request |
4789 | - self.rfile = StringIO(self.packet) |
4790 | - self.wfile = StringIO() |
4791 | - |
4792 | - def finish(self): |
4793 | - self.socket.sendto(self.wfile.getvalue(), self.client_address) |
4794 | |
4795 | === modified file 'setup.py' |
4796 | --- setup.py 2017-02-21 15:53:48 +0000 |
4797 | +++ setup.py 2017-05-22 10:09:36 +0000 |
4798 | @@ -36,11 +36,8 @@ |
4799 | from distutils.sysconfig import get_python_lib |
4800 | from setup_py2exe_custom import custom_py2exe, fixup_data_pytz_zoneinfo |
4801 | |
4802 | -has_py2exe = False |
4803 | py2exe_keywords = {} |
4804 | if os.name == 'nt': |
4805 | - import py2exe |
4806 | - has_py2exe = True |
4807 | py2exe_keywords['console'] = [ |
4808 | { "script": join("bin", "openerp-server.py"), |
4809 | "icon_resources": [(1, join("pixmaps","openerp-icon.ico"))] |
4810 | @@ -54,31 +51,48 @@ |
4811 | "collected_libs_dir": "libs", |
4812 | "collected_libs_data_relocate": "pytz", |
4813 | "package_build_extra_dirs": join(os.path.abspath(os.path.dirname(__file__)), "bin"), |
4814 | - "dist_dir": 'dist', |
4815 | "packages": [ |
4816 | "lxml", "lxml.builder", "lxml._elementpath", "lxml.etree", |
4817 | "lxml.objectify", "decimal", "xml", "xml", "xml.dom", |
4818 | "encodings", "dateutil", "wizard", "pychart", "PIL", "pyparsing", |
4819 | - "pydot", "asyncore","asynchat", "reportlab", "vobject", |
4820 | + "pydot", "asyncore","asynchat", "reportlab", |
4821 | "HTMLParser", "select", "mako", "poplib", |
4822 | - "imaplib", "smtplib", "email", "yaml", "DAV", |
4823 | + "imaplib", "smtplib", "email", "yaml", |
4824 | "uuid", "commands", "mx.DateTime", "json", |
4825 | "pylzma", "xlwt", "passlib", "bcrypt", "six", "cffi", |
4826 | ], |
4827 | - "excludes" : ["Tkconstants","Tkinter","tcl"], |
4828 | + 'dist_dir': 'dist', |
4829 | + 'excludes' : ["Tkconstants","Tkinter","tcl"], |
4830 | + 'dll_excludes': [ |
4831 | + 'w9xpopen.exe', 'PSAPI.dll', 'CRYPT32.dll', 'MPR.dll', |
4832 | + 'Secur32.dll', 'SHFOLDER.dll', |
4833 | + 'api-ms-win-core-delayload-l1-1-1.dll', |
4834 | + 'api-ms-win-core-errorhandling-l1-1-1.dll', |
4835 | + 'api-ms-win-core-heap-obsolete-l1-1-0.dll', |
4836 | + 'api-ms-win-core-libraryloader-l1-2-0.dll', |
4837 | + 'api-ms-win-core-processthreads-l1-1-2.dll', |
4838 | + 'api-ms-win-core-profile-l1-1-0.dll', |
4839 | + 'api-ms-win-core-string-obsolete-l1-1-0.dll', |
4840 | + 'api-ms-win-core-sysinfo-l1-2-1.dll', |
4841 | + 'api-ms-win-security-activedirectoryclient-l1-1-0.dll', |
4842 | + ], |
4843 | } |
4844 | } |
4845 | |
4846 | sys.path.append(join(os.path.abspath(os.path.dirname(__file__)), "bin")) |
4847 | |
4848 | +# The following are all overridden in release.py |
4849 | +name = None |
4850 | +description = None |
4851 | +long_desc = None |
4852 | +url = None |
4853 | +author = None |
4854 | +author_email = None |
4855 | +classifiers = None |
4856 | +version = None |
4857 | + |
4858 | execfile(join('bin', 'release.py')) |
4859 | |
4860 | -if 'bdist_rpm' in sys.argv: |
4861 | - version = version.split('-')[0] |
4862 | - |
4863 | -# get python short version |
4864 | -py_short_version = '%s.%s' % sys.version_info[:2] |
4865 | - |
4866 | # backports os.walk with followlinks from python 2.6 |
4867 | def walk_followlinks(top, topdown=True, onerror=None, followlinks=False): |
4868 | from os.path import join, isdir, islink |
4869 | @@ -108,9 +122,6 @@ |
4870 | if not topdown: |
4871 | yield top, dirs, nondirs |
4872 | |
4873 | -if sys.version_info < (2, 6): |
4874 | - os.walk = walk_followlinks |
4875 | - |
4876 | def find_addons(): |
4877 | for root, _, names in os.walk(join('bin', 'addons'), followlinks=True): |
4878 | if '__openerp__.py' in names or '__terp__.py' in names: |
4879 | @@ -152,8 +163,6 @@ |
4880 | #for root, _, names in os.walk('pixmaps'): |
4881 | # files.append((root, [join(root, name) for name in names])) |
4882 | files.append(('.', [join('bin', 'import_xml.rng'),])) |
4883 | - files.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*'))) |
4884 | - files.append((join('service','Microsoft.VC90.CRT'), glob.glob('C:\Microsoft.VC90.CRT\*.*'))) |
4885 | files.extend(fixup_data_pytz_zoneinfo()) |
4886 | else: |
4887 | man_directory = join('share', 'man') |
4888 | @@ -171,11 +180,6 @@ |
4889 | |
4890 | files.append((openerp_site_packages, [join('bin', 'import_xml.rng'),])) |
4891 | |
4892 | - if sys.version_info[0:2] == (2,5): |
4893 | - files.append((openerp_site_packages, [ join('python25-compat','BaseHTTPServer.py'), |
4894 | - join('python25-compat','SimpleXMLRPCServer.py'), |
4895 | - join('python25-compat','SocketServer.py')])) |
4896 | - |
4897 | for addonname, add_path in find_addons(): |
4898 | addon_path = join(get_python_lib(prefix=''), 'openerp-server','addons', addonname) |
4899 | for root, dirs, innerfiles in os.walk(add_path): |
4900 | @@ -213,8 +217,6 @@ |
4901 | install.run(self) |
4902 | |
4903 | |
4904 | - |
4905 | - |
4906 | setup(name = name, |
4907 | version = version, |
4908 | description = description, |
4909 | @@ -239,23 +241,22 @@ |
4910 | '': ['*.yml', '*.xml', '*.po', '*.pot', '*.csv'], |
4911 | }, |
4912 | package_dir = find_package_dirs(), |
4913 | + python_requires = ">=2.7.13", |
4914 | install_requires = [ |
4915 | 'lxml==3.7.3', |
4916 | - 'mako==0.2.5', |
4917 | - 'python-dateutil==2.5.3', |
4918 | - 'psycopg2==2.0.13', |
4919 | - 'pydot==1.0.2', |
4920 | - 'pytz==2010b0', |
4921 | - 'reportlab==2.4', |
4922 | + 'mako==1.0.6', |
4923 | + 'python-dateutil==2.6.0', |
4924 | + 'psycopg2==2.7.1', |
4925 | + 'pydot==1.2.3', |
4926 | + 'pytz==2017.2', |
4927 | + 'pylzma==0.4.8', |
4928 | + 'reportlab==3.4.0', |
4929 | 'pyyaml==3.12', |
4930 | 'egenix-mx-base==3.2.9', |
4931 | - 'passlib==1.6.5', |
4932 | - 'bcrypt==3.1.1', |
4933 | - 'xlwt==1.1.2', |
4934 | + 'passlib==1.7.1', |
4935 | + 'bcrypt==3.1.3', |
4936 | + 'xlwt==1.2.0', |
4937 | ], |
4938 | - extras_require={ |
4939 | - 'SSL' : ['pyopenssl'], |
4940 | - }, |
4941 | **py2exe_keywords |
4942 | ) |
4943 | |
4944 | |
4945 | === removed file 'ssl-cert.cfg' |
4946 | --- ssl-cert.cfg 2010-11-23 13:58:44 +0000 |
4947 | +++ ssl-cert.cfg 1970-01-01 00:00:00 +0000 |
4948 | @@ -1,89 +0,0 @@ |
4949 | -# X.509 Certificate options |
4950 | -# |
4951 | -# DN options |
4952 | - |
4953 | -# The organization of the subject. |
4954 | -organization = "Some organization." |
4955 | - |
4956 | -# The organizational unit of the subject. |
4957 | -unit = "ERP dept." |
4958 | - |
4959 | -# The locality of the subject. |
4960 | -# locality = |
4961 | - |
4962 | -# The state of the certificate owner. |
4963 | -state = "State" |
4964 | - |
4965 | -# The country of the subject. Two letter code. |
4966 | -country = BE |
4967 | - |
4968 | -# The common name of the certificate owner. |
4969 | -cn = "Some company" |
4970 | - |
4971 | -# A user id of the certificate owner. |
4972 | -#uid = "clauper" |
4973 | - |
4974 | -# If the supported DN OIDs are not adequate you can set |
4975 | -# any OID here. |
4976 | -# For example set the X.520 Title and the X.520 Pseudonym |
4977 | -# by using OID and string pairs. |
4978 | -#dn_oid = "2.5.4.12" "Dr." "2.5.4.65" "jackal" |
4979 | - |
4980 | -# This is deprecated and should not be used in new |
4981 | -# certificates. |
4982 | -# pkcs9_email = "none@none.org" |
4983 | - |
4984 | -# The serial number of the certificate |
4985 | -serial = 001 |
4986 | - |
4987 | -# In how many days, counting from today, this certificate will expire. |
4988 | -expiration_days = 700 |
4989 | - |
4990 | -# X.509 v3 extensions |
4991 | - |
4992 | -# A dnsname in case of a WWW server. |
4993 | -#dns_name = "www.none.org" |
4994 | -#dns_name = "www.morethanone.org" |
4995 | - |
4996 | -# An IP address in case of a server. |
4997 | -#ip_address = "192.168.1.1" |
4998 | - |
4999 | -# An email in case of a person |
5000 | -email = "none@none.org" |
The diff has been truncated for viewing.