Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 4362:74476eaac38a
more modernisation
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Fri, 26 Feb 2010 00:38:53 +0000 |
| parents | 85b00a3820b3 |
| children | fa5587802af9 |
comparison
equal
deleted
inserted
replaced
| 4360:661466ba19cd | 4362:74476eaac38a |
|---|---|
| 1 """WWW request handler (also used in the stand-alone server). | 1 """WWW request handler (also used in the stand-alone server). |
| 2 """ | 2 """ |
| 3 __docformat__ = 'restructuredtext' | 3 __docformat__ = 'restructuredtext' |
| 4 | 4 |
| 5 import base64, binascii, cgi, codecs, httplib, mimetypes, os | 5 import base64, binascii, cgi, codecs, mimetypes, os |
| 6 import quopri, random, re, rfc822, stat, sys, time, urllib, urlparse | 6 import quopri, random, re, rfc822, stat, sys, time |
| 7 import Cookie, socket, errno | 7 import socket, errno |
| 8 from Cookie import CookieError, BaseCookie, SimpleCookie | |
| 9 from cStringIO import StringIO | |
| 10 | 8 |
| 11 from roundup import roundupdb, date, hyperdb, password | 9 from roundup import roundupdb, date, hyperdb, password |
| 12 from roundup.cgi import templating, cgitb, TranslationService | 10 from roundup.cgi import templating, cgitb, TranslationService |
| 13 from roundup.cgi.actions import * | 11 from roundup.cgi.actions import * |
| 14 from roundup.exceptions import * | 12 from roundup.exceptions import * |
| 16 from roundup.cgi.form_parser import FormParser | 14 from roundup.cgi.form_parser import FormParser |
| 17 from roundup.mailer import Mailer, MessageSendError, encode_quopri | 15 from roundup.mailer import Mailer, MessageSendError, encode_quopri |
| 18 from roundup.cgi import accept_language | 16 from roundup.cgi import accept_language |
| 19 from roundup import xmlrpc | 17 from roundup import xmlrpc |
| 20 | 18 |
| 19 from roundup.anypy.cookie_ import CookieError, BaseCookie, SimpleCookie, \ | |
| 20 get_cookie_date | |
| 21 from roundup.anypy.io_ import StringIO | |
| 22 from roundup.anypy import http_ | |
| 23 from roundup.anypy import urllib_ | |
| 24 | |
| 21 def initialiseSecurity(security): | 25 def initialiseSecurity(security): |
| 22 '''Create some Permissions and Roles on the security object | 26 '''Create some Permissions and Roles on the security object |
| 23 | 27 |
| 24 This function is directly invoked by security.Security.__init__() | 28 This function is directly invoked by security.Security.__init__() |
| 25 as a part of the Security object instantiation. | 29 as a part of the Security object instantiation. |
| 41 def clean_message(message, mc=re.compile(CLEAN_MESSAGE_RE, re.I)): | 45 def clean_message(message, mc=re.compile(CLEAN_MESSAGE_RE, re.I)): |
| 42 return mc.sub(clean_message_callback, message) | 46 return mc.sub(clean_message_callback, message) |
| 43 def clean_message_callback(match, ok={'a':1,'i':1,'b':1,'br':1}): | 47 def clean_message_callback(match, ok={'a':1,'i':1,'b':1,'br':1}): |
| 44 """ Strip all non <a>,<i>,<b> and <br> tags from a string | 48 """ Strip all non <a>,<i>,<b> and <br> tags from a string |
| 45 """ | 49 """ |
| 46 if ok.has_key(match.group(3).lower()): | 50 if match.group(3).lower() in ok: |
| 47 return match.group(1) | 51 return match.group(1) |
| 48 return '<%s>'%match.group(2) | 52 return '<%s>'%match.group(2) |
| 49 | 53 |
| 50 | 54 |
| 51 error_message = ''"""<html><head><title>An error has occurred</title></head> | 55 error_message = ''"""<html><head><title>An error has occurred</title></head> |
| 291 if not self.base.endswith('/'): | 295 if not self.base.endswith('/'): |
| 292 self.base = self.base + '/' | 296 self.base = self.base + '/' |
| 293 | 297 |
| 294 # this is the "cookie path" for this tracker (ie. the path part of | 298 # this is the "cookie path" for this tracker (ie. the path part of |
| 295 # the "base" url) | 299 # the "base" url) |
| 296 self.cookie_path = urlparse.urlparse(self.base)[2] | 300 self.cookie_path = urllib_.urlparse(self.base)[2] |
| 297 # cookies to set in http responce | 301 # cookies to set in http responce |
| 298 # {(path, name): (value, expire)} | 302 # {(path, name): (value, expire)} |
| 299 self._cookies = {} | 303 self._cookies = {} |
| 300 | 304 |
| 301 # see if we need to re-parse the environment for the form (eg Zope) | 305 # see if we need to re-parse the environment for the form (eg Zope) |
| 499 # The user tried to log in, but did not provide a valid | 503 # The user tried to log in, but did not provide a valid |
| 500 # username and password. If we support HTTP | 504 # username and password. If we support HTTP |
| 501 # authorization, send back a response that will cause the | 505 # authorization, send back a response that will cause the |
| 502 # browser to prompt the user again. | 506 # browser to prompt the user again. |
| 503 if self.instance.config.WEB_HTTP_AUTH: | 507 if self.instance.config.WEB_HTTP_AUTH: |
| 504 self.response_code = httplib.UNAUTHORIZED | 508 self.response_code = http_.client.UNAUTHORIZED |
| 505 realm = self.instance.config.TRACKER_NAME | 509 realm = self.instance.config.TRACKER_NAME |
| 506 self.setHeader("WWW-Authenticate", | 510 self.setHeader("WWW-Authenticate", |
| 507 "Basic realm=\"%s\"" % realm) | 511 "Basic realm=\"%s\"" % realm) |
| 508 else: | 512 else: |
| 509 self.response_code = httplib.FORBIDDEN | 513 self.response_code = http_.client.FORBIDDEN |
| 510 self.renderFrontPage(message) | 514 self.renderFrontPage(message) |
| 511 except Unauthorised, message: | 515 except Unauthorised, message: |
| 512 # users may always see the front page | 516 # users may always see the front page |
| 513 self.response_code = 403 | 517 self.response_code = 403 |
| 514 self.renderFrontPage(message) | 518 self.renderFrontPage(message) |
| 524 self.write_html(self.renderContext()) | 528 self.write_html(self.renderContext()) |
| 525 except KeyError: | 529 except KeyError: |
| 526 # we can't map the URL to a class we know about | 530 # we can't map the URL to a class we know about |
| 527 # reraise the NotFound and let roundup_server | 531 # reraise the NotFound and let roundup_server |
| 528 # handle it | 532 # handle it |
| 529 raise NotFound, e | 533 raise NotFound(e) |
| 530 except FormError, e: | 534 except FormError, e: |
| 531 self.error_message.append(self._('Form Error: ') + str(e)) | 535 self.error_message.append(self._('Form Error: ') + str(e)) |
| 532 self.write_html(self.renderContext()) | 536 self.write_html(self.renderContext()) |
| 533 except: | 537 except: |
| 534 # Something has gone badly wrong. Therefore, we should | 538 # Something has gone badly wrong. Therefore, we should |
| 535 # make sure that the response code indicates failure. | 539 # make sure that the response code indicates failure. |
| 536 if self.response_code == httplib.OK: | 540 if self.response_code == http_.client.OK: |
| 537 self.response_code = httplib.INTERNAL_SERVER_ERROR | 541 self.response_code = http_.client.INTERNAL_SERVER_ERROR |
| 538 # Help the administrator work out what went wrong. | 542 # Help the administrator work out what went wrong. |
| 539 html = ("<h1>Traceback</h1>" | 543 html = ("<h1>Traceback</h1>" |
| 540 + cgitb.html(i18n=self.translator) | 544 + cgitb.html(i18n=self.translator) |
| 541 + ("<h1>Environment Variables</h1><table>%s</table>" | 545 + ("<h1>Environment Variables</h1><table>%s</table>" |
| 542 % cgitb.niceDict("", self.env))) | 546 % cgitb.niceDict("", self.env))) |
| 581 If the charset is found, and differs from the storage charset, | 585 If the charset is found, and differs from the storage charset, |
| 582 recode all form fields of type 'text/plain' | 586 recode all form fields of type 'text/plain' |
| 583 """ | 587 """ |
| 584 # look for client charset | 588 # look for client charset |
| 585 charset_parameter = 0 | 589 charset_parameter = 0 |
| 586 if self.form.has_key('@charset'): | 590 if '@charset' in self.form: |
| 587 charset = self.form['@charset'].value | 591 charset = self.form['@charset'].value |
| 588 if charset.lower() == "none": | 592 if charset.lower() == "none": |
| 589 charset = "" | 593 charset = "" |
| 590 charset_parameter = 1 | 594 charset_parameter = 1 |
| 591 elif self.cookie.has_key('roundup_charset'): | 595 elif 'roundup_charset' in self.cookie: |
| 592 charset = self.cookie['roundup_charset'].value | 596 charset = self.cookie['roundup_charset'].value |
| 593 else: | 597 else: |
| 594 charset = None | 598 charset = None |
| 595 if charset: | 599 if charset: |
| 596 # make sure the charset is recognized | 600 # make sure the charset is recognized |
| 623 uc = int(num[1:], 16) | 627 uc = int(num[1:], 16) |
| 624 else: | 628 else: |
| 625 uc = int(num) | 629 uc = int(num) |
| 626 return unichr(uc) | 630 return unichr(uc) |
| 627 | 631 |
| 628 for field_name in self.form.keys(): | 632 for field_name in self.form: |
| 629 field = self.form[field_name] | 633 field = self.form[field_name] |
| 630 if (field.type == 'text/plain') and not field.filename: | 634 if (field.type == 'text/plain') and not field.filename: |
| 631 try: | 635 try: |
| 632 value = decoder(field.value)[0] | 636 value = decoder(field.value)[0] |
| 633 except UnicodeError: | 637 except UnicodeError: |
| 638 def determine_language(self): | 642 def determine_language(self): |
| 639 """Determine the language""" | 643 """Determine the language""" |
| 640 # look for language parameter | 644 # look for language parameter |
| 641 # then for language cookie | 645 # then for language cookie |
| 642 # last for the Accept-Language header | 646 # last for the Accept-Language header |
| 643 if self.form.has_key("@language"): | 647 if "@language" in self.form: |
| 644 language = self.form["@language"].value | 648 language = self.form["@language"].value |
| 645 if language.lower() == "none": | 649 if language.lower() == "none": |
| 646 language = "" | 650 language = "" |
| 647 self.add_cookie("roundup_language", language) | 651 self.add_cookie("roundup_language", language) |
| 648 elif self.cookie.has_key("roundup_language"): | 652 elif "roundup_language" in self.cookie: |
| 649 language = self.cookie["roundup_language"].value | 653 language = self.cookie["roundup_language"].value |
| 650 elif self.instance.config["WEB_USE_BROWSER_LANGUAGE"]: | 654 elif self.instance.config["WEB_USE_BROWSER_LANGUAGE"]: |
| 651 hal = self.env.get('HTTP_ACCEPT_LANGUAGE') | 655 hal = self.env.get('HTTP_ACCEPT_LANGUAGE') |
| 652 language = accept_language.parse(hal) | 656 language = accept_language.parse(hal) |
| 653 else: | 657 else: |
| 671 self.clean_up() | 675 self.clean_up() |
| 672 | 676 |
| 673 user = None | 677 user = None |
| 674 # first up, try http authorization if enabled | 678 # first up, try http authorization if enabled |
| 675 if self.instance.config['WEB_HTTP_AUTH']: | 679 if self.instance.config['WEB_HTTP_AUTH']: |
| 676 if self.env.has_key('REMOTE_USER'): | 680 if 'REMOTE_USER' in self.env: |
| 677 # we have external auth (e.g. by Apache) | 681 # we have external auth (e.g. by Apache) |
| 678 user = self.env['REMOTE_USER'] | 682 user = self.env['REMOTE_USER'] |
| 679 elif self.env.get('HTTP_AUTHORIZATION', ''): | 683 elif self.env.get('HTTP_AUTHORIZATION', ''): |
| 680 # try handling Basic Auth ourselves | 684 # try handling Basic Auth ourselves |
| 681 auth = self.env['HTTP_AUTHORIZATION'] | 685 auth = self.env['HTTP_AUTHORIZATION'] |
| 727 """Check that the Anonymous user is actually allowed to use the web | 731 """Check that the Anonymous user is actually allowed to use the web |
| 728 interface and short-circuit all further processing if they're not. | 732 interface and short-circuit all further processing if they're not. |
| 729 """ | 733 """ |
| 730 # allow Anonymous to use the "login" and "register" actions (noting | 734 # allow Anonymous to use the "login" and "register" actions (noting |
| 731 # that "register" has its own "Register" permission check) | 735 # that "register" has its own "Register" permission check) |
| 732 if self.form.has_key(':action'): | 736 if ':action' in self.form: |
| 733 action = self.form[':action'].value.lower() | 737 action = self.form[':action'].value.lower() |
| 734 elif self.form.has_key('@action'): | 738 elif '@action' in self.form: |
| 735 action = self.form['@action'].value.lower() | 739 action = self.form['@action'].value.lower() |
| 736 else: | 740 else: |
| 737 action = None | 741 action = None |
| 738 if action in ('login', 'register'): | 742 if action in ('login', 'register'): |
| 739 return | 743 return |
| 745 return | 749 return |
| 746 | 750 |
| 747 # otherwise for everything else | 751 # otherwise for everything else |
| 748 if self.user == 'anonymous': | 752 if self.user == 'anonymous': |
| 749 if not self.db.security.hasPermission('Web Access', self.userid): | 753 if not self.db.security.hasPermission('Web Access', self.userid): |
| 750 raise Unauthorised, self._("Anonymous users are not " | 754 raise Unauthorised(self._("Anonymous users are not " |
| 751 "allowed to use the web interface") | 755 "allowed to use the web interface")) |
| 752 | 756 |
| 753 def opendb(self, username): | 757 def opendb(self, username): |
| 754 """Open the database and set the current user. | 758 """Open the database and set the current user. |
| 755 | 759 |
| 756 Opens a database once. On subsequent calls only the user is set on | 760 Opens a database once. On subsequent calls only the user is set on |
| 820 self.classname = None | 824 self.classname = None |
| 821 self.nodeid = None | 825 self.nodeid = None |
| 822 | 826 |
| 823 # see if a template or messages are specified | 827 # see if a template or messages are specified |
| 824 template_override = ok_message = error_message = None | 828 template_override = ok_message = error_message = None |
| 825 for key in self.form.keys(): | 829 for key in self.form: |
| 826 if self.FV_TEMPLATE.match(key): | 830 if self.FV_TEMPLATE.match(key): |
| 827 template_override = self.form[key].value | 831 template_override = self.form[key].value |
| 828 elif self.FV_OK_MESSAGE.match(key): | 832 elif self.FV_OK_MESSAGE.match(key): |
| 829 ok_message = self.form[key].value | 833 ok_message = self.form[key].value |
| 830 ok_message = clean_message(ok_message) | 834 ok_message = clean_message(ok_message) |
| 845 self.template = template_override | 849 self.template = template_override |
| 846 else: | 850 else: |
| 847 self.template = '' | 851 self.template = '' |
| 848 return | 852 return |
| 849 elif path[0] in ('_file', '@@file'): | 853 elif path[0] in ('_file', '@@file'): |
| 850 raise SendStaticFile, os.path.join(*path[1:]) | 854 raise SendStaticFile(os.path.join(*path[1:])) |
| 851 else: | 855 else: |
| 852 self.classname = path[0] | 856 self.classname = path[0] |
| 853 if len(path) > 1: | 857 if len(path) > 1: |
| 854 # send the file identified by the designator in path[0] | 858 # send the file identified by the designator in path[0] |
| 855 raise SendFile, path[0] | 859 raise SendFile(path[0]) |
| 856 | 860 |
| 857 # see if we got a designator | 861 # see if we got a designator |
| 858 m = dre.match(self.classname) | 862 m = dre.match(self.classname) |
| 859 if m: | 863 if m: |
| 860 self.classname = m.group(1) | 864 self.classname = m.group(1) |
| 861 self.nodeid = m.group(2) | 865 self.nodeid = m.group(2) |
| 862 try: | 866 try: |
| 863 klass = self.db.getclass(self.classname) | 867 klass = self.db.getclass(self.classname) |
| 864 except KeyError: | 868 except KeyError: |
| 865 raise NotFound, '%s/%s'%(self.classname, self.nodeid) | 869 raise NotFound('%s/%s'%(self.classname, self.nodeid)) |
| 866 if not klass.hasnode(self.nodeid): | 870 if not klass.hasnode(self.nodeid): |
| 867 raise NotFound, '%s/%s'%(self.classname, self.nodeid) | 871 raise NotFound('%s/%s'%(self.classname, self.nodeid)) |
| 868 # with a designator, we default to item view | 872 # with a designator, we default to item view |
| 869 self.template = 'item' | 873 self.template = 'item' |
| 870 else: | 874 else: |
| 871 # with only a class, we default to index view | 875 # with only a class, we default to index view |
| 872 self.template = 'index' | 876 self.template = 'index' |
| 873 | 877 |
| 874 # make sure the classname is valid | 878 # make sure the classname is valid |
| 875 try: | 879 try: |
| 876 self.db.getclass(self.classname) | 880 self.db.getclass(self.classname) |
| 877 except KeyError: | 881 except KeyError: |
| 878 raise NotFound, self.classname | 882 raise NotFound(self.classname) |
| 879 | 883 |
| 880 # see if we have a template override | 884 # see if we have a template override |
| 881 if template_override is not None: | 885 if template_override is not None: |
| 882 self.template = template_override | 886 self.template = template_override |
| 883 | 887 |
| 884 def serve_file(self, designator, dre=re.compile(r'([^\d]+)(\d+)')): | 888 def serve_file(self, designator, dre=re.compile(r'([^\d]+)(\d+)')): |
| 885 """ Serve the file from the content property of the designated item. | 889 """ Serve the file from the content property of the designated item. |
| 886 """ | 890 """ |
| 887 m = dre.match(str(designator)) | 891 m = dre.match(str(designator)) |
| 888 if not m: | 892 if not m: |
| 889 raise NotFound, str(designator) | 893 raise NotFound(str(designator)) |
| 890 classname, nodeid = m.group(1), m.group(2) | 894 classname, nodeid = m.group(1), m.group(2) |
| 891 | 895 |
| 892 try: | 896 try: |
| 893 klass = self.db.getclass(classname) | 897 klass = self.db.getclass(classname) |
| 894 except KeyError: | 898 except KeyError: |
| 895 # The classname was not valid. | 899 # The classname was not valid. |
| 896 raise NotFound, str(designator) | 900 raise NotFound(str(designator)) |
| 897 | 901 |
| 898 # perform the Anonymous user access check | 902 # perform the Anonymous user access check |
| 899 self.check_anonymous_access() | 903 self.check_anonymous_access() |
| 900 | 904 |
| 901 # make sure we have the appropriate properties | 905 # make sure we have the appropriate properties |
| 902 props = klass.getprops() | 906 props = klass.getprops() |
| 903 if not props.has_key('type'): | 907 if 'type' not in props: |
| 904 raise NotFound, designator | 908 raise NotFound(designator) |
| 905 if not props.has_key('content'): | 909 if 'content' not in props: |
| 906 raise NotFound, designator | 910 raise NotFound(designator) |
| 907 | 911 |
| 908 # make sure we have permission | 912 # make sure we have permission |
| 909 if not self.db.security.hasPermission('View', self.userid, | 913 if not self.db.security.hasPermission('View', self.userid, |
| 910 classname, 'content', nodeid): | 914 classname, 'content', nodeid): |
| 911 raise Unauthorised, self._("You are not allowed to view " | 915 raise Unauthorised(self._("You are not allowed to view " |
| 912 "this file.") | 916 "this file.")) |
| 913 | 917 |
| 914 mime_type = klass.get(nodeid, 'type') | 918 mime_type = klass.get(nodeid, 'type') |
| 915 # Can happen for msg class: | 919 # Can happen for msg class: |
| 916 if not mime_type: | 920 if not mime_type: |
| 917 mime_type = 'text/plain' | 921 mime_type = 'text/plain' |
| 960 prefix = os.path.normpath(prefix) | 964 prefix = os.path.normpath(prefix) |
| 961 filename = os.path.normpath(os.path.join(prefix, file)) | 965 filename = os.path.normpath(os.path.join(prefix, file)) |
| 962 if os.path.isfile(filename) and filename.startswith(prefix): | 966 if os.path.isfile(filename) and filename.startswith(prefix): |
| 963 break | 967 break |
| 964 else: | 968 else: |
| 965 raise NotFound, file | 969 raise NotFound(file) |
| 966 | 970 |
| 967 # last-modified time | 971 # last-modified time |
| 968 lmt = os.stat(filename)[stat.ST_MTIME] | 972 lmt = os.stat(filename)[stat.ST_MTIME] |
| 969 | 973 |
| 970 # detemine meta-type | 974 # detemine meta-type |
| 989 ims = None | 993 ims = None |
| 990 # see if there's an if-modified-since... | 994 # see if there's an if-modified-since... |
| 991 # XXX see which interfaces set this | 995 # XXX see which interfaces set this |
| 992 #if hasattr(self.request, 'headers'): | 996 #if hasattr(self.request, 'headers'): |
| 993 #ims = self.request.headers.getheader('if-modified-since') | 997 #ims = self.request.headers.getheader('if-modified-since') |
| 994 if self.env.has_key('HTTP_IF_MODIFIED_SINCE'): | 998 if 'HTTP_IF_MODIFIED_SINCE' in self.env: |
| 995 # cgi will put the header in the env var | 999 # cgi will put the header in the env var |
| 996 ims = self.env['HTTP_IF_MODIFIED_SINCE'] | 1000 ims = self.env['HTTP_IF_MODIFIED_SINCE'] |
| 997 if ims: | 1001 if ims: |
| 998 ims = rfc822.parsedate(ims)[:6] | 1002 ims = rfc822.parsedate(ims)[:6] |
| 999 lmtt = time.gmtime(lmt)[:6] | 1003 lmtt = time.gmtime(lmt)[:6] |
| 1060 result = result.replace('</body>', s) | 1064 result = result.replace('</body>', s) |
| 1061 return result | 1065 return result |
| 1062 except templating.NoTemplate, message: | 1066 except templating.NoTemplate, message: |
| 1063 return '<strong>%s</strong>'%message | 1067 return '<strong>%s</strong>'%message |
| 1064 except templating.Unauthorised, message: | 1068 except templating.Unauthorised, message: |
| 1065 raise Unauthorised, str(message) | 1069 raise Unauthorised(str(message)) |
| 1066 except: | 1070 except: |
| 1067 # everything else | 1071 # everything else |
| 1068 if self.instance.config.WEB_DEBUG: | 1072 if self.instance.config.WEB_DEBUG: |
| 1069 return cgitb.pt_html(i18n=self.translator) | 1073 return cgitb.pt_html(i18n=self.translator) |
| 1070 exc_info = sys.exc_info() | 1074 exc_info = sys.exc_info() |
| 1078 except: | 1082 except: |
| 1079 # Reraise the original exception. The user will | 1083 # Reraise the original exception. The user will |
| 1080 # receive an error message, and the adminstrator will | 1084 # receive an error message, and the adminstrator will |
| 1081 # receive a traceback, albeit with less information | 1085 # receive a traceback, albeit with less information |
| 1082 # than the one we tried to generate above. | 1086 # than the one we tried to generate above. |
| 1083 raise exc_info[0], exc_info[1], exc_info[2] | 1087 raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) |
| 1084 | 1088 |
| 1085 # these are the actions that are available | 1089 # these are the actions that are available |
| 1086 actions = ( | 1090 actions = ( |
| 1087 ('edit', EditItemAction), | 1091 ('edit', EditItemAction), |
| 1088 ('editcsv', EditCSVAction), | 1092 ('editcsv', EditCSVAction), |
| 1108 user, bypassing the usual template rendering. | 1112 user, bypassing the usual template rendering. |
| 1109 | 1113 |
| 1110 We explicitly catch Reject and ValueError exceptions and | 1114 We explicitly catch Reject and ValueError exceptions and |
| 1111 present their messages to the user. | 1115 present their messages to the user. |
| 1112 """ | 1116 """ |
| 1113 if self.form.has_key(':action'): | 1117 if ':action' in self.form: |
| 1114 action = self.form[':action'].value.lower() | 1118 action = self.form[':action'].value.lower() |
| 1115 elif self.form.has_key('@action'): | 1119 elif '@action' in self.form: |
| 1116 action = self.form['@action'].value.lower() | 1120 action = self.form['@action'].value.lower() |
| 1117 else: | 1121 else: |
| 1118 return None | 1122 return None |
| 1119 | 1123 |
| 1120 try: | 1124 try: |
| 1130 except (ValueError, Reject), err: | 1134 except (ValueError, Reject), err: |
| 1131 self.error_message.append(str(err)) | 1135 self.error_message.append(str(err)) |
| 1132 | 1136 |
| 1133 def get_action_class(self, action_name): | 1137 def get_action_class(self, action_name): |
| 1134 if (hasattr(self.instance, 'cgi_actions') and | 1138 if (hasattr(self.instance, 'cgi_actions') and |
| 1135 self.instance.cgi_actions.has_key(action_name)): | 1139 action_name in self.instance.cgi_actions): |
| 1136 # tracker-defined action | 1140 # tracker-defined action |
| 1137 action_klass = self.instance.cgi_actions[action_name] | 1141 action_klass = self.instance.cgi_actions[action_name] |
| 1138 else: | 1142 else: |
| 1139 # go with a default | 1143 # go with a default |
| 1140 for name, action_klass in self.actions: | 1144 for name, action_klass in self.actions: |
| 1141 if name == action_name: | 1145 if name == action_name: |
| 1142 break | 1146 break |
| 1143 else: | 1147 else: |
| 1144 raise ValueError, 'No such action "%s"'%action_name | 1148 raise ValueError('No such action "%s"'%action_name) |
| 1145 return action_klass | 1149 return action_klass |
| 1146 | 1150 |
| 1147 def _socket_op(self, call, *args, **kwargs): | 1151 def _socket_op(self, call, *args, **kwargs): |
| 1148 """Execute socket-related operation, catch common network errors | 1152 """Execute socket-related operation, catch common network errors |
| 1149 | 1153 |
| 1179 self._socket_op(self.request.wfile.write, content) | 1183 self._socket_op(self.request.wfile.write, content) |
| 1180 | 1184 |
| 1181 def write_html(self, content): | 1185 def write_html(self, content): |
| 1182 if not self.headers_done: | 1186 if not self.headers_done: |
| 1183 # at this point, we are sure about Content-Type | 1187 # at this point, we are sure about Content-Type |
| 1184 if not self.additional_headers.has_key('Content-Type'): | 1188 if 'Content-Type' not in self.additional_headers: |
| 1185 self.additional_headers['Content-Type'] = \ | 1189 self.additional_headers['Content-Type'] = \ |
| 1186 'text/html; charset=%s' % self.charset | 1190 'text/html; charset=%s' % self.charset |
| 1187 self.header() | 1191 self.header() |
| 1188 | 1192 |
| 1189 if self.env['REQUEST_METHOD'] == 'HEAD': | 1193 if self.env['REQUEST_METHOD'] == 'HEAD': |
| 1341 # should just ignore the invalid Range header. | 1345 # should just ignore the invalid Range header. |
| 1342 if if_range: | 1346 if if_range: |
| 1343 return None | 1347 return None |
| 1344 # Return code 416 with a Content-Range header giving the | 1348 # Return code 416 with a Content-Range header giving the |
| 1345 # allowable range. | 1349 # allowable range. |
| 1346 self.response_code = httplib.REQUESTED_RANGE_NOT_SATISFIABLE | 1350 self.response_code = http_.client.REQUESTED_RANGE_NOT_SATISFIABLE |
| 1347 self.setHeader("Content-Range", "bytes */%d" % length) | 1351 self.setHeader("Content-Range", "bytes */%d" % length) |
| 1348 return None | 1352 return None |
| 1349 # RFC 2616 10.2.7: 206 Partial Content | 1353 # RFC 2616 10.2.7: 206 Partial Content |
| 1350 # | 1354 # |
| 1351 # Tell the client that we are honoring the Range request by | 1355 # Tell the client that we are honoring the Range request by |
| 1352 # indicating that we are providing partial content. | 1356 # indicating that we are providing partial content. |
| 1353 self.response_code = httplib.PARTIAL_CONTENT | 1357 self.response_code = http_.client.PARTIAL_CONTENT |
| 1354 # RFC 2616 14.16: Content-Range | 1358 # RFC 2616 14.16: Content-Range |
| 1355 # | 1359 # |
| 1356 # Tell the client what data we are providing. | 1360 # Tell the client what data we are providing. |
| 1357 # | 1361 # |
| 1358 # content-range-spec = byte-content-range-spec | 1362 # content-range-spec = byte-content-range-spec |
| 1402 # Send the HTTP header. | 1406 # Send the HTTP header. |
| 1403 self.header() | 1407 self.header() |
| 1404 # If the client doesn't actually want the body, or if we are | 1408 # If the client doesn't actually want the body, or if we are |
| 1405 # indicating an invalid range. | 1409 # indicating an invalid range. |
| 1406 if (self.env['REQUEST_METHOD'] == 'HEAD' | 1410 if (self.env['REQUEST_METHOD'] == 'HEAD' |
| 1407 or self.response_code == httplib.REQUESTED_RANGE_NOT_SATISFIABLE): | 1411 or self.response_code == http_.client.REQUESTED_RANGE_NOT_SATISFIABLE): |
| 1408 return | 1412 return |
| 1409 # Use the optimized "sendfile" operation, if possible. | 1413 # Use the optimized "sendfile" operation, if possible. |
| 1410 if hasattr(self.request, "sendfile"): | 1414 if hasattr(self.request, "sendfile"): |
| 1411 self._socket_op(self.request.sendfile, filename, offset, length) | 1415 self._socket_op(self.request.sendfile, filename, offset, length) |
| 1412 return | 1416 return |
| 1437 headers.update(self.additional_headers) | 1441 headers.update(self.additional_headers) |
| 1438 | 1442 |
| 1439 if headers.get('Content-Type', 'text/html') == 'text/html': | 1443 if headers.get('Content-Type', 'text/html') == 'text/html': |
| 1440 headers['Content-Type'] = 'text/html; charset=utf-8' | 1444 headers['Content-Type'] = 'text/html; charset=utf-8' |
| 1441 | 1445 |
| 1442 headers = headers.items() | 1446 headers = list(headers.items()) |
| 1443 | 1447 |
| 1444 for ((path, name), (value, expire)) in self._cookies.items(): | 1448 for ((path, name), (value, expire)) in self._cookies.iteritems(): |
| 1445 cookie = "%s=%s; Path=%s;"%(name, value, path) | 1449 cookie = "%s=%s; Path=%s;"%(name, value, path) |
| 1446 if expire is not None: | 1450 if expire is not None: |
| 1447 cookie += " expires=%s;"%Cookie._getdate(expire) | 1451 cookie += " expires=%s;"%get_cookie_date(expire) |
| 1448 headers.append(('Set-Cookie', cookie)) | 1452 headers.append(('Set-Cookie', cookie)) |
| 1449 | 1453 |
| 1450 self._socket_op(self.request.start_response, headers, response) | 1454 self._socket_op(self.request.start_response, headers, response) |
| 1451 | 1455 |
| 1452 self.headers_done = 1 | 1456 self.headers_done = 1 |
