diff --git a/admin/__init__.py b/admin/__init__.py index e69de29bb..29c710d30 100644 --- a/admin/__init__.py +++ b/admin/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from mailng.lib import parameters +from django.utils.translation import ugettext as _ + +parameters.register("admin","STORAGE_PATH", "string", "/var/vmail", + help=_("Path to the root directory where messages are stored")) +parameters.register("admin", "VIRTUAL_UID", "string", "vmail", + help=_("UID of the virtual user which owns domains/mailboxes/messages on the filesystem")) +parameters.register("admin", "VIRTUAL_GID", "string", "vmail", + help=_("GID of the virtual user which owns domains/mailboxes/messages on the filesystem")) +parameters.register("admin", "MAILBOX_TYPE", "list", "maildir", + values=[("maildir", "maildir"), ("mbox", "mbox")], + help=_("Mailboxes storage format")) +parameters.register("admin", "MAILDIR_ROOT", "string", ".maildir", + help=_("Sub-directory (inside the mailbox) where messages are stored when using the maildir format")) +parameters.register("admin", "PASSWORD_SCHEME", "list", "crypt", + values=[("crypt", "crypt"), ("md5", "md5"), ("clear", "clear")], + help=_("Scheme used to crypt mailbox passwords")) diff --git a/admin/models.py b/admin/models.py index cb9e65a8f..d025f6c2a 100644 --- a/admin/models.py +++ b/admin/models.py @@ -1,8 +1,8 @@ from django.db import models from django.contrib.auth.models import User -from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from mailng.lib import exec_pipe, exec_as_vuser +from mailng.lib import parameters +from mailng.lib import exec_as_vuser import os class Domain(models.Model): @@ -19,17 +19,18 @@ class Domain(models.Model): ) def create_dir(self): - path = "%s/%s" % (settings.STORAGE_PATH, self.name) + path = "%s/%s" % (parameters.get("admin", "STORAGE_PATH"), self.name) return exec_as_vuser("mkdir -p %s" % path) def rename_dir(self, newname): + stpath = parameters.get("admin", "STORAGE_PATH") return exec_as_vuser("mv %s/%s %s/%s" \ - % (settings.STORAGE_PATH, self.name, - settings.STORAGE_PATH, newname)) + % (stpath, self.name, stpath, newname)) def delete_dir(self): return exec_as_vuser("rm -r %s/%s" \ - % (settings.STORAGE_PATH, self.name)) + % (parameters.get("admin", "STORAGE_PATH"), + self.name)) def __str__(self): return self.name @@ -56,20 +57,15 @@ class Mailbox(models.Model): def __init__(self, *args, **kwargs): super(Mailbox, self).__init__(*args, **kwargs) - try: - self.mbtype = getattr(settings, "MAILBOX_TYPE") - except AttributeError: - self.mbtype = "maildir" - try: - self.mdirroot = getattr(settings, "MAILDIR_ROOT") - except AttributeError: - self.mdirroot = ".maildir" + self.mbtype = parameters.get("admin", "MAILBOX_TYPE") + self.mdirroot = parameters.get("admin", "MAILDIR_ROOT") def __str__(self): return "%s" % (self.address) def create_dir(self, domain): - path = "%s/%s/%s" % (settings.STORAGE_PATH, domain.name, self.address) + path = "%s/%s/%s" % (parameters.get("admin", "STORAGE_PATH"), + domain.name, self.address) if os.path.exists(path): return True if not exec_as_vuser("mkdir -p %s" % path): @@ -90,7 +86,7 @@ class Mailbox(models.Model): return True def rename_dir(self, domain, newaddress): - path = "%s/%s" % (settings.STORAGE_PATH, domain) + path = "%s/%s" % (parameters.get("admin", "STORAGE_PATH"), domain) code = exec_as_vuser("mv %s/%s %s/%s" \ % (path, self.address, path, newaddress)) if code: @@ -99,8 +95,8 @@ class Mailbox(models.Model): def delete_dir(self): return exec_as_vuser("rm -r %s/%s/%s" \ - % (settings.STORAGE_PATH, - self.domain.name, self.address)) + % (parameters.get("admin", "STORAGE_PATH"), + self.domain.name, self.address)) class Alias(models.Model): address = models.CharField(_('address'), max_length=100, diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 6eb5fab93..6c0c59c35 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -70,6 +70,12 @@ def settings_menu(selection, perms): @register.simple_tag def domains_menu(selection, perms): entries = [ + {"name" : "newdomain", + "url" : reverse(admin.views.newdomain), + "label" : _("New domain"), + "img" : "/static/pics/add.png", + "class" : "boxed", + "rel" : "{handler:'iframe',size:{x:300,y:200}}"}, {"name" : "domains", "url" : reverse(admin.views.domains), "label" : _("Domains"), @@ -77,8 +83,8 @@ def domains_menu(selection, perms): ] entries += events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_box", perms=perms) - return render_to_string('main/menulist.html', - {"menu" : entries, "selection" : selection, + return render_to_string('main/menu.html', + {"entries" : entries, "selection" : selection, "perms" : perms}) @register.simple_tag @@ -109,6 +115,7 @@ def param(app, definition): values = definition["values"] value = definition.has_key("value") \ and definition["value"] or definition["default"] + print name, values for v in values: selected = "" if value == v[0]: diff --git a/admin/views.py b/admin/views.py index 4608c4a84..5e3dfeeca 100644 --- a/admin/views.py +++ b/admin/views.py @@ -178,8 +178,8 @@ def newmailbox(request, dom_id=None): mb.password = crypt_password(request.POST["password1"]) - mb.uid = pwd.getpwnam(settings.VIRTUAL_UID).pw_uid - mb.gid = pwd.getpwnam(settings.VIRTUAL_GID).pw_gid + mb.uid = pwd.getpwnam(parameters.get("admin", "VIRTUAL_UID")).pw_uid + mb.gid = pwd.getpwnam(parameters.get("admin", "VIRTUAL_GID")).pw_gid mb.domain = domain mb.quota = request.POST["quota"] if not mb.quota: @@ -417,11 +417,14 @@ def viewparameters(request): for app in apps: tmp = {"name" : app, "params" : []} for p in sorted(parameters._params[app]): - tmp["params"] += [{"name" : p, - "value" : parameters.get(app, p), - "help" : parameters._params[app][p]["help"], - "default" : parameters._params[app][p]["default"], - "type" : parameters._params[app][p]["type"]}] + newdef = {"name" : p, + "value" : parameters.get(app, p), + "help" : parameters._params[app][p]["help"], + "default" : parameters._params[app][p]["default"], + "type" : parameters._params[app][p]["type"]} + if "values" in parameters._params[app][p].keys(): + newdef["values"] = parameters._params[app][p]["values"] + tmp["params"] += [newdef] gparams += [tmp] return _render(request, 'admin/parameters.html', { diff --git a/extensions/amavis_quarantine/cleanup.py b/extensions/amavis_quarantine/cleanup.py index e2b819d08..f293f5b6e 100755 --- a/extensions/amavis_quarantine/cleanup.py +++ b/extensions/amavis_quarantine/cleanup.py @@ -1,15 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from mailng.lib import db -from django.conf import settings +from mailng.lib import db, parameters if __name__ == "__main__": conn = db.getconnection("amavis_quarantine") - try: - max_messages_age = getattr(settings, "MAX_MESSAGES_AGE") - except AttributeError: - max_messages_age = 14 + max_messages_age = parameters.get("amavis_quarantine", "MAX_MESSAGES_AGE") print "Deleting messages older than %d days..." % max_messages_age db.execute(conn, """ diff --git a/extensions/amavis_quarantine/lib.py b/extensions/amavis_quarantine/lib.py index f40425f99..0f8aa9d67 100644 --- a/extensions/amavis_quarantine/lib.py +++ b/extensions/amavis_quarantine/lib.py @@ -2,25 +2,18 @@ import socket import re import struct import string -from django.conf import settings - -def getParam(param, default=None): - try: - res = getattr(settings, param) - except AttributeError: - res = default - return res +from mailng.lib import parameters class AMrelease(object): def __init__(self): - mode = getParam('AM_PDP_MODE', "unix") + mode = parameters.get("amavis_quarantine", "AM_PDP_MODE") if mode == "inet": - host = getParam('AM_PDP_HOST', 'localhost') - port = getParam('AM_PDP_PORT', '9998') + host = parameters.get("amavis_quarantine", 'AM_PDP_HOST') + port = parameters.get("amavis_quarantine", 'AM_PDP_PORT') self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, int(port))) else: - path = getParam('AM_PDP_SOCKET', '/var/amavis/amavisd.sock') + path = parameters.get("amavis_quarantine", 'AM_PDP_SOCKET') self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(path) diff --git a/extensions/amavis_quarantine/main.py b/extensions/amavis_quarantine/main.py index c50d88085..3644d9a2d 100644 --- a/extensions/amavis_quarantine/main.py +++ b/extensions/amavis_quarantine/main.py @@ -27,6 +27,16 @@ def init(): events.register("UserMenuDisplay", menu) parameters.register("amavis_quarantine", "MAX_MESSAGES_AGE", "int", 14, help=_("Quarantine messages maximum age (in days) before deletion")) + parameters.register("amavis_quarantine", "AM_PDP_MODE", "list", "unix", + values=[("inet", "inet"), ("unix", "unix")], + help="") + parameters.register("amavis_quarantine", "AM_PDP_HOST", "string", "localhost", + help="") + parameters.register("amavis_quarantine", "AM_PDP_PORT", "int", 9998, + help="") + parameters.register("amavis_quarantine", "AM_PDP_SOCKET", "string", + "/var/amavis/amavisd.sock", + help="") def urls(): return (r'^mailng/quarantine/', diff --git a/extensions/postfix_autoreply/main.py b/extensions/postfix_autoreply/main.py index 0e5b51940..423a1420c 100644 --- a/extensions/postfix_autoreply/main.py +++ b/extensions/postfix_autoreply/main.py @@ -9,7 +9,7 @@ functionality into Postfix. """ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ -from mailng.lib import events +from mailng.lib import events, parameters from mailng import main from models import Transport, Alias @@ -22,6 +22,9 @@ def init(): events.register("UserMenuDisplay", menu) + parameters.register("postfix_autoreply", "AUTOREPLIES_TIMEOUT", "int", 86400, + help=_("Timeout in seconds between two auto-replies to the same recipient")) + def urls(): return () diff --git a/extensions/stats/grapher.py b/extensions/stats/grapher.py index 3761456c9..3a2090757 100644 --- a/extensions/stats/grapher.py +++ b/extensions/stats/grapher.py @@ -5,7 +5,7 @@ import time import datetime import rrdtool from django.utils.translation import ugettext as _ -from mailng.lib import getoption +from mailng.lib import parameters traffic_avg_template = { 'name' : 'traffic', @@ -55,8 +55,8 @@ tpl = {'traffic':traffic_avg_template, class Grapher(object): def __init__(self): - self.rrd_rootdir = getoption("RRD_ROOTDIR", "/tmp") - self.img_rootdir = getoption("IMG_ROOTDIR", "/tmp") + self.rrd_rootdir = parameters.get("stats", "RRD_ROOTDIR") + self.img_rootdir = parameters.get("stats", "IMG_ROOTDIR") def process(self, target, suffix, start, end, tpl=traffic_avg_template): rrdfile = "%s/%s.rrd" % (self.rrd_rootdir, target) diff --git a/extensions/stats/logparser.py b/extensions/stats/logparser.py index a3953de81..f1c2aec56 100755 --- a/extensions/stats/logparser.py +++ b/extensions/stats/logparser.py @@ -7,7 +7,7 @@ import re import rrdtool import string, pdb from optparse import OptionParser -from mailng.lib import getoption +from mailng.lib import parameters from mailng.admin.models import Domain import grapher @@ -276,8 +276,8 @@ class LogParser(object): G.make_defaults(dom, tpl=grapher.size_avg_template) if __name__ == "__main__": - log_file = getoption("LOGFILE", "/var/log/maillog") - rrd_rootdir = getoption("RRD_ROOTDIR", "/tmp") + log_file = parameters.get("stats", "LOGFILE") + rrd_rootdir = parameters.get("stats", "RRD_ROOTDIR") parser = OptionParser() parser.add_option("-l","--logFile", default=log_file, diff --git a/extensions/stats/main.py b/extensions/stats/main.py index d08aa7947..7dc1d9840 100644 --- a/extensions/stats/main.py +++ b/extensions/stats/main.py @@ -18,7 +18,6 @@ from django.contrib.auth.decorators \ import login_required, user_passes_test, permission_required from extensions.stats.grapher import Grapher, tpl from extensions.stats.logparser import str2Time -from mailng.lib import getoption graph_types = ['AVERAGE', 'MAX'] @@ -50,7 +49,8 @@ def menu(**kwargs): ] if kwargs["target"] == "admin_menu_box": return [ - {"name" : _("Statistics"), + {"name" : "stats", + "label" : _("Statistics"), "url" : reverse('fullindex'), "img" : "/static/pics/graph.png"} ] @@ -93,7 +93,7 @@ def graph_display(request,dom_id,graph_t=graph_types): "page" : "Domain statistics", "graph" : graph_nature, "domain": domain, "domains" : domains, "messages" : errors, "types" : graph_type, "tmp_path" : tmp_path, - "img_rootdir" : getoption("IMG_ROOTDIR", "/tmp")}) + "img_rootdir" : parameters.get("stats", "IMG_ROOTDIR")}) @login_required @user_passes_test(lambda u: u.is_superuser) @@ -115,7 +115,7 @@ def adminindex(request): "graphs" : graph_list, "periods" : ["day", "week", "month", "year","Custom"], "period" : period, "cal" : CH_print, - "img_rootdir" : getoption("IMG_ROOTDIR", "/tmp")}) + "img_rootdir" : parameters.get("stats", "IMG_ROOTDIR")}) @login_required @user_passes_test(lambda u: u.is_superuser) @@ -149,7 +149,7 @@ def custom_period(request): "start" : start_ref, "end" : end_ref, "messages" : ["Custom period not selected"], - "img_rootdir" : getoption("IMG_ROOTDIR", "/tmp")}) + "img_rootdir" : parameters.get("stats", "IMG_ROOTDIR")}) period_name = "%s_%s" %(start.replace('/',''),end.replace('/','')) G = Grapher() @@ -170,7 +170,7 @@ def custom_period(request): "start" : start_ref, "end" : end_ref, "messages" : None, - "img_rootdir" : getoption("IMG_ROOTDIR", "/tmp")}) + "img_rootdir" : parameters.get("stats", "IMG_ROOTDIR")}) @login_required @@ -187,4 +187,4 @@ def index(request, dom_id=None): "graphs" : graph_list, "periods" : ["day", "week", "month", "year","Custom"], "period" : period, - "img_rootdir" : getoption("IMG_ROOTDIR", "/tmp")}) + "img_rootdir" : parameters.get("stats", "IMG_ROOTDIR")}) diff --git a/lib/__init__.py b/lib/__init__.py index 8ff3c0930..c9d7cbfc5 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,42 +1,18 @@ # -*- coding: utf-8 -*- -import popen2 import os import sys import time -from django.conf import settings from django.template import RequestContext from django.shortcuts import render_to_response from django.template.loader import render_to_string +from mailng.lib import parameters -def exec_pipe(cmd): - """Exécute une commande et récupère la sortie générée +def exec_cmd(cmd): + import subprocess - 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 + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) + output = p.communicate()[0] + return p.returncode, output def _render(request, tpl, user_context): return render_to_response(tpl, user_context, @@ -47,9 +23,10 @@ def _render_error(request, user_context): context_instance=RequestContext(request)) def exec_as_vuser(cmd): - code, output = exec_pipe("sudo -u %s %s" % (settings.VIRTUAL_UID, cmd)) + code, output = exec_cmd("sudo -u %s %s" \ + % (parameters.get("admin", "VIRTUAL_UID"), cmd)) if code: - os.system("echo '%s' >> /tmp/vmail.log" % output) + exec_cmd("echo '%s' >> /tmp/vmail.log" % output) return False return True @@ -77,15 +54,6 @@ def decode(s, encodings=('utf8', 'latin1', 'windows-1252', 'ascii')): pass return s.decode('ascii', 'ignore') -def getoption(name, default=None): - res = None - try: - res = getattr(settings, name) - except AttributeError: - res = default - return res - - class Singleton(type): def __init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) diff --git a/lib/authbackends.py b/lib/authbackends.py index f9a784332..8acbe397a 100644 --- a/lib/authbackends.py +++ b/lib/authbackends.py @@ -1,10 +1,10 @@ from django.contrib.auth.models import User from mailng.admin.models import Mailbox -from django.conf import settings import hashlib import crypt import string from random import Random +from mailng.lib import parameters class SimpleBackend: def authenticate(self, username=None, password=None): @@ -26,15 +26,8 @@ class SimpleBackend: except User.DoesNotExist: return None -def __get_password_scheme(): - try: - scheme = getattr(settings, "PASSWORD_SCHEME") - except AttributeError: - scheme = "crypt" - return scheme - def check_password(password, crypted): - scheme = __get_password_scheme() + scheme = parameters.get("admin", "PASSWORD_SCHEME") if scheme == "crypt": return crypt.crypt(password, crypted) == crypted if scheme == "md5": @@ -42,7 +35,7 @@ def check_password(password, crypted): return password def crypt_password(password): - scheme = __get_password_scheme() + scheme = parameters.get("admin", "PASSWORD_SCHEME") if scheme == "crypt": salt = ''.join(Random().sample(string.letters + string.digits, 2)) return crypt.crypt(password, salt) diff --git a/lib/models.py b/lib/models.py new file mode 100644 index 000000000..3268c719b --- /dev/null +++ b/lib/models.py @@ -0,0 +1,5 @@ +from django.db import models + +class Parameter(models.Model): + name = models.CharField(max_length=255) + value = models.CharField(max_length=255) diff --git a/lib/parameters.py b/lib/parameters.py index cdcfe2ec0..b6c9d78bc 100644 --- a/lib/parameters.py +++ b/lib/parameters.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import string -from mailng.admin.models import Parameter +from mailng.lib.models import Parameter """ This interface provides a simple way to declare and store parameters @@ -10,7 +10,7 @@ in MailNG's database. Core components or extensions can register their own parameters, which will be available and modifiable directly from the web interface. -Only super users will be able to acces this part of the web interface. +Only super users will be able to access this part of the web interface. """ _params = {} @@ -21,8 +21,8 @@ class NotDefined(Exception): self.name = name def __str__(self): - return "Application '%s' and/or parameter '%s' not defined" % (self.app, - self.name) + return "Application '%s' and/or parameter '%s' not defined" \ + % (self.app, self.name) def register(app, name, type="string", deflt=None, help=None, **kwargs): """Register a new parameter. @@ -32,6 +32,8 @@ def register(app, name, type="string", deflt=None, help=None, **kwargs): if not app in _params.keys(): _params[app] = {} _params[app][name] = {"type" : type, "default" : deflt, "help" : help} + for k, v in kwargs.iteritems(): + _params[app][name][k] = v def save(app, name, value): if not app in _params.keys() or not name in _params[app].keys(): diff --git a/scripts/autoreply.py b/scripts/autoreply.py index c36b348fd..8e234aaea 100755 --- a/scripts/autoreply.py +++ b/scripts/autoreply.py @@ -5,7 +5,7 @@ import sys import smtplib from email.mime.text import MIMEText import datetime -from django.conf import settings +from mailng.lib import parameters from mailng.admin.models import Mailbox from mailng.main.models import ARmessage, ARhistoric @@ -23,10 +23,7 @@ if __name__ == "__main__": sys.exit(0) try: lastar = ARhistoric.objects.get(armessage=armessage.id, sender=sender) - try: - timeout = getattr(settings, "AUTOREPLIES_TIMEOUT") - except AttributeError: - timeout = 86400 # 24h + parameters.get("postfix_autoreply", "AUTOREPLIES_TIMEOUT") delta = datetime.timedelta(seconds=timeout) if lastar.last_sent + delta > datetime.datetime.now(): sys.exit(0) diff --git a/settings_sample.py b/settings_sample.py index c6ff9ae6b..8306d8d91 100644 --- a/settings_sample.py +++ b/settings_sample.py @@ -80,6 +80,7 @@ INSTALLED_APPS = ( 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', + 'mailng.lib', 'mailng.admin', 'mailng.main' @@ -92,41 +93,6 @@ AUTHENTICATION_BACKENDS = ( 'mailng.lib.authbackends.SimpleBackend' ) -# Mailng specific options -STORAGE_PATH = "/var/vmail" -VIRTUAL_UID = "vmail" -VIRTUAL_GID = "vmail" -PASSWORD_SCHEME = "crypt" # Possible values : crypt, md5 or "" for clear passwords - -# Mailboxes format: maildir (default) or mbox -# MAILBOX_TYPE = "" - -# Maildir storage directory -# MAILDIR_ROOT = ".maildir" - # Autoreplies sending timeout (default: 86400s) # AUTOREPLIES_TIMEOUT = -# Amavis quarantine options - -# Quarantine messages maximum age (in days) before deletion -# MAX_MESSAGES_AGE = 14 - -# AM.PDP daemon -# Modify above parameters if you want MailNG to be able -# to release messages from the quarantine. Mode can be -# either "inet" or "unix". - -# AM_PDP_MODE = "inet" -# AM_PDP_HOST = "localhost" -# AM_PDP_PORT = 9998 -# AM_PDP_SOCKET = "/var/amavis/amavisd.sock" - -# Statistics options - -# Path to mail log file -#LOGFILE = "/tmp/maillog" -# Path to directory where rrd files are stored -#RRD_ROOTDIR = "/tmp/mailng" -# Path to directory where png files are stored -#IMG_ROOTDIR = "/home/tonio/projets/mailng/static/graphs" diff --git a/static/js/settings.js b/static/js/settings.js index 5552a095d..e61c6b4bb 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -1,10 +1,4 @@ window.addEvent('domready', function() { - SqueezeBox.assign($$('a.boxed'), { - parse: 'rel', - - }); - setDivHeight("content", 20, 0); - if ($$("a[name=deletePerm]")) $$("a[name=deletePerm]").addEvent('click', function(event) { if (!confirm(gettext("Remove this permission?"))) diff --git a/urls.py b/urls.py index 54827847f..e6e5b4f1d 100644 --- a/urls.py +++ b/urls.py @@ -6,6 +6,7 @@ from django.conf import settings # admin.autodiscover() from mailng.extensions import loadextensions, loadmenus +from mailng.lib import parameters loadextensions() urlpatterns = patterns('', @@ -41,7 +42,7 @@ if settings.DEBUG: urlpatterns += patterns( '', (r'^img/stats/(?P.*)/$', 'django.views.static.serve', - {'document_root': settings.IMG_ROOTDIR}) + {'document_root': parameters.get("stats", "IMG_ROOTDIR")}) )