Snapshot release 20030306-1134

Charles Cazabon <[email protected]> 6 Mar 2003 17:35:00 -0000
Newsgroups gmane.mail.bikini.devel
Message-ID <[email protected]>

Snapshot 20030306-1134 released and uploaded.  From the CHANGELOG:

2003-03-06

  Move configuration defaults from constants.py to config.py.
  Add the sendmessage submodule.  This provides methods for making SEND work
    with qmail-queue as before (the default), new-inject (for cleaning up
    headers before sending, /usr/sbin/sendmail, pseudo-sendmail, or the special
    "null" method, which forbids the server from sending mail.  Change
    defaults['sendmethod'] in config.py to configure.  If you have another
    method to add, please do so and send me a diff.


Diff follows.

diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/classes.py bikini-20030306-1134/bikini/classes.py
--- bikini-20030306-0908/bikini/classes.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/classes.py	Thu Mar  6 11:34:36 2003
@@ -4,6 +4,7 @@
 import os
 import signal
 
+import config
 from errors import *
 from constants import *
 from utilities import *
@@ -105,7 +106,7 @@
         self.respond ('success', 'ok')
 
     ###################################
-    def timeout (self, timeout=defaults['timeout']):
+    def timeout (self, timeout=config.defaults['timeout']):
         '''Start a timer.
         '''
         log (TRACE, 'timeout %i\n' % timeout)
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/config.py bikini-20030306-1134/bikini/config.py
--- bikini-20030306-0908/bikini/config.py	Wed Dec 31 18:00:00 1969
+++ bikini-20030306-1134/bikini/config.py	Thu Mar  6 11:34:36 2003
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import os
+import string
+import sendmessage
+
+from constants import loglevels
+
+defaults = {
+    'loglevel'      : loglevels[os.environ.get ('LOGLEVEL', 'DEBUG')],
+    'timeout'       : int (os.environ.get ('TIMEOUT', 180)),    # three minutes
+    'newdirmode'    : string.atoi (os.environ.get ('TIMEOUT', '0700'), 0),
+    'maxauthtries'  : int (os.environ.get ('TIMEOUT', 5)),
+    'serverpath'    : '/usr/lib/python2.2/site-packages/bikini/server.py',
+    'sendmethod'    : sendmessage.qmailqueue,
+
+    'anonymous-mailstore' : '/tmp/bikini-mail/',
+    'anonymous-user' : 'nobody',
+}
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/constants.py bikini-20030306-1134/bikini/constants.py
--- bikini-20030306-0908/bikini/constants.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/constants.py	Thu Mar  6 11:34:36 2003
@@ -1,7 +1,6 @@
 #!/usr/bin/python
 
 import os
-import string
 
 # Components of stack trace (indices to tuple)
 FILENAME, LINENO, FUNCNAME = 0, 1, 2        #SOURCELINE = 3 ; not used
@@ -35,14 +34,3 @@
     'AUTH=LOGIN',               # 'auth login' support
     'MESSAGE-SIZE %d' % os.environ.get ('DATABYTES', 0),  # max message size for put
 )
-
-defaults = {
-    'loglevel'      : loglevels[os.environ.get ('LOGLEVEL', 'DEBUG')],
-    'timeout'       : int (os.environ.get ('TIMEOUT', 180)),    # three minutes
-    'newdirmode'    : string.atoi (os.environ.get ('TIMEOUT', '0700'), 0),
-    'maxauthtries'  : int (os.environ.get ('TIMEOUT', 5)),
-    'serverpath'    : '/usr/lib/python2.2/site-packages/bikini/server.py',
-
-    'anonymous-mailstore' : '/tmp/bikini-mail/',
-    'anonymous-user' : 'nobody',
-}
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/sasl.py bikini-20030306-1134/bikini/sasl.py
--- bikini-20030306-0908/bikini/sasl.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/sasl.py	Thu Mar  6 11:34:36 2003
@@ -3,7 +3,7 @@
 import sys
 
 from errors import *
-from constants import defaults
+import config
 
 class Mechanism:
     def checkpassword(self):
@@ -55,11 +55,11 @@
 
 class ANONYMOUS(Mechanism):
     def __init__(self, unused):
-        self.username = defaults['anonymous-user']
+        self.username = config.defaults['anonymous-user']
         self.password = None
         self.pwchecker = self
         self.checkpassword ()
-        self.dir = defaults['anonymous-mailstore']
+        self.dir = config.defaults['anonymous-mailstore']
     def check (self, *unused):
         return 0
 
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/sendmessage.py bikini-20030306-1134/bikini/sendmessage.py
--- bikini-20030306-0908/bikini/sendmessage.py	Wed Dec 31 18:00:00 1969
+++ bikini-20030306-1134/bikini/sendmessage.py	Thu Mar  6 11:34:36 2003
@@ -0,0 +1,161 @@
+#!/usr/bin/python2.2
+
+import os
+import time
+
+from errors import *
+from constants import *
+from utilities import *
+
+READ, WRITE = 0, 1
+
+#
+# Functions to send messages with
+#
+
+#######################################
+def null (path, envelope):
+    '''Fail to queue a message.  Use this to prohibit the bikini server from
+    sending mail.
+    '''
+    log (TRACE, 'path %s envelope %s\n' % (path, envelope))
+    raise PermanentCommandError, 'SEND prohibited'
+
+#######################################
+def qmailqueue (path, envelope):
+    '''Queue message using qmail-queue.  Sends message exactly as is.
+    '''
+    log (TRACE, 'path %s envelope %s\n' % (path, envelope))
+    cmd = '/var/qmail/bin/qmail-queue'
+    try:
+        messagepipe = os.pipe ()
+        envelopepipe = os.pipe ()
+        child_pid = os.fork ()
+        if child_pid == 0:
+            # child
+            os.close (messagepipe[WRITE])
+            os.close (envelopepipe[WRITE])
+            os.close (0)
+            os.close (1)
+            os.close (2)
+            os.dup2 (messagepipe[READ], 0)
+            os.dup2 (envelopepipe[READ], 1)
+            os.execl (cmd, cmd)
+            raise TemporaryCommandError, 'exec failed'
+        # Parent
+        log (DEBUG, 'forked pid %i\n' % child_pid)
+        os.close (messagepipe[READ])
+        os.close (envelopepipe[READ])
+        # Write message
+        os.write (messagepipe[WRITE], open (path, 'rb').read ())
+        os.close (messagepipe[WRITE])
+        # Write envelope
+        parts = envelope.split ('\0')
+        os.write (envelopepipe[WRITE], 'F%s\0' % parts[0])
+        for recip in parts[1:-1]:
+            os.write (envelopepipe[WRITE], 'T%s\0' % recip)
+        os.write (envelopepipe[WRITE], '\0')
+        os.close (envelopepipe[WRITE])
+        tries = 0
+        while tries < 10:
+            pid, rc = os.waitpid (child_pid, os.WNOHANG)
+            log (TRACE, 'os.waitpid returned %i, %s\n' % (pid, rc))
+            if pid == child_pid and os.WIFEXITED (rc):
+                if os.WEXITSTATUS (rc) == 0:
+                    log (TRACE, 'message queued\n')
+                    return 'this is a receipt, should be a queue id or similar' # ???
+                log (TRACE, 'message not queued (exited %i)\n' % os.WEXITSTATUS (rc))
+                raise TemporaryCommandError, '%s exited %i' % (cmd, os.WEXITSTATUS (rc))
+            time.sleep (1)
+        log (ERROR, 'child never exited?\n')
+        raise TemporaryCommandError, '%s never exited' % cmd
+
+    except UnhandledException, txt:
+        raise TemporaryCommandError, 'failure queuing message: %s' % txt
+
+#######################################
+class sendmessageBase:
+    '''Queue message using a command which takes the envelope as arguments and
+    reads stdin for the message content, returning only an exit code.
+    '''
+    ###################################
+    def __init__ (self, path, envelope):
+        log (TRACE, 'path %s envelope %s\n' % (path, envelope))
+        parts = envelope.split ('\0')
+        self.sender = parts[0]
+        self.recips = parts [1:-1]
+        self.setup ()
+        try:
+            messagepipe = os.pipe ()
+            child_pid = os.fork ()
+            if child_pid == 0:
+                # child
+                os.close (messagepipe[WRITE])
+                os.close (0)
+                os.dup2 (messagepipe[READ], 0)
+                os.execv (self.cmd, self.args)
+                raise TemporaryCommandError, 'exec failed'
+            # Parent
+            log (DEBUG, 'forked pid %i\n' % child_pid)
+            os.close (messagepipe[READ])
+            # Write message
+            os.write (messagepipe[WRITE], open (path, 'rb').read ())
+            os.close (messagepipe[WRITE])
+            tries = 0
+            while tries < 10:
+                pid, rc = os.waitpid (child_pid, os.WNOHANG)
+                log (TRACE, 'os.waitpid returned %i, %s\n' % (pid, rc))
+                if pid == child_pid and os.WIFEXITED (rc):
+                    if os.WEXITSTATUS (rc) == 0:
+                        log (TRACE, 'message queued\n')
+                        self.rc = 'this is a receipt, should be a queue id or similar' # ???
+                        return
+                    log (TRACE, 'message not queued (exited %i)\n' % os.WEXITSTATUS (rc))
+                    raise TemporaryCommandError, '%s exited %i' % (self.cmd, os.WEXITSTATUS (rc))
+                time.sleep (1)
+            log (ERROR, 'child never exited?\n')
+            raise TemporaryCommandError, '%s never exited' % self.cmd
+
+        except UnhandledException, txt:
+            raise TemporaryCommandError, 'failure queuing message: %s' % txt
+
+    ###################################
+    def __str__ (self):
+        return self.rc
+
+#######################################
+class newinject (sendmessageBase):
+    '''Queue message using new-inject.  new-inject cleans up the header before
+    queuing the mail.  new-inject is part of the mess822 package.
+    '''
+    ###################################
+    def setup (self):
+        log (TRACE)
+        self.cmd = '/usr/bin/new-inject'
+        self.args = [self.cmd, '-a', '-f', self.sender, '--']
+        self.args.extend (self.recips)
+
+#######################################
+class sendmail (sendmessageBase):
+    '''Queue message using a "real" sendmail interface.
+    '''
+    ###################################
+    def setup (self):
+        log (TRACE)
+        self.cmd = '/usr/sbin/sendmail'
+        self.args = [self.cmd, '-U', '-i', '-O', 'DeliveryMode=q', '-O', 'ErrorMode=q', '-f', self.sender, '--']
+        self.args.extend (self.recips)
+
+#######################################
+class simple_sendmail (sendmessageBase):
+    '''Queue message using a sendmail-type interface that doesn't understand
+    some options.  Use this if the default sendmail interface doesn't work
+    for you and there isn't a better, more MTA-specific interface for your
+    MTA.
+    '''
+    ###################################
+    def setup (self):
+        log (TRACE)
+        self.cmd = '/usr/sbin/sendmail'
+        self.args = [self.cmd, '-i', '-f', self.sender, '--']
+        self.args.extend (self.recips)
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/server.py bikini-20030306-1134/bikini/server.py
--- bikini-20030306-0908/bikini/server.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/server.py	Thu Mar  6 11:34:36 2003
@@ -49,6 +49,7 @@
 from classes import *
 from utilities import *
 from constants import *
+import config
 
 #
 # Globals
@@ -421,7 +422,7 @@
                 or len (parts) < 3):                    # Not enough parts
                 raise bSyntaxError, 'bad envelope format'
 
-            receipt = deliver_message (f, envelope)
+            receipt = config.defaults['sendmethod'] (f, envelope)
 
         except KeyError, err:
             raise PermanentCommandError, 'no such message %s' % path
@@ -446,7 +447,7 @@
             except PermanentCommandError:
                 raise PermanentCommandError, 'directory %s already exists' % path
         try:
-            os.mkdir (path, defaults['newdirmode'])
+            os.mkdir (path, config.defaults['newdirmode'])
             self.dirs.append (path + '/')
             self.dirs.sort ()
         except OSError, err:
@@ -486,10 +487,10 @@
             except PermanentCommandError:
                 raise PermanentCommandError, 'directory %s already exists' % path
         try:
-            os.mkdir (path, defaults['newdirmode'])
-            os.mkdir (os.path.join (path, 'cur'), defaults['newdirmode'])
-            os.mkdir (os.path.join (path, 'new'), defaults['newdirmode'])
-            os.mkdir (os.path.join (path, 'tmp'), defaults['newdirmode'])
+            os.mkdir (path, config.defaults['newdirmode'])
+            os.mkdir (os.path.join (path, 'cur'), config.defaults['newdirmode'])
+            os.mkdir (os.path.join (path, 'new'), config.defaults['newdirmode'])
+            os.mkdir (os.path.join (path, 'tmp'), config.defaults['newdirmode'])
             self.dirs.append (path)
             self.dirs.sort ()
         except OSError, err:
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/serverauth.py bikini-20030306-1134/bikini/serverauth.py
--- bikini-20030306-0908/bikini/serverauth.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/serverauth.py	Thu Mar  6 11:34:36 2003
@@ -50,6 +50,7 @@
 from constants import *
 import sasl
 import pwcheck
+import config
 
 #
 # Globals
@@ -87,7 +88,7 @@
         need_auth = 1
         while need_auth:
             try:
-                if need_auth > defaults['maxauthtries']:
+                if need_auth > config.defaults['maxauthtries']:
                     raise SessionTimeout, 'max tries exceeded'
                 self.timeout ()
                 cmd, args = self.getcmd ()
@@ -134,7 +135,7 @@
         os.setgid (auth.gid)
         os.setuid (auth.uid)
         os.chdir (auth.dir)
-        os.execl (defaults['serverpath'], defaults['serverpath'], './Mail', self.inbox)
+        os.execl (config.defaults['serverpath'], config.defaults['serverpath'], './Mail', self.inbox)
         raise TemporaryCommandError, 'execl failed'
 
 #######################################
diff -urN --exclude=bikini.html --exclude=bikini.txt --exclude=CHANGELOG bikini-20030306-0908/bikini/utilities.py bikini-20030306-1134/bikini/utilities.py
--- bikini-20030306-0908/bikini/utilities.py	Thu Mar  6 09:08:42 2003
+++ bikini-20030306-1134/bikini/utilities.py	Thu Mar  6 11:34:36 2003
@@ -10,6 +10,7 @@
 
 from errors import *
 from constants import *
+import config
 
 # For trace output
 newline = 1
@@ -35,59 +36,6 @@
         and os.path.isdir (os.path.join (path, 'new')))
 
 #######################################
-def deliver_message (path, envelope):
-    '''Ask the local MTA to deliver the message.
-    '''
-    log (TRACE, 'path %s envelope %s\n' % (path, envelope))
-    mail_command = '/var/qmail/bin/qmail-queue'
-    READ, WRITE = 0, 1
-    try:
-        messagepipe = os.pipe ()
-        envelopepipe = os.pipe ()
-        child_pid = os.fork ()
-        if child_pid == 0:
-            # child
-            os.close (messagepipe[WRITE])
-            os.close (envelopepipe[WRITE])
-            os.close (0)
-            os.close (1)
-            os.close (2)
-            os.dup2 (messagepipe[READ], 0)
-            os.dup2 (envelopepipe[READ], 1)
-            os.execl (mail_command, mail_command)
-            raise TemporaryCommandError, 'exec failed'
-        # Parent
-        log (DEBUG, 'forked pid %i\n' % child_pid)
-        os.close (messagepipe[READ])
-        os.close (envelopepipe[READ])
-        # Write message
-        os.write (messagepipe[WRITE], open (path, 'rb').read ())
-        os.close (messagepipe[WRITE])
-        # Write envelope
-        parts = envelope.split ('\0')
-        os.write (envelopepipe[WRITE], 'F%s\0' % parts[0])
-        for recip in parts[1:-1]:
-            os.write (envelopepipe[WRITE], 'T%s\0' % recip)
-        os.write (envelopepipe[WRITE], '\0')
-        os.close (envelopepipe[WRITE])
-        tries = 0
-        while tries < 10:
-            pid, rc = os.waitpid (child_pid, os.WNOHANG)
-            log (TRACE, 'os.waitpid returned %i, %s\n' % (pid, rc))
-            if pid == child_pid and os.WIFEXITED (rc):
-                if os.WEXITSTATUS (rc) == 0:
-                    log (TRACE, 'message queued\n')
-                    return 'this is a receipt, should be a queue id or similar' # ???
-                log (TRACE, 'message not queued (exited %i)\n' % os.WEXITSTATUS (rc))
-                raise TemporaryCommandError, '%s exited %i' % (mail_command, os.WEXITSTATUS (rc))
-            time.sleep (1)
-        log (ERROR, 'child never exited?\n')
-        raise TemporaryCommandError, '%s never exited' % mail_command
-
-    except UnhandledException, txt:
-        raise TemporaryCommandError, 'failure queuing message: %s' % txt
-
-#######################################
 def utcdate (timestamp):
     '''
     '''
@@ -205,7 +153,7 @@
             msg = arg
         elif type (arg) == IntType:
             level = arg
-    if level < defaults['loglevel']:
+    if level < config.defaults['loglevel']:
         return
     if level == TRACE:
         if not newline: