diff --git a/modoboa/extensions/webmail/__init__.py b/modoboa/extensions/webmail/__init__.py index 8299c6ddc..c25468e2a 100644 --- a/modoboa/extensions/webmail/__init__.py +++ b/modoboa/extensions/webmail/__init__.py @@ -2,7 +2,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy from django.core.urlresolvers import reverse from modoboa.lib import events, parameters -from modoboa.extensions import ModoExtension, exts_pool +from modoboa.core.extensions import ModoExtension, exts_pool class Webmail(ModoExtension): @@ -13,7 +13,7 @@ class Webmail(ModoExtension): needs_media = True def load(self): - from app_settings import ParametersForm, UserSettings + from .app_settings import ParametersForm, UserSettings parameters.register(ParametersForm, "Webmail") parameters.register(UserSettings, "Webmail") @@ -24,22 +24,24 @@ class Webmail(ModoExtension): exts_pool.register_extension(Webmail) + @events.observe("UserMenuDisplay") def menu(target, user): if target != "top_menu": return [] - if not user.has_mailbox: + if not user.mailbox_set.count(): return [] return [ - {"name" : "webmail", - "label" : _("Webmail"), - "url" : reverse("modoboa.extensions.webmail.views.index")}, - ] + {"name": "webmail", + "label": _("Webmail"), + "url": reverse("modoboa.extensions.webmail.views.index")}, + ] + @events.observe("UserLogout") def userlogout(request): - from lib import IMAPconnector - from exceptions import ImapError + from .lib import IMAPconnector + from .exceptions import ImapError if not request.user.has_mailbox: return diff --git a/modoboa/extensions/webmail/exceptions.py b/modoboa/extensions/webmail/exceptions.py index 15465e565..f5e56cf41 100644 --- a/modoboa/extensions/webmail/exceptions.py +++ b/modoboa/extensions/webmail/exceptions.py @@ -8,6 +8,7 @@ import re from django.utils.translation import ugettext as _ from modoboa.lib.exceptions import ModoboaException + class WebmailError(ModoboaException): errorexpr = re.compile("\[([^\]]+)\]\s*([^\.]+)") @@ -22,6 +23,7 @@ class WebmailError(ModoboaException): def __str__(self): return self.reason + class ImapError(ModoboaException): def __init__(self, reason): self.reason = reason diff --git a/modoboa/extensions/webmail/fetch_parser.py b/modoboa/extensions/webmail/fetch_parser.py index 22fd79a4b..b049c4de9 100644 --- a/modoboa/extensions/webmail/fetch_parser.py +++ b/modoboa/extensions/webmail/fetch_parser.py @@ -10,7 +10,10 @@ retrieve multiple attributes of a message. This simple module tries to fix that *problem*. """ -class ParseError(Exception): pass + +class ParseError(Exception): + pass + def parse_next_token(buf): """Look for the next *token* @@ -38,6 +41,7 @@ def parse_next_token(buf): raise ParseError("End of buffer reached while looking for a token end") return end + def parse_bodystructure(buf, depth=0, prefix=""): """Special parser for BODYSTRUCTURE response @@ -49,7 +53,7 @@ def parse_bodystructure(buf, depth=0, prefix=""): character into ``buf`` """ ret = [] - pos = 1 # skip the first ')' + pos = 1 # skip the first ')' nb_bodystruct = 0 pnum = 1 while pos < len(buf): @@ -79,7 +83,7 @@ def parse_bodystructure(buf, depth=0, prefix=""): if type(ret[0]) == list or len(ret) >= 7: partnum = prefix if partnum is not None: - return [{"partnum" : partnum, "struct" : ret}], pos + return [{"partnum": partnum, "struct": ret}], pos return ret, pos if c == ' ': pos += 1 @@ -91,6 +95,7 @@ def parse_bodystructure(buf, depth=0, prefix=""): raise ParseError("End of buffer reached while looking for a BODY/BODYSTRUCTURE end") + def parse_fetch_response(data): """Parse a FETCH response, previously issued by a UID command @@ -166,6 +171,7 @@ def parse_fetch_response(data): return result + def dump_bodystructure(bs, depth=0): if depth: print " " * (depth * 4), diff --git a/modoboa/extensions/webmail/imaputils.py b/modoboa/extensions/webmail/imaputils.py index 189351705..fe68c672c 100644 --- a/modoboa/extensions/webmail/imaputils.py +++ b/modoboa/extensions/webmail/imaputils.py @@ -3,7 +3,9 @@ :mod:`imaputils` --- Extra IMAPv4 utilities ------------------------------------------- """ -import imaplib, ssl, email +import imaplib +import ssl +import email import re import time from functools import wraps @@ -17,6 +19,7 @@ from fetch_parser import parse_fetch_response #imaplib.Debug = 4 + class capability(object): """ Simple decorator to check if the server presents the required @@ -38,6 +41,7 @@ class capability(object): return wrapped_func + class BodyStructure(object): """ BODYSTRUCTURE response parser. @@ -87,7 +91,7 @@ class BodyStructure(object): subtype = definition[1].lower() ftype = "%s/%s" % (definition[0].lower(), subtype) if ftype in ("text/plain", "text/html"): - if not self.contents.has_key(subtype): + if not subtype in self.contents: self.contents[subtype] = [params] else: self.contents[subtype].append(params) @@ -135,6 +139,7 @@ class BodyStructure(object): return att return None + class IMAPconnector(object): __metaclass__ = ConnectionsManager @@ -228,7 +233,7 @@ class IMAPconnector(object): if self.m is not None: try: self._cmd("CHECK") - except ImapError, e: + except ImapError: if hasattr(self, "current_mailbox"): del self.current_mailbox else: @@ -261,7 +266,7 @@ class IMAPconnector(object): data = self._cmd("LOGIN", user, passwd) self.m.state = "AUTH" - if self.m.untagged_responses.has_key("CAPABILITY"): + if "CAPABILITY" in self.m.untagged_responses: self.capabilities = \ self.m.untagged_responses.pop('CAPABILITY')[0].split() else: @@ -271,7 +276,7 @@ class IMAPconnector(object): def logout(self): try: self._cmd("CHECK") - except ImapError, e: + except ImapError: pass self._cmd("LOGOUT") del self.m @@ -353,7 +358,7 @@ class IMAPconnector(object): sdescr = d break if sdescr is None: - sdescr = {"name" : parts[0], "path" : path, "sub" : []} + sdescr = {"name": parts[0], "path": path, "sub": []} descr += [sdescr] if self._parse_mailbox_name(sdescr["sub"], path, delimiter, parts[1:]): sdescr["class"] = "subfolders" @@ -379,7 +384,7 @@ class IMAPconnector(object): if re.search("\%s" % delimiter, name): parts = name.split(".") - if not descr.has_key("path"): + if not "path" in descr: descr["path"] = parts[0] descr["sub"] = [] if self._parse_mailbox_name(descr["sub"], parts[0], delimiter, @@ -443,14 +448,16 @@ class IMAPconnector(object): if topmailbox: md_mailboxes = [] else: - md_mailboxes = [{"name" : "INBOX", "class" : "icon-inbox"}, - {"name" : parameters.get_user(user, "DRAFTS_FOLDER"), - "class" : "icon-file"}, - {"name" : 'Junk', "class" : "icon-fire"}, - {"name" : parameters.get_user(user, "SENT_FOLDER"), - "class" : "icon-envelope"}, - {"name" : parameters.get_user(user, "TRASH_FOLDER"), - "class" : "icon-trash"}] + md_mailboxes = [ + {"name": "INBOX", "class": "icon-inbox"}, + {"name": parameters.get_user(user, "DRAFTS_FOLDER"), + "class": "icon-file"}, + {"name": 'Junk', "class": "icon-fire"}, + {"name": parameters.get_user(user, "SENT_FOLDER"), + "class": "icon-envelope"}, + {"name": parameters.get_user(user, "TRASH_FOLDER"), + "class": "icon-trash"} + ] if until_mailbox: name, parent = separate_mailbox(until_mailbox) if parent: @@ -459,10 +466,10 @@ class IMAPconnector(object): if unseen_messages: for mb in md_mailboxes: - if not mb.has_key("send_status"): + if not "send_status" in mb: continue del mb["send_status"] - key = mb.has_key("path") and "path" or "name" + key = "path" if "path" in mb else "name" count = self.unseen_messages(mb[key]) if count == 0: continue @@ -619,7 +626,7 @@ class IMAPconnector(object): if r'\Answered' in data[int(uid)]['FLAGS']: msg['img_flags'] = [static_url('pics/answered.png')] if r'$Forwarded' in data[int(uid)]['FLAGS']: - if msg.has_key('img_flags'): + if 'img_flags' in msg: msg['img_flags'] += [static_url('pics/forwarded.png')] else: msg['img_flags'] = [static_url('pics/forwarded.png')] @@ -650,9 +657,10 @@ class IMAPconnector(object): data = self._cmd( "FETCH", mailid, "(BODYSTRUCTURE %s[HEADER.FIELDS (%s)])" % (bcmd, " ".join(headers)) - ) + ) return data[int(mailid)] + def separate_mailbox(fullname, sep="."): """Split a mailbox name @@ -666,6 +674,7 @@ def separate_mailbox(fullname, sep="."): return name, parent return fullname, None + def get_imapconnector(request): """Simple shortcut to create a connector @@ -679,4 +688,3 @@ def get_imapconnector(request): if __name__ == "__main__": import doctest doctest.testmod() - diff --git a/modoboa/extensions/webmail/lib.py b/modoboa/extensions/webmail/lib.py index e78463b75..df40d947a 100644 --- a/modoboa/extensions/webmail/lib.py +++ b/modoboa/extensions/webmail/lib.py @@ -1,24 +1,24 @@ # coding: utf-8 +import os +import re import time from datetime import datetime, timedelta import email import lxml import chardet - from django.core.files.uploadhandler import FileUploadHandler, SkipFile from django.utils.translation import ugettext as _, ugettext_lazy from django.conf import settings - -import os -import re from modoboa.lib import u2u_decode, tables, parameters from modoboa.lib.webutils import size2integer from modoboa.lib.email_listing import EmailListing -from modoboa.extensions.webmail.exceptions import WebmailError -from modoboa.extensions.webmail.imaputils import IMAPconnector, get_imapconnector, BodyStructure from modoboa.lib.emailutils import ( EmailAddress, Email, prepare_addresses, set_email_headers ) +from modoboa.extensions.webmail.exceptions import WebmailError +from modoboa.extensions.webmail.imaputils import ( + IMAPconnector, get_imapconnector, BodyStructure +) class WMtable(tables.Table): @@ -32,8 +32,12 @@ class WMtable(tables.Table): ) flags = tables.ImgColumn("flags", width="4%") withatts = tables.ImgColumn("withatts", width="2%") - subject = tables.Column("subject", label=ugettext_lazy("Subject"), width="50%", limit=60) - from_ = tables.Column("from", width="20%", label=ugettext_lazy("From"), limit=30) + subject = tables.Column( + "subject", label=ugettext_lazy("Subject"), width="50%", limit=60 + ) + from_ = tables.Column( + "from", width="20%", label=ugettext_lazy("From"), limit=30 + ) date = tables.Column("date", width="15%", label=ugettext_lazy("Date")) cols_order = ["select", "withatts", "flags", "subject", "from_", "date"] @@ -138,7 +142,7 @@ class ImapListing(EmailListing): def __init__(self, user, password, **kwargs): self.user = user self.mbc = IMAPconnector(user=user.username, password=password) - if kwargs.has_key("pattern"): + if "pattern" in kwargs: self.parse_search_parameters(kwargs["criteria"], kwargs["pattern"]) else: @@ -177,13 +181,14 @@ class ImapListing(EmailListing): def computequota(mbc): try: return int(float(mbc.quota_actual) \ - / float(mbc.quota_limit) * 100) + / float(mbc.quota_limit) * 100) except (AttributeError, TypeError): return -1 def getquota(self): return ImapListing.computequota(self.mbc) + class ImapEmail(Email): headernames = [ ('From', True), @@ -191,7 +196,7 @@ class ImapEmail(Email): ('Cc', True), ('Date', True), ('Subject', True), - ] + ] def __init__(self, mbox, mailid, request, dformat="DISPLAYMODE", addrfull=False, links=0): @@ -251,7 +256,7 @@ class ImapEmail(Email): except AttributeError: value = msg[name] if hdr[1]: - self.headers += [{"name" : label, "value" : value}] + self.headers += [{"name": label, "value": value}] try: label = re.sub("-", "_", label) setattr(self, label, value) @@ -260,11 +265,11 @@ class ImapEmail(Email): @property def headers_as_list(self): - return map(lambda hdr: hdr[0].upper(), self.headernames) + return [hdr[0].upper() for hdr in self.headernames] @property def headers_as_text(self): - return " ".join(map(lambda hdr: hdr[0].upper(), self.headernames)) + return " ".join(self.headers_as_list()) def _find_content_charset(self, part): for pos, elem in enumerate(part["params"]): @@ -277,12 +282,12 @@ class ImapEmail(Email): attname = "part_%s" % att["pnum"] params = None key = None - if att.has_key("params") and att["params"] != "NIL": + if "params" in att and att["params"] != "NIL": params = att["params"] key = "name" if key is None and \ - att.has_key("disposition") and len(att["disposition"]) > 1: + "disposition" in att and len(att["disposition"]) > 1: params = att["disposition"][1] key = "filename" @@ -308,13 +313,10 @@ class ImapEmail(Email): fp.write(decode_payload(params["encoding"], content)) fp.close() - def map_cid(self, url): - import re - m = re.match(".*cid:(.+)", url) if m: - if self.bs.inlines.has_key(m.group(1)): + if m.group(1) in self.bs.inlines: return self.bs.inlines[m.group(1)]["fname"] return url @@ -322,12 +324,13 @@ class ImapEmail(Email): from django.template.loader import render_to_string res = render_to_string("webmail/headers.html", { - "headers" : self.headers, - "folder" : kwargs["folder"], "mail_id" : kwargs["mail_id"], - "attachments" : self.attachments != {} and self.attachments or None - }) + "headers": self.headers, + "folder": kwargs["folder"], "mail_id": kwargs["mail_id"], + "attachments": self.attachments != {} and self.attachments or None + }) return res + class Modifier(ImapEmail): def __init__(self, *args, **kwargs): super(Modifier, self).__init__(*args, **kwargs) @@ -348,7 +351,7 @@ class ReplyModifier(Modifier): def __init__(self, mbox, mailid, request, form, **kwargs): super(ReplyModifier, self).__init__( mbox, mailid, request, dformat="EDITOR", **kwargs - ) + ) self.textheader = "%s %s" % (self.From, _("wrote:")) @@ -358,7 +361,7 @@ class ReplyModifier(Modifier): form.fields["to"].initial = self.From else: form.fields["to"].initial = self.Reply_To - if request.GET.get("all", "0") == "1": # reply-all + if request.GET.get("all", "0") == "1": # reply-all form.fields["cc"].initial = "" toparse = self.To.split(",") if hasattr(self, 'Cc'): @@ -386,11 +389,12 @@ class ReplyModifier(Modifier): body += ">%s" % l self.body = body + class ForwardModifier(Modifier): def __init__(self, mbox, mailid, request, form, **kwargs): super(ForwardModifier, self).__init__( mbox, mailid, request, dformat="EDITOR", **kwargs - ) + ) self._header() form.fields["subject"].initial = "Fwd: %s" % self.Subject @@ -433,6 +437,7 @@ class ForwardModifier(Modifier): def _header_end_html(self): return "" + class EmailSignature(object): """User signature @@ -459,6 +464,7 @@ class EmailSignature(object): def __repr__(self): return self._sig + def decode_payload(encoding, payload): """Decode the payload according to the given encoding @@ -477,6 +483,7 @@ def decode_payload(encoding, payload): return quopri.decodestring(payload) return payload + def find_images_in_body(body): """Looks for images inside a HTML body @@ -510,13 +517,14 @@ def find_images_in_body(body): fp.close() p["Content-ID"] = "<%s>" % cid ct = p["Content-Type"] - p.replace_header("Content-Type", '%s; name="%s"' \ - % (ct, os.path.basename(fname))) + p.replace_header("Content-Type", '%s; name="%s"' + % (ct, os.path.basename(fname))) p["Content-Disposition"] = "inline" parts.append(p) return lxml.html.tostring(html), parts + def set_compose_session(request): """Initialize a new "compose" session. @@ -529,9 +537,10 @@ def set_compose_session(request): """ import uuid randid = str(uuid.uuid4()).replace("-", "") - request.session["compose_mail"] = {"id" : randid, "attachments" : []} + request.session["compose_mail"] = {"id": randid, "attachments": []} return randid + def save_attachment(f): """Save a new attachment to the filesystem. @@ -554,6 +563,7 @@ def save_attachment(f): fp.close() return fp.name + def clean_attachments(attlist): """Remove all attachments from the filesystem @@ -564,9 +574,10 @@ def clean_attachments(attlist): fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"]) try: os.remove(fullpath) - except OSError, e: + except OSError: pass + def html2plaintext(content): """HTML to plain text translation @@ -586,8 +597,9 @@ def html2plaintext(content): return plaintext + def get_current_url(request): - if not request.session.has_key("folder"): + if not "folder" in request.session: return "" res = "%s?page=%s" % (request.session["folder"], request.session["page"]) @@ -596,6 +608,7 @@ def get_current_url(request): res += "&%s=%s" % (p, request.session[p]) return res + def create_mail_attachment(attdef): """Create the MIME part corresponding to the given attachment. @@ -629,7 +642,7 @@ def send_mail(request, posturl=None): :return: a 2-uple (True|False, HttpResponse) """ from email.mime.multipart import MIMEMultipart - from forms import ComposeMailForm + from .forms import ComposeMailForm from modoboa.lib.webutils import _render_to_string from modoboa.auth.lib import get_password @@ -709,11 +722,12 @@ def send_mail(request, posturl=None): return True, dict(url=get_current_url(request)) listing = _render_to_string(request, "webmail/compose.html", - {"form" : form, "noerrors" : True, - "body" : request.POST["id_body"].strip(), - "posturl" : posturl}) + {"form": form, "noerrors": True, + "body": request.POST["id_body"].strip(), + "posturl": posturl}) return False, dict(status="ko", listing=listing, editor=editormode) + class AttachmentUploadHandler(FileUploadHandler): """ Simple upload handler to limit the size of the attachments users diff --git a/modoboa/extensions/webmail/middleware.py b/modoboa/extensions/webmail/middleware.py index 6e4de692e..e5693f318 100644 --- a/modoboa/extensions/webmail/middleware.py +++ b/modoboa/extensions/webmail/middleware.py @@ -2,6 +2,7 @@ from modoboa.lib.webutils import ajax_response, _render_error from modoboa.extensions.webmail.exceptions import WebmailError, ImapError + class WebmailErrorMiddleware(object): def process_exception(self, request, exception): if not isinstance(exception, (WebmailError, ImapError)): diff --git a/modoboa/extensions/webmail/views.py b/modoboa/extensions/webmail/views.py index 8563bcc6b..09ed1a6af 100644 --- a/modoboa/extensions/webmail/views.py +++ b/modoboa/extensions/webmail/views.py @@ -1,25 +1,30 @@ # coding: utf-8 -from django.conf import settings import os +from django.conf import settings from django.http import HttpResponse +from django.shortcuts import render from django.template import Template, Context from django.utils.translation import ugettext as _, ungettext from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from modoboa.lib import parameters -from modoboa.lib.webutils import _render, _render_to_string, \ - ajax_response, ajax_simple_response -from modoboa.lib.decorators import needs_mailbox -from exceptions import WebmailError -from forms import FolderForm, AttachmentForm, ComposeMailForm -from imaputils import get_imapconnector, IMAPconnector, separate_mailbox -from lib import decode_payload, AttachmentUploadHandler, \ - save_attachment, ImapListing, EmailSignature, \ - clean_attachments, set_compose_session, send_mail, \ +from modoboa.lib.webutils import ( + _render_to_string, ajax_response, ajax_simple_response +) +from modoboa.extensions.admin.lib import needs_mailbox +from .exceptions import WebmailError +from .forms import FolderForm, AttachmentForm, ComposeMailForm +from .imaputils import get_imapconnector, IMAPconnector, separate_mailbox +from .lib import ( + decode_payload, AttachmentUploadHandler, + save_attachment, ImapListing, EmailSignature, + clean_attachments, set_compose_session, send_mail, ImapEmail, ReplyModifier, ForwardModifier +) from templatetags import webextras + @login_required @needs_mailbox() def getattachment(request): @@ -36,8 +41,6 @@ def getattachment(request): if not mbox or not mailid or not pnum: raise WebmailError(_("Invalid request")) - headers = {"Content-Type": "text/plain", - "Content-Transfer-Encoding": None} imapc = get_imapconnector(request) partdef, payload = imapc.fetchpart(mailid, mbox, pnum) resp = HttpResponse(decode_payload(partdef["encoding"], payload)) @@ -57,6 +60,7 @@ def getattachment(request): resp["Content-Length"] = partdef["size"] return resp + @login_required @needs_mailbox() def move(request): @@ -69,6 +73,7 @@ def move(request): resp.update(status="ok") return ajax_simple_response(resp) + @login_required @needs_mailbox() def delete(request): @@ -78,7 +83,8 @@ def delete(request): raise WebmailError(_("Invalid request")) selection = [item for item in selection if item.isdigit()] mbc = get_imapconnector(request) - mbc.move(",".join(selection), mbox, parameters.get_user(request.user, "TRASH_FOLDER")) + mbc.move(",".join(selection), mbox, + parameters.get_user(request.user, "TRASH_FOLDER")) count = len(selection) message = ungettext("%(count)d message deleted", "%(count)d messages deleted", @@ -86,6 +92,7 @@ def delete(request): resp = dict(status="ok", respmsg=message) return ajax_simple_response(resp) + @login_required @needs_mailbox() def mark(request, name): @@ -102,7 +109,8 @@ def mark(request, name): return ajax_simple_response( dict(status="ok", action=status, mbox=name, unseen=imapc.unseen_messages(name)) - ) + ) + @login_required @needs_mailbox() @@ -112,8 +120,9 @@ def empty(request, name): get_imapconnector(request).empty(name) content = "
%s
" % _("Empty mailbox") return ajax_simple_response(dict( - status="ok", listing=content, mailbox=name - )) + status="ok", listing=content, mailbox=name + )) + @login_required @needs_mailbox() @@ -122,6 +131,7 @@ def compact(request, name): imapc.compact(name) return ajax_simple_response(dict(status="ok")) + @login_required @needs_mailbox() def newfolder(request, tplname="webmail/folder.html"): @@ -142,8 +152,9 @@ def newfolder(request, tplname="webmail/folder.html"): pf = request.POST.get("parent_folder", None) mbc.create_folder(form.cleaned_data["name"], pf) return ajax_simple_response(dict( - status="ok", respmsg=_("Mailbox created"), newmb=form.cleaned_data["name"], parent=pf - )) + status="ok", respmsg=_("Mailbox created"), + newmb=form.cleaned_data["name"], parent=pf + )) ctx["form"] = form ctx["selected"] = None @@ -151,20 +162,21 @@ def newfolder(request, tplname="webmail/folder.html"): ctx["form"] = FolderForm() ctx["selected"] = None - return _render(request, tplname, ctx) + return render(request, tplname, ctx) + @login_required @needs_mailbox() def editfolder(request, tplname="webmail/folder.html"): mbc = IMAPconnector(user=request.user.username, password=request.session["password"]) - ctx = {"title" : _("Edit mailbox"), - "formid" : "mboxform", - "action" : reverse(editfolder), - "action_label" : _("Update"), - "action_classes" : "submit", - "withunseen" : False, - "selectonly" : True} + ctx = {"title": _("Edit mailbox"), + "formid": "mboxform", + "action": reverse(editfolder), + "action_label": _("Update"), + "action_classes": "submit", + "withunseen": False, + "selectonly": True} if request.method == "POST": form = FolderForm(request.POST) @@ -199,7 +211,8 @@ def editfolder(request, tplname="webmail/folder.html"): ctx["form"].fields["oldname"].initial = name ctx["form"].fields["name"].initial = shortname ctx["selected"] = parent - return _render(request, tplname, ctx) + return render(request, tplname, ctx) + @login_required @needs_mailbox() @@ -210,10 +223,11 @@ def delfolder(request): mbc = IMAPconnector(user=request.user.username, password=request.session["password"]) mbc.delete_folder(name) - if request.session.has_key("mbox"): + if "mbox" in request.session: del request.session["mbox"] return ajax_response(request) + @login_required @csrf_exempt @needs_mailbox() @@ -228,40 +242,41 @@ def attachments(request, tplname="webmail/attachments.html"): fobj = request.FILES["attachment"] tmpname = save_attachment(fobj) request.session["compose_mail"]["attachments"] \ - += [{"fname" : str(fobj), - "content-type" : fobj.content_type, - "size" : fobj.size, - "tmpname" : os.path.basename(tmpname)}] + += [{"fname": str(fobj), + "content-type": fobj.content_type, + "size": fobj.size, + "tmpname": os.path.basename(tmpname)}] request.session.modified = True - return _render(request, "webmail/upload_done.html", { - "status" : "ok", "fname" : request.FILES["attachment"], - "tmpname" : os.path.basename(tmpname) - }) + return render(request, "webmail/upload_done.html", { + "status": "ok", "fname": request.FILES["attachment"], + "tmpname": os.path.basename(tmpname) + }) except WebmailError, inst: error = _("Failed to save attachment: ") + str(inst) if csuploader.toobig: - error = _("Attachment is too big (limit: %s)" \ - % parameters.get_admin("MAX_ATTACHMENT_SIZE")) - return _render(request, "webmail/upload_done.html", { - "status" : "ko", "error" : error - }) + error = _("Attachment is too big (limit: %s)" + % parameters.get_admin("MAX_ATTACHMENT_SIZE")) + return render(request, "webmail/upload_done.html", { + "status": "ko", "error": error + }) ctx = { - "title" : _("Attachments"), - "formid" : "uploadfile", - "target" : "upload_target", - "enctype" : "multipart/form-data", - "form" : AttachmentForm(), - "action" : reverse(attachments), - "attachments" : request.session["compose_mail"]["attachments"] - } - return _render(request, tplname, ctx) + "title": _("Attachments"), + "formid": "uploadfile", + "target": "upload_target", + "enctype": "multipart/form-data", + "form": AttachmentForm(), + "action": reverse(attachments), + "attachments": request.session["compose_mail"]["attachments"] + } + return render(request, tplname, ctx) + @login_required @needs_mailbox() def delattachment(request): - if not request.session.has_key("compose_mail") \ - or not request.GET.has_key("name") \ + if not "compose_mail" in request.session \ + or not "name" in request.GET \ or not request.GET["name"]: return ajax_response(request, "ko", respmsg=_("Bad query")) @@ -269,7 +284,9 @@ def delattachment(request): for att in request.session["compose_mail"]["attachments"]: if att["tmpname"] == request.GET["name"]: request.session["compose_mail"]["attachments"].remove(att) - fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"]) + fullpath = os.path.join( + settings.MEDIA_ROOT, "webmail", att["tmpname"] + ) try: os.remove(fullpath) except OSError, e: @@ -281,6 +298,7 @@ def delattachment(request): error = _("Unknown attachment") return ajax_response(request, "ko", respmsg=error) + def render_mboxes_list(request, imapc): """Return the HTML representation of a mailboxes list @@ -290,19 +308,20 @@ def render_mboxes_list(request, imapc): """ curmbox = request.session.get("mbox", "INBOX") return _render_to_string(request, "webmail/folders.html", { - "selected" : curmbox, - "mboxes" : imapc.getmboxes(request.user), - "withunseen" : True - }) + "selected": curmbox, + "mboxes": imapc.getmboxes(request.user), + "withunseen": True + }) + def set_nav_params(request): - if not request.session.has_key("navparams"): + if not "navparams" in request.session: request.session["navparams"] = {} request.session["pageid"] = \ int(request.GET.get("page", 1)) if not request.GET.get("order", False): - if not request.session["navparams"].has_key("order"): + if not "order" in request.session["navparams"]: request.session["navparams"]["order"] = "-date" else: request.session["navparams"]["order"] = request.GET.get("order") @@ -310,9 +329,10 @@ def set_nav_params(request): for p in ["pattern", "criteria"]: if request.GET.get(p, False): request.session["navparams"][p] = request.GET[p] - elif request.session["navparams"].has_key(p): + elif p in request.session["navparams"]: del request.session["navparams"][p] + def listmailbox(request, defmailbox="INBOX", update_session=True): """Mailbox content listing @@ -329,13 +349,16 @@ def listmailbox(request, defmailbox="INBOX", update_session=True): set_nav_params(request) request.session["mbox"] = mbox - lst = ImapListing(request.user, request.session["password"], - baseurl="?action=listmailbox&mbox=%s&" % mbox, - folder=mbox, - elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")), - **request.session["navparams"]) + lst = ImapListing( + request.user, request.session["password"], + baseurl="?action=listmailbox&mbox=%s&" % mbox, + folder=mbox, + elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")), + **request.session["navparams"] + ) return lst.render(request, request.session["pageid"]) + def render_compose(request, form, posturl, email=None, insert_signature=False): editor = parameters.get_user(request.user, "EDITOR") if email is None: @@ -348,11 +371,11 @@ def render_compose(request, form, posturl, email=None, insert_signature=False): signature = EmailSignature(request.user) body += str(signature) randid = None - if not request.GET.has_key("id"): - if request.session.has_key("compose_mail"): + if not "id" in request.GET: + if "compose_mail" in request.session: clean_attachments(request.session["compose_mail"]["attachments"]) randid = set_compose_session(request) - elif not request.session.has_key("compose_mail") \ + elif not "compose_mail" in request.session \ or request.session["compose_mail"]["id"] != request.GET["id"]: randid = set_compose_session(request) @@ -360,21 +383,22 @@ def render_compose(request, form, posturl, email=None, insert_signature=False): if len(attachments): short_att_list = "(%s)" \ % ", ".join(map(lambda att: att["fname"], - attachments[:2] + [{"fname" : "..."}] \ + attachments[:2] + [{"fname": "..."}] \ if len(attachments) > 2 else attachments)) else: short_att_list = "" content = _render_to_string(request, "webmail/compose.html", { - "form" : form, "bodyheader" : textheader, - "body" : body, "posturl" : posturl, - "attachments" : attachments, "short_att_list" : short_att_list - }) + "form": form, "bodyheader": textheader, + "body": body, "posturl": posturl, + "attachments": attachments, "short_att_list": short_att_list + }) ctx = dict(listing=content, editor=editor) if randid is not None: ctx["id"] = randid return ctx + def compose(request): url = "?action=compose" if request.method == "POST": @@ -384,6 +408,7 @@ def compose(request): form = ComposeMailForm() return render_compose(request, form, url, insert_signature=True) + def compose_and_send(request, action, callback=None): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) @@ -401,30 +426,35 @@ def compose_and_send(request, action, callback=None): email = modclass(mbox, mailid, request, form, addrfull=True, links="1") return render_compose(request, form, url, email) + def reply(request): def msg_replied(mbox, mailid): get_imapconnector(request).msg_answered(mbox, mailid) return compose_and_send(request, "reply", msg_replied) + def forward(request): def msg_forwarded(mbox, mailid): get_imapconnector(request).msg_forwarded(mbox, mailid) return compose_and_send(request, "forward", msg_forwarded) + @login_required @needs_mailbox() def getmailcontent(request): mbox = request.GET.get("mbox", None) mailid = request.GET.get("mailid", None) if mbox is None or mailid is None: - raise WebmailError(_("Invalid request")) + raise WebmailError(_("Invalid request")) email = ImapEmail(mbox, mailid, request, links=int(request.GET["links"])) - return _render(request, "common/viewmail.html", { - "headers" : email.render_headers(folder=mbox, mail_id=mailid), - "folder" : mbox, "imapid" : mailid, "mailbody" : email.body if email.body else "" - }) + return render(request, "common/viewmail.html", { + "headers": email.render_headers(folder=mbox, mail_id=mailid), + "folder": mbox, "imapid": mailid, + "mailbody": email.body if email.body else "" + }) + def viewmail(request): mbox = request.GET.get("mbox", None) @@ -441,10 +471,11 @@ def viewmail(request): (mbox, mailid, links) content = Template(""" -""").render(Context({"url" : url})) +""").render(Context({"url": url})) return dict(listing=content, menuargs=dict(mail_id=mailid)) + @login_required @needs_mailbox() def submailboxes(request): @@ -452,6 +483,7 @@ def submailboxes(request): mboxes = get_imapconnector(request).getmboxes(request.user, topmailbox) return ajax_simple_response(dict(status="ok", mboxes=mboxes)) + @login_required @needs_mailbox() def check_unseen_messages(request): @@ -465,6 +497,7 @@ def check_unseen_messages(request): counters[mb] = imapc.unseen_messages(mb) return ajax_simple_response(dict(status="ok", counters=counters)) + @login_required @needs_mailbox() def index(request): @@ -492,7 +525,7 @@ def index(request): action = request.GET.get("action", None) if action is not None: - if not globals().has_key(action): + if not action in globals(): raise WebmailError(_("Undefined action")) response = globals()[action](request) else: @@ -517,13 +550,13 @@ def index(request): trash, parameters.get_user(request.user, "DRAFTS_FOLDER") ] - return _render(request, "webmail/index.html", response) + return render(request, "webmail/index.html", response) if action in ["reply", "forward"]: action = "compose" if request.session["lastaction"] != action: extra_args = {} - if response.has_key("menuargs"): + if "menuargs" in response: extra_args = response["menuargs"] del response["menuargs"] try: @@ -533,6 +566,6 @@ def index(request): pass response.update(callback=action) - if not response.has_key("status"): + if not "status" in response: response.update(status="ok") return ajax_simple_response(response)