1. Roger Haase
  2. moin-2.0

Source

moin-2.0 / MoinMoin / mail / sendmail.py

Thomas Waldmann 5568cf1 
Thomas Waldmann 4cd8edd 

Tarashish Mishra 4e7d736 
Thomas Waldmann 4cd8edd 

Thomas Waldmann 5568cf1 

Thomas Waldmann 4cd8edd 
Thomas Waldmann 5568cf1 
















Thomas Waldmann 8102f92 

Thomas Waldmann 5568cf1 
Thomas Waldmann 8102f92 
Thomas Waldmann 5568cf1 

Thomas Waldmann 1d8e9b1 


Thomas Waldmann 5568cf1 
Thomas Waldmann 1d8e9b1 


Thomas Waldmann 5568cf1 













Vedran Miletić 5bf6d7a 
Thomas Waldmann 5568cf1 






Julian Brost 699bc60 
Thomas Waldmann 5568cf1 



Julian Brost 699bc60 









Thomas Waldmann 1d8e9b1 

Julian Brost 699bc60 
Thomas Waldmann 1d8e9b1 

Thomas Waldmann 5568cf1 



Vedran Miletić 5bf6d7a 

Thomas Waldmann 5568cf1 
Julian Brost 699bc60 
Thomas Waldmann 5568cf1 





Tarashish Mishra 4e7d736 

Thomas Waldmann 5568cf1 
Tarashish Mishra 4e7d736 























































Tarashish Mishra 5839f9f 
Tarashish Mishra 4e7d736 
Thomas Waldmann 5568cf1 
Tarashish Mishra 4e7d736 






Thomas Waldmann 5568cf1 
Tarashish Mishra 4e7d736 




































Thomas Waldmann 5568cf1 
Tarashish Mishra 4e7d736 

Thomas Waldmann 5568cf1 
Thomas Waldmann 8102f92 
Thomas Waldmann 5568cf1 
Thomas Waldmann 8102f92 


Thomas Waldmann 1d8e9b1 
Thomas Waldmann 8102f92 




Thomas Waldmann 1d8e9b1 
Thomas Waldmann 5568cf1 





Vedran Miletić 5bf6d7a 
Thomas Waldmann 5568cf1 

Vedran Miletić 5bf6d7a 
Thomas Waldmann 5568cf1 


















Thomas Waldmann 1d8e9b1 


Thomas Waldmann 5568cf1 











# -*- coding: utf-8 -*-
# Copyright: 2003 Juergen Hermann <jh@web.de>
# Copyright: 2008-2009 MoinMoin:ThomasWaldmann
# Copyright: 2012 MoinMoin:TarashishMishra
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
    MoinMoin - email helper functions
"""


import os, re
from email.Header import Header

from MoinMoin import log
logging = log.getLogger(__name__)

from flask import current_app as app

from MoinMoin import config
from MoinMoin.i18n import _, L_, N_

_transdict = {"AT": "@", "DOT": ".", "DASH": "-"}


def encodeAddress(address, charset):
    """
    Encode email address to enable non ascii names

    E.g. '"Jürgen Hermann" <jh@web.de>'. According to the RFC, the name
    part should be encoded, the address should not.

    :param address: email address, possibly using '"name" <address>' format
    :type address: unicode
    :param charset: specifying both the charset and the encoding, e.g
                    quoted printable or base64.
    :type charset: email.Charset.Charset instance
    :rtype: string
    :returns: encoded address
    """
    assert isinstance(address, unicode)
    composite = re.compile(r'(?P<phrase>.*?)(?P<blanks>\s*)\<(?P<addr>.*)\>', re.UNICODE)
    match = composite.match(address)
    if match:
        phrase = match.group('phrase')
        try:
            str(phrase)  # is it pure ascii?
        except UnicodeEncodeError:
            phrase = phrase.encode(config.charset)
            phrase = Header(phrase, charset)
        blanks = match.group('blanks')
        addr = match.group('addr')
        if phrase:
            return "{0!s}{1!s}<{2!s}>".format(phrase, blanks, addr)
        else:
            return str(addr)
    else:
        # a pure email address, should encode to ascii without problem
        return str(address)


def sendmail(subject, text, to=None, cc=None, bcc=None, mail_from=None):
    """ Create and send a text/plain message

    Return a tuple of success or error indicator and message.

    :param subject: subject of email
    :type subject: unicode
    :param text: email body text
    :type text: unicode
    :param to: recipients
    :type to: list
    :param cc: recipients (CC)
    :type cc: list
    :param bcc: recipients (BCC)
    :type bcc: list
    :param mail_from: override default mail_from
    :type mail_from: unicode

    :rtype: tuple
    :returns: (is_ok, Description of error or OK message)
    """
    cfg = app.cfg
    mail_from = mail_from or cfg.mail_from

    logging.debug("send mail, from: {0!r}, subj: {1!r}".format(mail_from, subject))
    logging.debug("send mail, to: {0!r}".format(to))

    if not to and not cc and not bcc:
        return (1, _("No recipients, nothing to do"))

    # Create a text/plain body using CRLF (see RFC2822)
    text = text.replace(u'\n', u'\r\n')

    # Send the message
    if app.on_gae:
        from google.appengine.api import mail
        try:
            logging.debug("trying to send mail (mail.EmailMessage.send() on gae)")
            message = mail.EmailMessage(sender=mail_from,
                                        subject=subject,
                                        to=to,
                                        body=text)
            if cc:
                message.cc = cc
            if bcc:
                message.bcc = bcc
            message.send()
        except:
            logging.exception("gae's mail.sendmail failed with an exception.")
            return (0, _("Mail not sent"))
        return (1, _("Mail sent successfully"))
    else:
        import smtplib, socket
        from email.Message import Message
        from email.Charset import Charset, QP
        from email.Utils import formatdate, make_msgid

        subject = subject.encode(config.charset)
        text = text.encode(config.charset)
        # Create a message using config.charset and quoted printable
        # encoding, which should be supported better by mail clients.
        # TODO: check if its really works better for major mail clients
        msg = Message()
        charset = Charset(config.charset)
        charset.header_encoding = QP
        charset.body_encoding = QP
        msg.set_charset(charset)

        # work around a bug in python 2.4.3 and above:
        msg.set_payload('=')
        if msg.as_string().endswith('='):
            text = charset.body_encode(text)

        msg.set_payload(text)

        address = encodeAddress(mail_from, charset)
        msg['From'] = address
        if to:
            msg['To'] = ','.join(to)
        if cc:
            msg['CC'] = ','.join(cc)
        msg['Date'] = formatdate()
        msg['Message-ID'] = make_msgid()
        msg['Subject'] = Header(subject, charset)
        # See RFC 3834 section 5:
        msg['Auto-Submitted'] = 'auto-generated'

        if cfg.mail_sendmail:
            if bcc:
                # Set the BCC.  This will be stripped later by sendmail.
                msg['BCC'] = ','.join(bcc)
            # Set Return-Path so that it isn't set (generally incorrectly) for us.
            msg['Return-Path'] = address

        if not cfg.mail_sendmail:
            try:
                logging.debug("trying to send mail (smtp) via smtp server '{0}'".format(cfg.mail_smarthost))
                host, port = (cfg.mail_smarthost + ':25').split(':')[:2]
                server = smtplib.SMTP(host, int(port))
                try:
                    #server.set_debuglevel(1)
                    if cfg.mail_username is not None and cfg.mail_password is not None:
                        try: # try to do tls
                            server.ehlo()
                            if server.has_extn('starttls'):
                                server.starttls()
                                server.ehlo()
                                logging.debug("tls connection to smtp server established")
                        except:
                            logging.debug("could not establish a tls connection to smtp server, continuing without tls")
                        logging.debug("trying to log in to smtp server using account '{0}'".format(cfg.mail_username))
                        server.login(cfg.mail_username, cfg.mail_password)
                    server.sendmail(mail_from, (to or []) + (cc or []) + (bcc or []), msg.as_string())
                finally:
                    try:
                        server.quit()
                    except AttributeError:
                        # in case the connection failed, SMTP has no "sock" attribute
                        pass
            except smtplib.SMTPException as e:
                logging.exception("smtp mail failed with an exception.")
                return (0, str(e))
            except (os.error, socket.error) as e:
                logging.exception("smtp mail failed with an exception.")
                return (0, _("Connection to mailserver '%(server)s' failed: %(reason)s",
                    server=cfg.mail_smarthost,
                    reason=str(e)
                ))
        else:
            try:
                logging.debug("trying to send mail (sendmail)")
                sendmailp = os.popen(cfg.mail_sendmail, "w")
                # msg contains everything we need, so this is a simple write
                sendmailp.write(msg.as_string())
                sendmail_status = sendmailp.close()
                if sendmail_status:
                    logging.error("sendmail failed with status: {0!s}".format(sendmail_status))
                    return (0, str(sendmail_status))
            except:
                logging.exception("sendmail failed with an exception.")
                return (0, _("Mail not sent"))

        logging.debug("Mail sent successfully")
        return (1, _("Mail sent successfully"))


def encodeSpamSafeEmail(email_address, obfuscation_text=''):
    """
    Encodes a standard email address to an obfuscated address

    :param email_address: mail address to encode.
                          Known characters and their all-uppercase words translation::

                              "." -> " DOT "
                              "@" -> " AT "
                              "-" -> " DASH "
    :param obfuscation_text: optional text to obfuscate the email.
                             All characters in the string must be alphabetic
                             and they will be added in uppercase.
    """
    address = email_address.lower()
    # uppercase letters will be stripped by decodeSpamSafeEmail
    for word, sign in _transdict.items():
        address = address.replace(sign, ' {0} '.format(word))
    if obfuscation_text.isalpha():
        # is the obfuscation_text alphabetic
        address = address.replace(' AT ', ' AT {0} '.format(obfuscation_text.upper()))

    return address

def decodeSpamSafeEmail(address):
    """ Decode obfuscated email address to standard email address

    Decode a spam-safe email address in `address` by applying the
    following rules:

    Known all-uppercase words and their translation:
        "DOT"   -> "."
        "AT"    -> "@"
        "DASH"  -> "-"

    Any unknown all-uppercase words or an uppercase letter simply get stripped.
    Use that to make it even harder for spam bots!

    Blanks (spaces) simply get stripped.

    :param address: obfuscated email address string
    :rtype: string
    :returns: decoded email address
    """
    email = []

    # words are separated by blanks
    for word in address.split():
        # is it all-uppercase?
        if word.isalpha() and word == word.upper():
            # strip unknown CAPS words
            word = _transdict.get(word, '')
        email.append(word)

    # return concatenated parts
    return ''.join(email)