Code

set title on issues even when the email body is empty (sf bug 727430)
[roundup.git] / roundup / mailgw.py
index 53dc0417fbd433ffececfa1765b58f530c28ab07..ca9ed5a41aafd5db8af55116dbb737369ef9933e 100644 (file)
@@ -73,15 +73,14 @@ 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.111 2003-02-27 05:43:01 richard Exp $
+$Id: mailgw.py,v 1.121 2003-04-27 02:16:46 richard Exp $
 '''
 
 import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
 import time, random, sys
 import traceback, MimeWriter, rfc822
-import hyperdb, date, password
 
-import rfc2822
+from roundup import hyperdb, date, password, rfc2822
 
 SENDMAILDEBUG = os.environ.get('SENDMAILDEBUG', '')
 
@@ -133,6 +132,35 @@ def getparam(str, param):
                 return rfc822.unquote(f[i+1:].strip())
     return None
 
+def openSMTPConnection(config):
+    ''' Open an SMTP connection to the mailhost specified in the config
+    '''
+    smtp = smtplib.SMTP(config.MAILHOST)
+
+    # use TLS?
+    use_tls = getattr(config, 'MAILHOST_TLS', 'no')
+    if use_tls == 'yes':
+        # do we have key files too?
+        keyfile = getattr(config, 'MAILHOST_TLS_KEYFILE', '')
+        if keyfile:
+            certfile = getattr(config, 'MAILHOST_TLS_CERTFILE', '')
+            if certfile:
+                args = (keyfile, certfile)
+            else:
+                args = (keyfile, )
+        else:
+            args = ()
+        # start the TLS
+        smtp.starttls(*args)
+
+    # ok, now do we also need to log in?
+    mailuser = getattr(config, 'MAILUSER', None)
+    if mailuser:
+        smtp.login(*config.MAILUSER)
+
+    # that's it, a fully-configured SMTP connection ready to go
+    return smtp
+
 class Message(mimetools.Message):
     ''' subclass mimetools.Message so we can retrieve the parts of the
         message...
@@ -216,7 +244,12 @@ class MailGW:
         fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
         return 0
 
-    def do_pop(self, server, user='', password=''):
+    def do_apop(self, server, user='', password=''):
+        ''' Do authentication POP
+        '''
+        self.do_pop(server, user, password, apop=1)
+
+    def do_pop(self, server, user='', password='', apop=0):
         '''Read a series of messages from the specified POP server.
         '''
         import getpass, poplib, socket
@@ -236,8 +269,11 @@ class MailGW:
         except socket.error, message:
             print "POP server error:", message
             return 1
-        server.user(user)
-        server.pass_(password)
+        if apop:
+            server.apop(user, password)
+        else:
+            server.user(user)
+            server.pass_(password)
         numMessages = len(server.list()[1])
         for i in range(1, numMessages+1):
             # retr: returns 
@@ -338,7 +374,7 @@ class MailGW:
                     m.getvalue()))
         else:
             try:
-                smtp = smtplib.SMTP(self.instance.config.MAILHOST)
+                smtp = openSMTPConnection(self.instance.config)
                 smtp.sendmail(self.instance.config.ADMIN_EMAIL, sendto,
                     m.getvalue())
             except socket.error, value:
@@ -535,7 +571,6 @@ does not exist.
 Subject was: "%s"
 '''%(nodeid, subject)
 
-
         # Handle the arguments specified by the email gateway command line.
         # We do this by looping over the list of self.arguments looking for
         # a -C to tell us what class then the -S setting string.
@@ -643,11 +678,6 @@ Unknown address: %s
             if recipient:
                 recipients.append(recipient)
 
-        #
-        # XXX extract the args NOT USED WHY -- rouilj
-        #
-        subject_args = m.group('args')
-
         #
         # handle the subject argument list
         #
@@ -667,6 +697,11 @@ There were problems handling your subject line argument list:
 Subject was: "%s"
 '''%(errors, subject)
 
+
+        # set the issue title to the subject
+        if properties.has_key('title') and not issue_props.has_key('title'):
+            issue_props['title'] = title.strip()
+
         #
         # handle message-id and in-reply-to
         #
@@ -799,6 +834,7 @@ not find a text/plain part to use.
         # parse the body of the message, stripping out bits as appropriate
         summary, content = parseContent(content, keep_citations, 
             keep_body)
+        content = content.strip()
 
         # 
         # handle the attachments
@@ -809,6 +845,16 @@ not find a text/plain part to use.
                 name = "unnamed"
             files.append(self.db.file.create(type=mime_type, name=name,
                 content=data, **file_props))
+        # attach the files to the issue
+        if nodeid:
+            # extend the existing files list
+            fileprop = cl.get(nodeid, 'files')
+            fileprop.extend(files)
+            props['files'] = fileprop
+        else:
+            # pre-load the files list
+            props['files'] = files
+
 
         # 
         # create the message if there's a message body (content)
@@ -829,10 +875,6 @@ not find a text/plain part to use.
                 # pre-load the messages list
                 props['messages'] = [message_id]
 
-                # set the title to the subject
-                if properties.has_key('title') and not props.has_key('title'):
-                    props['title'] = title
-
         #
         # perform the node change / create
         #
@@ -1016,8 +1058,26 @@ def uidFromAddress(db, address, create=1, **user_props):
 
     # couldn't match address or username, so create a new user
     if create:
-        return db.user.create(username=address, address=address,
+        # generate a username
+        if '@' in address:
+            username = address.split('@')[0]
+        else:
+            username = address
+        trying = username
+        n = 0
+        while 1:
+            try:
+                # does this username exist already?
+                db.user.lookup(trying)
+            except KeyError:
+                break
+            n += 1
+            trying = username + str(n)
+
+        # create!
+        return db.user.create(username=trying, address=address,
             realname=realname, roles=db.config.NEW_EMAIL_USER_ROLES,
+            password=password.Password(password.generatePassword()),
             **user_props)
     else:
         return 0