diff -ruN roundup-1.4.8.orig/roundup/cgi/client.py roundup-1.4.8/roundup/cgi/client.py --- roundup-1.4.8.orig/roundup/cgi/client.py 2009-03-18 07:51:10.000000000 +0900 +++ roundup-1.4.8/roundup/cgi/client.py 2009-04-24 20:11:09.000000000 +0900 @@ -3,7 +3,7 @@ __docformat__ = 'restructuredtext' import base64, binascii, cgi, codecs, httplib, mimetypes, os -import quopri, random, re, rfc822, stat, sys, time, urllib, urlparse +import random, re, rfc822, stat, sys, time, urllib, urlparse import Cookie, socket, errno from Cookie import CookieError, BaseCookie, SimpleCookie from cStringIO import StringIO @@ -14,7 +14,7 @@ from roundup.exceptions import * from roundup.cgi.exceptions import * from roundup.cgi.form_parser import FormParser -from roundup.mailer import Mailer, MessageSendError, encode_quopri +from roundup.mailer import Mailer, MessageSendError from roundup.cgi import accept_language from roundup import xmlrpc @@ -993,10 +993,9 @@ content = cgitb.pt_html() 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_type('text/plain') + message.set_charset(charset) message.set_payload(content) - encode_quopri(message) self.mailer.smtp_send(to, str(message)) # Now report the error to the user. return self._(error_message) diff -ruN roundup-1.4.8.orig/roundup/mailer.py roundup-1.4.8/roundup/mailer.py --- roundup-1.4.8.orig/roundup/mailer.py 2009-03-18 08:12:45.000000000 +0900 +++ roundup-1.4.8/roundup/mailer.py 2009-04-24 20:10:06.000000000 +0900 @@ -3,29 +3,42 @@ __docformat__ = 'restructuredtext' # $Id: mailer.py,v 1.22 2008-07-21 01:44:58 richard Exp $ -import time, quopri, os, socket, smtplib, re, sys, traceback, email +import time, os, socket, smtplib, re, sys, traceback, email from cStringIO import StringIO from roundup import __version__ from roundup.date import get_timezone +from email import Encoders from email.Utils import formatdate, formataddr from email.Message import Message from email.Header import Header from email.MIMEText import MIMEText from email.MIMEMultipart import MIMEMultipart +from email.base64MIME import header_encode + +def convert_encoding(text, to_enc, from_enc="utf-8", errors="strict"): + assert isinstance(text, basestring) + if isinstance(text, str): + text = unicode(text, from_enc) + return text.encode(to_enc, errors) + +def base64_formataddr(pair, charset="utf-8"): + """base64 supported version of email.Utils.formataddr""" + + realname, email_address = pair + try: + convert_encoding(realname, "ascii") + except UnicodeError: + realname = convert_encoding( + realname, charset, errors="replace") + realname = header_encode(realname, charset) + return formataddr((realname, email_address)) class MessageSendError(RuntimeError): pass -def encode_quopri(msg): - orig = msg.get_payload() - encdata = quopri.encodestring(orig) - msg.set_payload(encdata) - del msg['Content-Transfer-Encoding'] - msg['Content-Transfer-Encoding'] = 'quoted-printable' - class Mailer: """Roundup-specific mail sending.""" def __init__(self, config): @@ -61,11 +74,6 @@ # encode header values if they need to be charset = getattr(self.config, 'EMAIL_CHARSET', 'utf-8') tracker_name = unicode(self.config.TRACKER_NAME, 'utf-8') - if not author: - author = formataddr((tracker_name, self.config.ADMIN_EMAIL)) - else: - name = unicode(author[0], 'utf-8') - author = formataddr((name, author[1])) if multipart: message = MIMEMultipart() @@ -77,12 +85,12 @@ try: message['Subject'] = subject.encode('ascii') except UnicodeError: + subject = convert_encoding(subject, charset) message['Subject'] = Header(subject, charset) message['To'] = ', '.join(to) - try: - message['From'] = author.encode('ascii') - except UnicodeError: - message['From'] = Header(author, charset) + if not author: + author = tracker_name, self.config.ADMIN_EMAIL + message['From'] = base64_formataddr(author, charset=charset) message['Date'] = formatdate(localtime=True) # add a Precedence header so autoresponders ignore us @@ -92,6 +100,7 @@ try: message['X-Roundup-Name'] = tracker_name.encode('ascii') except UnicodeError: + tracker_name = convert_encoding(tracker_name, charset) message['X-Roundup-Name'] = Header(tracker_name, charset) # and another one to avoid loops @@ -99,8 +108,6 @@ # finally, an aid to debugging problems message['X-Roundup-Version'] = __version__ - message['MIME-Version'] = '1.0' - return message def standard_message(self, to, subject, content, author=None): @@ -116,8 +123,11 @@ """ message = self.get_standard_message(to, subject, author) message.set_payload(content) - encode_quopri(message) - self.smtp_send(to, str(message)) + # Content-Transfer-Encoding is automaticaly set to base64, + # if charset is a 8bit encoding, + if message['content-transfer-encoding'] == "base64": + Encoders.encode_base64(message) + self.smtp_send(to, message.as_string()) def bounce_message(self, bounced_message, to, error, subject='Failed issue tracker submission'): diff -ruN roundup-1.4.8.orig/roundup/roundupdb.py roundup-1.4.8/roundup/roundupdb.py --- roundup-1.4.8.orig/roundup/roundupdb.py 2009-03-18 07:51:15.000000000 +0900 +++ roundup-1.4.8/roundup/roundupdb.py 2009-04-24 20:10:15.000000000 +0900 @@ -36,7 +36,7 @@ from roundup.i18n import _ # MessageSendError is imported for backwards compatibility -from roundup.mailer import Mailer, MessageSendError, encode_quopri +from roundup.mailer import Mailer, MessageSendError, convert_encoding class Database: @@ -444,6 +444,7 @@ try: message[header] = values.encode('ascii') except UnicodeError: + values = convert_encoding(values, charset) message[header] = Header(values, charset) if not inreplyto: @@ -460,8 +461,7 @@ # attach files if message_files: # first up the text as a part - part = MIMEText(content) - encode_quopri(part) + part = MIMEText(content, _charset=charset) message.attach(part) for fileid in message_files: @@ -471,15 +471,13 @@ if mime_type == 'text/plain': try: content.decode('ascii') - except UnicodeError: - # the content cannot be 7bit-encoded. - # use quoted printable - # XXX stuffed if we know the charset though :( - part = MIMEText(content) - encode_quopri(part) - else: part = MIMEText(content) - part['Content-Transfer-Encoding'] = '7bit' + except UnicodeError: + # you don't know encoding of the text so + # use base64 + part = MIMEBase('text', 'plain') + part.set_payload(content) + Encoders.encode_base64(part) else: # some other type, so encode it if not mime_type: @@ -491,17 +489,20 @@ part = MIMEBase(main, sub) part.set_payload(content) Encoders.encode_base64(part) + name = convert_encoding(name, charset) part['Content-Disposition'] = 'attachment;\n filename="%s"'%name message.attach(part) else: message.set_payload(content) - encode_quopri(message) - + # Content-Transfer-Encoding is automaticaly set to base64, + # if charset is a 8bit encoding, + if message['content-transfer-encoding'] == "base64": + Encoders.encode_base64(message) if first: - mailer.smtp_send(sendto + bcc_sendto, str(message)) + mailer.smtp_send(sendto + bcc_sendto, message.as_string()) else: - mailer.smtp_send(sendto, str(message)) + mailer.smtp_send(sendto, message.as_string()) first = False def email_signature(self, nodeid, msgid):