Skip to content

Commit

Permalink
Merge pull request #59 from modoboa/refactor/use_django_mail_functions
Browse files Browse the repository at this point in the history
Use mail functions provided by django.
  • Loading branch information
tonioo committed Mar 7, 2017
2 parents 224db26 + c1db3d9 commit 6e4f97c
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 102 deletions.
114 changes: 55 additions & 59 deletions modoboa_webmail/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

"""Webmail forms."""

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import os
import pkg_resources
from urlparse import urlparse

import lxml.html

from django import forms
from django.conf import settings
from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.utils.translation import ugettext_lazy as _

from modoboa.lib.email_utils import set_email_headers
from modoboa.lib import form_utils
from modoboa.lib import email_utils, form_utils
from modoboa.parameters import forms as param_forms

from .lib import (
Expand Down Expand Up @@ -54,18 +56,14 @@ def make_body_images_inline(body):
:param body: the HTML body to parse
"""
import os
from email.mime.image import MIMEImage
from urlparse import urlparse

html = lxml.html.fromstring(body)
parts = []
for tag in html.iter("img"):
src = tag.get("src")
if src is None:
continue
o = urlparse(src)
path = os.path.join(settings.MODOBOA_DIR, o.path[1:])
path = os.path.join(settings.BASE_DIR, o.path[1:])
if not os.path.exists(path):
continue
fname = os.path.basename(path)
Expand All @@ -83,15 +81,14 @@ def make_body_images_inline(body):


class ComposeMailForm(forms.Form):

"""Compose mail form."""

to = forms.CharField(
label=_("To"), validators=[validate_email_list])
cc = forms.CharField(
label=_("Cc"), required=False, validators=[validate_email_list])
cci = forms.CharField(
label=_("Cci"), required=False, validators=[validate_email_list])
bcc = forms.CharField(
label=_("Bcc"), required=False, validators=[validate_email_list])

subject = forms.CharField(
label=_("Subject"),
Expand All @@ -106,80 +103,83 @@ def __init__(self, *args, **kwargs):
super(ComposeMailForm, self).__init__(*args, **kwargs)
self.field_widths = {
"cc": "11",
"cci": "11",
"bcc": "11",
"subject": "11"
}

def _html_msg(self):
def clean_to(self):
"""Convert to a list."""
to = self.cleaned_data["to"]
return email_utils.prepare_addresses(to, "envelope")

def _html_msg(self, sender, headers):
"""Create a multipart message.
We attach two alternatives:
* text/html
* text/plain
"""
msg = MIMEMultipart(_subtype="related")
submsg = MIMEMultipart(_subtype="alternative")
body = self.cleaned_data["body"]
charset = "utf-8"
if body:
tbody = html2plaintext(body)
body, images = make_body_images_inline(body)
else:
tbody = ""
images = []
submsg.attach(
MIMEText(tbody.encode(charset), _subtype="plain", _charset=charset)
msg = EmailMultiAlternatives(
self.cleaned_data["subject"],
tbody,
sender, self.cleaned_data["to"],
cc=self.cleaned_data["cc"],
bcc=self.cleaned_data["bcc"],
headers=headers
)
submsg.attach(
MIMEText(body.encode(charset), _subtype="html", _charset=charset)
)
msg.attach(submsg)
msg.attach_alternative(body, "text/html")
for img in images:
msg.attach(img)
return msg

def _plain_msg(self):
"""Create a simple text message.
"""
charset = "utf-8"
text = MIMEText(self.cleaned_data["body"].encode(charset),
_subtype="plain", _charset=charset)
return text
def _plain_msg(self, sender, headers):
"""Create a simple text message."""
msg = EmailMessage(
self.cleaned_data["subject"],
self.cleaned_data["body"],
sender,
self.cleaned_data["to"],
cc=self.cleaned_data["cc"],
bcc=self.cleaned_data["bcc"],
headers=headers
)
return msg

def _build_msg(self, request):
"""Convert form's content to a MIME message.
"""
editormode = request.user.parameters.get_value("editor")
msg = getattr(self, "_%s_msg" % editormode)()
"""Build message to send.
if request.session["compose_mail"]["attachments"]:
wrapper = MIMEMultipart(_subtype="mixed")
wrapper.attach(msg)
for attdef in request.session["compose_mail"]["attachments"]:
wrapper.attach(create_mail_attachment(attdef))
msg = wrapper
return msg
Can be overidden by children.
"""
headers = {
"User-Agent": pkg_resources.get_distribution("modoboa").version
}
origmsgid = self.cleaned_data.get("origmsgid")
if origmsgid:
headers.update({
"References": origmsgid,
"In-Reply-To": origmsgid
})
mode = request.user.parameters.get_value("editor")
return getattr(self, "_{}_msg".format(mode))(
request.user.encoded_address, headers)

def to_msg(self, request):
"""Convert form's content to an object ready to send.
We set headers at the end to be sure no one will override
them.
"""
"""Convert form's content to an object ready to send."""
msg = self._build_msg(request)
set_email_headers(
msg, self.cleaned_data["subject"], request.user.encoded_address,
self.cleaned_data['to']
)
origmsgid = self.cleaned_data.get("origmsgid", None)
if origmsgid:
msg["References"] = msg["In-Reply-To"] = origmsgid
if request.session["compose_mail"]["attachments"]:
for attdef in request.session["compose_mail"]["attachments"]:
msg.attach(create_mail_attachment(attdef))
return msg


class ForwardMailForm(ComposeMailForm):

"""Forward mail form."""

def _build_msg(self, request):
Expand All @@ -192,10 +192,6 @@ def _build_msg(self, request):
msg = super(ForwardMailForm, self)._build_msg(request)
origmsg = ImapEmail(request, False, "%s:%s" % (mbox, mailid))
if origmsg.attachments:
if not msg.is_multipart or not msg.get_content_subtype() == "mixed":
wrapper = MIMEMultipart(_subtype="mixed")
wrapper.attach(msg)
msg = wrapper
for attpart, fname in origmsg.attachments.items():
attdef, payload = origmsg.fetch_attachment(attpart)
attdef["fname"] = fname
Expand Down
49 changes: 21 additions & 28 deletions modoboa_webmail/lib/sendmail.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import smtplib

from django.core import mail
from django.template.loader import render_to_string

from modoboa.lib.email_utils import prepare_addresses
from modoboa.lib.cryptutils import get_password
from modoboa.parameters import tools as param_tools

Expand Down Expand Up @@ -33,35 +31,30 @@ def send_mail(request, form, posturl=None):
return False, dict(status="ko", listing=listing, editor=editormode)

msg = form.to_msg(request)
rcpts = prepare_addresses(form.cleaned_data["to"], "envelope")
for hdr in ["cc", "cci"]:
if form.cleaned_data[hdr]:
msg[hdr.capitalize()] = prepare_addresses(form.cleaned_data[hdr])
rcpts += prepare_addresses(form.cleaned_data[hdr], "envelope")
try:
conf = dict(param_tools.get_global_parameters("modoboa_webmail"))
if conf["smtp_secured_mode"] == "ssl":
s = smtplib.SMTP_SSL(conf["smtp_server"], conf["smtp_port"])
else:
s = smtplib.SMTP(conf["smtp_server"], conf["smtp_port"])
if conf["smtp_secured_mode"] == "starttls":
s.starttls()
except Exception as text:
raise WebmailInternalError(str(text))

conf = dict(param_tools.get_global_parameters("modoboa_webmail"))
options = {
"host": conf["smtp_server"], "port": conf["smtp_port"]
}
if conf["smtp_secured_mode"] == "ssl":
options.update({"use_ssl": True})
elif conf["smtp_secured_mode"] == "starttls":
options.update({"use_tls": True})
if conf["smtp_authentication"]:
try:
s.login(request.user.username, get_password(request))
except smtplib.SMTPException as err:
raise WebmailInternalError(str(err))
options.update({
"username": request.user.username,
"password": get_password(request)
})
try:
s.sendmail(request.user.email, rcpts, msg.as_string())
s.quit()
except smtplib.SMTPException as err:
raise WebmailInternalError(str(err))
with mail.get_connection(**options) as connection:
msg.connection = connection
msg.send()
except Exception as inst:
raise WebmailInternalError(str(inst))

# Copy message to sent folder
sentfolder = request.user.parameters.get_value("sent_folder")
get_imapconnector(request).push_mail(sentfolder, msg)
get_imapconnector(request).push_mail(sentfolder, msg.message())
clean_attachments(request.session["compose_mail"]["attachments"])
del request.session["compose_mail"]

return True, {}
2 changes: 1 addition & 1 deletion modoboa_webmail/static/modoboa_webmail/js/webmail.js
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ Webmail.prototype = {
compose_callback: function(resp) {
this.page_update(resp);
$("#add_cc").click($.proxy(function(e) { this.add_field(e, "cc"); }, this));
$("#add_cci").click($.proxy(function(e) { this.add_field(e, "cci"); }, this));
$("#add_bcc").click($.proxy(function(e) { this.add_field(e, "bcc"); }, this));
this.editormode = resp.editor;
if (resp.editor == "html") {
var instance = CKEDITOR.instances[this.editorid];
Expand Down
18 changes: 9 additions & 9 deletions modoboa_webmail/templates/modoboa_webmail/compose.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
{% endif %}
</div>

{# handling cc and cci buttons #}
{% if not form.cc.value or not form.cci.value %}
{# handling cc and bcc buttons #}
{% if not form.cc.value or not form.bcc.value %}
<div class="col-sm-2">
{% if not form.cc.value %}
<button id="add_cc" class="btn btn-default btn-xs">
<span class="fa fa-plus"></span> {% trans "Cc" %}
</button>
{% endif %}
{% if not form.cci.value %}
<button id="add_cci" class="btn btn-default btn-xs">
<span class="fa fa-plus"> {% trans "Cci" %}</span>
{% if not form.bcc.value %}
<button id="add_bcc" class="btn btn-default btn-xs">
<span class="fa fa-plus"> {% trans "Bcc" %}</span>
</button>
{% endif %}
</div>
Expand All @@ -41,11 +41,11 @@
{% render_field form.cc label_width="col-sm-1" hidden=True %}
{% endif %}

{# display cci field when required #}
{% if form.cci.value %}
{% render_field form.cci label_width="col-sm-1" %}
{# display bcc field when required #}
{% if form.bcc.value %}
{% render_field form.bcc label_width="col-sm-1" %}
{% else %}
{% render_field form.cci label_width="col-sm-1" hidden=True %}
{% render_field form.bcc label_width="col-sm-1" hidden=True %}
{% endif %}

{% render_field form.subject label_width="col-sm-1" %}
Expand Down

0 comments on commit 6e4f97c

Please sign in to comment.