From d234b308b7908b750c3db9372aa155d2b1e34991 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 25 Mar 2009 18:56:02 +0100 Subject: [PATCH] More documentation. Creation/Removal of domains and mailboxes specific directories on filesystem. --- INSTALL | 47 +++++++++++++++++++++++++++++-- README | 11 ++++++-- admin/views.py | 53 ++++++++++++++++++++--------------- lib/__init__.py | 0 lib/fsops.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++ settings_sample.py | 5 ++++ 6 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 lib/__init__.py create mode 100644 lib/fsops.py diff --git a/INSTALL b/INSTALL index 0d4b5a0b8..f42717d87 100644 --- a/INSTALL +++ b/INSTALL @@ -13,7 +13,10 @@ How to install Download Mailng's tarball and extract it:: tar xzf mailng-.tar.gz -Enter the mailng repository, create a settings.py file (you can use settings-sample.py as a model). Specify database relative informations. You must choose between mysql and postgresql, other types are not supported by major email projects like postfix or dovecot. +Enter the mailng directory, create a settings.py file (you can use +settings-sample.py as a model). Specify database relative informations. You +must choose between mysql and postgresql, other types are not supported by +major email projects like postfix or dovecot. Initialise the database by running the following command:: python manage.py syncdb @@ -23,7 +26,45 @@ Create the super admin account (just follow the procedure). Testing installation -------------------- -As a typical Django application, Mailng can be tested using a simple web server. Just run this command to start it:: +As a typical Django application, Mailng can be tested using a simple web +server. Just run this command to start it:: python manage.py runserver -You will be able to play with mailng by looking at http://:8000/mailng/admin/ +You will be able to play with mailng by looking at +http://:8000/mailng/admin/. + +Tips to link Mailng with postfix +-------------------------------- + +First, define following maps on your server:: + +# /etc/postfix/sql-domains.cf + user = + password = + dbname = + query = SELECT name FROM domains WHERE name='%s' AND enabled='1' + +# /etc/postfix/sql-mailboxes.cf + user = + password = + dbname = + query = SELECT path FROM mailboxes WHERE address = '%s' AND enabled = '1' + +Then, use the following configuration in your /etc/postfix/main.cf file +(this is one possible configuration):: + + # Stuff before + mailbox_transport = virtual + home_mailbox = Maildir/ + maildrop_destination_recipient_limit = 1 + virtual_minimum_uid = 500 + virtual_gid_maps = static:500 + virtual_uid_maps = static:500 + virtual_mailbox_base = /var/vmail + + relay_domains = mysql:/etc/postfix/sql-domains.cf + virtual_mailbox_domains = mysql:/etc/postfix/sql-domains.cf + virtual_mailbox_maps = mysql:/etc/postfix/sql-mailboxes.cf + virtual_alias_domains = + # Stuff after + diff --git a/README b/README index 89bb35293..8c55b2e89 100644 --- a/README +++ b/README @@ -1,7 +1,12 @@ -Mailng is a light web messaging solution. It provides different tools that facilitates virtual domains hosting. It is designed to be integrated with email servers like postfix (http://www.postfix.org/) and/or dovecot (http://www.dovecot.org/). +Mailng is a light web messaging solution. It provides different tools that +facilitate virtual domains hosting. It is designed to be integrated with email +servers like postfix (http://www.postfix.org/) and/or dovecot +(http://www.dovecot.org/). -For now, only the administration component is available. Additional tools will come in a near future. +For now, only the administration component is available. Additional tools will +come in a near future. Django is needed to use Mailng (http://www.djangoproject.com/) -Mootools version 1.2 (http://www.mootools.net/) and SqueezeBox version 1.1 (http://digitarald.de/project/squeezebox/) are provided with Mailng. +Mootools version 1.2 (http://www.mootools.net/) and SqueezeBox version 1.1 +(http://digitarald.de/project/squeezebox/) are provided with Mailng. diff --git a/admin/views.py b/admin/views.py index 6de6feec5..18a900e36 100644 --- a/admin/views.py +++ b/admin/views.py @@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from mailng import admin +from mailng.lib.fsops import * from mailng.admin.models import Domain, Mailbox, Alias from forms import MailboxForm, DomainForm, AliasForm @@ -40,9 +41,12 @@ def newdomain(request): if Domain.objects.filter(name=request.POST["name"]): error = "Domain with this name already defined" else: - form.save() - ctx = _ctx_ok(reverse(admin.views.domains)) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + if create_domain(request.POST["name"]): + form.save() + ctx = _ctx_ok(reverse(admin.views.domains)) + return HttpResponse(simplejson.dumps(ctx), + mimetype="application/json") + error = "Failed to initialise domain, check permissions" ctx = _ctx_ko("admin/newdomain.html", { "form" : form, "error" : error }) @@ -52,6 +56,7 @@ def newdomain(request): return render_to_response('admin/newdomain.html', { "form" : form }) + @login_required def editdomain(request, dom_id): domain = Domain.objects.get(pk=dom_id) @@ -74,6 +79,7 @@ def editdomain(request, dom_id): @login_required def deldomain(request, dom_id): domain = Domain.objects.get(pk=dom_id) + destroy_domain(domain.name) domain.delete() return HttpResponseRedirect('/mailng/admin/') @@ -96,25 +102,27 @@ def newmailbox(request, dom_id=None): domain=dom_id): error = "Mailbox with this address already exists" else: - mb = form.save(commit=False) - user = User() - user.username = user.email = "%s@%s" % (mb.address, domain.name) - user.set_password(request.POST["password1"]) - user.is_active = request.POST.has_key("enabled") \ - and True or False - fname, lname = mb.name.split() - user.first_name = fname - - user.last_name = lname - user.save() - mb.user = user - - mb.uid = mb.gid = 500 - mb.domain = domain - mb.save() - ctx = _ctx_ok(reverse(admin.views.mailboxes, args=[domain.id])) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") + if create_mailbox(domain.name, request.POST["address"]): + mb = form.save(commit=False) + user = User() + user.username = user.email = "%s@%s" % (mb.address, domain.name) + user.set_password(request.POST["password1"]) + user.is_active = request.POST.has_key("enabled") \ + and True or False + fname, lname = mb.name.split() + user.first_name = fname + + user.last_name = lname + user.save() + mb.user = user + + mb.uid = mb.gid = 500 + mb.domain = domain + mb.save() + ctx = _ctx_ok(reverse(admin.views.mailboxes, args=[domain.id])) + return HttpResponse(simplejson.dumps(ctx), + mimetype="application/json") + error = "Failed to initialise mailbox, check permissions" ctx = _ctx_ko("admin/newmailbox.html", { "form" : form, "domain" : domain, "error" : error }) @@ -156,6 +164,7 @@ def editmailbox(request, dom_id, mbox_id=None): @login_required def delmailbox(request, dom_id, mbox_id=None): mb = Mailbox.objects.get(pk=mbox_id) + destroy_mailbox(mb.domain.name, mb.address) mb.delete() return HttpResponseRedirect(reverse(admin.views.mailboxes, args=[dom_id])) diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lib/fsops.py b/lib/fsops.py new file mode 100644 index 000000000..8d355f236 --- /dev/null +++ b/lib/fsops.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +import popen2 +import os +import time +from django.conf import settings + +def exec_pipe(cmd): + """Exécute une commande et récupère la sortie générée + + Le module popen2 est utilisé pour l'exécution de la commande. Un + objet Popen4 est créé car il regroupe la sortie standard et la sortie + d'erreur dans le même fd. + De plus, popen2 est le seul à pouvoir capturer retourner le code de + sortie de la commande. + + cmd -- commande à exécuter + + Retourne un couple (code de sortie, texte de sortie) + """ + child = popen2.Popen4(cmd) + child.tochild.close() + while True: + ret = child.poll() + if ret != -1: + break + time.sleep(0.001) + output = child.fromchild.read() + if not ret: + code = 0 + else: + if os.WIFEXITED(ret): + code = os.WEXITSTATUS(ret), output + else: + code = -1 + return code, output + + +def create_domain(name): + code, output = exec_pipe("sudo -u %s mkdir %s/%s" \ + % (settings.VIRTUAL_UID, settings.STORAGE_PATH, + name)) + if code: + os.system("echo '%s' >> /tmp/vmail.log" % output) + return False + return True + +def destroy_domain(name): + code, output = exec_pipe("sudo -u %s rm -r %s/%s" \ + % (settings.VIRTUAL_UID, settings.STORAGE_PATH, + name)) + if code: + return False + return True + +def create_mailbox(domain, name): + code, output = exec_pipe("sudo -u %s mkdir -p %s/%s/%s/.maildir" \ + % (settings.VIRTUAL_UID, settings.STORAGE_PATH, + domain, name)) + if code: + return False + return True + +def destroy_mailbox(domain, name): + code, output = exec_pipe("sudo -u %s rm -r %s/%s/%s" \ + % (settings.VIRTUAL_UID, settings.STORAGE_PATH, + domain, name)) + if code: + return False + return True diff --git a/settings_sample.py b/settings_sample.py index 049d462dd..55ff3800b 100644 --- a/settings_sample.py +++ b/settings_sample.py @@ -80,3 +80,8 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'mailng.admin' ) + +# Mailng specific options +STORAGE_PATH = "/var/vmail" +VIRTUAL_UID = "vmail" +VIRTUAL_GID = "vmail"