From bf971301b6f5f7154bd350230e85a20e7635b52a Mon Sep 17 00:00:00 2001 From: tonio Date: Fri, 15 Jan 2010 18:06:41 +0100 Subject: [PATCH] More work about webmail. It's almost possible to view emails content! --- auth/views.py | 1 + extensions/amavis_quarantine/main.py | 69 ++----------------- extensions/webmail/imap_listing.py | 19 ++++-- extensions/webmail/main.py | 28 +++++++- extensions/webmail/templates/index.html | 2 + extensions/webmail/templates/viewmail.html | 47 +++++++++++++ extensions/webmail/templatetags/__init__.py | 0 extensions/webmail/templatetags/webextras.py | 20 ++++++ extensions/webmail/urls.py | 6 +- lib/email_listing.py | 72 +++++++++++++++++++- static/stylesheets/default.css | 2 +- static/stylesheets/login.css | 7 +- templates/common/getmailcontent.html | 1 + templates/nlayout.html | 7 +- 14 files changed, 204 insertions(+), 77 deletions(-) create mode 100644 extensions/webmail/templates/viewmail.html create mode 100644 extensions/webmail/templatetags/__init__.py create mode 100644 extensions/webmail/templatetags/webextras.py create mode 100644 templates/common/getmailcontent.html diff --git a/auth/views.py b/auth/views.py index 7cd567b80..75e730a90 100755 --- a/auth/views.py +++ b/auth/views.py @@ -16,6 +16,7 @@ def dologin(request): if user: if user.is_active: login(request, user) + request.session["password"] = request.POST["password"] next = request.POST["next"] if next == "None": diff --git a/extensions/amavis_quarantine/main.py b/extensions/amavis_quarantine/main.py index c6f319ce2..946e2bf4d 100644 --- a/extensions/amavis_quarantine/main.py +++ b/extensions/amavis_quarantine/main.py @@ -7,10 +7,7 @@ import time import email import re import os -import lxml.html -from lxml.html.clean import Cleaner from django.http import HttpResponseRedirect, HttpResponse -from django.template import Template from django.utils import simplejson from django.conf.urls.defaults import * from django.utils.translation import ugettext as _ @@ -24,16 +21,6 @@ from mailng.admin.models import Mailbox from lib import AMrelease from sql_listing import * -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) parameters.register("amavis_quarantine", "MAX_MESSAGES_AGE", "int", 14, @@ -69,25 +56,10 @@ def index(request, message=None): lst = SQLlisting(filter) return lst.render(request, pageid=int(pageid), start=start) -@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): + from mailng.lib.email_listing import Email + conn = db.getconnection("amavis_quarantine") status, cursor = db.execute(conn, """ SELECT mail_text @@ -100,41 +72,8 @@ WHERE quarantine.mail_id='%s' 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 - }) + mail = Email(msg, mode, links) + return mail.render(request) @login_required def viewmail(request, mail_id): diff --git a/extensions/webmail/imap_listing.py b/extensions/webmail/imap_listing.py index b6ff4ad19..c5977d064 100644 --- a/extensions/webmail/imap_listing.py +++ b/extensions/webmail/imap_listing.py @@ -82,20 +82,27 @@ class IMAPconnector(MBconnector): result += [name] return sorted(result) - def fetch(self, start=None, stop=None, folder=None): - if not start or not stop: + def fetch(self, start=None, stop=None, folder=None, all=False): + if not start and not stop: return [] result = [] self.m.select(self._encodefolder(folder)) - range = "%d:%d" % (start, stop) - typ, data = self.m.fetch(range, '(BODY[HEADER.FIELDS (DATE FROM SUBJECT)])') - imapid = start + if start and stop: + range = "%d:%d" % (start, stop) + else: + range = start + if not all: + query = '(BODY[HEADER.FIELDS (DATE FROM TO SUBJECT)])' + else: + query = '(RFC822)' + typ, data = self.m.fetch(range, query) + imapid = int(start) for response_part in data: if isinstance(response_part, tuple): msg = email.message_from_string(response_part[1]) msg["imapid"] = imapid result += [msg] - imapid += 1 + imapid = imapid + 1 return result class ImapListing(EmailListing): diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py index 684587404..cc2dc6381 100644 --- a/extensions/webmail/main.py +++ b/extensions/webmail/main.py @@ -41,7 +41,8 @@ def index(request): @login_required def folder(request, folder): - lst = ImapListing(folder=folder) + lst = ImapListing(request.user.username, request.session["password"], + folder=folder) try: pageid = request.GET["page"] except KeyError: @@ -55,3 +56,28 @@ def folder(request, folder): navbar = "" ctx = {"status" : "ok", "listing" : content, "navbar" : navbar} return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + +def fetchmail(request, mail_id, all=False): + mbc = IMAPconnector(parameters.get("webmail", "SERVER_ADDRESS"), 143) + mbc.login(request.user.username, request.session["password"]) + res = mbc.fetch(start=mail_id, all=all) + if len(res): + return res[0] + return None + +@login_required +def viewmail(request, mail_id): + header = fetchmail(request, mail_id) + pageid = request.GET.has_key("page") and request.GET["page"] or "1" + if header: + return _render(request, "webmail/viewmail.html", { + "header" : header, "pageid" : pageid + }) + +@login_required +def getmailcontent(request, mail_id): + from mailng.lib.email_listing import Email + + msg = fetchmail(request, mail_id, True) + email = Email(msg) + return email.render(request) diff --git a/extensions/webmail/templates/index.html b/extensions/webmail/templates/index.html index af4971081..eb15bc75d 100644 --- a/extensions/webmail/templates/index.html +++ b/extensions/webmail/templates/index.html @@ -57,6 +57,8 @@ $("navbar").set("html", resp.navbar); }}).get(); }); + {% url extensions.webmail.main.viewmail as view_url %} + bindRows("{{ view_url }}", "{{ current_page }}"); {% endblock %} {% block middletitle %}{% trans "Webmail" %}{% endblock %} {% block menu %} diff --git a/extensions/webmail/templates/viewmail.html b/extensions/webmail/templates/viewmail.html new file mode 100644 index 000000000..b2cb5e310 --- /dev/null +++ b/extensions/webmail/templates/viewmail.html @@ -0,0 +1,47 @@ +{% extends "nlayout.html" %} +{% load i18n %} +{% block html_head %} + + +{% endblock %} +{% block middletitle %}{% trans "Email content" %}{% endblock %} +{% block menu %} + {% load webextras %} + {% viewm_menu "" header.imapid pageid perms %} +{% endblock %} +{% block content %} +
+ + + + + +
{% trans "Subject" %}{{ header.Subject }}
{% trans "From" %}{{ header.From }}
{% trans "To" %}{{ header.To }}
{% trans "Date" %}{{ header.Date }}
+
+ +{% url extensions.webmail.main.getmailcontent header.imapid as geturl %} + +{% endblock %} diff --git a/extensions/webmail/templatetags/__init__.py b/extensions/webmail/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/extensions/webmail/templatetags/webextras.py b/extensions/webmail/templatetags/webextras.py new file mode 100644 index 000000000..99bc4cc5c --- /dev/null +++ b/extensions/webmail/templatetags/webextras.py @@ -0,0 +1,20 @@ +from django import template +from django.template.loader import render_to_string +from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +from mailng.extensions import webmail + +register = template.Library() + +@register.simple_tag +def viewm_menu(selection, mail_id, page_id, perms): + entries = [ + {"name" : "back", + "url" : reverse(webmail.main.index) + "?page=%s" % page_id, + "img" : "/static/pics/back.png", + "label" : _("Back to list")}, + ] + + return render_to_string('main/menu.html', + {"selection" : selection, "entries" : entries, + "perms" : perms}) diff --git a/extensions/webmail/urls.py b/extensions/webmail/urls.py index c61787ee9..0203315fd 100644 --- a/extensions/webmail/urls.py +++ b/extensions/webmail/urls.py @@ -2,5 +2,9 @@ from django.conf.urls.defaults import * urlpatterns = patterns('mailng.extensions.webmail.main', (r'^$', "index"), - (r'^(?P.+)/$', "folder"), + url(r'^viewmail/(?P[\w\-\+]+)/$', 'viewmail', + name="viewmail_std"), + url(r'^viewmail/$', 'viewmail', name="viewmail_null"), + (r'^getmailcontent/(?P[\w\-\+]+)/$', 'getmailcontent'), +# (r'^(?P.+)/$', "folder"), ) diff --git a/lib/email_listing.py b/lib/email_listing.py index e56867631..c5bdf5d6a 100644 --- a/lib/email_listing.py +++ b/lib/email_listing.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- import time +import lxml from django.template.loader import render_to_string -from mailng.lib import _render +from django.template import Template +from mailng.lib import _render, decode + +attached_map = {} class MBconnector(object): def __init__(self, address, port): @@ -93,3 +97,69 @@ class EmailListing: "selection" : self.folder, "current_page" : pageid }) + +class Email(object): + def __init__(self, msg, mode="plain", links="0"): + self.attached_map = {} + contents = {"html" : "", "plain" : ""} + for part in msg.walk(): + 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: + self.attached_map[m.group(1)] = path + else: + self.attached_map[part["Content-ID"]] = path + + if not contents.has_key(mode) or contents[mode] == "": + if mode == "html": + mode = "plain" + else: + mode = "html" + self.pre, self.body = \ + getattr(self, "viewmail_%s" % mode)(contents[mode], links=links) + + def map_cid(self, url): + import re + + 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 render(self, request): + return _render(request, 'common/getmailcontent.html', { + "body" : self.body, "pre" : self.pre + }) + + def viewmail_plain(self, content, **kwargs): + body = decode(content) + return (True, body) + + def viewmail_html(self, content, **kwargs): + links = kwargs.has_key("links") and kwargs["links"] or "0" + html = lxml.html.fromstring(content) + if links == "0": + html.rewrite_links(lambda x: None) + else: + html.rewrite_links(self.map_cid) + body = lxml.html.tostring(html) + body = Template(decode(body)).render({}) + return (False, body) diff --git a/static/stylesheets/default.css b/static/stylesheets/default.css index 84ef58b6c..07b9c14e0 100644 --- a/static/stylesheets/default.css +++ b/static/stylesheets/default.css @@ -4,7 +4,7 @@ html, body { background: #153F65; margin: 0; padding: 0; - + height: 100%; } #topmenubar { diff --git a/static/stylesheets/login.css b/static/stylesheets/login.css index 11060e5c0..798736283 100644 --- a/static/stylesheets/login.css +++ b/static/stylesheets/login.css @@ -10,8 +10,13 @@ html, body { margin: 100px auto; width: 610px; padding-bottom: 15px; - border: 5px solid #333; background: white; + + border: 5px solid #333; + /*-moz-box-shadow: 0 10px 30px #222;*/ + -webkit-box-shadow: 0 10px 30px #222; + -moz-border-radius: 10px; + -webkit-border-radius: 5px; } #loginbox h2 { diff --git a/templates/common/getmailcontent.html b/templates/common/getmailcontent.html new file mode 100644 index 000000000..872bdaf88 --- /dev/null +++ b/templates/common/getmailcontent.html @@ -0,0 +1 @@ +{% if pre %}
{% endif %}{{ body }}{% if pre %}
{% endif %} diff --git a/templates/nlayout.html b/templates/nlayout.html index 85ef5d431..f86d4ab33 100644 --- a/templates/nlayout.html +++ b/templates/nlayout.html @@ -22,8 +22,13 @@