Merge lp:~jr.allen/unifield-server/py27 into lp:unifield-server

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
Reviewer Review Type Date Requested Status
jftempo Pending
Review via email: mp+324109@code.launchpad.net
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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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- print
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- print
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.

Subscribers

People subscribed via source and target branches

to all changes: