Esempio n. 1
0
 def translate(self, lang, encoding = None, do_Q=False):
     if lang == 'en':
         return self.data
     res = self.get(lang)
     if isinstance(res, unicode):
         if encoding is None:
             import registry
             try:
                 encoding = registry.team(lang).charset
             except KeyError:
                 encoding = 'utf-8'
             if encoding is None:
                 encoding = 'iso-8859-1'
         try:
             res = res.encode(encoding)
         except (KeyError, UnicodeError):
             encoding="utf-8"
             res = res.encode('utf-8')
     if do_Q and encoding:
         # Do quoted printable encoding.
         from email.Charset import Charset
         charset = Charset(encoding)
         lines = res.split('\n')
         for i in range(len(lines)):
             l = lines[i]
             try:
                 l.decode("us-ascii")
             except UnicodeError:
                 pass
             else:
                 continue
             f,b = l.split(": ",1) # RFC822 field and body
             lines[i] = f+": "+charset.header_encode(b)
         res = "\n".join(lines)
     return res
Esempio n. 2
0
def decodeMessageAsString(msg):
    """ This helper method takes Message object or string and returns
        string which does not contain base64 encoded parts
        Returns message without any encoding in parts
    """
    if isinstance(msg, str):
        msg = Parser().parsestr(msg)

    new = deepcopy(msg)
    # From is utf8 encoded: '=?utf-8?q?Site_Administrator_=3C=3E?='
    new.replace_header('From', decode_header(new['From'])[0][0])
    new.replace_header('Subject', decode_header(new['Subject'])[0][0])
    charset = Charset('utf-8')
    charset.header_encoding = SHORTEST
    charset.body_encoding = QP
    charset.output_charset = 'utf-8'

    for part in new.walk():
        if part.get_content_maintype() == "multipart":
            continue
        decoded = part.get_payload(decode=1)
        del part['Content-Transfer-Encoding']
        part.set_payload(decoded, charset)

    return new.as_string()
Esempio n. 3
0
def decodeMessageAsString(msg):
    """ This helper method takes Message object or string and returns
        string which does not contain base64 encoded parts
        Returns message without any encoding in parts
    """
    if isinstance(msg, str):
        msg = Parser().parsestr(msg)

    new = deepcopy(msg)
    # From is utf8 encoded: '=?utf-8?q?Site_Administrator_=3C=3E?='
    new.replace_header('From', decode_header(new['From'])[0][0])
    new.replace_header('Subject', decode_header(new['Subject'])[0][0])
    charset = Charset('utf-8')
    charset.header_encoding = SHORTEST
    charset.body_encoding = QP
    charset.output_charset = 'utf-8'

    for part in new.walk():
        if part.get_content_maintype() == "multipart":
            continue
        decoded = part.get_payload(decode=1)
        del part['Content-Transfer-Encoding']
        part.set_payload(decoded, charset)

    return new.as_string()
    def test_japanese_codecs(self):
        eq = self.ndiffAssertEqual
        j = Charset("euc-jp")
        g = Charset("iso-8859-1")
        h = Header("Hello World!")
        jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa'
        ghello = 'Gr\xfc\xdf Gott!'
        h.append(jhello, j)
        h.append(ghello, g)
        # BAW: This used to -- and maybe should -- fold the two iso-8859-1
        # chunks into a single encoded word.  However it doesn't violate the
        # standard to have them as two encoded chunks and maybe it's
        # reasonable <wink> for each .append() call to result in a separate
        # encoded word.
        eq(
            h.encode(), """\
Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
 =?iso-8859-1?q?Gr=FC=DF?= =?iso-8859-1?q?_Gott!?=""")
        eq(decode_header(h.encode()),
           [('Hello World!', None),
            ('\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'),
            ('Gr\xfc\xdf Gott!', 'iso-8859-1')])
        long = 'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9'
        h = Header(long, j, header_name="Subject")
        # test a very long header
        enc = h.encode()
        # TK: splitting point may differ by codec design and/or Header encoding
        eq(
            enc, """\
=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?=
 =?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""")
        # TK: full decode comparison
        eq(h.__unicode__().encode('euc-jp'), long)
Esempio n. 5
0
    def create_message(self):

        if self.attach:
            self.create_multipart_message()
        else:
            self.create_text_message()
        """comment
        """
        charset = Charset(self.encoding)
        charset.header_encoding = QP
        charset.body_encoding = QP
        self.msg.set_charset(charset)
        """
Esempio n. 6
0
    def create_message(self):

        if self.attach:
            self.create_multipart_message()
        else:
            self.create_text_message()

        """comment
        """
        charset = Charset(self.encoding)
        charset.header_encoding = QP
        charset.body_encoding = QP
        self.msg.set_charset(charset)
        """
Esempio n. 7
0
def prepare_message(template, variables, encoding="utf-8"):
    r"""Return a prepared email.Message object.

    >>> template = (u"Subject: @SUBJECT@\n"+
    ...             u"From: @FROM@\n"+
    ...             u"To: @TO@\n"+
    ...             u"BCC: @FROM@\n\n"+
    ...             u"Hello, @GREETED@!")
    >>> variables = dict(SUBJECT="Test",
    ...                  FROM="*****@*****.**",
    ...                  TO="*****@*****.**",
    ...                  GREETED="World")
    >>> message = prepare_message(template, variables)
    >>> message["SUBJECT"] == variables["SUBJECT"]
    True
    >>> message["TO"] == variables["TO"]
    True
    >>> message["FROM"] == message["BCC"] == variables["FROM"]
    True
    >>> message.get_payload()
    'Hello, World!'
    """

    template = u"\r\n".join(template.splitlines())
    template = replace_variables(template, variables)
    template = template.encode(encoding)
    message = message_from_string(template)

    DEFAULT_HEADERS = {"to": "@INVITEDEMAIL@", "from": "@INVITEREMAIL@"}
    for key, value in DEFAULT_HEADERS.iteritems():
        if key not in message:
            value = replace_variables(value, variables)
            if isinstance(value, unicode):
                value = value.encode(encoding)
            message[key] = value

    charset = Charset(encoding)
    charset.header_encoding = QP
    charset.body_encoding = QP
    message.set_charset(charset)

    for field in ("from", "to", "cc", "bcc"):
        try:
            encode_address_field(message, field, encoding, charset)
        except UnicodeEncodeError as error:
            raise InviteException("Invalid '{0}' address: {1}".format(
                field, error))

    return message
Esempio n. 8
0
def prepare_message(template, variables, encoding="utf-8"):
    r"""Return a prepared email.Message object.

    >>> template = (u"Subject: @SUBJECT@\n"+
    ...             u"From: @FROM@\n"+
    ...             u"To: @TO@\n"+
    ...             u"BCC: @FROM@\n\n"+
    ...             u"Hello, @GREETED@!")
    >>> variables = dict(SUBJECT="Test",
    ...                  FROM="*****@*****.**",
    ...                  TO="*****@*****.**",
    ...                  GREETED="World")
    >>> message = prepare_message(template, variables)
    >>> message["SUBJECT"] == variables["SUBJECT"]
    True
    >>> message["TO"] == variables["TO"]
    True
    >>> message["FROM"] == message["BCC"] == variables["FROM"]
    True
    >>> message.get_payload()
    'Hello, World!'
    """

    template = u"\r\n".join(template.splitlines())
    template = replace_variables(template, variables)
    template = template.encode(encoding)
    message = message_from_string(template)

    DEFAULT_HEADERS = {"to": "@INVITEDEMAIL@", "from": "@INVITEREMAIL@"}
    for key, value in DEFAULT_HEADERS.iteritems():
        if key not in message:
            value = replace_variables(value, variables)
            if isinstance(value, unicode):
                value = value.encode(encoding)
            message[key] = value

    charset = Charset(encoding)
    charset.header_encoding = QP
    charset.body_encoding = QP
    message.set_charset(charset)

    for field in ("from", "to", "cc", "bcc"):
        try:
            encode_address_field(message, field, encoding, charset)
        except UnicodeEncodeError as error:
            raise InviteException("Invalid '{0}' address: {1}".format(field, error))

    return message
Esempio n. 9
0
def mejl(tabelka, ustawieniaMejla):
    od, do, smtp = tuple([ustawieniaMejla[x] for x in ["od", "do", "smtp"]])
    tekst = u"<h2>Wyniki</h2>" + "<ul>"

    for dzien in tabelka.keys():
        tekst = tekst + "<li>" + uni(dzien) + "<ol>"
        for wynikDnia in tabelka[dzien]:
            tekst = tekst + "<li>" + uni(wynikDnia) + "</li>"
        tekst = tekst + "</ol></li>"

    tekst = tekst + ("</ul>" +"<br/>\r-- " +"<br/>\r %s") \
      % datetime.datetime.now().__str__()

    temat = "[MEDICOVER] %s" % (datetime.datetime.now())

    charset = Charset('utf-8')
    tresc = MIMEText(tekst.encode('utf-8'), 'html')
    tresc.set_charset(charset)
    tresc['From'] = od
    tresc['To'] = ", ".join(do)
    tresc['Subject'] = temat

    if ustawieniaMejla.get('smtp_tls'):
        smtp_pass = haslo(smtp, od, ustawieniaMejla.get('smtp_password'))
        serwer = smtplib.SMTP(smtp, 587)
        serwer.starttls()
        serwer.login(od, smtp_pass)
    else:
        serwer = smtplib.SMTP(smtp)
    serwer.sendmail(od, do, tresc.as_string())
    serwer.quit()
Esempio n. 10
0
 def append(self, s, charset = None, errors = 'strict'):
     if charset is None:
         charset = self._charset
     elif not isinstance(charset, Charset):
         charset = Charset(charset)
     
     if charset != '8bit':
         if isinstance(s, str):
             if not charset.input_codec:
                 pass
             incodec = 'us-ascii'
             ustr = unicode(s, incodec, errors)
             if not charset.output_codec:
                 pass
             outcodec = 'us-ascii'
             ustr.encode(outcodec, errors)
         elif isinstance(s, unicode):
             for charset in (USASCII, charset, UTF8):
                 
                 try:
                     if not charset.output_codec:
                         pass
                     outcodec = 'us-ascii'
                     s = s.encode(outcodec, errors)
                 continue
                 except UnicodeError:
                     continue
Esempio n. 11
0
 def __init__(self,
              s=None,
              charset=None,
              maxlinelen=None,
              header_name=None,
              continuation_ws=' ',
              errors='strict'):
     if charset is None:
         charset = USASCII
     if not isinstance(charset, Charset):
         charset = Charset(charset)
     self._charset = charset
     self._continuation_ws = continuation_ws
     cws_expanded_len = len(continuation_ws.replace('\t', SPACE8))
     self._chunks = []
     if s is not None:
         self.append(s, charset, errors)
     if maxlinelen is None:
         maxlinelen = MAXLINELEN
     if header_name is None:
         self._firstlinelen = maxlinelen
     else:
         self._firstlinelen = maxlinelen - len(header_name) - 2
     self._maxlinelen = maxlinelen - cws_expanded_len
     return
Esempio n. 12
0
def _encode_address_string(text, charset):
    """Split the email into parts and use header encoding on the name
    part if needed. We do this because the actual addresses need to be
    ASCII with no encoding for most SMTP servers, but the non-address
    parts should be encoded appropriately."""
    header = Header()
    name, addr = parseaddr(text)
    try:
        name.decode('us-ascii')
    except UnicodeDecodeError:
        if charset:
            charset = Charset(charset)
            name = charset.header_encode(name)
    # We again replace rather than raise an error or pass an 8bit string
    header.append(formataddr((name, addr)), errors='replace')
    return header
Esempio n. 13
0
def _encode_address_string(text, charset):
    """Split the email into parts and use header encoding on the name
    part if needed. We do this because the actual addresses need to be
    ASCII with no encoding for most SMTP servers, but the non-address
    parts should be encoded appropriately."""
    header = Header()
    name, addr = parseaddr(text)
    try:
        name.decode('us-ascii')
    except UnicodeDecodeError:
        if charset:
            charset = Charset(charset)
            name = charset.header_encode(name)
    # We again replace rather than raise an error or pass an 8bit string
    header.append(formataddr((name, addr)), errors='replace')
    return header
Esempio n. 14
0
class TestEncodeAddress(object):
    """ Address encoding tests

    See http://www.faqs.org/rfcs/rfc2822.html section 3.4.
    Address Specification.

    mailbox     =   name-addr / addr-spec
    name-addr   =   [display-name] angle-addr
    angle-addr  =   [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
    """
    charset = Charset(config.charset)
    charset.header_encoding = QP
    charset.body_encoding = QP

    def testSimpleAddress(self):
        """ mail.sendmail: encode simple address: local@domain """
        address = u'local@domain'
        expected = address.encode(config.charset)
        assert sendmail.encodeAddress(address, self.charset) == expected

    def testComposite(self):
        """ mail.sendmail: encode address: 'Phrase <local@domain>' """
        address = u'Phrase <local@domain>'
        expected = str(address)
        assert sendmail.encodeAddress(address, self.charset) == expected

    def testCompositeUnicode(self):
        """ mail.sendmail: encode Uncode address: 'ויקי <local@domain>' """
        address = u'ויקי <local@domain>'
        phrase = str(Header(u'ויקי'.encode('utf-8'), self.charset))
        expected = phrase + ' ' + '<local@domain>'
        assert sendmail.encodeAddress(address, self.charset) == expected

    def testEmptyPhrase(self):
        """ mail.sendmail: encode address with empty phrase: '<local@domain>' """
        address = u'<local@domain>'
        expected = 'local@domain'
        assert sendmail.encodeAddress(address, self.charset) == expected

    def testEmptyAddress(self):
        """ mail.sendmail: encode address with empty address: 'Phrase <>'

        Let the smtp server handle this. We may raise error in such
        case, but we don't do error checking for mail addresses.
        """
        address = u'Phrase <>'
        expected = str(address)
        assert sendmail.encodeAddress(address, self.charset) == expected

    def testInvalidAddress(self):
        """ mail.sendmail: encode invalid address 'Phrase <blah'

        Assume that this is a simple address. This address will
        probably cause an error when trying to send mail. Junk in, junk
        out.
        """
        address = u'Phrase <blah'
        expected = str(address)
        assert sendmail.encodeAddress(address, self.charset) == expected
Esempio n. 15
0
def to_cset_out(text, lcset):
    # Convert text from unicode or lcset to output cset.
    ocset = Charset(lcset).get_output_charset() or lcset
    if isinstance(text, unicode):
        return text.encode(ocset, errors='replace')
    else:
        return text.decode(lcset, errors='replace').encode(ocset,
                                                           errors='replace')
Esempio n. 16
0
def make_header(decoded_seq, maxlinelen = None, header_name = None, continuation_ws = ' '):
    h = Header(maxlinelen = maxlinelen, header_name = header_name, continuation_ws = continuation_ws)
    for (s, charset) in decoded_seq:
        if charset is not None and not isinstance(charset, Charset):
            charset = Charset(charset)
        
        h.append(s, charset)
    
    return h
Esempio n. 17
0
    def __init__(self,
                 s=None,
                 charset=None,
                 maxlinelen=None,
                 header_name=None,
                 continuation_ws=' ',
                 errors='strict'):
        """Create a MIME-compliant header that can contain many character sets.

        Optional s is the initial header value.  If None, the initial header
        value is not set.  You can later append to the header with .append()
        method calls.  s may be a byte string or a Unicode string, but see the
        .append() documentation for semantics.

        Optional charset serves two purposes: it has the same meaning as the
        charset argument to the .append() method.  It also sets the default
        character set for all subsequent .append() calls that omit the charset
        argument.  If charset is not provided in the constructor, the us-ascii
        charset is used both as s's initial charset and as the default for
        subsequent .append() calls.

        The maximum line length can be specified explicit via maxlinelen.  For
        splitting the first line to a shorter value (to account for the field
        header which isn't included in s, e.g. `Subject') pass in the name of
        the field in header_name.  The default maxlinelen is 76.

        continuation_ws must be RFC 2822 compliant folding whitespace (usually
        either a space or a hard tab) which will be prepended to continuation
        lines.

        errors is passed through to the .append() call.
        """
        if charset is None:
            charset = USASCII
        if not isinstance(charset, Charset):
            charset = Charset(charset)
        self._charset = charset
        self._continuation_ws = continuation_ws
        cws_expanded_len = len(continuation_ws.replace('\t', SPACE8))
        # BAW: I believe `chunks' and `maxlinelen' should be non-public.
        self._chunks = []
        if s is not None:
            self.append(s, charset, errors)
        if maxlinelen is None:
            maxlinelen = MAXLINELEN
        if header_name is None:
            # We don't know anything about the field header so the first line
            # is the same length as subsequent lines.
            self._firstlinelen = maxlinelen
        else:
            # The first line should be shorter to take into account the field
            # header.  Also subtract off 2 extra for the colon and space.
            self._firstlinelen = maxlinelen - len(header_name) - 2
        # Second and subsequent lines should subtract off the length in
        # columns of the continuation whitespace prefix.
        self._maxlinelen = maxlinelen - cws_expanded_len
Esempio n. 18
0
    def append(self, s, charset=None, errors='strict'):
        """Append a string to the MIME header.

        Optional charset, if given, should be a Charset instance or the name
        of a character set (which will be converted to a Charset instance).  A
        value of None (the default) means that the charset given in the
        constructor is used.

        s may be a byte string or a Unicode string.  If it is a byte string
        (i.e. isinstance(s, StringType) is true), then charset is the encoding
        of that byte string, and a UnicodeError will be raised if the string
        cannot be decoded with that charset.  If s is a Unicode string, then
        charset is a hint specifying the character set of the characters in
        the string.  In this case, when producing an RFC 2822 compliant header
        using RFC 2047 rules, the Unicode string will be encoded using the
        following charsets in order: us-ascii, the charset hint, utf-8.  The
        first character set not to provoke a UnicodeError is used.

        Optional `errors' is passed as the third argument to any unicode() or
        ustr.encode() call.
        """
        if charset is None:
            charset = self._charset
        elif not isinstance(charset, Charset):
            charset = Charset(charset)
        # If the charset is our faux 8bit charset, leave the string unchanged
        if charset <> '8bit':
            # We need to test that the string can be converted to unicode and
            # back to a byte string, given the input and output codecs of the
            # charset.
            if isinstance(s, StringType):
                # Possibly raise UnicodeError if the byte string can't be
                # converted to a unicode with the input codec of the charset.
                incodec = charset.input_codec or 'us-ascii'
                ustr = unicode(s, incodec, errors)
                # Now make sure that the unicode could be converted back to a
                # byte string with the output codec, which may be different
                # than the iput coded.  Still, use the original byte string.
                outcodec = charset.output_codec or 'us-ascii'
                ustr.encode(outcodec, errors)
            elif isinstance(s, UnicodeType):
                # Now we have to be sure the unicode string can be converted
                # to a byte string with a reasonable output codec.  We want to
                # use the byte string in the chunk.
                for charset in USASCII, charset, UTF8:
                    try:
                        outcodec = charset.output_codec or 'us-ascii'
                        s = s.encode(outcodec, errors)
                        break
                    except UnicodeError:
                        pass
                else:
                    assert False, 'utf-8 conversion failed'
        self._chunks.append((s, charset))
Esempio n. 19
0
    def mejl(self, tabelka, ustawieniaMejla):
        od, do, smtp = tuple(
            [ustawieniaMejla[x] for x in ["od", "do", "smtp"]])
        tekst = u"<h2>Wyniki</h2><ul>"

        poprzedniDzien = ""
        for wiersz in tabelka:
            if wiersz[0] != poprzedniDzien:
                tekst = tekst + "</ul><h4>%s</h4><ul>" % wiersz[0].strftime(
                    "%A, %Y-%m-%d")
            reprezentacja = self.pozbadzSiePolskichLiter(
                "%s" % (", ".join(wiersz[1:])))
            style = ""
            if not self.sprawdzCzyJuzSpotkalismy("%s" % wiersz[0] +
                                                 reprezentacja):
                style = " style='color: green'"
            tekst = tekst + "<li%s>%s</li>" % (style, reprezentacja)
            poprzedniDzien = wiersz[0]

        tekst = tekst + "</ul><br/><br/>"

        tekst = tekst + ("<br/>\r-- " +
                         "<br/>\r %s") % datetime.datetime.now().__str__()

        temat = "[%s] %s" % (self.naglowekWMejlu, datetime.datetime.now())

        charset = Charset('utf-8')
        tresc = MIMEText(tekst.encode('utf-8'), 'html')
        tresc.set_charset(charset)
        tresc['From'] = od
        tresc['To'] = ", ".join(do)
        tresc['Subject'] = temat

        if ustawieniaMejla.get('smtp_tls'):
            smtp_pass = hasla.haslo(smtp, od,
                                    ustawieniaMejla.get('smtp_password'))
            serwer = smtplib.SMTP(smtp, 587)
            serwer.starttls()
            serwer.login(od, smtp_pass)
        else:
            serwer = smtplib.SMTP(smtp)
        serwer.sendmail(od, do, tresc.as_string())
        serwer.quit()
Esempio n. 20
0
def encode_header_param(param_text):
    """Returns an appropriate RFC2047 encoded representation of the given
       header parameter value, suitable for direct assignation as the
       param value (e.g. via Message.set_param() or Message.add_header())
       RFC2822 assumes that headers contain only 7-bit characters,
       so we ensure it is the case, using RFC2047 encoding when needed.

       :param param_text: unicode or utf-8 encoded string with header value
       :rtype: string
       :return: if ``param_text`` represents a plain ASCII string,
                return the same 7-bit string, otherwise returns an
                ASCII string containing the RFC2047 encoded text.
    """
    # For details see the encode_header() method that uses the same logic
    if not param_text: return ""
    param_text_utf8 = tools.ustr(param_text).encode('utf-8')
    param_text_ascii = try_coerce_ascii(param_text_utf8)
    return param_text_ascii if param_text_ascii\
         else Charset('utf8').header_encode(param_text_utf8)
Esempio n. 21
0
def make_header(decoded_seq, maxlinelen=None, header_name=None,
                continuation_ws=' '):
    """Create a Header from a sequence of pairs as returned by decode_header()

    decode_header() takes a header value string and returns a sequence of
    pairs of the format (decoded_string, charset) where charset is the string
    name of the character set.

    This function takes one of those sequence of pairs and returns a Header
    instance.  Optional maxlinelen, header_name, and continuation_ws are as in
    the Header constructor.
    """
    h = Header(maxlinelen=maxlinelen, header_name=header_name,
               continuation_ws=continuation_ws)
    for s, charset in decoded_seq:
        # None means us-ascii but we can simply pass it on to h.append()
        if charset is not None and not isinstance(charset, Charset):
            charset = Charset(charset)
        h.append(s, charset)
    return h
Esempio n. 22
0
 def __init__(self, recip, sender, subject=None, text=None, lang=None):
     Message.__init__(self)
     charset = None
     if lang is not None:
         charset = Charset(Utils.GetCharSet(lang))
     if text is not None:
         self.set_payload(text, charset)
     if subject is None:
         subject = '(no subject)'
     self['Subject'] = Header(subject,
                              charset,
                              header_name='Subject',
                              errors='replace')
     self['From'] = sender
     if isinstance(recip, ListType):
         self['To'] = COMMASPACE.join(recip)
         self.recips = recip
     else:
         self['To'] = recip
         self.recips = [recip]
Esempio n. 23
0
 def _init_pref_encoding(self):
     from email.Charset import BASE64, QP, SHORTEST, Charset
     self._charset = Charset()
     self._charset.input_charset = 'utf-8'
     self._charset.output_charset = 'utf-8'
     self._charset.input_codec = 'utf-8'
     self._charset.output_codec = 'utf-8'
     pref = self.config.get('notification', 'mime_encoding').lower()
     if pref == 'base64':
         self._charset.header_encoding = BASE64
         self._charset.body_encoding = BASE64
     elif pref in ('qp', 'quoted-printable'):
         self._charset.header_encoding = QP
         self._charset.body_encoding = QP
     elif pref == 'none':
         self._charset.header_encoding = SHORTEST
         self._charset.body_encoding = None
     else:
         raise TracError(_("Invalid email encoding setting: %(pref)s",
                           pref=pref))
Esempio n. 24
0
    def append(self, s, charset=None, errors='strict'):
        if charset is None:
            charset = self._charset
        elif not isinstance(charset, Charset):
            charset = Charset(charset)
        if charset != '8bit':
            if isinstance(s, str):
                incodec = charset.input_codec or 'us-ascii'
                ustr = unicode(s, incodec, errors)
                outcodec = charset.output_codec or 'us-ascii'
                ustr.encode(outcodec, errors)
            elif isinstance(s, unicode):
                for charset in (USASCII, charset, UTF8):
                    try:
                        outcodec = charset.output_codec or 'us-ascii'
                        s = s.encode(outcodec, errors)
                        break
                    except UnicodeError:
                        pass

        self._chunks.append((s, charset))
        return
Esempio n. 25
0
 def __setstate__(self, d):
     # The base class attributes have changed over time.  Which could
     # affect Mailman if messages are sitting in the queue at the time of
     # upgrading the email package.  We shouldn't burden email with this,
     # so we handle schema updates here.
     self.__dict__ = d
     # We know that email 2.4.3 is up-to-date
     version = d.get('__version__', (0, 0, 0))
     d['__version__'] = VERSION
     if version >= VERSION:
         return
     # Messages grew a _charset attribute between email version 0.97 and 1.1
     if not d.has_key('_charset'):
         self._charset = None
     # Messages grew a _default_type attribute between v2.1 and v2.2
     if not d.has_key('_default_type'):
         # We really have no idea whether this message object is contained
         # inside a multipart/digest or not, so I think this is the best we
         # can do.
         self._default_type = 'text/plain'
     # Header instances used to allow both strings and Charsets in their
     # _chunks, but by email 2.4.3 now it's just Charsets.
     headers = []
     hchanged = 0
     for k, v in self._headers:
         if isinstance(v, Header):
             chunks = []
             cchanged = 0
             for s, charset in v._chunks:
                 if isinstance(charset, StringType):
                     charset = Charset(charset)
                     cchanged = 1
                 chunks.append((s, charset))
             if cchanged:
                 v._chunks = chunks
                 hchanged = 1
         headers.append((k, v))
     if hchanged:
         self._headers = headers
Esempio n. 26
0
 def _init_pref_encoding(self):
     self._charset = Charset()
     self._charset.input_charset = 'utf-8'
     pref = self.mime_encoding.lower()
     if pref == 'base64':
         self._charset.header_encoding = BASE64
         self._charset.body_encoding = BASE64
         self._charset.output_charset = 'utf-8'
         self._charset.input_codec = 'utf-8'
         self._charset.output_codec = 'utf-8'
     elif pref in ['qp', 'quoted-printable']:
         self._charset.header_encoding = QP
         self._charset.body_encoding = QP
         self._charset.output_charset = 'utf-8'
         self._charset.input_codec = 'utf-8'
         self._charset.output_codec = 'utf-8'
     elif pref == 'none':
         self._charset.header_encoding = None
         self._charset.body_encoding = None
         self._charset.input_codec = None
         self._charset.output_charset = 'ascii'
     else:
         raise TracError(_('Invalid email encoding setting: %s' % pref))
Esempio n. 27
0
 def _init_pref_encoding(self):
     from email.Charset import Charset, QP, BASE64
     self._charset = Charset()
     self._charset.input_charset = 'utf-8'
     pref = self.env.config.get('notification', 'mime_encoding').lower()
     if pref == 'base64':
         self._charset.header_encoding = BASE64
         self._charset.body_encoding = BASE64
         self._charset.output_charset = 'utf-8'
         self._charset.input_codec = 'utf-8'
         self._charset.output_codec = 'utf-8'
     elif pref in ['qp', 'quoted-printable']:
         self._charset.header_encoding = QP
         self._charset.body_encoding = QP
         self._charset.output_charset = 'utf-8'
         self._charset.input_codec = 'utf-8'
         self._charset.output_codec = 'utf-8'
     elif pref == 'none':
         self._charset.header_encoding = None
         self._charset.body_encoding = None
         self._charset.input_codec = None
         self._charset.output_charset = 'ascii'
     else:
         raise TracError, 'Invalid email encoding setting: %s' % pref
Esempio n. 28
0
def process(mlist, msg, msgdata):
    # Digests and Mailman-craft messages should not get additional headers
    if msgdata.get('isdigest') or msgdata.get('nodecorate'):
        return
    d = {}
    if msgdata.get('personalize'):
        # Calculate the extra personalization dictionary.  Note that the
        # length of the recips list better be exactly 1.
        recips = msgdata.get('recips')
        assert type(recips) == ListType and len(recips) == 1
        member = recips[0].lower()
        d['user_address'] = member
        try:
            d['user_delivered_to'] = mlist.getMemberCPAddress(member)
            # BAW: Hmm, should we allow this?
            d['user_password'] = mlist.getMemberPassword(member)
            d['user_language'] = mlist.getMemberLanguage(member)
            username = mlist.getMemberName(member) or None
            try:
                username = username.encode(Utils.GetCharSet(d['user_language']))
            except (AttributeError, UnicodeError):
                username = member
            d['user_name'] = username
            d['user_optionsurl'] = mlist.GetOptionsURL(member)
        except Errors.NotAMemberError:
            pass
    # These strings are descriptive for the log file and shouldn't be i18n'd
    d.update(msgdata.get('decoration-data', {}))
    header = decorate(mlist, mlist.msg_header, 'non-digest header', d)
    footer = decorate(mlist, mlist.msg_footer, 'non-digest footer', d)
    # Escape hatch if both the footer and header are empty
    if not header and not footer:
        return
    # Be MIME smart here.  We only attach the header and footer by
    # concatenation when the message is a non-multipart of type text/plain.
    # Otherwise, if it is not a multipart, we make it a multipart, and then we
    # add the header and footer as text/plain parts.
    #
    # BJG: In addition, only add the footer if the message's character set
    # matches the charset of the list's preferred language.  This is a
    # suboptimal solution, and should be solved by allowing a list to have
    # multiple headers/footers, for each language the list supports.
    #
    # Also, if the list's preferred charset is us-ascii, we can always
    # safely add the header/footer to a plain text message since all
    # charsets Mailman supports are strict supersets of us-ascii --
    # no, UTF-16 emails are not supported yet.
    #
    # TK: Message with 'charset=' cause trouble. So, instead of
    #     mgs.get_content_charset('us-ascii') ...
    mcset = msg.get_content_charset() or 'us-ascii'
    lcset = Utils.GetCharSet(mlist.preferred_language)
    msgtype = msg.get_content_type()
    # BAW: If the charsets don't match, should we add the header and footer by
    # MIME multipart chroming the message?
    wrap = True
    if not msg.is_multipart() and msgtype == 'text/plain':
        # TK: Set up a list for Decorate charsets
        csets = []
        for cs in mm_cfg.DECORATE_CHARSETS:
            if cs == mm_cfg.DECORATE_MCSET:
                cs = mcset
            if cs == mm_cfg.DECORATE_LCSET:
                cs = lcset
            cs = Charset(cs).output_charset
            if cs not in csets:
                csets.append(cs)
        # TK: Try to keep the message plain by converting the header/
        # footer/oldpayload into unicode and encode with mcset/lcset.
        # Try to decode qp/base64 also.
        # It is possible header/footer is already unicode if it was
        # interpolated with a unicode.
        if isinstance(header, unicode):
            uheader = header
        else:
            uheader = unicode(header, lcset, 'ignore')
        if isinstance(footer, unicode):
            ufooter = footer
        else:
            ufooter = unicode(footer, lcset, 'ignore')
        try:
            oldpayload = unicode(msg.get_payload(decode=True), mcset)
            frontsep = endsep = u''
            if header and not header.endswith('\n'):
                frontsep = u'\n'
            if footer and not oldpayload.endswith('\n'):
                endsep = u'\n'
            payload = uheader + frontsep + oldpayload + endsep + ufooter
            for cs in csets:
                try:
                    newpayload = payload.encode(cs)
                    format = msg.get_param('format')
                    delsp = msg.get_param('delsp')
                    cte = msg.get('content-transfer-encoding', '').lower()
                    del msg['content-transfer-encoding']
                    del msg['content-type']
                    if mm_cfg.DECORATE_PREFER_8BIT or cs == mcset:
                        cs = Charset(cs)
                        if cte == 'quoted-printable':
                            cs.body_encoding = QP
                        elif cte == 'base64':
                            cs.body_encoding = BASE64
                        else:
                            cs.body_encoding = None
                    msg.set_payload(newpayload, cs)
                    if format:
                        msg.set_param('Format', format)
                    if delsp:
                        msg.set_param('DelSp', delsp)
                    wrap = False
                    break
                except (UnicodeError, TypeError):
                    continue
        except (LookupError, UnicodeError):
            pass
    elif msg.get_content_type() == 'multipart/mixed':
        # The next easiest thing to do is just prepend the header and append
        # the footer as additional subparts
        payload = msg.get_payload()
        if not isinstance(payload, ListType):
            payload = [payload]
        if footer:
            mimeftr = MIMEText(footer, 'plain', lcset)
            mimeftr['Content-Disposition'] = 'inline'
            payload.append(mimeftr)
        if header:
            mimehdr = MIMEText(header, 'plain', lcset)
            mimehdr['Content-Disposition'] = 'inline'
            payload.insert(0, mimehdr)
        msg.set_payload(payload)
        wrap = False
    # If we couldn't add the header or footer in a less intrusive way, we can
    # at least do it by MIME encapsulation.  We want to keep as much of the
    # outer chrome as possible.
    if not wrap:
        return
    # Because of the way Message objects are passed around to process(), we
    # need to play tricks with the outer message -- i.e. the outer one must
    # remain the same instance.  So we're going to create a clone of the outer
    # message, with all the header chrome intact, then copy the payload to it.
    # This will give us a clone of the original message, and it will form the
    # basis of the interior, wrapped Message.
    inner = Message()
    # Which headers to copy?  Let's just do the Content-* headers
    copied = False
    for h, v in msg.items():
        if h.lower().startswith('content-'):
            inner[h] = v
            copied = True
    inner.set_payload(msg.get_payload())
    # For completeness
    inner.set_unixfrom(msg.get_unixfrom())
    inner.preamble = msg.preamble
    inner.epilogue = msg.epilogue
    # Don't copy get_charset, as this might be None, even if
    # get_content_charset isn't.  However, do make sure there is a default
    # content-type, even if the original message was not MIME.
    inner.set_default_type(msg.get_default_type())
    if not copied:
        inner['Content-Type'] = inner.get_content_type()
    if msg['mime-version'] == None:
        msg['MIME-Version'] = '1.0'
    # BAW: HACK ALERT.
    if hasattr(msg, '__version__'):
        inner.__version__ = msg.__version__
    # Now, play games with the outer message to make it contain three
    # subparts: the header (if any), the wrapped message, and the footer (if
    # any).
    payload = [inner]
    if header:
        mimehdr = MIMEText(header, 'plain', lcset)
        mimehdr['Content-Disposition'] = 'inline'
        payload.insert(0, mimehdr)
    if footer:
        mimeftr = MIMEText(footer, 'plain', lcset)
        mimeftr['Content-Disposition'] = 'inline'
        payload.append(mimeftr)
    msg.set_payload(payload)
    del msg['content-type']
    del msg['content-transfer-encoding']
    del msg['content-disposition']
    msg['Content-Type'] = 'multipart/mixed'
Esempio n. 29
0
    def __init__(self, message=None, sequence=0, keepHeaders=[],
                       lang=mm_cfg.DEFAULT_SERVER_LANGUAGE, mlist=None):
        self.__super_init(message, sequence, keepHeaders)
        self.prev = None
        self.next = None
        # Trim Re: from the subject line
        i = 0
        while i != -1:
            result = REpat.match(self.subject)
            if result:
                i = result.end(0)
                self.subject = self.subject[i:]
            else:
                i = -1
        # Useful to keep around
        self._lang = lang
        self._mlist = mlist

        if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
            # Avoid i18n side-effects.  Note that the language for this
            # article (for this list) could be different from the site-wide
            # preferred language, so we need to ensure no side-effects will
            # occur.  Think what happens when executing bin/arch.
            otrans = i18n.get_translation()
            try:
                i18n.set_language(lang)
                if self.author == self.email:
                    self.author = self.email = re.sub('@', _(' at '),
                                                      self.email)
                else:
                    self.email = re.sub('@', _(' at '), self.email)
            finally:
                i18n.set_translation(otrans)

        # Snag the content-* headers.  RFC 1521 states that their values are
        # case insensitive.
        ctype = message.get('Content-Type', 'text/plain')
        cenc = message.get('Content-Transfer-Encoding', '')
        self.ctype = ctype.lower()
        self.cenc = cenc.lower()
        self.decoded = {}
        cset = Utils.GetCharSet(mlist.preferred_language)
        cset_out = Charset(cset).output_charset or cset
        if isinstance(cset_out, unicode):
            # email 3.0.1 (python 2.4) doesn't like unicode
            cset_out = cset_out.encode('us-ascii')
        charset = message.get_content_charset(cset_out)
        if charset:
            charset = charset.lower().strip()
            if charset[0]=='"' and charset[-1]=='"':
                charset = charset[1:-1]
            if charset[0]=="'" and charset[-1]=="'":
                charset = charset[1:-1]
            try:
                body = message.get_payload(decode=True)
            except binascii.Error:
                body = None
            if body and charset != Utils.GetCharSet(self._lang):
                # decode body
                try:
                    body = unicode(body, charset)
                except (UnicodeError, LookupError):
                    body = None
            if body:
                self.body = [l + "\n" for l in body.splitlines()]

        self.decode_headers()
Esempio n. 30
0
def send_i18n_digests(mlist, mboxfp):
    mbox = Mailbox(mboxfp)
    # Prepare common information (first lang/charset)
    lang = mlist.preferred_language
    lcset = Utils.GetCharSet(lang)
    lcset_out = Charset(lcset).output_charset or lcset
    # Common Information (contd)
    realname = mlist.real_name
    volume = mlist.volume
    issue = mlist.next_digest_number
    digestid = _('%(realname)s Digest, Vol %(volume)d, Issue %(issue)d')
    digestsubj = Header(digestid, lcset, header_name='Subject')
    # Set things up for the MIME digest.  Only headers not added by
    # CookHeaders need be added here.
    # Date/Message-ID should be added here also.
    mimemsg = Message.Message()
    mimemsg['Content-Type'] = 'multipart/mixed'
    mimemsg['MIME-Version'] = '1.0'
    mimemsg['From'] = mlist.GetRequestEmail()
    mimemsg['Subject'] = digestsubj
    mimemsg['To'] = mlist.GetListEmail()
    mimemsg['Reply-To'] = mlist.GetListEmail()
    mimemsg['Date'] = formatdate(localtime=1)
    mimemsg['Message-ID'] = Utils.unique_message_id(mlist)
    # Set things up for the rfc1153 digest
    plainmsg = StringIO()
    rfc1153msg = Message.Message()
    rfc1153msg['From'] = mlist.GetRequestEmail()
    rfc1153msg['Subject'] = digestsubj
    rfc1153msg['To'] = mlist.GetListEmail()
    rfc1153msg['Reply-To'] = mlist.GetListEmail()
    rfc1153msg['Date'] = formatdate(localtime=1)
    rfc1153msg['Message-ID'] = Utils.unique_message_id(mlist)
    separator70 = '-' * 70
    separator30 = '-' * 30
    # In the rfc1153 digest, the masthead contains the digest boilerplate plus
    # any digest header.  In the MIME digests, the masthead and digest header
    # are separate MIME subobjects.  In either case, it's the first thing in
    # the digest, and we can calculate it now, so go ahead and add it now.
    mastheadtxt = Utils.maketext(
        'masthead.txt', {
            'real_name': mlist.real_name,
            'got_list_email': mlist.GetListEmail(),
            'got_listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
            'got_request_email': mlist.GetRequestEmail(),
            'got_owner_email': mlist.GetOwnerEmail(),
        },
        mlist=mlist)
    # MIME
    masthead = MIMEText(mastheadtxt, _charset=lcset)
    masthead['Content-Description'] = digestid
    mimemsg.attach(masthead)
    # RFC 1153
    print >> plainmsg, mastheadtxt
    print >> plainmsg
    # Now add the optional digest header but only if more than whitespace.
    if re.sub('\s', '', mlist.digest_header):
        headertxt = decorate(mlist, mlist.digest_header, _('digest header'))
        # MIME
        header = MIMEText(headertxt, _charset=lcset)
        header['Content-Description'] = _('Digest Header')
        mimemsg.attach(header)
        # RFC 1153
        print >> plainmsg, headertxt
        print >> plainmsg
    # Now we have to cruise through all the messages accumulated in the
    # mailbox file.  We can't add these messages to the plainmsg and mimemsg
    # yet, because we first have to calculate the table of contents
    # (i.e. grok out all the Subjects).  Store the messages in a list until
    # we're ready for them.
    #
    # Meanwhile prepare things for the table of contents
    toc = StringIO()
    print >> toc, _("Today's Topics:\n")
    # Now cruise through all the messages in the mailbox of digest messages,
    # building the MIME payload and core of the RFC 1153 digest.  We'll also
    # accumulate Subject: headers and authors for the table-of-contents.
    messages = []
    msgcount = 0
    msg = mbox.next()
    while msg is not None:
        if msg == '':
            # It was an unparseable message
            msg = mbox.next()
            continue
        msgcount += 1
        messages.append(msg)
        # Get the Subject header
        msgsubj = msg.get('subject', _('(no subject)'))
        subject = Utils.oneline(msgsubj, lcset)
        # Don't include the redundant subject prefix in the toc
        mo = re.match('(re:? *)?(%s)' % re.escape(mlist.subject_prefix),
                      subject, re.IGNORECASE)
        if mo:
            subject = subject[:mo.start(2)] + subject[mo.end(2):]
        username = ''
        addresses = getaddresses([Utils.oneline(msg.get('from', ''), lcset)])
        # Take only the first author we find
        if isinstance(addresses, ListType) and addresses:
            username = addresses[0][0]
            if not username:
                username = addresses[0][1]
        if username:
            username = '******' % username
        # Put count and Wrap the toc subject line
        wrapped = Utils.wrap('%2d. %s' % (msgcount, subject), 65)
        slines = wrapped.split('\n')
        # See if the user's name can fit on the last line
        if len(slines[-1]) + len(username) > 70:
            slines.append(username)
        else:
            slines[-1] += username
        # Add this subject to the accumulating topics
        first = True
        for line in slines:
            if first:
                print >> toc, ' ', line
                first = False
            else:
                print >> toc, '     ', line.lstrip()
        # We do not want all the headers of the original message to leak
        # through in the digest messages.  For this phase, we'll leave the
        # same set of headers in both digests, i.e. those required in RFC 1153
        # plus a couple of other useful ones.  We also need to reorder the
        # headers according to RFC 1153.  Later, we'll strip out headers for
        # for the specific MIME or plain digests.
        keeper = {}
        all_keepers = {}
        for header in (mm_cfg.MIME_DIGEST_KEEP_HEADERS +
                       mm_cfg.PLAIN_DIGEST_KEEP_HEADERS):
            all_keepers[header] = True
        all_keepers = all_keepers.keys()
        for keep in all_keepers:
            keeper[keep] = msg.get_all(keep, [])
        # Now remove all unkempt headers :)
        for header in msg.keys():
            del msg[header]
        # And add back the kept header in the RFC 1153 designated order
        for keep in all_keepers:
            for field in keeper[keep]:
                msg[keep] = field
        # And a bit of extra stuff
        msg['Message'] = ` msgcount `
        # Get the next message in the digest mailbox
        msg = mbox.next()
    # Now we're finished with all the messages in the digest.  First do some
    # sanity checking and then on to adding the toc.
    if msgcount == 0:
        # Why did we even get here?
        return
    toctext = to_cset_out(toc.getvalue(), lcset)
    # MIME
    tocpart = MIMEText(toctext, _charset=lcset)
    tocpart['Content-Description'] = _(
        "Today's Topics (%(msgcount)d messages)")
    mimemsg.attach(tocpart)
    # RFC 1153
    print >> plainmsg, toctext
    print >> plainmsg
    # For RFC 1153 digests, we now need the standard separator
    print >> plainmsg, separator70
    print >> plainmsg
    # Now go through and add each message
    mimedigest = MIMEBase('multipart', 'digest')
    mimemsg.attach(mimedigest)
    first = True
    for msg in messages:
        # MIME.  Make a copy of the message object since the rfc1153
        # processing scrubs out attachments.
        mimedigest.attach(MIMEMessage(copy.deepcopy(msg)))
        # rfc1153
        if first:
            first = False
        else:
            print >> plainmsg, separator30
            print >> plainmsg
        # Use Mailman.Handlers.Scrubber.process() to get plain text
        try:
            msg = scrubber(mlist, msg)
        except Errors.DiscardMessage:
            print >> plainmsg, _('[Message discarded by content filter]')
            continue
        # Honor the default setting
        for h in mm_cfg.PLAIN_DIGEST_KEEP_HEADERS:
            if msg[h]:
                uh = Utils.wrap('%s: %s' % (h, Utils.oneline(msg[h], lcset)))
                uh = '\n\t'.join(uh.split('\n'))
                print >> plainmsg, uh
        print >> plainmsg
        # If decoded payload is empty, this may be multipart message.
        # -- just stringfy it.
        payload = msg.get_payload(decode=True) \
                  or msg.as_string().split('\n\n',1)[1]
        mcset = msg.get_content_charset('')
        if mcset and mcset <> lcset and mcset <> lcset_out:
            try:
                payload = unicode(payload, mcset,
                                  'replace').encode(lcset, 'replace')
            except (UnicodeError, LookupError):
                # TK: Message has something unknown charset.
                #     _out means charset in 'outer world'.
                payload = unicode(payload, lcset_out,
                                  'replace').encode(lcset, 'replace')
        print >> plainmsg, payload
        if not payload.endswith('\n'):
            print >> plainmsg
    # Now add the footer but only if more than whitespace.
    if re.sub('\s', '', mlist.digest_footer):
        footertxt = decorate(mlist, mlist.digest_footer, _('digest footer'))
        # MIME
        footer = MIMEText(footertxt, _charset=lcset)
        footer['Content-Description'] = _('Digest Footer')
        mimemsg.attach(footer)
        # RFC 1153
        # MAS: There is no real place for the digest_footer in an RFC 1153
        # compliant digest, so add it as an additional message with
        # Subject: Digest Footer
        print >> plainmsg, separator30
        print >> plainmsg
        print >> plainmsg, 'Subject: ' + _('Digest Footer')
        print >> plainmsg
        print >> plainmsg, footertxt
        print >> plainmsg
        print >> plainmsg, separator30
        print >> plainmsg
    # Do the last bit of stuff for each digest type
    signoff = _('End of ') + digestid
    # MIME
    # BAW: This stuff is outside the normal MIME goo, and it's what the old
    # MIME digester did.  No one seemed to complain, probably because you
    # won't see it in an MUA that can't display the raw message.  We've never
    # got complaints before, but if we do, just wax this.  It's primarily
    # included for (marginally useful) backwards compatibility.
    mimemsg.postamble = signoff
    # rfc1153
    print >> plainmsg, signoff
    print >> plainmsg, '*' * len(signoff)
    # Do our final bit of housekeeping, and then send each message to the
    # outgoing queue for delivery.
    mlist.next_digest_number += 1
    virginq = get_switchboard(mm_cfg.VIRGINQUEUE_DIR)
    # Calculate the recipients lists
    plainrecips = []
    mimerecips = []
    drecips = mlist.getDigestMemberKeys() + mlist.one_last_digest.keys()
    for user in mlist.getMemberCPAddresses(drecips):
        # user might be None if someone who toggled off digest delivery
        # subsequently unsubscribed from the mailing list.  Also, filter out
        # folks who have disabled delivery.
        if user is None or mlist.getDeliveryStatus(user) <> ENABLED:
            continue
        # Otherwise, decide whether they get MIME or RFC 1153 digests
        if mlist.getMemberOption(user, mm_cfg.DisableMime):
            plainrecips.append(user)
        else:
            mimerecips.append(user)
    # Zap this since we're now delivering the last digest to these folks.
    mlist.one_last_digest.clear()
    # MIME
    virginq.enqueue(mimemsg,
                    recips=mimerecips,
                    listname=mlist.internal_name(),
                    isdigest=True)
    # RFC 1153
    rfc1153msg.set_payload(to_cset_out(plainmsg.getvalue(), lcset), lcset)
    virginq.enqueue(rfc1153msg,
                    recips=plainrecips,
                    listname=mlist.internal_name(),
                    isdigest=True)
Esempio n. 31
0
    def _set_charset(self, mime):
        from email.Charset import Charset, QP, BASE64, SHORTEST
        mime_encoding = self.config.get('notification',
                                        'mime_encoding').lower()

        charset = Charset()
        charset.input_charset = 'utf-8'
        charset.output_charset = 'utf-8'
        charset.input_codec = 'utf-8'
        charset.output_codec = 'utf-8'
        if mime_encoding == 'base64':
            charset.header_encoding = BASE64
            charset.body_encoding = BASE64
        elif mime_encoding in ('qp', 'quoted-printable'):
            charset.header_encoding = QP
            charset.body_encoding = QP
        elif mime_encoding == 'none':
            charset.header_encoding = SHORTEST
            charset.body_encoding = None

        del mime['Content-Transfer-Encoding']
        mime.set_charset(charset)
Esempio n. 32
0
def verpdeliver(mlist, msg, msgdata, envsender, failures, conn):
    for recip in msgdata['recips']:
        # We now need to stitch together the message with its header and
        # footer.  If we're VERPIng, we have to calculate the envelope sender
        # for each recipient.  Note that the list of recipients must be of
        # length 1.
        #
        # BAW: ezmlm includes the message number in the envelope, used when
        # sending a notification to the user telling her how many messages
        # they missed due to bouncing.  Neat idea.
        msgdata['recips'] = [recip]
        # Make a copy of the message and decorate + delivery that
        msgcopy = copy.deepcopy(msg)
        Decorate.process(mlist, msgcopy, msgdata)
        # Calculate the envelope sender, which we may be VERPing
        if msgdata.get('verp'):
            bmailbox, bdomain = Utils.ParseEmail(envsender)
            rmailbox, rdomain = Utils.ParseEmail(recip)
            if rdomain is None:
                # The recipient address is not fully-qualified.  We can't
                # deliver it to this person, nor can we craft a valid verp
                # header.  I don't think there's much we can do except ignore
                # this recipient.
                syslog('smtp', 'Skipping VERP delivery to unqual recip: %s',
                       recip)
                continue
            d = {'bounces': bmailbox,
                 'mailbox': rmailbox,
                 'host'   : DOT.join(rdomain),
                 }
            envsender = '%s@%s' % ((mm_cfg.VERP_FORMAT % d), DOT.join(bdomain))
        if mlist.personalize == 2:
            # When fully personalizing, we want the To address to point to the
            # recipient, not to the mailing list
            del msgcopy['to']
            name = None
            if mlist.isMember(recip):
                name = mlist.getMemberName(recip)
            if name:
                # Convert the name to an email-safe representation.  If the
                # name is a byte string, convert it first to Unicode, given
                # the character set of the member's language, replacing bad
                # characters for which we can do nothing about.  Once we have
                # the name as Unicode, we can create a Header instance for it
                # so that it's properly encoded for email transport.
                charset = Utils.GetCharSet(mlist.getMemberLanguage(recip))
                if charset == 'us-ascii':
                    # Since Header already tries both us-ascii and utf-8,
                    # let's add something a bit more useful.
                    charset = 'iso-8859-1'
                charset = Charset(charset)
                codec = charset.input_codec or 'ascii'
                if not isinstance(name, UnicodeType):
                    name = unicode(name, codec, 'replace')
                name = Header(name, charset).encode()
                msgcopy['To'] = formataddr((name, recip))
            else:
                msgcopy['To'] = recip
        # We can flag the mail as a duplicate for each member, if they've
        # already received this message, as calculated by Message-ID.  See
        # AvoidDuplicates.py for details.
        del msgcopy['x-mailman-copy']
        if msgdata.get('add-dup-header', {}).has_key(recip):
            msgcopy['X-Mailman-Copy'] = 'yes'
        # For the final delivery stage, we can just bulk deliver to a party of
        # one. ;)
        bulkdeliver(mlist, msgcopy, msgdata, envsender, failures, conn)
Esempio n. 33
0
def pending_requests(mlist):
    # Must return a byte string
    lcset = mlist.preferred_language.charset
    pending = []
    first = True
    requestsdb = IListRequests(mlist)
    for request in requestsdb.of_type(RequestType.subscription):
        if first:
            pending.append(_('Pending subscriptions:'))
            first = False
        key, data = requestsdb.get_request(request.id)
        when = data['when']
        addr = data['addr']
        fullname = data['fullname']
        passwd = data['passwd']
        digest = data['digest']
        lang = data['lang']
        if fullname:
            if isinstance(fullname, unicode):
                fullname = fullname.encode(lcset, 'replace')
            fullname = ' (%s)' % fullname
        pending.append('    %s%s %s' % (addr, fullname, time.ctime(when)))
    first = True
    for request in requestsdb.of_type(RequestType.held_message):
        if first:
            pending.append(_('\nPending posts:'))
            first = False
        key, data = requestsdb.get_request(request.id)
        when = data['when']
        sender = data['sender']
        subject = data['subject']
        reason = data['reason']
        text = data['text']
        msgdata = data['msgdata']
        subject = Utils.oneline(subject, lcset)
        date = time.ctime(when)
        reason = _(reason)
        pending.append(
            _("""\
From: $sender on $date
Subject: $subject
Cause: $reason"""))
        pending.append('')
    # Coerce all items in pending to a Unicode so we can join them
    upending = []
    charset = mlist.preferred_language.charset
    for s in pending:
        if isinstance(s, unicode):
            upending.append(s)
        else:
            upending.append(unicode(s, charset, 'replace'))
    # Make sure that the text we return from here can be encoded to a byte
    # string in the charset of the list's language.  This could fail if for
    # example, the request was pended while the list's language was French,
    # but then it was changed to English before checkdbs ran.
    text = NL.join(upending)
    charset = Charset(mlist.preferred_language.charset)
    incodec = charset.input_codec or 'ascii'
    outcodec = charset.output_codec or 'ascii'
    if isinstance(text, unicode):
        return text.encode(outcodec, 'replace')
    # Be sure this is a byte string encodeable in the list's charset
    utext = unicode(text, incodec, 'replace')
    return utext.encode(outcodec, 'replace')
Esempio n. 34
0
def process(mlist, msg, msgdata=None):
    # main entry code for the Handler
    global DEBUG
    if hasattr(mlist, 'debug'):
        DEBUG = mlist.debug

    debug('AttachmentMove Enter ' + '-' * 30)

    if msgdata is None:
        msgdata = {}

    modified = False

    #dir = calculate_attachments_dir(mlist, msg, msgdata)
    dir = 'attachments-moved'
    # Now walk over all subparts of this message and scrub out various types
    seen_attachment = []
    boundary = None

    # as we replace some content we will have to fight with encoding
    # set some default list encoding
    lcset = Utils.GetCharSet(mlist.preferred_language)
    lcset_out = Charset(lcset).output_charset or lcset

    for part in msg.walk():
        ctype = part.get_content_type()
        partlen = len(part.get_payload())
        debug('met part : %s %d', ctype, partlen)

        # If the part is text/plain, we leave it alone
        if ctype == 'text/plain':
            continue
        elif ctype == 'text/html':
            continue
        elif ctype == 'message/rfc822':
            continue
        elif partlen > 0 and not part.is_multipart():
            # we met an attachment
            debug('> part is attachment %s', ctype)
            if part.has_key('Content-ID'):
                debug('> part as Content-ID %s', part['Content-ID'])
                # keep it
                continue
            else:
                debug('> detaching...')

            # we are going to detach it and store it localy and remotly
            # a dic storing attachment related data
            attachment = {}
            fname = get_attachment_fname(mlist, part)
            debug('get_attachment_fname:%s, type:%s', fname, type(fname))
            attachment['name'] = fname
            attachment['orig'] = fname
            attachment['size'] = sizeof_fmt(partlen)
            debug('> att: %s', fname)
            # save attachment to the disk, at this stage duplicate name
            # are resolved
            path, url = save_attachment(mlist, part, dir)
            debug('> detached: %s %s', path, url)
            # remote storing, no trouble very simple code here using
            # secured FTP and the remote user config
            if 'disable_upload' in msgdata:
                debug('> uploading disabled')
                remote_fname = 'disabled'
            else:
                remote_fname = ftp_upload_attchment(mlist, path)
            # build the new url of the document, will be used when
            # modifying parts, see bellow.
            url = mlist.remote_http_base + remote_fname
            attachment['url'] = url
            reset_payload(part, 'removed', fname, url)
            seen_attachment.append(attachment)
            modified = True
            continue
        elif mutipartre.search(ctype):
            # match multipart/*
            boundary = part.get_boundary()
            debug('>>> is multipart part %s, boundary: %s', ctype, boundary)
            continue
        else:
            if boundary != None and part.get_boundary() == boundary:
                debug('same boundary skiped : %s', ctype)
                continue
            else:
                boundary = None
            debug('attachement : %s', ctype)

        debug('end of loop?? : %s', ctype)

    if not modified:
        return msg

    # rewrite content
    # d is a dict for simple storage of mutliple parameters
    # will be passed to the recursive func fix_msg()
    d = {}
    d['footer_attach'] = ''
    d['html_footer_attach'] = ''

    clip_cid = "clip.12345789"
    # the clip is already base64 encoded above
    d['clip'] = MIMEImage(ATTACH_CLIP, 'png', _encoder=encoders.encode_noop)
    d['clip']['Content-Transfer-Encoding'] = 'base64'
    d['clip'].add_header('Content-ID', '<part1.%s>' % clip_cid)

    replace = {}
    replace['CID_clip'] = 'part1.' + clip_cid
    # compose attachment url
    for att in seen_attachment:
        d['footer_attach'] += make_link(att) + "\n"
        replace['FNAME_replace'] = att['orig']
        replace['URL_replace'] = att['url']
        replace['SIZE_replace'] = att['size']
        d['html_footer_attach'] += HTML_ATTACHMENT_CLIP_TPL % replace

    debug('================ start fix_msg() ==================')
    d['lcset'] = lcset
    d['lcset_out'] = lcset_out

    d['do_txt'] = True
    d['do_html'] = True

    fix_msg(msg, d)

    return msg
Esempio n. 35
0
def fix_msg(msg, data):
    """
    Scan the message recursively to replace the text/html by a 
    multipart/related containing the original text/html and the new 
    clip_payload png attachment. The attachment detected and moved at
    the first pass (with Header X-Mailman-Part) will be removed.
    """

    if msg.is_multipart():
        parts = msg.get_payload()
        # remove the next level parts, then process and reattach them
        msg.set_payload(None)
        for p in parts:
            # recursive call
            r = fix_msg(p, data)
            # don't embbed related twice
            if msg.get_content_type() == 'multipart/related' and \
                 r.get_content_type() == 'multipart/related':
                for newp in r.get_payload():
                    msg.attach(newp)
            elif r == None:
                # removed
                continue
            else:
                msg.attach(r)
        # finished
        return msg
    else:
        # process the 'leaf' parts
        ctype = msg.get_content_type()
        # will be used to write back payload with correct encoding
        charset = msg.get_content_charset()
        c = Charset('utf-8')
        c.body_encoding = QP
        debug('ctype:%s charset:%s', ctype, charset)
        if ctype == 'text/plain':
            if msg['X-Mailman-Part']:
                # remove it!
                return None

            if data['do_txt']:
                # A normal txt part, add footer to plain text
                new_footer = TXT_ATTACHT_REPLACE
                new_footer += data['footer_attach']
                old_content = msg.get_payload(decode=True)
                debug('old_content:%s, new_footer:%s', \
                    type(old_content), type(new_footer))

                del msg['Content-type']
                del msg['content-transfer-encoding']
                msg.set_payload(old_content + new_footer, charset=c)

                debug('add txt footer')
                data['do_txt'] = False

            return msg
        elif ctype == 'text/html' and data['do_html']:
            # build multipart/related for HTML, will be canceled by the
            # parent recursive call if needed
            related = MIMEMultipart('related')

            html_footer = HTML_ATTACHMENT_HOLDER % \
                {'HTML_HERE': data['html_footer_attach'] }
            html_footer += '</body>'
            old_content = msg.get_payload(decode=True)
            new_content = re.sub(r'</body>', html_footer, old_content)

            if old_content != new_content:
                debug('add html footer')
            else:
                debug('no html footer added')

            del msg['content-transfer-encoding']
            msg.set_payload(new_content, charset=c)

            related.attach(msg)
            related.attach(data['clip'])
            data['do_html'] = False
            return related
        # unmodified
        return msg
Esempio n. 36
0
    def __init__(self,
                 message=None,
                 sequence=0,
                 keepHeaders=[],
                 lang=mm_cfg.DEFAULT_SERVER_LANGUAGE,
                 mlist=None):
        self.__super_init(message, sequence, keepHeaders)
        self.prev = None
        self.next = None
        # Trim Re: from the subject line
        i = 0
        while i != -1:
            result = REpat.match(self.subject)
            if result:
                i = result.end(0)
                self.subject = self.subject[i:]
            else:
                i = -1
        # Useful to keep around
        self._lang = lang
        self._mlist = mlist

        if mm_cfg.ARCHIVER_OBSCURES_EMAILADDRS:
            # Avoid i18n side-effects.  Note that the language for this
            # article (for this list) could be different from the site-wide
            # preferred language, so we need to ensure no side-effects will
            # occur.  Think what happens when executing bin/arch.
            otrans = i18n.get_translation()
            try:
                i18n.set_language(lang)
                if self.author == self.email:
                    self.author = self.email = re.sub('@', _(' at '),
                                                      self.email)
                else:
                    self.email = re.sub('@', _(' at '), self.email)
            finally:
                i18n.set_translation(otrans)

        # Snag the content-* headers.  RFC 1521 states that their values are
        # case insensitive.
        ctype = message.get('Content-Type', 'text/plain')
        cenc = message.get('Content-Transfer-Encoding', '')
        self.ctype = ctype.lower()
        self.cenc = cenc.lower()
        self.decoded = {}
        cset = Utils.GetCharSet(mlist.preferred_language)
        cset_out = Charset(cset).output_charset or cset
        if isinstance(cset_out, unicode):
            # email 3.0.1 (python 2.4) doesn't like unicode
            cset_out = cset_out.encode('us-ascii')
        charset = message.get_content_charset(cset_out)
        if charset:
            charset = charset.lower().strip()
            if charset[0] == '"' and charset[-1] == '"':
                charset = charset[1:-1]
            if charset[0] == "'" and charset[-1] == "'":
                charset = charset[1:-1]
            try:
                body = message.get_payload(decode=True)
            except binascii.Error:
                body = None
            if body and charset != Utils.GetCharSet(self._lang):
                # decode body
                try:
                    body = unicode(body, charset)
                except (UnicodeError, LookupError):
                    body = None
            if body:
                self.body = [l + "\n" for l in body.splitlines()]

        self.decode_headers()
Esempio n. 37
0
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)
    """
    import smtplib, socket
    from email.Message import Message
    from email.Charset import Charset, QP
    from email.Utils import formatdate, make_msgid

    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"))

    subject = subject.encode(config.charset)

    # Create a text/plain body using CRLF (see RFC2822)
    text = text.replace(u'\n', u'\r\n')
    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

    # Send the message
    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"))
Esempio n. 38
0
CRLFSPACE = '\r\n '
CRLF = '\r\n'
NL = '\n'
SPACE = ' '
USPACE = u' '
SPACE8 = ' ' * 8
EMPTYSTRING = ''
UEMPTYSTRING = u''

MAXLINELEN = 76

ENCODE = 1
DECODE = 2

USASCII = Charset('us-ascii')
UTF8 = Charset('utf-8')

# Match encoded-word strings in the form =?charset?q?Hello_World?=
ecre = re.compile(
    r'''
  =\?                   # literal =?
  (?P<charset>[^?]*?)   # non-greedy up to the next ? is the charset
  \?                    # literal ?
  (?P<encoding>[qb])    # either a "q" or a "b", case insensitive
  \?                    # literal ?
  (?P<encoded>.*?)      # non-greedy up to the next ?= is the encoded string
  \?=                   # literal ?=
  ''', re.VERBOSE | re.IGNORECASE)

pcre = re.compile('([,;])')
Esempio n. 39
0
def sendmail(request, to, subject, text, mail_from=None):
    """ Create and send a text/plain message

    Return a tuple of success or error indicator and message.

    @param request: the request object
    @param to: recipients (list)
    @param subject: subject of email (unicode)
    @param text: email body text (unicode)
    @param mail_from: override default mail_from
    @type mail_from: unicode
    @rtype: tuple
    @return: (is_ok, Description of error or OK message)
    """
    import smtplib, socket
    from email.Message import Message
    from email.Charset import Charset, QP
    from email.Utils import formatdate, make_msgid

    _ = request.getText
    cfg = request.cfg
    mail_from = mail_from or cfg.mail_from
    subject = subject.encode(config.charset)

    # Create a text/plain body using CRLF (see RFC2822)
    text = text.replace(u'\n', u'\r\n')
    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)

    # Create message headers
    # Don't expose emails addreses of the other subscribers, instead we
    # use the same mail_from, e.g. u"Jürgen Wiki <*****@*****.**>"
    address = encodeAddress(mail_from, charset)
    msg['From'] = address
    msg['To'] = address
    msg['Date'] = formatdate()
    msg['Message-ID'] = make_msgid()
    msg['Subject'] = Header(subject, charset)

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

    # Send the message
    if not cfg.mail_sendmail:
        try:
            logging.debug("trying to send mail (smtp) via smtp server '%s'" % 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_login:
                    user, pwd = cfg.mail_login.split()
                    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 '%s'" % user)
                    server.login(user, pwd)
                server.sendmail(mail_from, to, msg.as_string())
            finally:
                try:
                    server.quit()
                except AttributeError:
                    # in case the connection failed, SMTP has no "sock" attribute
                    pass
        except smtplib.SMTPException, e:
            logging.exception("smtp mail failed with an exception.")
            return (0, str(e))
        except (os.error, socket.error), 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)
            })
Esempio n. 40
0
# NNTP message encoding trivia

from email.Header import Header
from email.Charset import Charset
from StringIO import StringIO
import email.Charset
import base64
import quopri
import re

charset = Charset('utf-8')
charset.header_encoding = email.Charset.SHORTEST
charset.body_encoding = email.Charset.SHORTEST

line_end_re = re.compile(r'\r\n|\n\r|\n(?!\r)|\r(?!\n)')

def encode_header_word(word):
    if type(word) is unicode:
        # see if it is plain ascii first
        try:
            return word.encode('us-ascii')
        except:
            # try to encode non-ascii headers using email.Header.  The
            # 1000000 value is a maximum line length, meaning never
            # fold header lines.  This is important for the XOVER
            # response.
            return str(Header(word, charset, 1000000))
    else:
        return word

# base64 encode a string, splitting lines in the output