diff --git a/modoboa/extensions/admin/fixtures/demo.json b/modoboa/extensions/admin/fixtures/demo.json
index f1971a053..36ac1cf2f 100644
--- a/modoboa/extensions/admin/fixtures/demo.json
+++ b/modoboa/extensions/admin/fixtures/demo.json
@@ -1 +1,304 @@
-[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-05-03T16:58:51+01:00", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26T15:19:25+01:00"}}, {"pk": 2, "model": "core.user", "fields": {"username": "admin@demo.local", "first_name": "Demo", "last_name": "Administrator", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-24T05:00:12+01:00", "groups": [1], "user_permissions": [], "password": "{CRYPT}SJ2bQWwabfH/6", "email": "admin@demo.local", "date_joined": "2012-05-03T16:50:15+01:00"}}, {"pk": 4, "model": "core.user", "fields": {"username": "user@demo.local", "first_name": "Demo", "last_name": "User", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-03T16:56:50+01:00", "groups": [2], "user_permissions": [], "password": "{CRYPT}2iAFXtxUA6.7Y", "email": "user@demo.local", "date_joined": "2012-05-03T16:56:50+01:00"}}, {"pk": 2, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 3, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 2}}, {"pk": 4, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 5, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 6, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 7, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 8, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["core", "user"], "object_id": 2}}, {"pk": 9, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 13, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 4}}, {"pk": 14, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 15, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 16, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["core", "user"], "object_id": 4}}, {"pk": 1, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:49:40+01:00", "last_modification": "2012-05-03T16:49:40+01:00"}}, {"pk": 2, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:50:15+01:00", "last_modification": "2012-05-03T16:50:36+01:00"}}, {"pk": 3, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:50:15+01:00", "last_modification": "2012-05-03T16:50:15+01:00"}}, {"pk": 4, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:51:04+01:00", "last_modification": "2012-05-03T16:51:04+01:00"}}, {"pk": 5, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:56:50+01:00", "last_modification": "2012-05-03T16:56:50+01:00"}}, {"pk": 1, "model": "admin.domain", "fields": {"dates": 1, "enabled": true, "name": "demo.local", "quota": 10}}, {"pk": 1, "model": "admin.mailbox", "fields": {"dates": 2, "quota": 10, "user": 2, "domain": 1, "address": "admin"}}, {"pk": 3, "model": "admin.mailbox", "fields": {"dates": 5, "quota": 10, "user": 4, "domain": 1, "address": "user"}}, {"pk": 1, "model": "admin.alias", "fields": {"dates": 3, "enabled": true, "domain": 1, "mboxes": [1], "address": "postmaster", "extmboxes": ""}}, {"pk": 1, "model": "core.extension", "fields": {"enabled": true, "name": "limits"}}, {"pk": 2, "model": "core.extension", "fields": {"enabled": true, "name": "postfix_autoreply"}}, {"pk": 3, "model": "core.extension", "fields": {"enabled": true, "name": "stats"}}, {"pk": 4, "model": "core.extension", "fields": {"enabled": true, "name": "amavis"}}, {"pk": 5, "model": "core.extension", "fields": {"enabled": true, "name": "webmail"}}, {"pk": 6, "model": "core.extension", "fields": {"enabled": true, "name": "sievefilters"}}, {"pk": 7, "model": "core.extension", "fields": {"enabled": true, "name": "postfix_relay_domains"}}, {"pk": "admin@demo.local", "model": "admin.quota", "fields": {"mbox": 1, "username": "admin@demo.local", "bytes": 0, "messages": 0}}, {"pk": "user@demo.local", "model": "admin.quota", "fields": {"mbox": 3, "username": "user@demo.local", "bytes": 0, "messages": 0}}]
+[
+ {
+ "pk": 1,
+ "model": "core.user",
+ "fields": {
+ "username": "admin",
+ "first_name": "",
+ "last_name": "",
+ "is_active": true,
+ "is_superuser": true,
+ "is_staff": false,
+ "last_login": "2012-05-03T16:58:51+01:00",
+ "groups": [],
+ "user_permissions": [],
+ "password": "{CRYPT}dTTsGDkA5ZHKg",
+ "email": "",
+ "date_joined": "2012-01-26T15:19:25+01:00"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "core.user",
+ "fields": {
+ "username": "admin@demo.local",
+ "first_name": "Demo",
+ "last_name": "Administrator",
+ "is_active": true,
+ "is_superuser": false,
+ "is_staff": false,
+ "last_login": "2012-05-24T05:00:12+01:00",
+ "groups": [1],
+ "user_permissions": [],
+ "password": "{CRYPT}SJ2bQWwabfH/6",
+ "email": "admin@demo.local",
+ "date_joined": "2012-05-03T16:50:15+01:00"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "core.user",
+ "fields": {
+ "username": "user@demo.local",
+ "first_name": "Demo", "last_name": "User",
+ "is_active": true,
+ "is_superuser": false,
+ "is_staff": false,
+ "last_login": "2012-05-03T16:56:50+01:00",
+ "groups": [2],
+ "user_permissions": [],
+ "password": "{CRYPT}2iAFXtxUA6.7Y",
+ "email": "user@demo.local",
+ "date_joined": "2012-05-03T16:56:50+01:00"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["admin", "domain"],
+ "object_id": 1
+ }
+ },
+ {
+ "pk": 3,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["core", "user"],
+ "object_id": 2
+ }
+ },
+ {
+ "pk": 4,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["admin", "mailbox"],
+ "object_id": 1
+ }
+ },
+ {
+ "pk": 5,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["admin", "alias"],
+ "object_id": 1
+ }
+ },
+ {
+ "pk": 6,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["admin", "domain"],
+ "object_id": 1
+ }
+ },
+ {
+ "pk": 7,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["admin", "mailbox"],
+ "object_id": 1
+ }
+ },
+ {
+ "pk": 8,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["core", "user"],
+ "object_id": 2
+ }
+ },
+ {
+ "pk": 9,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["admin",
+ "alias"],
+ "object_id": 1}},
+ {
+ "pk": 13,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["core",
+ "user"],
+ "object_id": 4}},
+ {
+ "pk": 14,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": true,
+ "user": 1,
+ "content_type": ["admin",
+ "mailbox"],
+ "object_id": 3}},
+ {
+ "pk": 15,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["admin",
+ "mailbox"],
+ "object_id": 3}},
+ {
+ "pk": 16,
+ "model": "core.objectaccess",
+ "fields": {
+ "is_owner": false,
+ "user": 2,
+ "content_type": ["core",
+ "user"],
+ "object_id": 4}},
+ {
+ "pk": 1,
+ "model": "admin.objectdates",
+ "fields": {
+ "creation": "2012-05-03T16:49:40+01:00",
+ "last_modification": "2012-05-03T16:49:40+01:00"}},
+ {
+ "pk": 2,
+ "model": "admin.objectdates",
+ "fields": {
+ "creation": "2012-05-03T16:50:15+01:00",
+ "last_modification": "2012-05-03T16:50:36+01:00"}},
+ {
+ "pk": 3,
+ "model": "admin.objectdates",
+ "fields": {
+ "creation": "2012-05-03T16:50:15+01:00",
+ "last_modification": "2012-05-03T16:50:15+01:00"}},
+ {
+ "pk": 4,
+ "model": "admin.objectdates",
+ "fields": {
+ "creation": "2012-05-03T16:51:04+01:00",
+ "last_modification": "2012-05-03T16:51:04+01:00"}},
+ {
+ "pk": 5,
+ "model": "admin.objectdates",
+ "fields": {
+ "creation": "2012-05-03T16:56:50+01:00",
+ "last_modification": "2012-05-03T16:56:50+01:00"}},
+ {
+ "pk": 1,
+ "model": "admin.domain",
+ "fields": {
+ "dates": 1,
+ "enabled": true,
+ "name": "demo.local",
+ "quota": 10}},
+ {
+ "pk": 1,
+ "model": "admin.mailbox",
+ "fields": {
+ "dates": 2,
+ "quota": 10,
+ "user": 2,
+ "domain": 1,
+ "address": "admin",
+ "quota_value": "admin@demo.local"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "admin.mailbox",
+ "fields": {
+ "dates": 5,
+ "quota": 10,
+ "user": 4,
+ "domain": 1,
+ "address": "user",
+ "quota_value": "user@demo.local"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "admin.alias",
+ "fields": {
+ "dates": 3,
+ "enabled": true,
+ "domain": 1,
+ "mboxes": [1],
+ "address": "postmaster",
+ "extmboxes": ""}},
+ {
+ "pk": 1,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "limits"}},
+ {
+ "pk": 2,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "postfix_autoreply"}},
+ {
+ "pk": 3,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "stats"}},
+ {
+ "pk": 4,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "amavis"}},
+ {
+ "pk": 5,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "webmail"}},
+ {
+ "pk": 6,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "sievefilters"}},
+ {
+ "pk": 7,
+ "model": "core.extension",
+ "fields": {
+ "enabled": true,
+ "name": "postfix_relay_domains"
+ }
+ },
+ {
+ "pk": "admin@demo.local",
+ "model": "admin.quota",
+ "fields": {
+ "username": "admin@demo.local",
+ "bytes": 0,
+ "messages": 0
+ }
+ },
+ {
+ "pk": "user@demo.local",
+ "model": "admin.quota",
+ "fields": {
+ "username": "user@demo.local",
+ "bytes": 0,
+ "messages": 0
+ }
+ }
+]
diff --git a/modoboa/extensions/amavis/general_callbacks.py b/modoboa/extensions/amavis/general_callbacks.py
index b86ab7cf8..2855b781b 100644
--- a/modoboa/extensions/amavis/general_callbacks.py
+++ b/modoboa/extensions/amavis/general_callbacks.py
@@ -1,6 +1,7 @@
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.template import Template, Context
+
from modoboa.lib import events, parameters
from modoboa.extensions.admin.models import DomainAlias
from modoboa.extensions.amavis.lib import (
@@ -70,15 +71,17 @@ def check_for_pending_requests(user, include_all):
"""
Check if release requests are pending.
"""
- from .sql_listing import get_wrapper
+ from .sql_connector import get_connector
if parameters.get_admin("USER_CAN_RELEASE") == "yes" \
or user.group == "SimpleUsers":
return []
- nbrequests = get_wrapper().get_pending_requests(user)
+
+ nbrequests = get_connector(user=user).get_pending_requests()
if not nbrequests:
return [{"id": "nbrequests", "counter": 0}] if include_all \
else []
+
url = reverse("modoboa.extensions.amavis.views.index")
url += "#listing/?viewrequests=1"
return [{
diff --git a/modoboa/extensions/amavis/lib.py b/modoboa/extensions/amavis/lib.py
index 9a9ed39bd..20f02a6c3 100644
--- a/modoboa/extensions/amavis/lib.py
+++ b/modoboa/extensions/amavis/lib.py
@@ -75,7 +75,7 @@ recipient=%s
""" % (mailid, secretid, recipient))
answer = self.sock.recv(1024)
answer = self.decode(answer)
- if re.search("250 [\d\.]+ Ok", answer):
+ if re.search(r"250 [\d\.]+ Ok", answer):
return True
return False
@@ -88,7 +88,9 @@ class QuarantineNavigationParameters(NavigationParameters):
super(QuarantineNavigationParameters, self).__init__(
request, 'quarantine_navparams'
)
- self.parameters += [('msgtype', None, False)]
+ self.parameters += [
+ ('msgtype', None, False), ('viewrequests', None, False)
+ ]
def back_to_listing(self):
"""Return the current listing URL.
diff --git a/modoboa/extensions/amavis/management/commands/amnotify.py b/modoboa/extensions/amavis/management/commands/amnotify.py
index 54846b12e..1ed96f37d 100644
--- a/modoboa/extensions/amavis/management/commands/amnotify.py
+++ b/modoboa/extensions/amavis/management/commands/amnotify.py
@@ -1,20 +1,23 @@
#!/usr/bin/env python
# coding: utf-8
from optparse import make_option
+
from django import db
from django.core.management.base import BaseCommand, CommandError
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
+
from modoboa.core.models import User
from modoboa.lib import parameters
from modoboa.lib.emailutils import sendmail_simple
from modoboa.extensions.admin.models import Domain
+
from modoboa.extensions.amavis import Amavis
from modoboa.extensions.amavis.models import (
Msgrcpt
)
-from modoboa.extensions.amavis.sql_listing import get_wrapper
+from modoboa.extensions.amavis.sql_connector import get_connector
class Command(BaseCommand):
@@ -75,7 +78,7 @@ class Command(BaseCommand):
if not da.mailbox_set.count():
continue
rcpt = da.mailbox_set.all()[0].full_address
- reqs = get_wrapper().get_domains_pending_requests(
+ reqs = get_connector().get_domains_pending_requests(
Domain.objects.get_for_admin(da)
)
if reqs.count():
diff --git a/modoboa/extensions/amavis/sql_connector.py b/modoboa/extensions/amavis/sql_connector.py
new file mode 100644
index 000000000..bd2758ca9
--- /dev/null
+++ b/modoboa/extensions/amavis/sql_connector.py
@@ -0,0 +1,305 @@
+"""
+SQL connector module.
+"""
+from django.db.models import Q
+
+from modoboa.extensions.admin.models import Domain
+from modoboa.lib.dbutils import db_type
+from modoboa.lib.webutils import static_url
+
+from .models import Quarantine, Msgrcpt, Maddr
+
+
+class SQLconnector(object):
+
+ """
+ This class handles all database operations.
+ """
+
+ ORDER_TRANSLATION_TABLE = {
+ "type": "content",
+ "score": "bspam_level",
+ "date": "mail__time_num",
+ "subject": "mail__subject",
+ "from": "mail__from_addr",
+ "to": "rid__email"
+ }
+
+ QUARANTINE_FIELDS = [
+ "content",
+ "bspam_level",
+ "rs",
+ "rid__email",
+ "mail__from_addr",
+ "mail__subject",
+ "mail__mail_id",
+ "mail__time_num",
+ ]
+
+ def __init__(self, user=None, navparams=None):
+ """Constructor."""
+ self.user = user
+ self.navparams = navparams
+ self.messages = None
+
+ self._messages_count = None
+
+ def _exec(self, query, args):
+ """Execute a raw SQL query.
+
+ :param string query: query to execute
+ :param list args: a list of arguments to replace in :kw:`query`
+ """
+ from django.db import connections, transaction
+
+ cursor = connections['amavis'].cursor()
+ cursor.execute(query, args)
+ transaction.commit_unless_managed(using='amavis')
+
+ def _apply_msgrcpt_filters(self, flt):
+ """Apply filters based on user's role.
+
+ """
+ if self.user.group == 'SimpleUsers':
+ rcpts = [self.user.email] \
+ + self.user.mailbox_set.all()[0].alias_addresses
+ flt &= Q(rid__email__in=rcpts)
+ elif not self.user.is_superuser:
+ doms = Domain.objects.get_for_admin(self.user)
+ regexp = "(%s)" % '|'.join([dom.name for dom in doms])
+ flt &= Q(rid__email__regex=regexp)
+
+ def _apply_extra_search_filter(self, crit, pattern):
+ """Apply search filters using additional criterias.
+
+ """
+ if crit == "to":
+ return Q(rid__email__contains=pattern)
+ return None
+
+ def _apply_extra_select_filters(self, messages):
+ """Just a hook to apply additional filters to the queryset.
+
+ """
+ return messages
+
+ def _get_quarantine_content(self):
+ """Fetch quarantine content.
+
+ Filters: rs, rid, content
+ """
+ flt = Q(rs__in=[' ', 'V', 'R', 'p']) \
+ if self.navparams.get('viewrequests', '0') != '1' else Q(rs='p')
+ self._apply_msgrcpt_filters(flt)
+ pattern = self.navparams.get("pattern", "")
+ if pattern:
+ criteria = self.navparams.get('criteria')
+ if criteria == "both":
+ criteria = "from_addr,subject,to"
+ search_flt = None
+ for crit in criteria.split(","):
+ if crit == "from_addr":
+ nfilter = Q(mail__from_addr__contains=pattern)
+ elif crit == "subject":
+ nfilter = Q(mail__subject__contains=pattern)
+ else:
+ nfilter = self._apply_extra_search_filter(crit, pattern)
+ if nfilter is None:
+ continue
+ search_flt = nfilter \
+ if search_flt is None else search_flt | nfilter
+ if search_flt:
+ flt &= search_flt
+ msgtype = self.navparams.get('msgtype', None)
+ if msgtype is not None:
+ flt &= Q(content=msgtype)
+
+ flt &= Q(
+ mail__in=Quarantine.objects.filter(chunk_ind=1).values("mail_id")
+ )
+
+ messages = Msgrcpt.objects.select_related().filter(flt)
+ messages = self._apply_extra_select_filters(messages)
+ return messages
+
+ def messages_count(self, **kwargs):
+ """Return the total number of messages living in the quarantine.
+
+ We also store the built queryset for a later use.
+ """
+ if self.user is None or self.navparams is None:
+ return None
+ if self._messages_count is None:
+ self.messages = self._get_quarantine_content()
+ self.messages = self.messages.values(*self.QUARANTINE_FIELDS)
+
+ order = self.navparams.get("order")
+ if order is not None:
+ sign = ""
+ if order[0] == "-":
+ sign = "-"
+ order = order[1:]
+ order = self.ORDER_TRANSLATION_TABLE[order]
+ self.messages = self.messages.order_by(sign + order)
+
+ self._messages_count = self.messages.count()
+
+ return self._messages_count
+
+ def fetch(self, start=None, stop=None, **kwargs):
+ """Fetch a range of messages from the internal cache.
+
+ """
+ emails = []
+ for qm in self.messages[start - 1:stop]:
+ m = {"from": qm["mail__from_addr"],
+ "to": qm["rid__email"],
+ "subject": qm["mail__subject"],
+ "mailid": qm["mail__mail_id"],
+ "date": qm["mail__time_num"],
+ "type": qm["content"],
+ "score": qm["bspam_level"]}
+ rs = qm["rs"]
+ if rs == 'D':
+ continue
+ elif rs in ['', ' ']:
+ m["class"] = "unseen"
+ elif rs == 'R':
+ m["img_rstatus"] = static_url("pics/release.png")
+ elif rs == 'p':
+ m["class"] = "pending"
+ emails.append(m)
+ return emails
+
+ def get_recipient_message(self, address, mailid):
+ """Retrieve a message for a given recipient.
+ """
+ return Msgrcpt.objects.get(mail=mailid, rid__email=address)
+
+ def set_msgrcpt_status(self, address, mailid, status):
+ """Change the status (rs field) of a message recipient.
+
+ :param string status: status
+ """
+ addr = Maddr.objects.get(email=address)
+ self._exec(
+ "UPDATE msgrcpt SET rs=%s WHERE mail_id=%s AND rid=%s",
+ [status, mailid, addr.id]
+ )
+
+ def get_domains_pending_requests(self, domains):
+ """Retrieve pending release requests for a list of domains.
+ """
+ regexp = "(%s)" % '|'.join([dom.name for dom in domains])
+ return Msgrcpt.objects.filter(rs='p', rid__email__regex=regexp)
+
+ def get_pending_requests(self):
+ """Return the number of requests currently pending."""
+ rq = Q(rs='p')
+ if not self.user.is_superuser:
+ doms = Domain.objects.get_for_admin(self.user)
+ if not doms.count():
+ return 0
+ regexp = "(%s)" % '|'.join([dom.name for dom in doms])
+ doms_q = Q(rid__email__regex=regexp)
+ rq &= doms_q
+ return Msgrcpt.objects.filter(rq).count()
+
+ def get_mail_content(self, mailid):
+ """Retrieve the content of a message.
+ """
+ return Quarantine.objects.filter(mail=mailid)
+
+
+class PgSQLconnector(SQLconnector):
+
+ """
+ The postgres version.
+
+ Make use of ``QuerySet.extra`` and postgres ``convert_from``
+ function to let the quarantine manager work as expected !
+ """
+
+ def _apply_msgrcpt_filters(self, flt):
+ """Return filters based on user's role.
+
+ """
+ self._where = []
+ if self.user.group == 'SimpleUsers':
+ rcpts = [self.user.email] \
+ + self.user.mailbox_set.all()[0].alias_addresses
+ self._where.append(
+ "convert_from(maddr.email, 'UTF8') IN (%s)" \
+ % (','.join(["'%s'" % rcpt for rcpt in rcpts])))
+ elif not self.user.is_superuser:
+ doms = Domain.objects.get_for_admin(self.user)
+ regexp = "(%s)" % '|'.join([dom.name for dom in doms])
+ self._where.append(
+ "convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp)
+
+ def _apply_extra_search_filter(self, crit, pattern):
+ """Apply search filters using additional criterias.
+
+ """
+ if crit == "to":
+ self._where.append(
+ "convert_from(maddr.email, 'UTF8') LIKE '%%%s%%'"
+ % pattern)
+ return None
+
+ def _apply_extra_select_filters(self, messages):
+ """Just a hook to apply additional filters to the queryset.
+
+ """
+ return messages.extra(where=self._where)
+
+ def get_recipient_message(self, address, mailid):
+ """Retrieve a message for a given recipient.
+ """
+ qset = Msgrcpt.objects.filter(mail=mailid).extra(
+ where=["msgrcpt.rid=maddr.id",
+ "convert_from(maddr.email, 'UTF8') = '%s'" % address],
+ tables=['maddr']
+ )
+ return qset.all()[0]
+
+ def get_domains_pending_requests(self, domains):
+ """Retrieve pending release requests for a list of domains.
+ """
+ regexp = "(%s)" % '|'.join([dom.name for dom in domains])
+ return Msgrcpt.objects.filter(rs='p').extra(
+ where=["msgrcpt.rid=maddr.id",
+ "convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp],
+ tables=['maddr']
+ )
+
+ def get_pending_requests(self):
+ """Return the number of requests currently pending."""
+ rq = Q(rs='p')
+ if not self.user.is_superuser:
+ doms = Domain.objects.get_for_admin(self.user)
+ if not doms.count():
+ return 0
+ regexp = "(%s)" % '|'.join([dom.name for dom in doms])
+ return Msgrcpt.objects.filter(rq).extra(
+ where=["msgrcpt.rid=maddr.id",
+ "convert_from(maddr.email, 'UTF8') ~ '%s'" % (regexp,)],
+ tables=['maddr']
+ ).count()
+ return Msgrcpt.objects.filter(rq).count()
+
+ def get_mail_content(self, mailid):
+ """Retrieve the content of a message."""
+ return Quarantine.objects.filter(mail=mailid).extra(
+ select={'mail_text': "convert_from(mail_text, 'UTF8')"}
+ )
+
+
+def get_connector(**kwargs):
+ """Return the appropriate *connector* class.
+
+ The result depends on the DB engine in use.
+ """
+ if db_type("amavis") == 'postgres':
+ return PgSQLconnector(**kwargs)
+ return SQLconnector(**kwargs)
diff --git a/modoboa/extensions/amavis/sql_listing.py b/modoboa/extensions/amavis/sql_listing.py
index ac1b378ed..c8cbaf122 100644
--- a/modoboa/extensions/amavis/sql_listing.py
+++ b/modoboa/extensions/amavis/sql_listing.py
@@ -1,16 +1,14 @@
# coding: utf-8
-import re
from datetime import datetime
+
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy
-from django.db.models import Q
+
from modoboa.lib import tables
-from modoboa.lib.webutils import static_url
-from modoboa.lib.email_listing import MBconnector, EmailListing
+from modoboa.lib.email_listing import EmailListing
from modoboa.lib.emailutils import Email
-from modoboa.lib.dbutils import db_type
-from modoboa.extensions.admin.models import Domain
-from .models import Quarantine, Msgrcpt
+
+from .sql_connector import get_connector
class Qtable(tables.Table):
@@ -60,238 +58,6 @@ class Qtable(tables.Table):
return datetime.fromtimestamp(value)
-class SQLconnector(MBconnector):
- order_translation_table = {
- "type": "mail__msgrcpt__content",
- "score": "mail__msgrcpt__bspam_level",
- "date": "mail__time_num",
- "subject": "mail__subject",
- "from": "mail__from_addr",
- "to": "mail__msgrcpt__rid__email"
- }
-
- def __init__(self, mail_ids=None, filter=None):
- self.count = None
- self.messages = None
- self.mail_ids = mail_ids
- self.filter = filter
-
- def messages_count(self, **kwargs):
- if self.count is None:
- filter = Q(chunk_ind=1)
- if self.mail_ids is not None:
- filter &= Q(mail__in=self.mail_ids)
- if self.filter:
- filter &= self.filter
- self.messages = Quarantine.objects.filter(filter).values(
- "mail__from_addr",
- "mail__msgrcpt__rid__email",
- "mail__subject",
- "mail__mail_id",
- "mail__time_num",
- "mail__msgrcpt__content",
- "mail__msgrcpt__bspam_level",
- "mail__msgrcpt__rs"
- )
- if "order" in kwargs:
- order = kwargs["order"]
- sign = ""
- if order[0] == "-":
- sign = "-"
- order = order[1:]
- order = self.order_translation_table[order]
- self.messages = self.messages.order_by(sign + order)
-
- self.count = self.messages.count()
- return self.count
-
- def fetch(self, start=None, stop=None, **kwargs):
- emails = []
- for qm in self.messages[start - 1:stop]:
- m = {"from": qm["mail__from_addr"],
- "to": qm["mail__msgrcpt__rid__email"],
- "subject": qm["mail__subject"],
- "mailid": qm["mail__mail_id"],
- "date": qm["mail__time_num"],
- "type": qm["mail__msgrcpt__content"],
- "score": qm["mail__msgrcpt__bspam_level"]}
- rs = qm["mail__msgrcpt__rs"]
- if rs == 'D':
- continue
- elif rs in ['', ' ']:
- m["class"] = "unseen"
- elif rs == 'R':
- m["img_rstatus"] = static_url("pics/release.png")
- elif rs == 'p':
- m["class"] = "pending"
- emails.append(m)
- return emails
-
-
-class SQLWrapper(object):
- """A simple SQL wrapper.
-
- This wrapper has been added just to answer the *Postgres bytea
- fields* issue :p
-
- The base class doesn't add anything special but defines the method
- (and so piece of SQL queries) that should be overloaded when
- Postgres is in use.
-
- See ``PgWrapper`` for the real mess...
- """
-
- def _exec(self, query, args):
- """Execute a raw SQL query.
-
- :param string query: query to execute
- :param list args: a list of arguments to replace in :kw:`query`
- """
- from django.db import connections, transaction
-
- cursor = connections['amavis'].cursor()
- cursor.execute(query, args)
- transaction.commit_unless_managed(using='amavis')
-
- def get_mails(self, request, rcptfilter=None):
- """Retrieve all messages visible by a user.
-
- Simple users can only see the messages
-
- :rtype: QuerySet
- """
- q = Q(rs='p') \
- if request.GET.get("viewrequests", None) == "1" else ~Q(rs='D')
- if request.user.group == 'SimpleUsers':
- rcpts = [request.user.email] \
- + request.user.mailbox_set.all()[0].alias_addresses
- q &= Q(rid__email__in=rcpts)
- elif not request.user.is_superuser:
- doms = Domain.objects.get_for_admin(request.user)
- regexp = "(%s)" % '|'.join([dom.name for dom in doms])
- doms_q = Q(rid__email__regex=regexp)
- q &= doms_q
- if rcptfilter is not None:
- q &= Q(rid__email__contains=rcptfilter)
- return Msgrcpt.objects.filter(q).values("mail_id")
-
- def get_recipient_message(self, address, mailid):
- return Msgrcpt.objects.get(mail=mailid, rid__email=address)
-
- def set_msgrcpt_status(self, address, mailid, status):
- """Change the status (rs field) of a message recipient.
-
- :param string status: status
- """
- from modoboa.extensions.amavis.models import Maddr
-
- addr = Maddr.objects.get(email=address)
- self._exec(
- "UPDATE msgrcpt SET rs=%s WHERE mail_id=%s AND rid=%s",
- [status, mailid, addr.id]
- )
-
- def get_recipient_messages(self, address, mailids):
- return Msgrcpt.objects.filter(mail__in=mailids, rid__email=address)
-
- def get_domains_pending_requests(self, domains):
- regexp = "(%s)" % '|'.join([dom.name for dom in domains])
- return Msgrcpt.objects.filter(rs='p', rid__email__regex=regexp)
-
- def get_pending_requests(self, user):
- """Return the number of current pending requests
-
- :param user: a ``User`` instance
- """
- rq = Q(rs='p')
- if not user.is_superuser:
- doms = Domain.objects.get_for_admin(user)
- if not doms.count():
- return 0
- regexp = "(%s)" % '|'.join([dom.name for dom in doms])
- doms_q = Q(rid__email__regex=regexp)
- rq &= doms_q
- return Msgrcpt.objects.filter(rq).count()
-
- def get_mail_content(self, mailid):
- return Quarantine.objects.filter(mail=mailid)
-
-
-class PgWrapper(SQLWrapper):
- """The postgres wrapper
-
- Make use of ``QuerySet.extra`` and postgres ``convert_from``
- function to let the quarantine manager work as expected !
- """
-
- def get_mails(self, request, rcptfilter=None):
- q = Q(rs='p') \
- if request.GET.get("viewrequests", None) == "1" else ~Q(rs='D')
- where = ["U0.rid=maddr.id"]
- if request.user.group == 'SimpleUsers':
- rcpts = [request.user.email] \
- + request.user.mailbox_set.all()[0].alias_addresses
- where.append("convert_from(maddr.email, 'UTF8') IN (%s)" \
- % (','.join(["'%s'" % rcpt for rcpt in rcpts])))
- elif not request.user.is_superuser:
- doms = Domain.objects.get_for_admin(request.user)
- regexp = "(%s)" % '|'.join([dom.name for dom in doms])
- where.append("convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp)
- if rcptfilter is not None:
- where.append("convert_from(maddr.email, 'UTF8') LIKE '%%%s%%'"
- % rcptfilter)
- return Msgrcpt.objects.filter(q)\
- .extra(where=where, tables=['maddr']).values("mail_id")
-
- def get_recipient_message(self, address, mailid):
- qset = Msgrcpt.objects.filter(mail=mailid).extra(
- where=["msgrcpt.rid=maddr.id", "convert_from(maddr.email, 'UTF8') = '%s'" % address],
- tables=['maddr']
- )
- return qset.all()[0]
-
- def get_recipient_messages(self, address, mailids):
- return Msgrcpt.objects.filter(mail__in=mailids).extra(
- where=["U0.rid=maddr.id", "convert_from(maddr.email, 'UTF8') = '%s'" % address],
- tables=['maddr']
- )
-
- def get_domains_pending_requests(self, domains):
- regexp = "(%s)" % '|'.join([dom.name for dom in domains])
- return Msgrcpt.objects.filter(rs='p').extra(
- where=["msgrcpt.rid=maddr.id", "convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp],
- tables=['maddr']
- )
-
- def get_pending_requests(self, user):
- rq = Q(rs='p')
- if not user.is_superuser:
- doms = Domain.objects.get_for_admin(user)
- if not doms.count():
- return 0
- regexp = "(%s)" % '|'.join([dom.name for dom in doms])
- return Msgrcpt.objects.filter(rq).extra(
- where=["msgrcpt.rid=maddr.id", "convert_from(maddr.email, 'UTF8') ~ '%s'" % (regexp,)],
- tables=['maddr']
- ).count()
- return Msgrcpt.objects.filter(rq).count()
-
- def get_mail_content(self, mailid):
- return Quarantine.objects.filter(mail=mailid).extra(
- select={'mail_text': "convert_from(mail_text, 'UTF8')"}
- )
-
-
-def get_wrapper():
- """Return the appropriate *Wrapper class
-
- The result depends on the DB engine in use.
- """
- if db_type("amavis") == 'postgres':
- return PgWrapper()
- return SQLWrapper()
-
-
class SQLlisting(EmailListing):
tpl = "amavis/index.html"
tbltype = Qtable
@@ -299,8 +65,8 @@ class SQLlisting(EmailListing):
defcallback = "updatelisting"
reset_wm_url = True
- def __init__(self, user, msgs, filter, **kwargs):
- self.mbc = SQLconnector(msgs, filter)
+ def __init__(self, user, **kwargs):
+ self.mbc = get_connector(user=user, navparams=kwargs["navparams"])
super(SQLlisting, self).__init__(**kwargs)
self.show_listing_headers = True
@@ -333,7 +99,7 @@ class SQLemail(Email):
import email
if self._msg is None:
- qmails = get_wrapper().get_mail_content(self.mailid)
+ qmails = get_connector().get_mail_content(self.mailid)
self._msg = email.message_from_string(
"".join([qm.mail_text for qm in qmails])
)
diff --git a/modoboa/extensions/amavis/views.py b/modoboa/extensions/amavis/views.py
index b375efcd3..587f37cba 100644
--- a/modoboa/extensions/amavis/views.py
+++ b/modoboa/extensions/amavis/views.py
@@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _, ungettext
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators \
import login_required, user_passes_test
-from django.db.models import Q
from modoboa.lib import parameters
from modoboa.lib.exceptions import BadRequest
from modoboa.lib.webutils import (
@@ -19,8 +18,9 @@ from modoboa.extensions.amavis.templatetags.amavis_tags import (
quar_menu, viewm_menu
)
from .lib import selfservice, AMrelease, QuarantineNavigationParameters
-from .sql_listing import SQLlisting, SQLemail, get_wrapper
from .models import Msgrcpt
+from .sql_connector import get_connector
+from .sql_listing import SQLlisting, SQLemail
def empty_quarantine(request):
@@ -31,45 +31,21 @@ def empty_quarantine(request):
@login_required
def _listing(request):
- flt = None
- rcptfilter = None
- msgs = None
-
if not request.user.is_superuser and request.user.group != 'SimpleUsers':
if not Domain.objects.get_for_admin(request.user).count():
return empty_quarantine(request)
navparams = QuarantineNavigationParameters(request)
navparams.store()
- pattern = navparams.get('pattern', '')
- if pattern:
- criteria = navparams.get('criteria')
- if criteria == "both":
- criteria = "from_addr,subject,to"
- for c in criteria.split(","):
- if c == "from_addr":
- nfilter = Q(mail__from_addr__contains=pattern)
- elif c == "subject":
- nfilter = Q(mail__subject__contains=pattern)
- elif c == "to":
- rcptfilter = pattern
- continue
- else:
- raise BadRequest("unsupported search criteria %s" % c)
- flt = nfilter if flt is None else flt | nfilter
- msgtype = navparams.get('msgtype', None)
- if msgtype is not None:
- nfilter = Q(mail__msgrcpt__content=msgtype)
- flt = flt | nfilter if flt is not None else nfilter
- msgs = get_wrapper().get_mails(request, rcptfilter)
- page = navparams.get('page')
lst = SQLlisting(
- request.user, msgs, flt,
- navparams=request.session["quarantine_navparams"],
- elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE"))
+ request.user,
+ navparams=navparams,
+ elems_per_page=int(
+ parameters.get_user(request.user, "MESSAGES_PER_PAGE")
+ )
)
- page = lst.paginator.getpage(page)
+ page = lst.paginator.getpage(navparams.get('page'))
if not page:
return empty_quarantine(request)
@@ -129,7 +105,7 @@ def viewmail(request, mail_id):
if request.user.mailbox_set.count():
mb = Mailbox.objects.get(user=request.user)
if rcpt == mb.full_address or rcpt in mb.alias_addresses:
- get_wrapper().set_msgrcpt_status(rcpt, mail_id, 'V')
+ get_connector().set_msgrcpt_status(rcpt, mail_id, 'V')
content = Template("""
@@ -143,7 +119,7 @@ def viewmail(request, mail_id):
@login_required
def viewheaders(request, mail_id):
content = ""
- for qm in get_wrapper().get_mail_content(mail_id):
+ for qm in get_connector().get_mail_content(mail_id):
content += qm.mail_text
msg = email.message_from_string(content)
return render(request, 'amavis/viewheader.html', {
@@ -165,7 +141,7 @@ def delete_selfservice(request, mail_id):
if rcpt is None:
raise BadRequest(_("Invalid request"))
try:
- get_wrapper().set_msgrcpt_status(rcpt, mail_id, 'D')
+ get_connector().set_msgrcpt_status(rcpt, mail_id, 'D')
except Msgrcpt.DoesNotExist:
raise BadRequest(_("Invalid request"))
return render_to_json_response(_("Message deleted"))
@@ -178,7 +154,7 @@ def delete(request, mail_id):
:param str mail_id: message unique identifier
"""
mail_id = check_mail_id(request, mail_id)
- wrapper = get_wrapper()
+ connector = get_connector()
mb = Mailbox.objects.get(user=request.user) \
if request.user.group == 'SimpleUsers' else None
for mid in mail_id:
@@ -186,7 +162,7 @@ def delete(request, mail_id):
if mb is not None and r != mb.full_address \
and not r in mb.alias_addresses:
continue
- wrapper.set_msgrcpt_status(r, i, 'D')
+ connector.set_msgrcpt_status(r, i, 'D')
message = ungettext("%(count)d message deleted successfully",
"%(count)d messages deleted successfully",
len(mail_id)) % {"count": len(mail_id)}
@@ -201,21 +177,21 @@ def release_selfservice(request, mail_id):
secret_id = request.GET.get("secret_id", None)
if rcpt is None or secret_id is None:
raise BadRequest(_("Invalid request"))
- wrapper = get_wrapper()
+ connector = get_connector()
try:
- msgrcpt = wrapper.get_recipient_message(rcpt, mail_id)
+ msgrcpt = connector.get_recipient_message(rcpt, mail_id)
except Msgrcpt.DoesNotExist:
raise BadRequest(_("Invalid request"))
if secret_id != msgrcpt.mail.secret_id:
raise BadRequest(_("Invalid request"))
if parameters.get_admin("USER_CAN_RELEASE") == "no":
- wrapper.set_msgrcpt_status(rcpt, mail_id, 'p')
+ connector.set_msgrcpt_status(rcpt, mail_id, 'p')
msg = _("Request sent")
else:
amr = AMrelease()
result = amr.sendreq(mail_id, secret_id, rcpt)
if result:
- wrapper.set_msgrcpt_status(rcpt, mail_id, 'R')
+ connector.set_msgrcpt_status(rcpt, mail_id, 'R')
msg = _("Message released")
else:
raise BadRequest(result)
@@ -230,7 +206,7 @@ def release(request, mail_id):
"""
mail_id = check_mail_id(request, mail_id)
msgrcpts = []
- wrapper = get_wrapper()
+ connector = get_connector()
mb = Mailbox.objects.get(user=request.user) \
if request.user.group == 'SimpleUsers' else None
for mid in mail_id:
@@ -238,10 +214,10 @@ def release(request, mail_id):
if mb is not None and r != mb.full_address \
and not r in mb.alias_addresses:
continue
- msgrcpts += [wrapper.get_recipient_message(r, i)]
+ msgrcpts += [connector.get_recipient_message(r, i)]
if mb is not None and parameters.get_admin("USER_CAN_RELEASE") == "no":
for msgrcpt in msgrcpts:
- wrapper.set_msgrcpt_status(
+ connector.set_msgrcpt_status(
msgrcpt.rid.email, msgrcpt.mail.mail_id, 'p'
)
message = ungettext("%(count)d request sent",
@@ -259,7 +235,7 @@ def release(request, mail_id):
rcpt.mail.mail_id, rcpt.mail.secret_id, rcpt.rid.email
)
if result:
- wrapper.set_msgrcpt_status(rcpt.rid.email, rcpt.mail.mail_id, 'R')
+ connector.set_msgrcpt_status(rcpt.rid.email, rcpt.mail.mail_id, 'R')
else:
error = result
break
@@ -288,3 +264,4 @@ def process(request):
if request.POST["action"] == "delete":
return delete(request, ids)
+
diff --git a/modoboa/extensions/sievefilters/static/sievefilters/js/lib.js b/modoboa/extensions/sievefilters/static/sievefilters/js/lib.js
index 700a4e423..9edd81f62 100644
--- a/modoboa/extensions/sievefilters/static/sievefilters/js/lib.js
+++ b/modoboa/extensions/sievefilters/static/sievefilters/js/lib.js
@@ -20,7 +20,7 @@ function removefilter(evt) {
url: $this.attr("href"),
dataType: 'json'
}).done(function(response) {
- history.update(true);
+ histomanager.update(true);
$("body").notify('success', response, 2000);
});
}
@@ -30,7 +30,7 @@ function removefilter(evt) {
*/
function changefs(evt) {
evt.preventDefault();
- history.baseurl($(this).val()).update();
+ histomanager.baseurl($(this).val()).update();
}
/*
@@ -48,7 +48,7 @@ function init_filters_list() {
* It updates the middle DIV and initializes the user interface.
*/
function loadfs(response) {
- curscript = history.getbaseurl();
+ curscript = histomanager.getbaseurl();
$("#curfset").val(curscript);
$("#set_content").html(response.content);
@@ -78,7 +78,7 @@ function send_command(cmd, extracb) {
extracb(response);
}
if (!response.norefresh) {
- history.update(true);
+ histomanager.update(true);
}
$("body").notify('success', response.respmsg, 2000);
});
@@ -133,7 +133,7 @@ function removefs(evt) {
var $curfset = $("#curfset");
$curfset.find("option[value='" + curscript + "']").remove();
- history.baseurl(response.newfs, 1);
+ histomanager.baseurl(response.newfs, 1);
});
}
@@ -206,7 +206,7 @@ function filterset_created(data) {
}
});
}
- history.baseurl(data.url, 1).update(1);
+ histomanager.baseurl(data.url, 1).update(1);
}
function filtersetform_cb() {
@@ -240,4 +240,4 @@ function fetch_templates(url, container) {
result = data;
});
return result;
-}
\ No newline at end of file
+}
diff --git a/modoboa/extensions/sievefilters/templates/sievefilters/index.html b/modoboa/extensions/sievefilters/templates/sievefilters/index.html
index 63ac35146..884040aa4 100644
--- a/modoboa/extensions/sievefilters/templates/sievefilters/index.html
+++ b/modoboa/extensions/sievefilters/templates/sievefilters/index.html
@@ -7,7 +7,7 @@