diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 9c1bac412ee8e6f2c14df615edb7e935a5ce15e6..9a3083adfd49b628a1ab9fb5c064fc08638be81e 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.53 2002-01-16 07:20:54 richard Exp $
+$Id: mailgw.py,v 1.63 2002-02-12 08:08:55 grubert Exp $
'''
s.seek(0)
return Message(s)
-subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re)\s*\W?\s*)*'
+subject_re = re.compile(r'(?P<refwd>\s*\W?\s*(fwd|re|aw)\s*\W?\s*)*'
r'\s*(\[(?P<classname>[^\d\s]+)(?P<nodeid>\d+)?\])'
r'\s*(?P<title>[^[]+)?(\[(?P<args>.+?)\])?', re.I)
m = self.bounce_message(message, sendto, m)
except:
# bounce the message back to the sender with the error message
- sendto = [sendto[0][1]]
+ sendto = [sendto[0][1], self.instance.ADMIN_EMAIL]
m = ['']
+ m.append('An unexpected error occurred during the processing')
+ m.append('of your message. The tracker administrator is being')
+ m.append('notified.\n')
m.append('---- traceback of failure ----')
s = cStringIO.StringIO()
import traceback
writer.lastpart()
return msg
+ def get_part_data_decoded(self,part):
+ encoding = part.getencoding()
+ data = None
+ if encoding == 'base64':
+ # BUG: is base64 really used for text encoding or
+ # are we inserting zip files here.
+ data = binascii.a2b_base64(part.fp.read())
+ elif encoding == 'quoted-printable':
+ # the quopri module wants to work with files
+ decoded = cStringIO.StringIO()
+ quopri.decode(part.fp, decoded)
+ data = decoded.getvalue()
+ elif encoding == 'uuencoded':
+ data = binascii.a2b_uu(part.fp.read())
+ else:
+ # take it as text
+ data = part.fp.read()
+ return data
+
def handle_message(self, message):
''' message - a Message instance
Subject was: "%s"
'''%(key, message, subject)
elif isinstance(proptype, hyperdb.Link):
- link = self.db.classes[proptype.classname]
- propkey = link.labelprop(default_to_id=1)
- props[key] = value
+ linkcl = self.db.classes[proptype.classname]
+ propkey = linkcl.labelprop(default_to_id=1)
+ try:
+ props[key] = linkcl.lookup(value)
+ except KeyError, message:
+ raise MailUsageError, '''
+Subject argument list contains an invalid value for %s.
+
+Error was: %s
+Subject was: "%s"
+'''%(key, message, subject)
elif isinstance(proptype, hyperdb.Multilink):
# get the linked class
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
for item in value.split(','):
- item = item.split()
+ item = item.strip()
+ try:
+ item = linkcl.lookup(item)
+ except KeyError, message:
+ raise MailUsageError, '''
+Subject argument list contains an invalid value for %s.
+
+Error was: %s
+Subject was: "%s"
+'''%(key, message, subject)
if props.has_key(key):
props[key].append(item)
else:
#
content_type = message.gettype()
attachments = []
+ # General multipart handling:
+ # Take the first text/plain part, anything else is considered an
+ # attachment.
+ # multipart/mixed: multiple "unrelated" parts.
+ # multipart/signed (rfc 1847):
+ # The control information is carried in the second of the two
+ # required body parts.
+ # ACTION: Default, so if content is text/plain we get it.
+ # multipart/encrypted (rfc 1847):
+ # The control information is carried in the first of the two
+ # required body parts.
+ # ACTION: Not handleable as the content is encrypted.
+ # multipart/related (rfc 1872, 2112, 2387):
+ # The Multipart/Related content-type addresses the MIME representation
+ # of compound objects.
+ # ACTION: Default. If we are lucky there is a text/plain.
+ # TODO: One should use the start part and look for an Alternative
+ # that is text/plain.
+ # multipart/Alternative (rfc 1872, 1892):
+ # only in "related" ?
+ # multipart/report (rfc 1892):
+ # e.g. mail system delivery status reports.
+ # ACTION: Default. Could be ignored or used for Delivery Notification
+ # flagging.
+ # multipart/form-data:
+ # For web forms only.
if content_type == 'multipart/mixed':
# skip over the intro to the first boundary
part = message.getPart()
# parse it
subtype = part.gettype()
if subtype == 'text/plain' and not content:
- # add all text/plain parts to the message content
- if content is None:
- content = part.fp.read()
- else:
- content = content + part.fp.read()
-
+ # The first text/plain part is the message content.
+ content = self.get_part_data_decoded(part)
elif subtype == 'message/rfc822':
# handle message/rfc822 specially - the name should be
# the subject of the actual e-mail embedded here
name = mailmess.getheader('subject')
part.fp.seek(i)
attachments.append((name, 'message/rfc822', part.fp.read()))
-
else:
# try name on Content-Type
name = part.getparam('name')
# this is just an attachment
- encoding = part.getencoding()
- if encoding == 'base64':
- data = binascii.a2b_base64(part.fp.read())
- elif encoding == 'quoted-printable':
- # the quopri module wants to work with files
- decoded = cStringIO.StringIO()
- quopri.decode(part.fp, decoded)
- data = decoded.getvalue()
- elif encoding == 'uuencoded':
- data = binascii.a2b_uu(part.fp.read())
+ data = self.get_part_data_decoded(part)
attachments.append((name, part.gettype(), data))
-
if content is None:
raise MailUsageError, '''
Roundup requires the submission to be plain text. The message parser could
break
# parse it
if part.gettype() == 'text/plain' and not content:
- # this one's our content
- content = part.fp.read()
+ content = self.get_part_data_decoded(part)
if content is None:
raise MailUsageError, '''
Roundup requires the submission to be plain text. The message parser could
'''
else:
- content = message.fp.read()
-
+ content = self.get_part_data_decoded(message)
+
summary, content = parseContent(content)
#
#
files = []
for (name, mime_type, data) in attachments:
+ if not name:
+ name = "unnamed"
files.append(self.db.file.create(type=mime_type, name=name,
content=data))
except KeyError:
pass
else:
+ current_status = cl.get(nodeid, 'status')
if (not props.has_key('status') and
- properties['status'] == unread_id or
- properties['status'] == resolved_id):
+ current_status == unread_id or
+ current_status == resolved_id):
props['status'] = chatting_id
# add nosy in arguments to issue's nosy list
n[nid] = 1
props['nosy'] = n.keys()
# add assignedto to the nosy list
- try:
- assignedto = self.db.user.lookup(props['assignedto'])
+ if props.has_key('assignedto'):
+ assignedto = props['assignedto']
if assignedto not in props['nosy']:
props['nosy'].append(assignedto)
- except:
- pass
message_id = self.db.msg.create(author=author,
recipients=recipients, date=date.Date('.'), summary=summary,
nosy = props.get('nosy', [])
n = {}
for value in nosy:
- if self.db.hasnode('user', value):
- nid = value
- else:
- continue
+ nid = value
if n.has_key(nid): continue
n[nid] = 1
props['nosy'] = n.keys()
# add assignedto to the nosy list
if properties.has_key('assignedto') and props.has_key('assignedto'):
assignedto = props['assignedto']
- if not re.match('^\d+$', assignedto):
- try:
- assignedto = self.db.user.lookup(assignedto)
- except KeyError:
- raise MailUsageError, '''
-There was a problem with the message you sent:
- Assignedto user '%s' doesn't exist
-'''%props['assignedto']
if not n.has_key(assignedto):
props['nosy'].append(assignedto)
n[assignedto] = 1
#
# $Log: not supported by cvs2svn $
+# Revision 1.62 2002/02/05 14:15:29 grubert
+# . respect encodings in non multipart messages.
+#
+# Revision 1.61 2002/02/04 09:40:21 grubert
+# . add test for multipart messages with first part being encoded.
+#
+# Revision 1.60 2002/02/01 07:43:12 grubert
+# . mailgw checks encoding on first part too.
+#
+# Revision 1.59 2002/01/23 21:43:23 richard
+# tabnuke
+#
+# Revision 1.58 2002/01/23 21:41:56 richard
+# . mailgw failures (unexpected ones) are forwarded to the roundup admin
+#
+# Revision 1.57 2002/01/22 22:27:43 richard
+# . handle stripping of "AW:" from subject line
+#
+# Revision 1.56 2002/01/22 11:54:45 rochecompaan
+# Fixed status change in mail gateway.
+#
+# Revision 1.55 2002/01/21 10:05:47 rochecompaan
+# Feature:
+# . the mail gateway now responds with an error message when invalid
+# values for arguments are specified for link or multilink properties
+# . modified unit test to check nosy and assignedto when specified as
+# arguments
+#
+# Fixed:
+# . fixed setting nosy as argument in subject line
+#
+# Revision 1.54 2002/01/16 09:14:45 grubert
+# . if the attachment has no name, name it unnamed, happens with tnefs.
+#
+# Revision 1.53 2002/01/16 07:20:54 richard
+# simple help command for mailgw
+#
# Revision 1.52 2002/01/15 00:12:40 richard
# #503340 ] creating issue with [asignedto=p.ohly]
#