Index: MANIFEST
===================================================================
--- MANIFEST	(revision 17)
+++ MANIFEST	(revision 23)
@@ -2,7 +2,9 @@
 README
 setup.py
+bin/mailjam-cli
 bin/mailjam-mta
 bin/mailjam-server
 mailjam/__init__.py
+mailjam/cli.py
 mailjam/config.py
 mailjam/daemon.py
Index: bin/mailjam-cli
===================================================================
--- bin/mailjam-cli	(revision 23)
+++ bin/mailjam-cli	(revision 23)
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# Run the Mailjam CLI client
+
+import argparse
+from mailjam.cli import CLIClient
+
+msg = 'mailjam-cli: Mailing lists CLI client'
+parser = argparse.ArgumentParser(description=msg)
+parser.add_argument('-c', '--config', action='store', dest='config',
+                    help='Set the path to a valid mailjam cli client configuration file')
+parser.add_argument('-v', '--version', action='version',
+                    version='mailjam cli client 0.1.0')
+
+if __name__ == '__main__':
+    results = parser.parse_args()
+    if results.config:
+        cli_client = CLIClient(configfile=results.config)
+    else:
+        cli_client = CLIClient()
+    cli_client.cmdloop()
Index: bin/mailjam-mta
===================================================================
--- bin/mailjam-mta	(revision 21)
+++ bin/mailjam-mta	(revision 23)
@@ -3,5 +3,5 @@
 # Run the Mailjam MTA client
 
-import argparse, sys
+import argparse
 from mailjam.mta import MTAClient
 
Index: conf/mailjam-cli.conf
===================================================================
--- conf/mailjam-cli.conf	(revision 23)
+++ conf/mailjam-cli.conf	(revision 23)
@@ -0,0 +1,20 @@
+#
+# mailjam-cli.conf - Mailjam CLI client configuration file
+#
+
+[server]
+address = localhost
+port = 9876
+uri = http://%(address)s:%(port)s
+ssl = false
+# FIXME: This parameter here is a nonsense, but right now we have to pass
+# it as an argument when adding mailing lists (BUG!)
+configfile = etc/mailjam.conf
+
+[archive]
+enabled = true
+path = /usr/local/mailjam/archive-cli
+
+[history]
+enabled = true
+path = ~/.mailjam/cli/history
Index: mailjam/cli.py
===================================================================
--- mailjam/cli.py	(revision 23)
+++ mailjam/cli.py	(revision 23)
@@ -0,0 +1,135 @@
+# -*- coding: utf-8 -*-
+
+"""
+The mailjam project - cli.py
+
+This file is released under the BSD license, see LICENSE for
+more information.
+
+Francisco de Borja Lopez Rio - <borja@codigo23.net>
+Soluciones Informaticas Codigo23 S.L.U. - http://codigo23.net
+"""
+
+import os, cmd, xmlrpclib
+from mailjam.config import CLIClientConfig
+
+class CLIClient(cmd.Cmd):    
+
+    def __init__(self, configfile=None, *args, **kwargs):
+        cmd.Cmd.__init__(self)
+        cli_config = CLIClientConfig(configfile=configfile)
+        cli_config.load()
+        self.config = cli_config.config
+        self.rpc = xmlrpclib.ServerProxy(self.config['server']['uri'])
+        self.prompt = "mailjam-cli > "
+        self.intro = "Welcome to Mailjam's CLI client - type help for a list of commands"
+        self._history = []
+        
+    def do_history(self, line):
+        """
+        history
+        Show a list of recently executed commands
+
+        Alias: h
+        """
+        if not self._history:
+            print 'No history data available'
+        else:
+            for i in self._history:
+                print self._history.index(i), ' - ', i
+                
+    def do_h(self, line):
+        """
+        h
+        Alias for the history command. To learn more type:
+
+        help history
+        """
+        self.do_history(line)
+    
+    def do_add_mailing_list(self, line):
+        """
+        add_mailing_list [name] address
+        Add a new mailing list to the remote mailjam server. You have to
+        provide a valid email address for the mailing list to be added. If no
+        name is provided, the address itself will be used.
+
+        Alias: aml
+        """
+        if not line:
+            print 'usage: add_mailing_list [name] address'
+        else:
+            params = line.split(' ')
+            address = params.pop()
+            if not params:
+                name = address
+            else:
+                name = ' '.join(params)
+            info = {'name': name, 'address': address, 'members': {},
+                    'configfile': self.config['server']['configfile']}
+            result = self.rpc.lists.add(info)
+            print result
+
+    def do_aml(self, line):
+        """
+        aml [name] address
+        Alias for the add_mailing_list command. To learn more type:
+
+        help add_mailing_list
+        """
+        self.do_add_mailing_list(line)
+
+    def do_add_mailing_list_member(self, line):
+        """
+        add_mailing_list_member member_address list_address
+        Add a new member to a given mailing list. You have to provide valid
+        email addresses for both the member and the list. The mailing list
+        must be handled by the remote mailjam server for this operation to
+        work.
+
+        Alias: amlm
+        """
+        pass
+
+    def do_show_mailing_list_members(self, line):
+        """
+        show_mailing_list_members address
+        """
+        pass
+    
+    def do_EOF(self, line):
+        return True
+
+    def precmd(self, line):
+        # save the commands to the history, if it is enabled in the config file
+        if self.config['history']['enabled']:            
+            self._history.append(line)
+        return line
+    
+    def postloop(self):
+        # save history to a file, if it is enabled in the config file.
+        if self.config['history']['enabled']:
+            history_file_name = self.config['history']['path']
+            if '~' in history_file_name:
+                # we have to replace that with the proper home path
+                # FIXME: we should call some "sanitize" function here, to
+                # get rid of dangerous paths in the file name
+                home = os.path.expanduser("~")
+                history_file_name = history_file_name.replace('~', home)
+            if not os.path.exists(history_file_name):
+                try:
+                    os.makedirs(os.path.dirname(history_file_name))
+                except OSError, e:
+                    # If the dir already exists do not complain, if it is
+                    # any other error, raise the exception
+                    if e.errno != errno.EEXIST:
+                        raise
+            history_file = open(history_file_name, 'a')
+            for i in self._history:
+                if 'EOF' not in i:
+                    history_file.write(i+'\n')
+            history_file.close()
+        print 'Bye!'
+
+    def emptyline(self):
+        pass
Index: mailjam/config.py
===================================================================
--- mailjam/config.py	(revision 20)
+++ mailjam/config.py	(revision 23)
@@ -89,4 +89,5 @@
         return self.config[section]
 
+
 class MTAClientConfig(DaemonConfig):
     @property
@@ -97,4 +98,14 @@
     def sections(self):
         return ['server', 'archive']
+
+
+class CLIClientConfig(DaemonConfig):
+    @property
+    def default_filename(self):
+        return 'mailjam-cli.conf'
+
+    @property
+    def sections(self):
+        return ['server', 'archive', 'history']
 
 
Index: mailjam/daemon.py
===================================================================
--- mailjam/daemon.py	(revision 20)
+++ mailjam/daemon.py	(revision 23)
@@ -163,6 +163,8 @@
     def _listMethods(self):
         public_methods = []
-        public_methods += ['lists.'+i for i in dir(MailingListXMLRPC) if '_' not in i]
-        public_methods += ['members.'+i for i in dir(MemberXMLRPC) if '_' not in i]
+        public_methods += ['lists.'+i for i in dir(MailingListXMLRPC) \
+                           if '_' not in i]
+        public_methods += ['members.'+i for i in dir(MemberXMLRPC) \
+                           if '_' not in i]
         return public_methods
 
@@ -177,7 +179,11 @@
         self.mailjam.load()
     def add(self, info={}):
-        self.mailjam.add_mailing_list(info)
+        try:
+            self.mailjam.add_mailing_list(info)
+        except IndexError, e:
+            return str(e)
+        return 'Added mailing list ' + info['address']  
     def addresses(self):
-        return self.mailjam.mailings_addresses    
+        return self.mailjam.mailings_addresses
 
 
@@ -187,5 +193,9 @@
         self.mailjam.load()
     def add(self, member_addr=None, list_addr=None):
-        self.mailjam.add_mailing_member(member_addr, list_addr)
+        try:
+            self.mailjam.add_mailing_member(member_addr, list_addr)
+        except IndexError, e:
+            return str(e)
+        return 'Added member ' + member_addr + ' to ' + list_addr
     def list(self, mailing):
         if mailing in self.mailjam.mailings_addresses:
Index: mailjam/models.py
===================================================================
--- mailjam/models.py	(revision 16)
+++ mailjam/models.py	(revision 23)
@@ -33,5 +33,5 @@
     def _validate_address(self, address):
         if not validate_email_address(address):
-            raise ValueError(address, ' is not a valid email address')
+            raise ValueError(str(address) + ' is not a valid email address')
         return address
 
@@ -63,5 +63,5 @@
     def _validate_member_object(self, member=None):
         if not isinstance(member, Member):
-            raise TypeError(member, ' is not a valid Member instance')
+            raise TypeError(str(member) + ' is not a valid Member instance')
         return member
 
@@ -72,5 +72,5 @@
     def _validate_member_by_address(self, address=None):
         if not validate_email_address(address):
-            raise ValueError(address, ' is not a valid email address')
+            raise ValueError(str(address) + ' is not a valid email address')
         return address in self.members_addresses()
 
Index: setup.py
===================================================================
--- setup.py	(revision 16)
+++ setup.py	(revision 23)
@@ -23,5 +23,5 @@
     description='Mailing lists management software',
     long_description=open('README').read(),
-    scripts=['bin/mailjam-server', 'bin/mailjam-mta'],
+    scripts=['bin/mailjam-server', 'bin/mailjam-mta', 'bin/mailjam-cli'],
     #data_files=[(proper_etc_path()+'mailjam', ['conf/mailjam.conf',
     #                                           'conf/mailjam-mta.conf']),
