mercurial/mail.py
author timeless@mozdev.org
Fri, 28 Aug 2015 10:53:55 -0400
changeset 26098 ce26928cbe41
parent 25957 ae21d51bdc43
child 26587 56b2bcea2529
permissions -rw-r--r--
spelling: behaviour -> behavior
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     1
# mail.py - mail sending bits for mercurial
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     2
#
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     3
# Copyright 2006 Matt Mackall <mpm@selenic.com>
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     4
#
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 7948
diff changeset
     5
# This software may be used and distributed according to the terms of the
10263
25e572394f5c Update license to GPLv2+
Matt Mackall <mpm@selenic.com>
parents: 9715
diff changeset
     6
# GNU General Public License version 2 or any later version.
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     7
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
     8
from __future__ import absolute_import
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
     9
19790
53f16f4aff33 mail: correct import of email module
Augie Fackler <raf@durin42.com>
parents: 19050
diff changeset
    10
import email
25957
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    11
import os
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    12
import quopri
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    13
import smtplib
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    14
import socket
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    15
import sys
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    16
import time
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    17
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    18
from .i18n import _
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    19
from . import (
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    20
    encoding,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    21
    sslutil,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    22
    util,
ae21d51bdc43 mail: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25842
diff changeset
    23
)
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    24
11542
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    25
_oldheaderinit = email.Header.Header.__init__
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    26
def _unifiedheaderinit(self, *args, **kw):
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    27
    """
17428
72803c8edaa4 avoid using abbreviations that look like spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 17424
diff changeset
    28
    Python 2.7 introduces a backwards incompatible change
11542
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    29
    (Python issue1974, r70772) in email.Generator.Generator code:
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    30
    pre-2.7 code passed "continuation_ws='\t'" to the Header
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    31
    constructor, and 2.7 removed this parameter.
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    32
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    33
    Default argument is continuation_ws=' ', which means that the
26098
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 25957
diff changeset
    34
    behavior is different in <2.7 and 2.7
11542
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    35
26098
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 25957
diff changeset
    36
    We consider the 2.7 behavior to be preferable, but need
ce26928cbe41 spelling: behaviour -> behavior
timeless@mozdev.org
parents: 25957
diff changeset
    37
    to have an unified behavior for versions 2.4 to 2.7
11542
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    38
    """
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    39
    # override continuation_ws
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    40
    kw['continuation_ws'] = ' '
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    41
    _oldheaderinit(self, *args, **kw)
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    42
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    43
email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
594b98846ce1 mail: ensure that Python2.4 to 2.7 use the same header format
Nicolas Dumazet <nicdumz.commits@gmail.com>
parents: 10264
diff changeset
    44
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    45
class STARTTLS(smtplib.SMTP):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    46
    '''Derived class to verify the peer certificate for STARTTLS.
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    47
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    48
    This class allows to pass any keyword arguments to SSL socket creation.
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    49
    '''
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    50
    def __init__(self, sslkwargs, **kwargs):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    51
        smtplib.SMTP.__init__(self, **kwargs)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    52
        self._sslkwargs = sslkwargs
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    53
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    54
    def starttls(self, keyfile=None, certfile=None):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    55
        if not self.has_extn("starttls"):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    56
            msg = "STARTTLS extension not supported by server"
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    57
            raise smtplib.SMTPException(msg)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    58
        (resp, reply) = self.docmd("STARTTLS")
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    59
        if resp == 220:
25429
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25205
diff changeset
    60
            self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile,
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25205
diff changeset
    61
                                           **self._sslkwargs)
18885
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    62
            if not util.safehasattr(self.sock, "read"):
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    63
                # using httplib.FakeSocket with Python 2.5.x or earlier
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    64
                self.sock.read = self.sock.recv
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    65
            self.file = smtplib.SSLFakeFile(self.sock)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    66
            self.helo_resp = None
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    67
            self.ehlo_resp = None
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    68
            self.esmtp_features = {}
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    69
            self.does_esmtp = 0
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    70
        return (resp, reply)
cf1304fbc184 smtp: add the class to verify the certificate of the SMTP server for STARTTLS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 17428
diff changeset
    71
18886
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    72
if util.safehasattr(smtplib.SMTP, '_get_socket'):
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    73
    class SMTPS(smtplib.SMTP):
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    74
        '''Derived class to verify the peer certificate for SMTPS.
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    75
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    76
        This class allows to pass any keyword arguments to SSL socket creation.
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    77
        '''
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    78
        def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    79
            self.keyfile = keyfile
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    80
            self.certfile = certfile
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    81
            smtplib.SMTP.__init__(self, **kwargs)
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    82
            self.default_port = smtplib.SMTP_SSL_PORT
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    83
            self._sslkwargs = sslkwargs
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    84
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    85
        def _get_socket(self, host, port, timeout):
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    86
            if self.debuglevel > 0:
18916
6edb0e18b83c mail: add missing import of sys
Bryan O'Sullivan <bryano@fb.com>
parents: 18888
diff changeset
    87
                print >> sys.stderr, 'connect:', (host, port)
18886
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    88
            new_socket = socket.create_connection((host, port), timeout)
25429
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25205
diff changeset
    89
            new_socket = sslutil.wrapsocket(new_socket,
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25205
diff changeset
    90
                                            self.keyfile, self.certfile,
9d1c61715939 ssl: rename ssl_wrap_socket() to conform to our naming convention
Yuya Nishihara <yuya@tcha.org>
parents: 25205
diff changeset
    91
                                            **self._sslkwargs)
18886
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    92
            self.file = smtplib.SSLFakeFile(new_socket)
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    93
            return new_socket
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    94
else:
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    95
    def SMTPS(sslkwargs, keyfile=None, certfile=None, **kwargs):
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    96
        raise util.Abort(_('SMTPS requires Python 2.6 or later'))
14a60a0f7122 smtp: add the class to verify the certificate of the SMTP server for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18885
diff changeset
    97
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    98
def _smtp(ui):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
    99
    '''build an smtp connection and return a function to send mail'''
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   100
    local_hostname = ui.config('smtp', 'local_hostname')
13244
d8f92c3a17d6 mail: fix regression when parsing unset smtp.tls option
Patrick Mezard <pmezard@gmail.com>
parents: 13201
diff changeset
   101
    tls = ui.config('smtp', 'tls', 'none')
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   102
    # backward compatible: when tls = true, we use starttls.
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   103
    starttls = tls == 'starttls' or util.parsebool(tls)
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   104
    smtps = tls == 'smtps'
14965
194b043dfa51 mail: use safehasattr instead of hasattr
Augie Fackler <durin42@gmail.com>
parents: 14271
diff changeset
   105
    if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   106
        raise util.Abort(_("can't use TLS: Python SSL support not installed"))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   107
    mailhost = ui.config('smtp', 'host')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   108
    if not mailhost:
12090
ee601a6264e0 mail: use standard section.entry format in error message
Martin Geisler <mg@lazybytes.net>
parents: 11542
diff changeset
   109
        raise util.Abort(_('smtp.host not configured - cannot send mail'))
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   110
    verifycert = ui.config('smtp', 'verifycert', 'strict')
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   111
    if verifycert not in ['strict', 'loose']:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   112
        if util.parsebool(verifycert) is not False:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   113
            raise util.Abort(_('invalid smtp.verifycert configuration: %s')
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   114
                             % (verifycert))
23223
a4af6fd99fb0 mail: actually use the verifycert config value
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 19810
diff changeset
   115
        verifycert = False
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   116
    if (starttls or smtps) and verifycert:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   117
        sslkwargs = sslutil.sslkwargs(ui, mailhost)
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   118
    else:
25463
03af5c2ddf75 mail: pass ui to sslutil.wrapsocket() even if verifycert is off (issue4713)
Yuya Nishihara <yuya@tcha.org>
parents: 25429
diff changeset
   119
        # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs()
03af5c2ddf75 mail: pass ui to sslutil.wrapsocket() even if verifycert is off (issue4713)
Yuya Nishihara <yuya@tcha.org>
parents: 25429
diff changeset
   120
        sslkwargs = {'ui': ui}
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   121
    if smtps:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   122
        ui.note(_('(using smtps)\n'))
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   123
        s = SMTPS(sslkwargs, local_hostname=local_hostname)
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   124
    elif starttls:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   125
        s = STARTTLS(sslkwargs, local_hostname=local_hostname)
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   126
    else:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   127
        s = smtplib.SMTP(local_hostname=local_hostname)
19050
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
   128
    if smtps:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
   129
        defaultport = 465
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
   130
    else:
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
   131
        defaultport = 25
601c1e226889 smtp: use 465 as default port for SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18916
diff changeset
   132
    mailport = util.getport(ui.config('smtp', 'port', defaultport))
2964
26c8d37496c2 fix typo in mail.py
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 2929
diff changeset
   133
    ui.note(_('sending mail: smtp host %s, port %s\n') %
26c8d37496c2 fix typo in mail.py
Alexis S. L. Carvalho <alexis@cecm.usp.br>
parents: 2929
diff changeset
   134
            (mailhost, mailport))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   135
    s.connect(host=mailhost, port=mailport)
13201
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   136
    if starttls:
f05250572467 smtp: fix for server doesn't support starttls extension
Zhigang Wang <zhigang.x.wang@oracle.com>
parents: 12091
diff changeset
   137
        ui.note(_('(using starttls)\n'))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   138
        s.ehlo()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   139
        s.starttls()
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   140
        s.ehlo()
18888
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   141
    if (starttls or smtps) and verifycert:
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   142
        ui.note(_('(verifying remote certificate)\n'))
19d489404d79 smtp: verify the certificate of the SMTP server for STARTTLS/SMTPS
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 18886
diff changeset
   143
        sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   144
    username = ui.config('smtp', 'username')
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   145
    password = ui.config('smtp', 'password')
5749
4fba4fee0718 Patchbomb: Prompt password when using SMTP/TLS and no password in .hgrc.
Arun Thomas <arun.thomas@gmail.com>
parents: 5472
diff changeset
   146
    if username and not password:
4fba4fee0718 Patchbomb: Prompt password when using SMTP/TLS and no password in .hgrc.
Arun Thomas <arun.thomas@gmail.com>
parents: 5472
diff changeset
   147
        password = ui.getpass()
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   148
    if username and password:
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   149
        ui.note(_('(authenticating to mail server as %s)\n') %
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   150
                  (username))
9246
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
   151
        try:
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
   152
            s.login(username, password)
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25463
diff changeset
   153
        except smtplib.SMTPException as inst:
9246
2de7d96593db email: Catch exceptions during send.
David Soria Parra <dsp@php.net>
parents: 8343
diff changeset
   154
            raise util.Abort(inst)
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   155
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   156
    def send(sender, recipients, msg):
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   157
        try:
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   158
            return s.sendmail(sender, recipients, msg)
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25463
diff changeset
   159
        except smtplib.SMTPRecipientsRefused as inst:
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   160
            recipients = [r[1] for r in inst.recipients.values()]
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   161
            raise util.Abort('\n' + '\n'.join(recipients))
25660
328739ea70c3 global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25463
diff changeset
   162
        except smtplib.SMTPException as inst:
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   163
            raise util.Abort(inst)
5947
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5866
diff changeset
   164
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   165
    return send
5947
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5866
diff changeset
   166
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   167
def _sendmail(ui, sender, recipients, msg):
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   168
    '''send mail using sendmail.'''
25842
fa3f0301cf91 email: fix config default value inconsistency
Matt Mackall <mpm@selenic.com>
parents: 25660
diff changeset
   169
    program = ui.config('email', 'method', 'smtp')
5975
75d9fe70c654 templater: move email function to util
Matt Mackall <mpm@selenic.com>
parents: 5973
diff changeset
   170
    cmdline = '%s -f %s %s' % (program, util.email(sender),
75d9fe70c654 templater: move email function to util
Matt Mackall <mpm@selenic.com>
parents: 5973
diff changeset
   171
                               ' '.join(map(util.email, recipients)))
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   172
    ui.note(_('sending mail: %s\n') % cmdline)
6548
962eb403165b replace usage of os.popen() with util.popen()
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5975
diff changeset
   173
    fp = util.popen(cmdline, 'w')
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   174
    fp.write(msg)
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   175
    ret = fp.close()
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   176
    if ret:
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   177
        raise util.Abort('%s %s' % (
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   178
            os.path.basename(program.split(None, 1)[0]),
14234
600e64004eb5 rename explain_exit to explainexit
Adrian Buehlmann <adrian@cadifra.com>
parents: 13244
diff changeset
   179
            util.explainexit(ret)[0]))
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   180
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   181
def _mbox(mbox, sender, recipients, msg):
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   182
    '''write mails to mbox'''
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   183
    fp = open(mbox, 'ab+')
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   184
    # Should be time.asctime(), but Windows prints 2-characters day
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   185
    # of month instead of one. Make them print the same thing.
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   186
    date = time.strftime('%a %b %d %H:%M:%S %Y', time.localtime())
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   187
    fp.write('From %s %s\n' % (sender, date))
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   188
    fp.write(msg)
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   189
    fp.write('\n\n')
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   190
    fp.close()
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   191
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   192
def connect(ui, mbox=None):
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   193
    '''make a mail connection. return a function to send mail.
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   194
    call as sendmail(sender, list-of-recipients, msg).'''
15560
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   195
    if mbox:
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   196
        open(mbox, 'wb').close()
cc58c228503e mail: mbox handling as a part of mail handling, refactored from patchbomb
Mads Kiilerich <mads@kiilerich.com>
parents: 14965
diff changeset
   197
        return lambda s, r, m: _mbox(mbox, s, r, m)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   198
    if ui.config('email', 'method', 'smtp') == 'smtp':
5947
528c986f0162 Backed out changeset dc6ed2736c81
Bryan O'Sullivan <bos@serpentine.com>
parents: 5866
diff changeset
   199
        return _smtp(ui)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   200
    return lambda s, r, m: _sendmail(ui, s, r, m)
2889
20b95aef3fe0 Move ui.sendmail to mail.connect/sendmail
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   201
15561
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
   202
def sendmail(ui, sender, recipients, msg, mbox=None):
ca572e94d8e7 notify: add option for writing to mbox
Mads Kiilerich <mads@kiilerich.com>
parents: 15560
diff changeset
   203
    send = connect(ui, mbox=mbox)
5973
ea77f6f77514 patchbomb: undo backout and fix bugs in the earlier patch
Matt Mackall <mpm@selenic.com>
parents: 5947
diff changeset
   204
    return send(sender, recipients, msg)
4489
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   205
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   206
def validateconfig(ui):
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   207
    '''determine if we have enough config data to try sending email.'''
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   208
    method = ui.config('email', 'method', 'smtp')
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   209
    if method == 'smtp':
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   210
        if not ui.config('smtp', 'host'):
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   211
            raise util.Abort(_('smtp specified as email transport, '
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   212
                               'but no smtp host configured'))
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   213
    else:
14271
4030630fb59c rename util.find_exe to findexe
Adrian Buehlmann <adrian@cadifra.com>
parents: 14234
diff changeset
   214
        if not util.findexe(method):
4489
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   215
            raise util.Abort(_('%r specified as email transport, '
a11e13d50645 patchbomb: Validate email config before we start prompting for info.
Bryan O'Sullivan <bos@serpentine.com>
parents: 4096
diff changeset
   216
                               'but not in PATH') % method)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   217
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   218
def mimetextpatch(s, subtype='plain', display=False):
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   219
    '''Return MIME message suitable for a patch.
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   220
    Charset will be detected as utf-8 or (possibly fake) us-ascii.
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   221
    Transfer encodings will be used if necessary.'''
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   222
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   223
    cs = 'us-ascii'
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   224
    if not display:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   225
        try:
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   226
            s.decode('us-ascii')
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   227
        except UnicodeDecodeError:
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   228
            try:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   229
                s.decode('utf-8')
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   230
                cs = 'utf-8'
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   231
            except UnicodeDecodeError:
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   232
                # We'll go with us-ascii as a fallback.
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   233
                pass
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   234
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   235
    return mimetextqp(s, subtype, cs)
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   236
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   237
def mimetextqp(body, subtype, charset):
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   238
    '''Return MIME message.
17424
e7cfe3587ea4 fix trivial spelling errors
Mads Kiilerich <mads@kiilerich.com>
parents: 15562
diff changeset
   239
    Quoted-printable transfer encoding will be used if necessary.
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   240
    '''
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   241
    enc = None
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   242
    for line in body.splitlines():
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   243
        if len(line) > 950:
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   244
            body = quopri.encodestring(body)
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   245
            enc = "quoted-printable"
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   246
            break
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   247
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   248
    msg = email.MIMEText.MIMEText(body, subtype, charset)
8332
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   249
    if enc:
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   250
        del msg['Content-Transfer-Encoding']
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   251
        msg['Content-Transfer-Encoding'] = enc
3e544c074459 patchbomb: quoted-printable encode overly long lines
Rocco Rutte <pdmef@gmx.net>
parents: 8312
diff changeset
   252
    return msg
7191
d14212218582 mail: mime-encode patches that are utf-8
Christian Ebert <blacktrash@gmx.net>
parents: 7114
diff changeset
   253
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   254
def _charsets(ui):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   255
    '''Obtains charsets to send mail parts not containing patches.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   256
    charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
   257
    fallbacks = [encoding.fallbackencoding.lower(),
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
   258
                 encoding.encoding.lower(), 'utf-8']
8343
6fbbb90261b1 mail: updated comment
Martin Geisler <mg@lazybytes.net>
parents: 8332
diff changeset
   259
    for cs in fallbacks: # find unique charsets while keeping order
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   260
        if cs not in charsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   261
            charsets.append(cs)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   262
    return [cs for cs in charsets if not cs.endswith('ascii')]
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   263
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   264
def _encode(ui, s, charsets):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   265
    '''Returns (converted) string, charset tuple.
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   266
    Finds out best charset by cycling through sendcharsets in descending
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
   267
    order. Tries both encoding and fallbackencoding for input. Only as
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   268
    last resort send as is in fake ascii.
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   269
    Caveat: Do not use for mail parts containing patches!'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   270
    try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   271
        s.decode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   272
    except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   273
        sendcharsets = charsets or _charsets(ui)
7948
de377b1a9a84 move encoding bits from util to encoding
Matt Mackall <mpm@selenic.com>
parents: 7195
diff changeset
   274
        for ics in (encoding.encoding, encoding.fallbackencoding):
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   275
            try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   276
                u = s.decode(ics)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   277
            except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   278
                continue
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   279
            for ocs in sendcharsets:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   280
                try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   281
                    return u.encode(ocs), ocs
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   282
                except UnicodeEncodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   283
                    pass
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   284
                except LookupError:
7195
9fabcb1fe68d mail: correct typo in variable name
Christian Ebert <blacktrash@gmx.net>
parents: 7191
diff changeset
   285
                    ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   286
    # if ascii, or all conversion attempts fail, send (broken) ascii
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   287
    return s, 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   288
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   289
def headencode(ui, s, charsets=None, display=False):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   290
    '''Returns RFC-2047 compliant header from given string.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   291
    if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   292
        # split into words?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   293
        s, cs = _encode(ui, s, charsets)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   294
        return str(email.Header.Header(s, cs))
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   295
    return s
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   296
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   297
def _addressencode(ui, name, addr, charsets=None):
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   298
    name = headencode(ui, name, charsets)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   299
    try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   300
        acc, dom = addr.split('@')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   301
        acc = acc.encode('ascii')
9715
f0e99a2eac76 patchbomb: fix handling of email addresses with Unicode domains (IDNA)
Marti Raudsepp <marti@juffo.org>
parents: 9246
diff changeset
   302
        dom = dom.decode(encoding.encoding).encode('idna')
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   303
        addr = '%s@%s' % (acc, dom)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   304
    except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   305
        raise util.Abort(_('invalid email address: %s') % addr)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   306
    except ValueError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   307
        try:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   308
            # too strict?
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   309
            addr = addr.encode('ascii')
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   310
        except UnicodeDecodeError:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   311
            raise util.Abort(_('invalid local address: %s') % addr)
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   312
    return email.Utils.formataddr((name, addr))
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   313
9948
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   314
def addressencode(ui, address, charsets=None, display=False):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   315
    '''Turns address into RFC-2047 compliant header.'''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   316
    if display or not address:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   317
        return address or ''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   318
    name, addr = email.Utils.parseaddr(address)
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   319
    return _addressencode(ui, name, addr, charsets)
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   320
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   321
def addrlistencode(ui, addrs, charsets=None, display=False):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   322
    '''Turns a list of addresses into a list of RFC-2047 compliant headers.
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   323
    A single element of input list may contain multiple addresses, but output
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   324
    always has one address per item'''
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   325
    if display:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   326
        return [a.strip() for a in addrs if a.strip()]
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   327
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   328
    result = []
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   329
    for name, addr in email.Utils.getaddresses(addrs):
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   330
        if name or addr:
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   331
            result.append(_addressencode(ui, name, addr, charsets))
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   332
    return result
e5b44a7986d0 mail: add parseaddrlist() function for parsing many addresses at once
Marti Raudsepp <marti@juffo.org>
parents: 9715
diff changeset
   333
7114
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   334
def mimeencode(ui, s, charsets=None, display=False):
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   335
    '''creates mime text object, encodes it if needed, and sets
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   336
    charset and transfer-encoding accordingly.'''
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   337
    cs = 'us-ascii'
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   338
    if not display:
30e49d54c537 mail: add methods to handle non-ascii chars
Christian Ebert <blacktrash@gmx.net>
parents: 6548
diff changeset
   339
        s, cs = _encode(ui, s, charsets)
15562
a82b6038ff08 mail: use quoted-printable for mime encoding to avoid too long lines (issue3075)
Mads Kiilerich <mads@kiilerich.com>
parents: 15561
diff changeset
   340
    return mimetextqp(s, 'plain', cs)