diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index ae9b9faef1cf974506027e5e8b50a2113f9a56df..ad9c10594e1aabf76365c9cf31997bc9d1c860a0 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.
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.69 2002-05-06 23:37:21 richard Exp $
+$Id: mailgw.py,v 1.75 2002-07-09 01:21:24 richard Exp $
'''
'''
self.instance = instance
self.db = db
self.instance = instance
self.db = db
+ # should we trap exceptions (normal usage) or pass them through
+ # (for testing)
+ self.trapExceptions = 1
+
def main(self, fp):
''' fp - the file from which to read the Message.
'''
def main(self, fp):
''' fp - the file from which to read the Message.
'''
# its way into here... try to handle it gracefully
sendto = message.getaddrlist('from')
if sendto:
# its way into here... try to handle it gracefully
sendto = message.getaddrlist('from')
if sendto:
+ if not self.trapExceptions:
+ return self.handle_message(message)
try:
return self.handle_message(message)
except MailUsageHelp:
try:
return self.handle_message(message)
except MailUsageHelp:
title = ''
# but we do need either a title or a nodeid...
title = ''
# but we do need either a title or a nodeid...
- if not nodeid and not title:
+ if nodeid is None and not title:
raise MailUsageError, '''
I cannot match your message to a node in the database - you need to either
supply a full node identifier (with number, eg "[issue123]" or keep the
raise MailUsageError, '''
I cannot match your message to a node in the database - you need to either
supply a full node identifier (with number, eg "[issue123]" or keep the
Subject was: "%s"
'''%subject
Subject was: "%s"
'''%subject
- # extract the args
- subject_args = m.group('args')
-
# If there's no nodeid, check to see if this is a followup and
# maybe someone's responded to the initial mail that created an
# entry. Try to find the matching nodes with the same title, and
# use the _last_ one matched (since that'll _usually_ be the most
# recent...)
# If there's no nodeid, check to see if this is a followup and
# maybe someone's responded to the initial mail that created an
# entry. Try to find the matching nodes with the same title, and
# use the _last_ one matched (since that'll _usually_ be the most
# recent...)
- if not nodeid and m.group('refwd'):
+ if nodeid is None and m.group('refwd'):
l = cl.stringFind(title=title)
if l:
nodeid = l[-1]
l = cl.stringFind(title=title)
if l:
nodeid = l[-1]
- # start of the props
+ # if a nodeid was specified, make sure it's valid
+ if nodeid is not None and not cl.hasnode(nodeid):
+ raise MailUsageError, '''
+The node specified by the designator in the subject of your message ("%s")
+does not exist.
+
+Subject was: "%s"
+'''%(nodeid, subject)
+
+ #
+ # extract the args
+ #
+ subject_args = m.group('args')
+
+ #
+ # handle the subject argument list
+ #
+ # figure what the properties of this Class are
properties = cl.getprops()
props = {}
properties = cl.getprops()
props = {}
-
- # handle the args
args = m.group('args')
if args:
args = m.group('args')
if args:
+ errors = []
for prop in string.split(args, ';'):
# extract the property name and value
try:
for prop in string.split(args, ';'):
# extract the property name and value
try:
- key, value = prop.split('=')
+ propname, value = prop.split('=')
except ValueError, message:
except ValueError, message:
- raise MailUsageError, '''
-Subject argument list not of form [arg=value,value,...;arg=value,value...]
- (specific exception message was "%s")
-
-Subject was: "%s"
-'''%(message, subject)
+ errors.append('not of form [arg=value,'
+ 'value,...;arg=value,value...]')
+ break
# ensure it's a valid property name
# ensure it's a valid property name
- key = key.strip()
+ propname = propname.strip()
try:
try:
- proptype = properties[key]
+ proptype = properties[propname]
except KeyError:
except KeyError:
- raise MailUsageError, '''
-Subject argument list refers to an invalid property: "%s"
-
-Subject was: "%s"
-'''%(key, subject)
+ errors.append('refers to an invalid property: '
+ '"%s"'%propname)
+ continue
# convert the string value to a real property value
if isinstance(proptype, hyperdb.String):
# convert the string value to a real property value
if isinstance(proptype, hyperdb.String):
- props[key] = value.strip()
+ props[propname] = value.strip()
if isinstance(proptype, hyperdb.Password):
if isinstance(proptype, hyperdb.Password):
- props[key] = password.Password(value.strip())
+ props[propname] = password.Password(value.strip())
elif isinstance(proptype, hyperdb.Date):
try:
elif isinstance(proptype, hyperdb.Date):
try:
- props[key] = date.Date(value.strip())
+ props[propname] = date.Date(value.strip())
except ValueError, message:
except ValueError, message:
- raise UsageError, '''
-Subject argument list contains an invalid date for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('contains an invalid date for '
+ '%s.'%propname)
elif isinstance(proptype, hyperdb.Interval):
try:
elif isinstance(proptype, hyperdb.Interval):
try:
- props[key] = date.Interval(value) # no strip needed
+ props[propname] = date.Interval(value)
except ValueError, message:
except ValueError, message:
- raise UsageError, '''
-Subject argument list contains an invalid date interval for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('contains an invalid date interval'
+ 'for %s.'%propname)
elif isinstance(proptype, hyperdb.Link):
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
try:
elif isinstance(proptype, hyperdb.Link):
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
try:
- props[key] = linkcl.lookup(value)
+ props[propname] = linkcl.lookup(value)
except KeyError, message:
except KeyError, message:
- raise MailUsageError, '''
-Subject argument list contains an invalid value for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('"%s" is not a value for %s.'%(value,
+ propname))
elif isinstance(proptype, hyperdb.Multilink):
# get the linked class
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
elif isinstance(proptype, hyperdb.Multilink):
# get the linked class
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
+ if nodeid:
+ curvalue = cl.get(nodeid, propname)
+ else:
+ curvalue = []
+
+ # handle each add/remove in turn
for item in value.split(','):
item = item.strip()
for item in value.split(','):
item = item.strip()
+
+ # handle +/-
+ remove = 0
+ if item.startswith('-'):
+ remove = 1
+ item = item[1:]
+ elif item.startswith('+'):
+ item = item[1:]
+
+ # look up the value
try:
item = linkcl.lookup(item)
except KeyError, message:
try:
item = linkcl.lookup(item)
except KeyError, message:
- raise MailUsageError, '''
-Subject argument list contains an invalid value for %s.
+ errors.append('"%s" is not a value for %s.'%(item,
+ propname))
+ continue
+
+ # perform the add/remove
+ if remove:
+ try:
+ curvalue.remove(item)
+ except ValueError:
+ errors.append('"%s" is not currently in '
+ 'for %s.'%(item, propname))
+ continue
+ else:
+ if item not in curvalue:
+ curvalue.append(item)
+
+ # that's it, set the new Multilink property value
+ props[propname] = curvalue
+
+ # handle any errors parsing the argument list
+ if errors:
+ errors = '\n- '.join(errors)
+ raise MailUsageError, '''
+There were problems handling your subject line argument list:
+- %s
-Error was: %s
Subject was: "%s"
Subject was: "%s"
-'''%(key, message, subject)
- if props.has_key(key):
- props[key].append(item)
- else:
- props[key] = [item]
+'''%(errors, subject)
#
# handle the users
#
# handle the users
else:
content = self.get_part_data_decoded(message)
else:
content = self.get_part_data_decoded(message)
- keep_citations = self.instance.EMAIL_KEEP_QUOTED_TEXT == 'yes'
- keep_body = self.instance.EMAIL_LEAVE_BODY_UNCHANGED == 'yes'
+ # figure how much we should muck around with the email body
+ keep_citations = getattr(self.instance, 'EMAIL_KEEP_QUOTED_TEXT',
+ 'no') == 'yes'
+ keep_body = getattr(self.instance, 'EMAIL_LEAVE_BODY_UNCHANGED',
+ 'no') == 'yes'
+
+ # parse the body of the message, stripping out bits as appropriate
summary, content = parseContent(content, keep_citations,
keep_body)
summary, content = parseContent(content, keep_citations,
keep_body)
files.append(self.db.file.create(type=mime_type, name=name,
content=data))
files.append(self.db.file.create(type=mime_type, name=name,
content=data))
+ #
+ # create the message if there's a message body (content)
#
#
- # now handle the db stuff
- #
- if nodeid:
- # If an item designator (class name and id number) is found there,
- # the newly created "msg" node is added to the "messages" property
- # for that item, and any new "file" nodes are added to the "files"
- # property for the item.
-
- # if the message is currently 'unread' or 'resolved', then set
- # it to 'chatting'
- if properties.has_key('status'):
- try:
- # determine the id of 'unread', 'resolved' and 'chatting'
- unread_id = self.db.status.lookup('unread')
- resolved_id = self.db.status.lookup('resolved')
- chatting_id = self.db.status.lookup('chatting')
- except KeyError:
- pass
- else:
- current_status = cl.get(nodeid, 'status')
- if (not props.has_key('status') and
- current_status == unread_id or
- current_status == resolved_id):
- props['status'] = chatting_id
-
- # update the nosy list
- current = {}
- for nid in cl.get(nodeid, 'nosy'):
- current[nid] = 1
- self.updateNosy(props, author, recipients, current)
-
- # create the message
+ if content:
message_id = self.db.msg.create(author=author,
recipients=recipients, date=date.Date('.'), summary=summary,
content=content, files=files, messageid=messageid,
inreplyto=inreplyto)
message_id = self.db.msg.create(author=author,
recipients=recipients, date=date.Date('.'), summary=summary,
content=content, files=files, messageid=messageid,
inreplyto=inreplyto)
- try:
+
+ # attach the message to the node
+ if nodeid:
+ # add the message to the node's list
messages = cl.get(nodeid, 'messages')
messages = cl.get(nodeid, 'messages')
- except IndexError:
- raise MailUsageError, '''
-The node specified by the designator in the subject of your message ("%s")
-does not exist.
+ messages.append(message_id)
+ props['messages'] = messages
+ else:
+ # pre-load the messages list
+ props['messages'] = [message_id]
-Subject was: "%s"
-'''%(nodeid, subject)
- messages.append(message_id)
- props['messages'] = messages
+ # set the title to the subject
+ if properties.has_key('title') and not props.has_key('title'):
+ props['title'] = title
- # now apply the changes
- try:
+ #
+ # perform the node change / create
+ #
+ try:
+ if nodeid:
cl.set(nodeid, **props)
cl.set(nodeid, **props)
- except (TypeError, IndexError, ValueError), message:
- raise MailUsageError, '''
-There was a problem with the message you sent:
- %s
-'''%message
- # commit the changes to the DB
- self.db.commit()
- else:
- # If just an item class name is found there, we attempt to create a
- # new item of that class with its "messages" property initialized to
- # contain the new "msg" node and its "files" property initialized to
- # contain any new "file" nodes.
- message_id = self.db.msg.create(author=author,
- recipients=recipients, date=date.Date('.'), summary=summary,
- content=content, files=files, messageid=messageid,
- inreplyto=inreplyto)
-
- # pre-set the issue to unread
- if properties.has_key('status') and not props.has_key('status'):
- try:
- # determine the id of 'unread'
- unread_id = self.db.status.lookup('unread')
- except KeyError:
- pass
- else:
- props['status'] = '1'
-
- # set the title to the subject
- if properties.has_key('title') and not props.has_key('title'):
- props['title'] = title
-
- # pre-load the messages list
- props['messages'] = [message_id]
-
- # set up (clean) the nosy list
- self.updateNosy(props, author, recipients)
-
- # and attempt to create the new node
- try:
+ else:
nodeid = cl.create(**props)
nodeid = cl.create(**props)
- except (TypeError, IndexError, ValueError), message:
- raise MailUsageError, '''
+ except (TypeError, IndexError, ValueError), message:
+ raise MailUsageError, '''
There was a problem with the message you sent:
%s
'''%message
There was a problem with the message you sent:
%s
'''%message
- # commit the new node(s) to the DB
- self.db.commit()
+ # commit the changes to the DB
+ self.db.commit()
return nodeid
return nodeid
- def updateNosy(self, props, author, recipients, current=None):
- '''Determine what the nosy list should be given:
-
- props: properties specified on the subject line of the message
- author: the sender of the message
- recipients: the recipients (to, cc) of the message
- current: if the issue already exists, this is the current nosy
- list, as a dictionary.
- '''
- if current is None:
- current = {}
- ok = ('new', 'yes')
- else:
- ok = ('yes',)
-
- # add nosy in arguments to issue's nosy list
- nosy = props.get('nosy', [])
- for value in nosy:
- if not self.db.hasnode('user', value):
- continue
- if not current.has_key(value):
- current[value] = 1
-
- # add the author to the nosy list
- if getattr(self.instance, 'ADD_AUTHOR_TO_NOSY', 'new') in ok:
- if not current.has_key(author):
- current[author] = 1
-
- # add on the recipients of the message
- if getattr(self.instance, 'ADD_RECIPIENTS_TO_NOSY', 'new') in ok:
- for recipient in recipients:
- if not current.has_key(recipient):
- current[recipient] = 1
-
- # add assignedto to the nosy list
- if props.has_key('assignedto'):
- assignedto = props['assignedto']
- if not current.has_key(assignedto):
- current[assignedto] = 1
-
- props['nosy'] = current.keys()
-
def parseContent(content, keep_citations, keep_body,
blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
eol=re.compile(r'[\r\n]+'),
signature=re.compile(r'^[>|\s]*[-_]+\s*$'),
def parseContent(content, keep_citations, keep_body,
blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
eol=re.compile(r'[\r\n]+'),
signature=re.compile(r'^[>|\s]*[-_]+\s*$'),
- original_message=re.compile(r'^-----Original Message-----$')):
+ original_message=re.compile(r'^[>|\s]*-----Original Message-----$')):
''' The message body is divided into sections by blank lines.
Sections where the second and all subsequent lines begin with a ">" or "|"
character are considered "quoting sections". The first line of the first
''' The message body is divided into sections by blank lines.
Sections where the second and all subsequent lines begin with a ">" or "|"
character are considered "quoting sections". The first line of the first
#
# $Log: not supported by cvs2svn $
#
# $Log: not supported by cvs2svn $
+# Revision 1.74 2002/05/29 01:16:17 richard
+# Sorry about this huge checkin! It's fixing a lot of related stuff in one go
+# though.
+#
+# . #541941 ] changing multilink properties by mail
+# . #526730 ] search for messages capability
+# . #505180 ] split MailGW.handle_Message
+# - also changed cgi client since it was duplicating the functionality
+# . build htmlbase if tests are run using CVS checkout (removed note from
+# installation.txt)
+# . don't create an empty message on email issue creation if the email is empty
+#
+# Revision 1.73 2002/05/22 04:12:05 richard
+# . applied patch #558876 ] cgi client customization
+# ... with significant additions and modifications ;)
+# - extended handling of ML assignedto to all places it's handled
+# - added more NotFound info
+#
+# Revision 1.72 2002/05/22 01:24:51 richard
+# Added note to MIGRATION about new config vars. Also made us more resilient
+# for upgraders. Reinstated list header style (oops)
+#
+# Revision 1.71 2002/05/08 02:40:55 richard
+# grr
+#
+# Revision 1.70 2002/05/06 23:40:07 richard
+# hrm
+#
+# Revision 1.69 2002/05/06 23:37:21 richard
+# Tweaking the signature deletion from mail messages.
+# Added nuking of the "-----Original Message-----" crap from Outlook.
+#
# Revision 1.68 2002/05/02 07:56:34 richard
# . added option to automatically add the authors and recipients of messages
# to the nosy lists with the options ADD_AUTHOR_TO_NOSY (default 'new') and
# Revision 1.68 2002/05/02 07:56:34 richard
# . added option to automatically add the authors and recipients of messages
# to the nosy lists with the options ADD_AUTHOR_TO_NOSY (default 'new') and