import email.message import mimetypes import smtplib import urllib.request from email.utils import make_msgid from flask import current_app from flask import request from flask import url_for from flask_babel import gettext as _ from flask_themer import render_template from .apputils import get_current_domain from .apputils import get_current_mail_domain from .apputils import profile_hash DEFAULT_SMTP_HOST = "localhost" DEFAULT_SMTP_PORT = 25 DEFAULT_SMTP_TLS = False DEFAULT_SMTP_SSL = False def logo(): logo_url = current_app.config.get("LOGO") if not logo_url: return None, None, None logo_filename = logo_url.split("/")[-1] if not logo_url.startswith("http"): if current_app.config.get("SERVER_NAME"): logo_url = "{}://{}/{}".format( current_app.config.get("PREFERRED_URL_SCHEME"), get_current_domain(), logo_url, ) else: logo_url = f"{request.url_root}{logo_url}" try: with urllib.request.urlopen(logo_url) as f: logo_raw = f.read() except (urllib.error.HTTPError, urllib.error.URLError): logo_filename = None logo_raw = None logo_cid = make_msgid(domain=get_current_mail_domain()) return logo_cid, logo_filename, logo_raw def send_email(subject, recipient, text, html, attachements=None): current_app.logger.debug(f"Sending a mail to {recipient}: {subject}") msg = email.message.EmailMessage() msg.set_content(text) msg.add_alternative(html, subtype="html") msg["Subject"] = subject msg["To"] = f"<{recipient}>" name = current_app.config.get("NAME", "Canaille") address = current_app.config["SMTP"].get("FROM_ADDR") if not address: domain = get_current_mail_domain() address = f"admin@{domain}" msg["From"] = f'"{name}" <{address}>' attachements = attachements or [] for cid, filename, value in attachements: maintype, subtype = mimetypes.guess_type(filename)[0].split("/") msg.get_payload()[1].add_related( value, maintype=maintype, subtype=subtype, cid=cid ) smtp = None try: connection_func = ( smtplib.SMTP_SSL if current_app.config["SMTP"].get("SSL", DEFAULT_SMTP_SSL) else smtplib.SMTP ) with connection_func( host=current_app.config["SMTP"].get("HOST", DEFAULT_SMTP_HOST), port=current_app.config["SMTP"].get("PORT", DEFAULT_SMTP_PORT), ) as smtp: if current_app.config["SMTP"].get("TLS", DEFAULT_SMTP_TLS): smtp.starttls() if current_app.config["SMTP"].get("LOGIN"): smtp.login( user=current_app.config["SMTP"].get("LOGIN"), password=current_app.config["SMTP"].get("PASSWORD"), ) smtp.send_message(msg) except smtplib.SMTPRecipientsRefused: pass except OSError as exc: current_app.logger.warning(f"Could not send email: {exc}") return False return True def send_test_mail(email): base_url = url_for("account.index", _external=True) logo_cid, logo_filename, logo_raw = logo() subject = _("Test email from {website_name}").format( website_name=current_app.config.get("NAME", "Canaille") ) text_body = render_template( "mail/test.txt", site_name=current_app.config.get("NAME", "Canaille"), site_url=base_url, ) html_body = render_template( "mail/test.html", site_name=current_app.config.get("NAME", "Canaille"), site_url=base_url, logo=f"cid:{logo_cid[1:-1]}" if logo_cid else None, ) return send_email( subject=subject, recipient=email, text=text_body, html=html_body, attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) def send_password_reset_mail(user): base_url = url_for("account.index", _external=True) reset_url = url_for( "account.reset", uid=user.uid[0], hash=profile_hash( user.uid[0], user.mail[0], user.userPassword[0] if user.has_password() else "", ), _external=True, ) logo_cid, logo_filename, logo_raw = logo() subject = _("Password reset on {website_name}").format( website_name=current_app.config.get("NAME", reset_url) ) text_body = render_template( "mail/reset.txt", site_name=current_app.config.get("NAME", reset_url), site_url=base_url, reset_url=reset_url, ) html_body = render_template( "mail/reset.html", site_name=current_app.config.get("NAME", reset_url), site_url=base_url, reset_url=reset_url, logo=f"cid:{logo_cid[1:-1]}" if logo_cid else None, ) return send_email( subject=subject, recipient=user.mail[0], text=text_body, html=html_body, attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) def send_password_initialization_mail(user): base_url = url_for("account.index", _external=True) reset_url = url_for( "account.reset", uid=user.uid[0], hash=profile_hash( user.uid[0], user.mail[0], user.userPassword[0] if user.has_password() else "", ), _external=True, ) logo_cid, logo_filename, logo_raw = logo() subject = _("Password initialization on {website_name}").format( website_name=current_app.config.get("NAME", reset_url) ) text_body = render_template( "mail/firstlogin.txt", site_name=current_app.config.get("NAME", reset_url), site_url=base_url, reset_url=reset_url, ) html_body = render_template( "mail/firstlogin.html", site_name=current_app.config.get("NAME", reset_url), site_url=base_url, reset_url=reset_url, logo=f"cid:{logo_cid[1:-1]}" if logo_cid else None, ) return send_email( subject=subject, recipient=user.mail[0], text=text_body, html=html_body, attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) def send_invitation_mail(email, registration_url): base_url = url_for("account.index", _external=True) logo_cid, logo_filename, logo_raw = logo() subject = _("You have been invited to create an account on {website_name}").format( website_name=current_app.config.get("NAME", registration_url) ) text_body = render_template( "mail/invitation.txt", site_name=current_app.config.get("NAME", registration_url), site_url=base_url, registration_url=registration_url, ) html_body = render_template( "mail/invitation.html", site_name=current_app.config.get("NAME", registration_url), site_url=base_url, registration_url=registration_url, logo=f"cid:{logo_cid[1:-1]}" if logo_cid else None, ) return send_email( subject=subject, recipient=email, text=text_body, html=html_body, attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, )