X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fmailgw.py;h=7057597e0f8de790f55f195932e4cda842ad7579;hb=3d106aaea151d4ee72945a7c8f26a8deb8f482a6;hp=ff2346bfd5b7602271854a572e3bfdcba693a4d9;hpb=9fdb76121879e5bc0df38c598e1896e637388e08;p=roundup.git diff --git a/roundup/mailgw.py b/roundup/mailgw.py index ff2346b..7057597 100644 --- a/roundup/mailgw.py +++ b/roundup/mailgw.py @@ -73,10 +73,9 @@ are calling the create() method to create a new node). If an auditor raises an exception, the original message is bounced back to the sender with the explanatory message given in the exception. -$Id: mailgw.py,v 1.80 2002-08-01 00:56:22 richard Exp $ +$Id: mailgw.py,v 1.87 2002-09-11 01:19:16 richard Exp $ ''' - import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri import time, random import traceback, MimeWriter @@ -102,10 +101,11 @@ def initialiseSecurity(security): This function is directly invoked by security.Security.__init__() as a part of the Security object instantiation. ''' - newid = security.addPermission(name="Email Registration", + security.addPermission(name="Email Registration", description="Anonymous may register through e-mail") - security.addPermission(name="Email Access", + p = security.addPermission(name="Email Access", description="User may use the email interface") + security.addPermissionToRole('Admin', p) class Message(mimetools.Message): ''' subclass mimetools.Message so we can retrieve the parts of the @@ -143,6 +143,79 @@ class MailGW: # (for testing) self.trapExceptions = 1 + def do_pipe(self): + ''' Read a message from standard input and pass it to the mail handler. + ''' + self.main(sys.stdin) + return 0 + + def do_mailbox(self, filename): + ''' Read a series of messages from the specified unix mailbox file and + pass each to the mail handler. + ''' + # open the spool file and lock it + import fcntl, FCNTL + f = open(filename, 'r+') + fcntl.flock(f.fileno(), FCNTL.LOCK_EX) + + # handle and clear the mailbox + try: + from mailbox import UnixMailbox + mailbox = UnixMailbox(f, factory=Message) + # grab one message + message = mailbox.next() + while message: + # handle this message + self.handle_Message(message) + message = mailbox.next() + # nuke the file contents + os.ftruncate(f.fileno(), 0) + except: + import traceback + traceback.print_exc() + return 1 + fcntl.flock(f.fileno(), FCNTL.LOCK_UN) + return 0 + + def do_pop(self, server, user='', password=''): + '''Read a series of messages from the specified POP server. + ''' + import getpass, poplib, socket + try: + if not user: + user = raw_input(_('User: ')) + if not password: + password = getpass.getpass() + except (KeyboardInterrupt, EOFError): + # Ctrl C or D maybe also Ctrl Z under Windows. + print "\nAborted by user." + return 1 + + # open a connection to the server and retrieve all messages + try: + server = poplib.POP3(server) + except socket.error, message: + print "POP server error:", message + return 1 + server.user(user) + server.pass_(password) + numMessages = len(server.list()[1]) + for i in range(1, numMessages+1): + # retr: returns + # [ pop response e.g. '+OK 459 octets', + # [ array of message lines ], + # number of octets ] + lines = server.retr(i)[1] + s = cStringIO.StringIO('\n'.join(lines)) + s.seek(0) + self.handle_Message(Message(s)) + # delete the message + server.dele(i) + + # quit the server to commit changes. + server.quit() + return 0 + def main(self, fp): ''' fp - the file from which to read the Message. ''' @@ -191,7 +264,7 @@ class MailGW: m = self.bounce_message(message, sendto, m) except: # bounce the message back to the sender with the error message - sendto = [sendto[0][1], self.instance.ADMIN_EMAIL] + sendto = [sendto[0][1], self.instance.config.ADMIN_EMAIL] m = [''] m.append('An unexpected error occurred during the processing') m.append('of your message. The tracker administrator is being') @@ -204,7 +277,7 @@ class MailGW: m = self.bounce_message(message, sendto, m) else: # very bad-looking message - we don't even know who sent it - sendto = [self.instance.ADMIN_EMAIL] + sendto = [self.instance.config.ADMIN_EMAIL] m = ['Subject: badly formed message from mail gateway'] m.append('') m.append('The mail gateway retrieved a message which has no From:') @@ -217,11 +290,13 @@ class MailGW: # now send the message if SENDMAILDEBUG: open(SENDMAILDEBUG, 'w').write('From: %s\nTo: %s\n%s\n'%( - self.instance.ADMIN_EMAIL, ', '.join(sendto), m.getvalue())) + self.instance.config.ADMIN_EMAIL, ', '.join(sendto), + m.getvalue())) else: try: - smtp = smtplib.SMTP(self.instance.MAILHOST) - smtp.sendmail(self.instance.ADMIN_EMAIL, sendto, m.getvalue()) + smtp = smtplib.SMTP(self.instance.config.MAILHOST) + smtp.sendmail(self.instance.config.ADMIN_EMAIL, sendto, + m.getvalue()) except socket.error, value: raise MailGWError, "Couldn't send error email: "\ "mailhost %s"%value @@ -237,8 +312,8 @@ class MailGW: msg = cStringIO.StringIO() writer = MimeWriter.MimeWriter(msg) writer.addheader('Subject', subject) - writer.addheader('From', '%s <%s>'% (self.instance.INSTANCE_NAME, - self.instance.ISSUE_TRACKER_EMAIL)) + writer.addheader('From', '%s <%s>'% (self.instance.config.TRACKER_NAME, + self.instance.config.TRACKER_EMAIL)) writer.addheader('To', ','.join(sendto)) writer.addheader('MIME-Version', '1.0') part = writer.startmultipartbody('mixed') @@ -316,9 +391,9 @@ class MailGW: classname = m.group('classname') if classname is None: # no classname, fallback on the default - if hasattr(self.instance, 'MAIL_DEFAULT_CLASS') and \ - self.instance.MAIL_DEFAULT_CLASS: - classname = self.instance.MAIL_DEFAULT_CLASS + if hasattr(self.instance.config, 'MAIL_DEFAULT_CLASS') and \ + self.instance.config.MAIL_DEFAULT_CLASS: + classname = self.instance.config.MAIL_DEFAULT_CLASS else: # fail m = None @@ -389,6 +464,63 @@ does not exist. Subject was: "%s" '''%(nodeid, subject) + # + # handle the users + # + # Don't create users if anonymous isn't allowed to register + create = 1 + anonid = self.db.user.lookup('anonymous') + if not self.db.security.hasPermission('Email Registration', anonid): + create = 0 + + # ok, now figure out who the author is - create a new user if the + # "create" flag is true + author = uidFromAddress(self.db, message.getaddrlist('from')[0], + create=create) + + # no author? means we're not author + if not author: + raise Unauthorized, ''' +You are not a registered user. + +Unknown address: %s +'''%message.getaddrlist('from')[0][1] + + # make sure the author has permission to use the email interface + if not self.db.security.hasPermission('Email Access', author): + raise Unauthorized, 'You are not permitted to access this tracker.' + + # make sure they're allowed to edit this class of information + if not self.db.security.hasPermission('Edit', author, classname): + raise Unauthorized, 'You are not permitted to edit %s.'%classname + + # the author may have been created - make sure the change is + # committed before we reopen the database + self.db.commit() + + # reopen the database as the author + username = self.db.user.get(author, 'username') + self.db = self.instance.open(username) + + # re-get the class with the new database connection + cl = self.db.getclass(classname) + + # now update the recipients list + recipients = [] + tracker_email = self.instance.config.TRACKER_EMAIL.lower() + for recipient in message.getaddrlist('to') + message.getaddrlist('cc'): + r = recipient[1].strip().lower() + if r == tracker_email or not r: + continue + + # look up the recipient - create if necessary (and we're + # allowed to) + recipient = uidFromAddress(self.db, recipient, create) + + # if all's well, add the recipient to the list + if recipient: + recipients.append(recipient) + # # extract the args # @@ -519,60 +651,6 @@ There were problems handling your subject line argument list: Subject was: "%s" '''%(errors, subject) - # - # handle the users - # - - # Don't create users if anonymous isn't allowed to register - create = 1 - anonid = self.db.user.lookup('anonymous') - if not self.db.security.hasPermission('Email Registration', anonid): - create = 0 - - # ok, now figure out who the author is - create a new user if the - # "create" flag is true - author = uidFromAddress(self.db, message.getaddrlist('from')[0], - create=create) - - # no author? means we're not author - if not author: - raise Unauthorized, ''' -You are not a registered user. - -Unknown address: %s -'''%message.getaddrlist('from')[0][1] - - # make sure the author has permission to use the email interface - if not self.db.security.hasPermission('Email Access', author): - raise Unauthorized, 'You are not permitted to access this tracker.' - - # the author may have been created - make sure the change is - # committed before we reopen the database - self.db.commit() - - # reopen the database as the author - username = self.db.user.get(author, 'username') - self.db = self.instance.open(username) - - # re-get the class with the new database connection - cl = self.db.getclass(classname) - - # now update the recipients list - recipients = [] - tracker_email = self.instance.ISSUE_TRACKER_EMAIL.lower() - for recipient in message.getaddrlist('to') + message.getaddrlist('cc'): - r = recipient[1].strip().lower() - if r == tracker_email or not r: - continue - - # look up the recipient - create if necessary (and we're - # allowed to) - recipient = uidFromAddress(self.db, recipient, create) - - # if all's well, add the recipient to the list - if recipient: - recipients.append(recipient) - # # handle message-id and in-reply-to # @@ -581,7 +659,7 @@ Unknown address: %s # generate a messageid if there isn't one if not messageid: messageid = "<%s.%s.%s%s@%s>"%(time.time(), random.random(), - classname, nodeid, self.instance.MAIL_DOMAIN) + classname, nodeid, self.instance.config.MAIL_DOMAIN) # # now handle the body - find the message @@ -790,9 +868,13 @@ def parseContent(content, keep_citations, keep_body, signature=re.compile(r'^[>|\s]*[-_]+\s*$'), 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 - non-quoting section becomes the summary of the message. + Sections where the second and all subsequent lines begin with a ">" + or "|" character are considered "quoting sections". The first line of + the first non-quoting section becomes the summary of the message. + + If keep_citations is true, then we keep the "quoting sections" in the + content. + If keep_body is true, we even keep the signature sections. ''' # strip off leading carriage-returns / newlines i = 0 @@ -845,391 +927,12 @@ def parseContent(content, keep_citations, keep_body, # and add the section to the output l.append(section) - # we only set content for those who want to delete cruft from the - # message body, otherwise the body is left untouched. + + # Now reconstitute the message content minus the bits we don't care + # about. if not keep_body: content = '\n\n'.join(l) + return summary, content -# -# $Log: not supported by cvs2svn $ -# Revision 1.79 2002/07/26 08:26:59 richard -# Very close now. The cgi and mailgw now use the new security API. The two -# templates have been migrated to that setup. Lots of unit tests. Still some -# issue in the web form for editing Roles assigned to users. -# -# Revision 1.78 2002/07/25 07:14:06 richard -# Bugger it. Here's the current shape of the new security implementation. -# Still to do: -# . call the security funcs from cgi and mailgw -# . change shipped templates to include correct initialisation and remove -# the old config vars -# ... that seems like a lot. The bulk of the work has been done though. Honest :) -# -# Revision 1.77 2002/07/18 11:17:31 gmcm -# Add Number and Boolean types to hyperdb. -# Add conversion cases to web, mail & admin interfaces. -# Add storage/serialization cases to back_anydbm & back_metakit. -# -# Revision 1.76 2002/07/10 06:39:37 richard -# . made mailgw handle set and modify operations on multilinks (bug #579094) -# -# Revision 1.75 2002/07/09 01:21:24 richard -# Added ability for unit tests to turn off exception handling in mailgw so -# that exceptions are reported earlier (and hence make sense). -# -# 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 -# ADD_RECIPIENTS_TO_NOSY (default 'new'). These settings emulate the current -# behaviour. Setting them to 'yes' will add the author/recipients to the nosy -# on messages that create issues and followup messages. -# . added missing documentation for a few of the config option values -# -# Revision 1.67 2002/04/23 15:46:49 rochecompaan -# . stripping of the email message body can now be controlled through -# the config variables EMAIL_KEEP_QUOTED_TEST and -# EMAIL_LEAVE_BODY_UNCHANGED. -# -# Revision 1.66 2002/03/14 23:59:24 richard -# . #517734 ] web header customisation is obscure -# -# Revision 1.65 2002/02/15 00:13:38 richard -# . #503204 ] mailgw needs a default class -# - partially done - the setting of additional properties can wait for a -# better configuration system. -# -# Revision 1.64 2002/02/14 23:46:02 richard -# . #516883 ] mail interface + ANONYMOUS_REGISTER -# -# Revision 1.63 2002/02/12 08:08:55 grubert -# . Clean up mail handling, multipart handling. -# -# 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] -# -# Revision 1.51 2002/01/14 02:20:15 richard -# . changed all config accesses so they access either the instance or the -# config attriubute on the db. This means that all config is obtained from -# instance_config instead of the mish-mash of classes. This will make -# switching to a ConfigParser setup easier too, I hope. -# -# At a minimum, this makes migration a _little_ easier (a lot easier in the -# 0.5.0 switch, I hope!) -# -# Revision 1.50 2002/01/11 22:59:01 richard -# . #502342 ] pipe interface -# -# Revision 1.49 2002/01/10 06:19:18 richard -# followup lines directly after a quoted section were being eaten. -# -# Revision 1.48 2002/01/08 04:12:05 richard -# Changed message-id format to "<%s.%s.%s%s@%s>" so it complies with RFC822 -# -# Revision 1.47 2002/01/02 02:32:38 richard -# ANONYMOUS_ACCESS -> ANONYMOUS_REGISTER -# -# Revision 1.46 2002/01/02 02:31:38 richard -# Sorry for the huge checkin message - I was only intending to implement #496356 -# but I found a number of places where things had been broken by transactions: -# . modified ROUNDUPDBSENDMAILDEBUG to be SENDMAILDEBUG and hold a filename -# for _all_ roundup-generated smtp messages to be sent to. -# . the transaction cache had broken the roundupdb.Class set() reactors -# . newly-created author users in the mailgw weren't being committed to the db -# -# Stuff that made it into CHANGES.txt (ie. the stuff I was actually working -# on when I found that stuff :): -# . #496356 ] Use threading in messages -# . detectors were being registered multiple times -# . added tests for mailgw -# . much better attaching of erroneous messages in the mail gateway -# -# Revision 1.45 2001/12/20 15:43:01 rochecompaan -# Features added: -# . Multilink properties are now displayed as comma separated values in -# a textbox -# . The add user link is now only visible to the admin user -# . Modified the mail gateway to reject submissions from unknown -# addresses if ANONYMOUS_ACCESS is denied -# -# Revision 1.44 2001/12/18 15:30:34 rochecompaan -# Fixed bugs: -# . Fixed file creation and retrieval in same transaction in anydbm -# backend -# . Cgi interface now renders new issue after issue creation -# . Could not set issue status to resolved through cgi interface -# . Mail gateway was changing status back to 'chatting' if status was -# omitted as an argument -# -# Revision 1.43 2001/12/15 19:39:01 rochecompaan -# Oops. -# -# Revision 1.42 2001/12/15 19:24:39 rochecompaan -# . Modified cgi interface to change properties only once all changes are -# collected, files created and messages generated. -# . Moved generation of change note to nosyreactors. -# . We now check for changes to "assignedto" to ensure it's added to the -# nosy list. -# -# Revision 1.41 2001/12/10 00:57:38 richard -# From CHANGES: -# . Added the "display" command to the admin tool - displays a node's values -# . #489760 ] [issue] only subject -# . fixed the doc/index.html to include the quoting in the mail alias. -# -# Also: -# . fixed roundup-admin so it works with transactions -# . disabled the back_anydbm module if anydbm tries to use dumbdbm -# -# Revision 1.40 2001/12/05 14:26:44 rochecompaan -# Removed generation of change note from "sendmessage" in roundupdb.py. -# The change note is now generated when the message is created. -# -# Revision 1.39 2001/12/02 05:06:16 richard -# . We now use weakrefs in the Classes to keep the database reference, so -# the close() method on the database is no longer needed. -# I bumped the minimum python requirement up to 2.1 accordingly. -# . #487480 ] roundup-server -# . #487476 ] INSTALL.txt -# -# I also cleaned up the change message / post-edit stuff in the cgi client. -# There's now a clearly marked "TODO: append the change note" where I believe -# the change note should be added there. The "changes" list will obviously -# have to be modified to be a dict of the changes, or somesuch. -# -# More testing needed. -# -# Revision 1.38 2001/12/01 07:17:50 richard -# . We now have basic transaction support! Information is only written to -# the database when the commit() method is called. Only the anydbm -# backend is modified in this way - neither of the bsddb backends have been. -# The mail, admin and cgi interfaces all use commit (except the admin tool -# doesn't have a commit command, so interactive users can't commit...) -# . Fixed login/registration forwarding the user to the right page (or not, -# on a failure) -# -# Revision 1.37 2001/11/28 21:55:35 richard -# . login_action and newuser_action return values were being ignored -# . Woohoo! Found that bloody re-login bug that was killing the mail -# gateway. -# (also a minor cleanup in hyperdb) -# -# Revision 1.36 2001/11/26 22:55:56 richard -# Feature: -# . Added INSTANCE_NAME to configuration - used in web and email to identify -# the instance. -# . Added EMAIL_SIGNATURE_POSITION to indicate where to place the roundup -# signature info in e-mails. -# . Some more flexibility in the mail gateway and more error handling. -# . Login now takes you to the page you back to the were denied access to. -# -# Fixed: -# . Lots of bugs, thanks Roché and others on the devel mailing list! -# -# Revision 1.35 2001/11/22 15:46:42 jhermann -# Added module docstrings to all modules. -# -# Revision 1.34 2001/11/15 10:24:27 richard -# handle the case where there is no file attached -# -# Revision 1.33 2001/11/13 21:44:44 richard -# . re-open the database as the author in mail handling -# -# Revision 1.32 2001/11/12 22:04:29 richard -# oops, left debug in there -# -# Revision 1.31 2001/11/12 22:01:06 richard -# Fixed issues with nosy reaction and author copies. -# -# Revision 1.30 2001/11/09 22:33:28 richard -# More error handling fixes. -# -# Revision 1.29 2001/11/07 05:29:26 richard -# Modified roundup-mailgw so it can read e-mails from a local mail spool -# file. Truncates the spool file after parsing. -# Fixed a couple of small bugs introduced in roundup.mailgw when I started -# the popgw. -# -# Revision 1.28 2001/11/01 22:04:37 richard -# Started work on supporting a pop3-fetching server -# Fixed bugs: -# . bug #477104 ] HTML tag error in roundup-server -# . bug #477107 ] HTTP header problem -# -# Revision 1.27 2001/10/30 11:26:10 richard -# Case-insensitive match for ISSUE_TRACKER_EMAIL in address in e-mail. -# -# Revision 1.26 2001/10/30 00:54:45 richard -# Features: -# . #467129 ] Lossage when username=e-mail-address -# . #473123 ] Change message generation for author -# . MailGW now moves 'resolved' to 'chatting' on receiving e-mail for an issue. -# -# Revision 1.25 2001/10/28 23:22:28 richard -# fixed bug #474749 ] Indentations lost -# -# Revision 1.24 2001/10/23 22:57:52 richard -# Fix unread->chatting auto transition, thanks Roch'e -# -# Revision 1.23 2001/10/21 04:00:20 richard -# MailGW now moves 'unread' to 'chatting' on receiving e-mail for an issue. -# -# Revision 1.22 2001/10/21 03:35:13 richard -# bug #473125: Paragraph in e-mails -# -# Revision 1.21 2001/10/21 00:53:42 richard -# bug #473130: Nosy list not set correctly -# -# Revision 1.20 2001/10/17 23:13:19 richard -# Did a fair bit of work on the admin tool. Now has an extra command "table" -# which displays node information in a tabular format. Also fixed import and -# export so they work. Removed freshen. -# Fixed quopri usage in mailgw from bug reports. -# -# Revision 1.19 2001/10/11 23:43:04 richard -# Implemented the comma-separated printing option in the admin tool. -# Fixed a typo (more of a vim-o actually :) in mailgw. -# -# Revision 1.18 2001/10/11 06:38:57 richard -# Initial cut at trying to handle people responding to CC'ed messages that -# create an issue. -# -# Revision 1.17 2001/10/09 07:25:59 richard -# Added the Password property type. See "pydoc roundup.password" for -# implementation details. Have updated some of the documentation too. -# -# Revision 1.16 2001/10/05 02:23:24 richard -# . roundup-admin create now prompts for property info if none is supplied -# on the command-line. -# . hyperdb Class getprops() method may now return only the mutable -# properties. -# . Login now uses cookies, which makes it a whole lot more flexible. We can -# now support anonymous user access (read-only, unless there's an -# "anonymous" user, in which case write access is permitted). Login -# handling has been moved into cgi_client.Client.main() -# . The "extended" schema is now the default in roundup init. -# . The schemas have had their page headings modified to cope with the new -# login handling. Existing installations should copy the interfaces.py -# file from the roundup lib directory to their instance home. -# . Incorrectly had a Bizar Software copyright on the cgitb.py module from -# Ping - has been removed. -# . Fixed a whole bunch of places in the CGI interface where we should have -# been returning Not Found instead of throwing an exception. -# . Fixed a deviation from the spec: trying to modify the 'id' property of -# an item now throws an exception. -# -# Revision 1.15 2001/08/30 06:01:17 richard -# Fixed missing import in mailgw :( -# -# Revision 1.14 2001/08/13 23:02:54 richard -# Make the mail parser a little more robust. -# -# Revision 1.13 2001/08/12 06:32:36 richard -# using isinstance(blah, Foo) now instead of isFooType -# -# Revision 1.12 2001/08/08 01:27:00 richard -# Added better error handling to mailgw. -# -# Revision 1.11 2001/08/08 00:08:03 richard -# oops ;) -# -# Revision 1.10 2001/08/07 00:24:42 richard -# stupid typo -# -# Revision 1.9 2001/08/07 00:15:51 richard -# Added the copyright/license notice to (nearly) all files at request of -# Bizar Software. -# -# Revision 1.8 2001/08/05 07:06:07 richard -# removed some print statements -# -# Revision 1.7 2001/08/03 07:18:22 richard -# Implemented correct mail splitting (was taking a shortcut). Added unit -# tests. Also snips signatures now too. -# -# Revision 1.6 2001/08/01 04:24:21 richard -# mailgw was assuming certain properties existed on the issues being created. -# -# Revision 1.5 2001/07/29 07:01:39 richard -# Added vim command to all source so that we don't get no steenkin' tabs :) -# -# Revision 1.4 2001/07/28 06:43:02 richard -# Multipart message class has the getPart method now. Added some tests for it. -# -# Revision 1.3 2001/07/28 00:34:34 richard -# Fixed some non-string node ids. -# -# Revision 1.2 2001/07/22 12:09:32 richard -# Final commit of Grande Splite -# -# # vim: set filetype=python ts=4 sw=4 et si