Code

- fix handling of traceback mails to the roundup admin
[roundup.git] / roundup / cgi / client.py
index 793a0d8472b32b4f183910948e953e83e2c005a4..11a22172e9c1f69437c139afa66525d78d6986a1 100644 (file)
@@ -5,6 +5,7 @@ __docformat__ = 'restructuredtext'
 import base64, binascii, cgi, codecs, mimetypes, os
 import quopri, random, re, rfc822, stat, sys, time
 import socket, errno
+from traceback import format_exc
 
 from roundup import roundupdb, date, hyperdb, password
 from roundup.cgi import templating, cgitb, TranslationService
@@ -22,6 +23,10 @@ from roundup.anypy.io_ import StringIO
 from roundup.anypy import http_
 from roundup.anypy import urllib_
 
+from email.MIMEBase import MIMEBase
+from email.MIMEText import MIMEText
+from email.MIMEMultipart import MIMEMultipart
+
 def initialiseSecurity(security):
     '''Create some Permissions and Roles on the security object
 
@@ -547,7 +552,7 @@ class Client:
             if not self.instance.config.WEB_DEBUG:
                 exc_info = sys.exc_info()
                 subject = "Error: %s" % exc_info[1]
-                self.send_html_to_admin(subject, html)
+                self.send_error_to_admin(subject, html, format_exc())
                 self.write_html(self._(error_message))
             else:
                 self.write_html(html)
@@ -733,12 +738,17 @@ class Client:
         """
         # allow Anonymous to use the "login" and "register" actions (noting
         # that "register" has its own "Register" permission check)
+
         if ':action' in self.form:
-            action = self.form[':action'].value.lower()
+            action = self.form[':action']
         elif '@action' in self.form:
-            action = self.form['@action'].value.lower()
+            action = self.form['@action']
         else:
-            action = None
+            action = ''
+        if isinstance(action, list):
+            raise SeriousError('broken form: multiple @action values submitted')
+        elif action != '':
+            action = action.value.lower()
         if action in ('login', 'register'):
             return
 
@@ -915,7 +925,10 @@ class Client:
             raise Unauthorised(self._("You are not allowed to view "
                 "this file."))
 
-        mime_type = klass.get(nodeid, 'type')
+        try:
+            mime_type = klass.get(nodeid, 'type')
+        except IndexError, e:
+            raise NotFound(e)
         # Can happen for msg class:
         if not mime_type:
             mime_type = 'text/plain'
@@ -1010,16 +1023,24 @@ class Client:
             self.additional_headers['Content-Length'] = str(len(content))
             self.write(content)
 
-    def send_html_to_admin(self, subject, content):
-
+    def send_error_to_admin(self, subject, html, txt):
+        """Send traceback information to admin via email.
+           We send both, the formatted html (with more information) and
+           the text version of the traceback. We use
+           multipart/alternative so the receiver can chose which version
+           to display.
+        """
         to = [self.mailer.config.ADMIN_EMAIL]
-        message = self.mailer.get_standard_message(to, subject)
-        # delete existing content-type headers
-        del message['Content-type']
-        message['Content-type'] = 'text/html; charset=utf-8'
-        message.set_payload(content)
-        encode_quopri(message)
-        self.mailer.smtp_send(to, str(message))
+        message = MIMEMultipart('alternative')
+        self.mailer.set_message_attributes(message, to, subject)
+        part = MIMEBase('text', 'html')
+        part.set_charset('utf-8')
+        part.set_payload(html)
+        encode_quopri(part)
+        message.attach(part)
+        part = MIMEText(txt)
+        message.attach(part)
+        self.mailer.smtp_send(to, message.as_string())
     
     def renderFrontPage(self, message):
         """Return the front page of the tracker."""
@@ -1064,9 +1085,9 @@ class Client:
                 result = result.replace('</body>', s)
             return result
         except templating.NoTemplate, message:
-            return '<strong>%s</strong>'%message
+            return '<strong>%s</strong>'%cgi.escape(str(message))
         except templating.Unauthorised, message:
-            raise Unauthorised(str(message))
+            raise Unauthorised(cgi.escape(str(message)))
         except:
             # everything else
             if self.instance.config.WEB_DEBUG:
@@ -1076,7 +1097,7 @@ class Client:
                 # If possible, send the HTML page template traceback
                 # to the administrator.
                 subject = "Templating Error: %s" % exc_info[1]
-                self.send_html_to_admin(subject, cgitb.pt_html())
+                self.send_error_to_admin(subject, cgitb.pt_html(), format_exc())
                 # Now report the error to the user.
                 return self._(error_message)
             except:
@@ -1115,12 +1136,17 @@ class Client:
             present their messages to the user.
         """
         if ':action' in self.form:
-            action = self.form[':action'].value.lower()
+            action = self.form[':action']
         elif '@action' in self.form:
-            action = self.form['@action'].value.lower()
+            action = self.form['@action']
         else:
             return None
 
+        if isinstance(action, list):
+            raise SeriousError('broken form: multiple @action values submitted')
+        else:
+            action = action.value.lower()
+
         try:
             action_klass = self.get_action_class(action)