Files
modoboa/extensions/amavis_quarantine/main.py
T

313 lines
10 KiB
Python

# -*- coding: utf-8 -*-
"""
"""
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404, render_to_response
from django.template.loader import render_to_string
from django.template import Template
from django.utils import simplejson
from django.conf.urls.defaults import *
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator
from django.contrib.auth.decorators \
import login_required
from mailng.lib import events
from mailng.lib import _render, _ctx_ok, _ctx_ko, decode
from mailng.lib import db
from mailng.admin.models import Mailbox
from lib import AMrelease
from datetime import datetime
import email
import re
import os
import lxml.html
from lxml.html.clean import Cleaner
attached_map = {}
def map_cid(url):
map = globals()["attached_map"]
m = re.match(".*cid:([^\"]+)", url)
if m:
if map.has_key(m.group(1)):
return map[m.group(1)]
return url
def init():
events.register("UserMenuDisplay", menu)
def urls():
return (r'^mailng/quarantine/',
include('mailng.extensions.amavis_quarantine.urls'))
def menu(**kwargs):
if kwargs["target"] != "user_menu_box":
return []
return [
{"name" : _("Quarantine"),
"url" : reverse(index),
"img" : "/static/pics/quarantine.png"}
]
def lookup(*args):
filter = ""
for a in args:
if not a:
continue
if a[0] == "&":
filter += " AND "
else:
filter += " OR "
filter += a[1:]
conn = db.getconnection("amavis_quarantine")
status, cursor = db.execute(conn, """
SELECT msgs.from_addr, maddr.email, msgs.subject, msgs.content, quarantine.mail_id,
msgs.time_num, msgs.content
FROM quarantine, maddr, msgrcpt, msgs
WHERE quarantine.mail_id=msgrcpt.mail_id
AND msgrcpt.rid=maddr.id
AND msgrcpt.mail_id=msgs.mail_id
AND quarantine.chunk_ind=1
%s
ORDER BY msgs.time_num DESC
""" % filter)
return cursor
def render_listing(request, cursor):
emails = []
rows = cursor.fetchall()
for row in rows:
emails.append({"from" : row[0], "to" : row[1],
"subject" : row[2], "content" : row[3],
"mailid" : row[4], "time" : datetime.fromtimestamp(row[5]),
"type" : row[6]})
paginator = Paginator(emails, 10)
try:
pagenum = int(request.GET.get('page', '1'))
except ValueError:
pagenum = 1
page = paginator.page(pagenum)
return render_to_string("amavis_quarantine/listing.html", {
"count" : len(rows),
"emails" : page, "current_page" : pagenum,
"last_page" : paginator._get_num_pages()
})
@login_required
def index(request, message=None):
if not request.user.is_superuser:
mb = Mailbox.objects.get(user=request.user.id)
filter = "&maddr.email='%s'" % mb.full_address
else:
filter = None
cursor = lookup(filter)
return _render(request, 'amavis_quarantine/index.html', {
"content" : render_listing(request, cursor)
})
@login_required
def viewmail_plain(request, content):
body = decode(content)
return (True, body)
@login_required
def viewmail_html(request, content):
links = request.GET.has_key("links") and request.GET["links"] or "0"
html = lxml.html.fromstring(content)
if links == "0":
html.rewrite_links(lambda x: None)
else:
html.rewrite_links(map_cid)
body = lxml.html.tostring(html)
body = Template(decode(body)).render({})
return (False, body)
@login_required
def getmailcontent(request, mail_id):
conn = db.getconnection("amavis_quarantine")
status, cursor = db.execute(conn, """
SELECT mail_text
FROM quarantine
WHERE quarantine.mail_id='%s'
""" % mail_id)
content = ""
for part in cursor.fetchall():
content += part[0]
msg = email.message_from_string(content)
links = request.GET.has_key("links") and request.GET["links"] or "0"
mode = request.GET.has_key("mode") and request.GET["mode"] or "plain"
contents = {"html" : "", "plain" : ""}
for part in msg.walk():
print part.get_content_type()
if part.get_content_maintype() == 'multipart':
continue
if part.get_content_type() in ("text/html", "text/plain"):
contents[part.get_content_subtype()] += part.get_payload(decode=True)
if mode != "html" or links == "0":
continue
if part.get_content_maintype() == "image":
if part.has_key("Content-Location"):
fname = part["Content-Location"]
if re.match("^http:", fname):
path = fname
else:
path = "/%s" % os.path.join("static/tmp", fname)
fp = open(path, "wb")
fp.write(part.get_payload(decode=True))
fp.close()
m = re.match("<(.+)>", part["Content-ID"])
if m:
attached_map[m.group(1)] = path
else:
attached_map[part["Content-ID"]] = path
if not contents.has_key(mode) or contents[mode] == "":
if mode == "html":
mode = "plain"
else:
mode = "html"
(pre, body) = globals()["viewmail_%s" % mode](request, contents[mode])
return _render(request, 'amavis_quarantine/getmailcontent.html', {
"body" : body, "pre" : pre
})
@login_required
def viewmail(request, mail_id):
conn = db.getconnection("amavis_quarantine")
status, cursor = db.execute(conn, """
SELECT mail_text
FROM quarantine
WHERE quarantine.mail_id='%s'
""" % mail_id)
content = ""
for part in cursor.fetchall():
content += part[0]
msg = email.message_from_string(content)
return _render(request, 'amavis_quarantine/viewmail.html', {
"headers" : msg, "alert" : msg.get("X-Amavis-Alert"),
"mail_id" : mail_id, "page_id" : request.GET["page"],
"params" : request.GET
})
@login_required
def viewheaders(request, mail_id):
conn = db.getconnection("amavis_quarantine")
status, cursor = db.execute(conn, """
SELECT mail_text
FROM quarantine
WHERE quarantine.mail_id='%s'
""" % mail_id)
content = ""
for part in cursor.fetchall():
content += part[0]
msg = email.message_from_string(content)
return _render(request, 'amavis_quarantine/viewheader.html', {
"headers" : msg.items()
})
def _redirect_to_index(request, message, count):
request.user.message_set.create(message=message)
if count > 1:
return
page = request.GET.has_key("page") and request.GET["page"] or "1"
return HttpResponseRedirect(reverse(index) + "?page=%s" % page)
@login_required
def delete(request, mail_id, count=1):
conn = db.getconnection("amavis_quarantine")
status, error = db.execute(conn,
"DELETE FROM msgs WHERE mail_id IN (%s)" % mail_id)
if status:
message = _("%(count)d message%(plural)s deleted successfully" \
% ({"count" : count, "plural" : count > 1 and "s" or ""}))
else:
message = error
return _redirect_to_index(request, message, count)
@login_required
def release(request, mail_id, count=1):
conn = db.getconnection("amavis_quarantine")
status, cursor = db.execute(conn, """
SELECT msgs.mail_id,secret_id,quar_type,maddr.email FROM msgs, maddr, msgrcpt
WHERE msgrcpt.mail_id=msgs.mail_id AND msgrcpt.rid=maddr.id AND msgs.mail_id IN (%s)
""" % (mail_id))
emails = {}
amr = AMrelease()
for row in cursor.fetchall():
if not emails.has_key(row[0]):
emails[row[0]] = {}
emails[row[0]]["rcpts"] = []
emails[row[0]]["secret"] = row[1]
emails[row[0]]["rcpts"] += [row[3]]
count = 0
error = None
for k, values in emails.iteritems():
result = amr.sendreq(k, values["secret"], *values["rcpts"])
# cmd = "/tmp/amavisd-release "
# cmd += "%s %s" % (k, values["secret"])
# for rcpt in values["rcpts"]:
# cmd += " %s" % rcpt
# cmd += "\n"
# result = os.system(cmd)
# if re.search("250 [^\s]+ Ok", result):
if result:
count += 1
else:
error = result
break
if not error:
message = _("%(count)d message%(plural)s released successfully" \
% {"count" : count, "plural" : count > 1 and "s" or ""})
else:
message = error
_redirect_to_index(request, message, count)
@login_required
def process(request):
ids = ""
count = len(request.POST.getlist("selection"))
for id in request.POST.getlist("selection"):
if ids != "":
ids += ","
ids += "'%s'" % id
if ids == "":
return HttpResponseRedirect(reverse(index))
if request.POST["action"] == "release":
release(request, ids, count)
if request.POST["action"] == "delete":
delete(request, ids, count)
try:
pagenum = int(request.POST.get('pagenum', '1'))
except ValueError:
pagenum = 1
ctx = _ctx_ok(reverse(index) + "?page=%s" % pagenum)
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
@login_required
def search(request):
if not request.GET.has_key("pattern"):
return
if request.GET.has_key("criteria"):
criteria = request.GET["criteria"].split(',')
else:
criteria = ["from_addr"]
filter = ""
pattern = re.escape(request.GET["pattern"])
for c in criteria:
if filter != "":
filter += " OR "
filter += "msgs.%s LIKE '%%%s%%'" % (c, pattern)
filter = "&(%s)" % filter
cursor = lookup(filter)
ctx = {"status" : "ok", "content" : render_listing(request, cursor)}
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")