End of ticket #26

This commit is contained in:
tonio
2010-04-19 18:28:29 +02:00
parent ffbb054089
commit a49fe36cb0
19 changed files with 114 additions and 161 deletions
+19
View File
@@ -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"))
+14 -18
View File
@@ -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,
+9 -2
View File
@@ -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]:
+10 -7
View File
@@ -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', {
+2 -6
View File
@@ -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, """
+5 -12
View File
@@ -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)
+10
View File
@@ -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/',
+4 -1
View File
@@ -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 ()
+3 -3
View File
@@ -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)
+3 -3
View File
@@ -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,
+7 -7
View File
@@ -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")})
+9 -41
View File
@@ -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)
+3 -10
View File
@@ -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)
+5
View File
@@ -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)
+6 -4
View File
@@ -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():
+2 -5
View File
@@ -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)
+1 -35
View File
@@ -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"
-6
View File
@@ -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?")))
+2 -1
View File
@@ -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<path>.*)/$', 'django.views.static.serve',
{'document_root': settings.IMG_ROOTDIR})
{'document_root': parameters.get("stats", "IMG_ROOTDIR")})
)