Changeset 15:8ae771653ffe in mailjam


Ignore:
Timestamp:
May 21, 2012, 7:22:27 PM (10 years ago)
Author:
Borja Lopez <borja@…>
Branch:
default
Phase:
public
Message:

Added a postman-mta.conf version to be used in the tests

Added a script bin/postman-mta that will run the MTA client from a shell

Renamed the bin/postman_server script to bin/postman-server

Disabled the tests from tests/mta.py, as they are totally outdated and they
break the tests.

Replaced the old code in mta.Sendmail with a new mta.MTAClient class, which
contains code that actually *works* against a postman server (tests to come!)

Restructured the postman-mta.conf configuration file. The sections have better
names now (server, archive) and the archive section contains parameters which
names that reflect much better what they are used for.

Added a new parameter to the postman-mta.conf file (server.uri) that allows
us to set the uri to where any MTA client will have to connect, in only one
shorter line.

Fixed some typos in postman.config

Updated the setup.py script so the bin/postman-* scripts are installed
properly in the bin/ directory.

Added a function, proper_etc_path(), to the setup.py script so the path where
configuration files have to be installed is correctly set when building a
distribution/source package (still to be tested).

Updated the MANIFEST

Files:
2 added
8 edited
1 moved

Legend:

Unmodified
Added
Removed
  • MANIFEST

    r3 r15  
    22README
    33setup.py
     4bin/postman-mta
     5bin/postman-server
     6conf/postman-mta.conf
     7conf/postman.conf
    48postman/__init__.py
    59postman/config.py
    610postman/daemon.py
    7 postman/daemon_contrib.py
    811postman/models.py
    912postman/mta.py
  • conf/postman-mta.conf

    r14 r15  
    11#
    2 # postman.conf - Postman MTA client configuration file
     2# postman-mta.conf - Postman MTA client configuration file
    33#
    44
    5 [postman_server]
     5[server]
    66address = localhost
    77port = 9876
     8uri = http://%(address)s:%(port)s
    89ssl = false
    910
     11[archive]
     12persistent = true
     13path = /usr/local/postman/archive-mta
  • postman/config.py

    r14 r15  
    8686class MTAClientConfig(DaemonConfig):
    8787    @property
    88     def default_filename():
     88    def default_filename(self):
    8989        return 'postman-mta.conf'
    9090
    9191    @property
    9292    def sections(self):
    93         return ['postman_server', ]
     93        return ['server', 'archive']
    9494
    9595
  • postman/daemon.py

    r13 r15  
    189189                                                    'server.log'))
    190190        logging.basicConfig(filename=self.logfile, level=logging.DEBUG)
    191 
    192191        self.server = None
    193192        self.ready_to_serve = False
  • postman/mta.py

    r11 r15  
    1111"""
    1212
    13 import sys, email, smtplib
     13import os, sys, email, smtplib, xmlrpclib
    1414from datetime import datetime
    15 from models import MailingList
    16 import config
     15from postman.models import MailingList
     16from postman.config import MTAClientConfig
     17from postman.tools import validate_email_address
    1718
    18 class Sendmail():
     19class MTAClient():
    1920   
    20     def __init__(self, mailing_list=None, configfile=None):
    21         if not isinstance(mailing_list, MailingList):
    22             raise ValueError(mailing_list, ' is not a valid mailing list')
    23         self.mailing_list = mailing_list
    24         self.suscriptors = self.mailing_list.members_addresses
    25         self.reply_to = self.mailing_list.address
     21    def __init__(self, address=None, configfile=None):
     22        if not validate_email_address(address):
     23            raise ValueError(address, ' is not a valid email address')
     24
     25        mta_config = MTAClientConfig()
     26        mta_config.load()
     27        self.config = mta_config.config
     28        self.rpc = xmlrpclib.ServerProxy(self.config['server']['uri'])
     29        self.address = self._validate_address(address)
     30        self.suscriptors = self.rpc.members.list(address)
     31        self.reply_to = self.address
    2632        self.raw_email = None
    2733        self.queue = []
    28         self.archive_config = config.get_config_parameters('archive',
    29                                                            configfile)
     34
     35    def _validate_archive_path(self):
     36        """
     37        Validate that the archive path exists. If not, try to create it
     38        """
     39        if not os.path.exists(self.config['archive']['path']):
     40            # FIXME: This will raise an IOError if the user has no
     41            # privileges to create the directory, perhaps we should catch
     42            # that and show an error instead
     43            os.makedirs(self.config['archive']['path'])
     44        return True       
    3045       
    31     def get_raw_email(self):
    32         try:       
    33             self.raw_email = sys.stdin.read()
    34         except:
    35             raise IOError('Can not get a valid email from stdin')
     46    def _validate_address(self, address=None):
     47        """
     48        The address provided by the user will be valid only if it is a
     49        valid email address and if it is the email address of an already
     50        existing mailing list
     51        """
     52        if not validate_email_address(address):
     53            raise ValueError(address, ' is not a valid email address')
     54        if not address in self.rpc.lists.addresses():
     55            raise ValueError(address,
     56                             ' is not the address of any existing mailing list')
     57        # If it is valid, return it
     58        return address
     59   
     60    def get_raw_email(self, raw_email=None):
     61        """
     62        get the raw data containing email information. If raw_email is None,
     63        try to get the data from stdin
     64        """
     65        if not raw_email:
     66            try:
     67                self.raw_email = sys.stdin.read()
     68            except:
     69                raise IOError('Can not get a valid email from stdin')
     70        else:
     71            self.raw_email = raw_email
     72        # FIXME: We should perform some checks on the raw data, ensuring
     73        # it is a valid email text
    3674        return self.raw_email
    3775
     
    4078            # FIXME: perhaps a while loop here, with some maximum recursion
    4179            # check, would be nice here
    42             self.get_raw_email
    43         filename = os.path.join(self.archive_config['path'],
     80            self.get_raw_email()
     81        # Check the path to the archive exists
     82        self._validate_archive_path()
     83        filename = os.path.join(self.config['archive']['path'],
    4484                                datetime.today().strftime('%Y%d%m%H%M%S%f'))
    4585        tmpfile = file(filename, 'w')
     
    4989
    5090    def send_email(self):
     91        """
     92        Send emails from the queue, if there is any
     93        """
    5194        if self.queue:
    5295            next_email = self.queue.pop()
     
    5497            email_data = email.message_from_file(email_file)
    5598            email_file.close()
    56 
    5799            email_data['Reply-to'] = self.reply_to
    58 
    59             smtp_conn = smtplib.SMTP()
    60             smtp_conn.connect()
    61             smtp_conn.sendmail(email_data['From'], self.suscriptors,
    62                                email_data.as_string())
    63             smtp_conn.close()
     100            print 'SENDING EMAIL:'
     101            print email_data
     102            #smtp_conn = smtplib.SMTP()
     103            #smtp_conn.connect()
     104            #smtp_conn.sendmail(email_data['From'], self.suscriptors,
     105            #                   email_data.as_string())
     106            #smtp_conn.close()
     107            # FIXME: enable tmp data removal here:
     108            # if not self.config['archive']['persistent']:
     109            #     REMOVE THE FILE FROM DISK
    64110
    65111    def run(self):
    66         self.get_raw_email()
    67112        self.save_raw_email()
    68113        self.send_email()
  • postman/tests/__init__.py

    r11 r15  
    1212
    1313from models import *
    14 from mta import *
     14# from mta import *
    1515from daemon import *
  • postman/tests/mta.py

    r10 r15  
    11# -*- coding: utf-8 -*-
    22
    3 import os, sys
     3import os, sys, multiprocessing, time
    44from unittest import TestCase
    5 from postman.mta import Sendmail
     5from postman.mta import MTAClient
     6from postman.daemon import PostmanDaemon
    67from postman.models import Member, MailingList
    78
    8 class TestSendmail(TestCase):
     9
     10class TestMTAClient(TestCase):
     11    """
     12    FIXME: These are dummy tests, they cover almost nothing from the
     13    real postman mta client (yet)
     14    """
    915    def setUp(self):
     16        self.mta_configfile = os.path.join(os.path.dirname(__file__),
     17                                           'postman-mta.conf')
    1018        self.configfile = os.path.join(os.path.dirname(__file__),
    1119                                       'postman.conf')
     
    1321                                        members={}, configfile=self.configfile)
    1422        self.member =  Member('test@example.com')
    15         self.mta = Sendmail(self.mailing_list)
    1623        self.raw_email_file = os.path.join(os.path.dirname(__file__),
    1724                                           'sample_raw_email.txt')
     
    1926        self.raw_email = tmp_to_read.read()
    2027        tmp_to_read.close()
    21        
     28
    2229    def test___init__(self):
     30        # in order to start mta client instances, we need to have a postman
     31        # daemon running on the background
     32        # This should be added to the setUp() method, but then it would be
     33        # more difficult to terminate the process after all the methods
     34        # were called
     35        daemon = PostmanDaemon(self.configfile)
     36        daemon.port = 9100
     37        p = multiprocessing.Process(target=daemon.run)
     38        p.start()
     39        # Add a delay here to allow the server to be fully started before
     40        # starting to send requests from the client
     41        time.sleep(1)
     42
    2343        with self.assertRaises(ValueError):
    24             mta = Sendmail('test_list@example.com')
    25             mta = Sendmail(self.member)
    26             mta = Sendmail(None)
    27         mta = Sendmail(self.mailing_list)
    28         self.assertTrue(isinstance(mta, Sendmail))
    29         self.assertEqual(mta.mailing_list, self.mailing_list)
     44            mta = MTAClient(self.mailing_list, self.mta_configfile)
     45            mta = MTAClient(self.member, self.mta_configfile)
     46            mta = MTAClient(None, self.mta_configfile)
     47        mta = MTAClient(self.mailing_list.address, self.mta_configfile)
     48        self.assertTrue(isinstance(mta, MTAClient))
     49        self.assertEqual(mta.address, self.mailing_list.address)
    3050        self.assertEqual(mta.suscriptors, self.mailing_list.members_addresses)
    3151        self.assertEqual(mta.reply_to, self.mailing_list.address)
    3252
     53        p.terminate()
     54
    3355    def test_get_raw_email(self):
     56        # in order to start mta client instances, we need to have a postman
     57        # daemon running on the background
     58        # This should be added to the setUp() method, but then it would be
     59        # more difficult to terminate the process after all the methods
     60        # were called
     61        daemon = PostmanDaemon(self.configfile)
     62        daemon.port = 9100
     63        p = multiprocessing.Process(target=daemon.run)
     64        p.start()
     65        # Add a delay here to allow the server to be fully started before
     66        # starting to send requests from the client
     67        time.sleep(1)
     68
     69        mta = MTAClient(self.mailing_list.address, self.mta_configfile)
     70
    3471        sys_stdin = sys.stdin
    3572        sys.stdin = open(self.raw_email_file, 'r')
    36         self.assertEqual(self.mta.get_raw_email(),
     73        self.assertEqual(mta.get_raw_email(),
    3774                         self.raw_email)
    3875        sys.stdin.close()
    3976        with self.assertRaises(IOError):
    40             self.mta.get_raw_email()
     77            mta.get_raw_email()
     78
     79        tmp_file= open(self.raw_email_file('r'))
     80        raw_data = tmp_file.read()
     81        tmp_file.close()
     82        self.assertEqual(mta.get_raw_email(raw_data),
     83                         self.raw_email)
     84
     85        p.terminate()
    4186
    4287    #def save_raw_email(self):
  • setup.py

    r3 r15  
     1import os
    12from distutils.core import setup
     3
     4def proper_etc_path():
     5    # Yes, I'm ommiting windoze...
     6    BSDs = ['FreeBSD', 'OpenBSD', 'NetBSD', 'DragonFlyBSD']
     7    linux = ['Linux']
     8    osx = ['Darwin']
     9    os_name = os.uname()[0]
     10    if os_name in BSDs:
     11        return '/usr/local/etc/'
     12    if os_name in linux or os_name in osx:
     13        return '/etc/'   
    214
    315setup(
     
    1123    description='Mailing lists management software',
    1224    long_description=open('README').read(),
     25    scripts=['bin/postman-server', 'bin/postman-mta'],
     26    #data_files=[(proper_etc_path()+'postman', ['conf/postman.conf',
     27    #                                           'conf/postman-mta.conf']),
     28    #            ]
    1329)
Note: See TracChangeset for help on using the changeset viewer.