diff --git a/extensions/amavis_quarantine/sql_listing.py b/extensions/amavis_quarantine/sql_listing.py index 5e29fc3e7..fe24843a8 100644 --- a/extensions/amavis_quarantine/sql_listing.py +++ b/extensions/amavis_quarantine/sql_listing.py @@ -57,7 +57,7 @@ AND quarantine.chunk_ind=1 ORDER BY msgs.time_num DESC """ % self.filter - def messages_count(self, folder=None): + def messages_count(self, **kwargs): return self.count def fetch(self, start=None, stop=None, **kwargs): diff --git a/extensions/webmail/forms.py b/extensions/webmail/forms.py index a57423774..32c5ede87 100644 --- a/extensions/webmail/forms.py +++ b/extensions/webmail/forms.py @@ -4,6 +4,6 @@ from django.utils.translation import ugettext as _ class ComposeMailForm(forms.Form): from_ = forms.CharField(label=_("From")) to = forms.CharField(label=_("To"), widget=forms.Textarea()) - cc = forms.CharField(label=_("Cc"), widget=forms.Textarea()) + cc = forms.CharField(label=_("Cc"), widget=forms.Textarea(), required=False) subject = forms.CharField(label=_("Subject"), max_length=255) diff --git a/extensions/webmail/imap_listing.py b/extensions/webmail/imap_listing.py index 7d5bd9371..2a3fc2dbb 100644 --- a/extensions/webmail/imap_listing.py +++ b/extensions/webmail/imap_listing.py @@ -31,21 +31,36 @@ class WMtable(tables.Table): except AttributeError: return value -class IMAPheader(object): - addr_exp = re.compile("([^<\(]+)<|\(([^>\)]+)>|\)") - name_exp = re.compile("\"(.+)\"") +class EmailAddress(object): + addr_exp = re.compile("([^<\(]+)[<(]([^>)]+)[>)]") + name_exp = re.compile('"([^"]+)"') + def __init__(self, address): + self.fulladdress = address + self.name = self.address = None + m = EmailAddress.addr_exp.match(self.fulladdress) + if m is None: + return + name = m.group(1).strip() + self.address = m.group(2).strip() + m2 = EmailAddress.name_exp.match(name) + if m2: + name = m2.group(1) + self.name = "" + for part in decode_header(name): + if self.name != "": + self.name += " " + if part[1] is None: + self.name += part[0] + else: + self.name += unicode(part[0], part[1]) + self.fulladdress = "%s <%s>" % (self.name, self.address) + +class IMAPheader(object): @staticmethod def parse_address(value): - m = IMAPheader.addr_exp.match(value) - if m: - name = m.group(1) - name = name.strip() - m2 = IMAPheader.name_exp.match(name) - if m2: - return IMAPheader.parse_subject(m2.group(1)) - return IMAPheader.parse_subject(name) - return value + addr = EmailAddress(value) + return addr.name and addr.name or value @staticmethod def parse_address_list(values): @@ -126,16 +141,27 @@ class IMAPconnector(object): return "INBOX" return folder.encode("imap4-utf-7") - def messages_count(self, folder=None, order=None): - if order: - sign = order[:1] - criterion = order[1:].upper() + def messages_count(self, **kwargs): + """An enhanced version of messages_count + + With IMAP, to know how many messages a mailbox contains, we + have to make a request to the server. To avoid requests + multiplications, we sort messages in the same time. This will + be usefull for other methods. + + :param order: sorting order + :param folder: mailbox to scan + """ + if "order" in kwargs.keys() and kwargs["order"]: + sign = kwargs["order"][:1] + criterion = kwargs["order"][1:].upper() if sign == '-': criterion = "REVERSE %s" % criterion else: criterion = "REVERSE DATE" print criterion + folder = kwargs.has_key("folder") and kwargs["folder"] or None (status, data) = self.m.select(self._encodefolder(folder)) (status, data) = self.m.sort("(%s)" % criterion, "UTF-8", "(NOT DELETED)") self.messages = data[0].split() @@ -210,9 +236,8 @@ class ImapListing(EmailListing): tpl = "webmail/index.html" tbltype = WMtable - def __init__(self, user, password, baseurl=None, navparams={}, **kwargs): + def __init__(self, user, password, baseurl=None, **kwargs): self.mbc = IMAPconnector(user=user, password=password) - self.navparams = navparams EmailListing.__init__(self, **kwargs) if baseurl: self.paginator.baseurl = baseurl @@ -232,7 +257,7 @@ class ImapEmail(Email): def __init__(self, msg, **kwargs): Email.__init__(self, msg, **kwargs) - fields = ["Subject", "From", "To", "Cc", "Date"] + fields = ["Subject", "From", "To", "Reply-To", "Cc", "Date"] for f in fields: label = f if not f in msg.keys(): diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py index b87224cd8..d25263f7a 100644 --- a/extensions/webmail/main.py +++ b/extensions/webmail/main.py @@ -22,13 +22,18 @@ def init(): parameters.register("webmail", "IMAP_SERVER", "string", "127.0.0.1", help=_("Address of your IMAP server")) parameters.register("webmail", "IMAP_SECURED", "list_yesno", "no", - help=_("Use a secure connexion to access IMAP server")) + help=_("Use a secured connection to access IMAP server")) parameters.register("webmail", "IMAP_PORT", "int", "143", help=_("Listening port of your IMAP server")) parameters.register("webmail", "SMTP_SERVER", "string", "127.0.0.1", help=_("Address of your SMTP server")) parameters.register("webmail", "SMTP_PORT", "int", "25", help=_("Listening port of your SMTP server")) + parameters.register("webmail", "SMTP_AUTHENTICATION", "list_yesno", "no", + help=_("Server needs authentication")) + parameters.register("webmail", "SMTP_SECURED", "list_yesno", "no", + help=_("Use a secured connection to access SMTP server")) + def urls(): return (r'^mailng/webmail/', @@ -57,7 +62,7 @@ def getctx(status, level=1, **kwargs): def folder(request, name, updatenav=True): if not name: name = "INBOX" - order = request.GET.has_key("order") and request.GET["order"] or None + order = request.GET.has_key("order") and request.GET["order"] or "-date" if updatenav: pageid = request.GET.has_key("page") and int(request.GET["page"]) or 1 if not "navparams" in request.session.keys(): @@ -183,23 +188,40 @@ def render_compose(request, form, bodyheader=None, body=None): ctx = getctx("ok", level=2, menu=menu, listing=content) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") +def send_mail(request): + form = ComposeMailForm(request.POST) + if form.is_valid(): + from email.mime.text import MIMEText + import smtplib + + msg = MIMEText(request.POST["id_body"].encode("utf-8"), + _charset="utf-8") + msg["Subject"] = request.POST["subject"] + msg["From"] = request.POST["from_"] + msg["To"] = request.POST["to"] + rcpts = msg['To'].split(',') + if "cc" in request.POST.keys(): + msg["Cc"] = request.POST["cc"] + rcpts += msg["Cc"].split(",") + try: + s = smtplib.SMTP(parameters.get("webmail", "SMTP_SERVER")) + if parameters.get("webmail", "SMTP_SECURED") == "yes": + s.starttls() + except (smtplib.SMTPException, ssl.SSLError), error: + print error + + if parameters.get("webmail", "SMTP_AUTHENTICATION") == "yes": + s.login(request.user.username, request.session["password"]) + s.sendmail(msg['From'], rcpts, msg.as_string()) + s.quit() + ctx = getctx("ok", url="%s?page=%s" % (request.session["folder"], + request.session["page"])) + return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + @login_required def compose(request): if request.method == "POST": - form = ComposeMailForm(request.POST) - if form.is_valid(): - from email.mime.text import MIMEText - import smtplib - - msg = MIMEText(request.POST["id_body"].encode("utf-8"), - _charset="utf-8") - msg["Subject"] = request.POST["subject"] - msg["From"] = request.POST["from_"] - msg["to"] = request.POST["to"] - s = smtplib.SMTP(parameters.get("webmail", "SMTP_SERVER")) - s.sendmail(msg['From'], [msg['To']], msg.as_string()) - s.quit() - return folder(request, request.session["navparams"]["folder"], False) + return send_mail(request) form = ComposeMailForm() form.fields["from_"].initial = request.user.username @@ -208,7 +230,7 @@ def compose(request): @login_required def reply(request, folder, mail_id): if request.method == "POST": - return + return send_mail(request) msg = fetchmail(request, folder, mail_id, True) email = ImapEmail(msg) lines = email.body.split('\n') @@ -219,11 +241,27 @@ def reply(request, folder, mail_id): body += ">%s" % l form = ComposeMailForm() form.fields["from_"].initial = request.user.username - form.fields["to"].initial = msg["From"] - m = re.match("re\s*:\s*.+", msg["Subject"].lower()) - if m: - form.fields["subject"].initial = msg["Subject"] + if not "Reply-To" in msg.keys(): + form.fields["to"].initial = msg["From"] else: - form.fields["subject"].initial = "Re: %s" % msg["Subject"] - textheader = msg["From"] + " " + _("wrote:") + form.fields["to"].initial = msg["Reply-To"] + if request.GET.has_key("all"): + form.fields["cc"].initial = "" + toparse = msg["To"].split(",") + if "Cc" in msg.keys(): + toparse += msg["Cc"].split(",") + for addr in toparse: + tmp = EmailAddress(addr) + if tmp.address and tmp.address == request.user.username: + continue + if form.fields["cc"].initial != "": + form.fields["cc"].initial += ", " + form.fields["cc"].initial += tmp.fulladdress + subject = IMAPheader.parse_subject(msg["Subject"]) + m = re.match("re\s*:\s*.+", subject.lower()) + if m: + form.fields["subject"].initial = subject + else: + form.fields["subject"].initial = "Re: %s" % subject + textheader = EmailAddress(msg["From"]).fulladdress + " " + _("wrote:") return render_compose(request, form, textheader, body) diff --git a/extensions/webmail/templates/compose.html b/extensions/webmail/templates/compose.html index 01bb05acd..369cbbd62 100644 --- a/extensions/webmail/templates/compose.html +++ b/extensions/webmail/templates/compose.html @@ -43,8 +43,13 @@
+