##// END OF EJS Templates
mail: correct typo in variable name
Christian Ebert -
r7195:9fabcb1f default
parent child Browse files
Show More
@@ -1,170 +1,170 b''
1 # mail.py - mail sending bits for mercurial
1 # mail.py - mail sending bits for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import os, smtplib, socket
9 import os, smtplib, socket
10 import email.Header, email.MIMEText, email.Utils
10 import email.Header, email.MIMEText, email.Utils
11 import util
11 import util
12
12
13 def _smtp(ui):
13 def _smtp(ui):
14 '''build an smtp connection and return a function to send mail'''
14 '''build an smtp connection and return a function to send mail'''
15 local_hostname = ui.config('smtp', 'local_hostname')
15 local_hostname = ui.config('smtp', 'local_hostname')
16 s = smtplib.SMTP(local_hostname=local_hostname)
16 s = smtplib.SMTP(local_hostname=local_hostname)
17 mailhost = ui.config('smtp', 'host')
17 mailhost = ui.config('smtp', 'host')
18 if not mailhost:
18 if not mailhost:
19 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
19 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
20 mailport = int(ui.config('smtp', 'port', 25))
20 mailport = int(ui.config('smtp', 'port', 25))
21 ui.note(_('sending mail: smtp host %s, port %s\n') %
21 ui.note(_('sending mail: smtp host %s, port %s\n') %
22 (mailhost, mailport))
22 (mailhost, mailport))
23 s.connect(host=mailhost, port=mailport)
23 s.connect(host=mailhost, port=mailport)
24 if ui.configbool('smtp', 'tls'):
24 if ui.configbool('smtp', 'tls'):
25 if not hasattr(socket, 'ssl'):
25 if not hasattr(socket, 'ssl'):
26 raise util.Abort(_("can't use TLS: Python SSL support "
26 raise util.Abort(_("can't use TLS: Python SSL support "
27 "not installed"))
27 "not installed"))
28 ui.note(_('(using tls)\n'))
28 ui.note(_('(using tls)\n'))
29 s.ehlo()
29 s.ehlo()
30 s.starttls()
30 s.starttls()
31 s.ehlo()
31 s.ehlo()
32 username = ui.config('smtp', 'username')
32 username = ui.config('smtp', 'username')
33 password = ui.config('smtp', 'password')
33 password = ui.config('smtp', 'password')
34 if username and not password:
34 if username and not password:
35 password = ui.getpass()
35 password = ui.getpass()
36 if username and password:
36 if username and password:
37 ui.note(_('(authenticating to mail server as %s)\n') %
37 ui.note(_('(authenticating to mail server as %s)\n') %
38 (username))
38 (username))
39 s.login(username, password)
39 s.login(username, password)
40
40
41 def send(sender, recipients, msg):
41 def send(sender, recipients, msg):
42 try:
42 try:
43 return s.sendmail(sender, recipients, msg)
43 return s.sendmail(sender, recipients, msg)
44 except smtplib.SMTPRecipientsRefused, inst:
44 except smtplib.SMTPRecipientsRefused, inst:
45 recipients = [r[1] for r in inst.recipients.values()]
45 recipients = [r[1] for r in inst.recipients.values()]
46 raise util.Abort('\n' + '\n'.join(recipients))
46 raise util.Abort('\n' + '\n'.join(recipients))
47 except smtplib.SMTPException, inst:
47 except smtplib.SMTPException, inst:
48 raise util.Abort(inst)
48 raise util.Abort(inst)
49
49
50 return send
50 return send
51
51
52 def _sendmail(ui, sender, recipients, msg):
52 def _sendmail(ui, sender, recipients, msg):
53 '''send mail using sendmail.'''
53 '''send mail using sendmail.'''
54 program = ui.config('email', 'method')
54 program = ui.config('email', 'method')
55 cmdline = '%s -f %s %s' % (program, util.email(sender),
55 cmdline = '%s -f %s %s' % (program, util.email(sender),
56 ' '.join(map(util.email, recipients)))
56 ' '.join(map(util.email, recipients)))
57 ui.note(_('sending mail: %s\n') % cmdline)
57 ui.note(_('sending mail: %s\n') % cmdline)
58 fp = util.popen(cmdline, 'w')
58 fp = util.popen(cmdline, 'w')
59 fp.write(msg)
59 fp.write(msg)
60 ret = fp.close()
60 ret = fp.close()
61 if ret:
61 if ret:
62 raise util.Abort('%s %s' % (
62 raise util.Abort('%s %s' % (
63 os.path.basename(program.split(None, 1)[0]),
63 os.path.basename(program.split(None, 1)[0]),
64 util.explain_exit(ret)[0]))
64 util.explain_exit(ret)[0]))
65
65
66 def connect(ui):
66 def connect(ui):
67 '''make a mail connection. return a function to send mail.
67 '''make a mail connection. return a function to send mail.
68 call as sendmail(sender, list-of-recipients, msg).'''
68 call as sendmail(sender, list-of-recipients, msg).'''
69 if ui.config('email', 'method', 'smtp') == 'smtp':
69 if ui.config('email', 'method', 'smtp') == 'smtp':
70 return _smtp(ui)
70 return _smtp(ui)
71 return lambda s, r, m: _sendmail(ui, s, r, m)
71 return lambda s, r, m: _sendmail(ui, s, r, m)
72
72
73 def sendmail(ui, sender, recipients, msg):
73 def sendmail(ui, sender, recipients, msg):
74 send = connect(ui)
74 send = connect(ui)
75 return send(sender, recipients, msg)
75 return send(sender, recipients, msg)
76
76
77 def validateconfig(ui):
77 def validateconfig(ui):
78 '''determine if we have enough config data to try sending email.'''
78 '''determine if we have enough config data to try sending email.'''
79 method = ui.config('email', 'method', 'smtp')
79 method = ui.config('email', 'method', 'smtp')
80 if method == 'smtp':
80 if method == 'smtp':
81 if not ui.config('smtp', 'host'):
81 if not ui.config('smtp', 'host'):
82 raise util.Abort(_('smtp specified as email transport, '
82 raise util.Abort(_('smtp specified as email transport, '
83 'but no smtp host configured'))
83 'but no smtp host configured'))
84 else:
84 else:
85 if not util.find_exe(method):
85 if not util.find_exe(method):
86 raise util.Abort(_('%r specified as email transport, '
86 raise util.Abort(_('%r specified as email transport, '
87 'but not in PATH') % method)
87 'but not in PATH') % method)
88
88
89 def mimetextpatch(s, subtype='plain', display=False):
89 def mimetextpatch(s, subtype='plain', display=False):
90 '''If patch in utf-8 transfer-encode it.'''
90 '''If patch in utf-8 transfer-encode it.'''
91 if not display:
91 if not display:
92 for cs in ('us-ascii', 'utf-8'):
92 for cs in ('us-ascii', 'utf-8'):
93 try:
93 try:
94 s.decode(cs)
94 s.decode(cs)
95 return email.MIMEText.MIMEText(s, subtype, cs)
95 return email.MIMEText.MIMEText(s, subtype, cs)
96 except UnicodeDecodeError:
96 except UnicodeDecodeError:
97 pass
97 pass
98 return email.MIMEText.MIMEText(s, subtype)
98 return email.MIMEText.MIMEText(s, subtype)
99
99
100 def _charsets(ui):
100 def _charsets(ui):
101 '''Obtains charsets to send mail parts not containing patches.'''
101 '''Obtains charsets to send mail parts not containing patches.'''
102 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
102 charsets = [cs.lower() for cs in ui.configlist('email', 'charsets')]
103 fallbacks = [util._fallbackencoding.lower(),
103 fallbacks = [util._fallbackencoding.lower(),
104 util._encoding.lower(), 'utf-8']
104 util._encoding.lower(), 'utf-8']
105 for cs in fallbacks: # util.unique does not keep order
105 for cs in fallbacks: # util.unique does not keep order
106 if cs not in charsets:
106 if cs not in charsets:
107 charsets.append(cs)
107 charsets.append(cs)
108 return [cs for cs in charsets if not cs.endswith('ascii')]
108 return [cs for cs in charsets if not cs.endswith('ascii')]
109
109
110 def _encode(ui, s, charsets):
110 def _encode(ui, s, charsets):
111 '''Returns (converted) string, charset tuple.
111 '''Returns (converted) string, charset tuple.
112 Finds out best charset by cycling through sendcharsets in descending
112 Finds out best charset by cycling through sendcharsets in descending
113 order. Tries both _encoding and _fallbackencoding for input. Only as
113 order. Tries both _encoding and _fallbackencoding for input. Only as
114 last resort send as is in fake ascii.
114 last resort send as is in fake ascii.
115 Caveat: Do not use for mail parts containing patches!'''
115 Caveat: Do not use for mail parts containing patches!'''
116 try:
116 try:
117 s.decode('ascii')
117 s.decode('ascii')
118 except UnicodeDecodeError:
118 except UnicodeDecodeError:
119 sendcharsets = charsets or _charsets(ui)
119 sendcharsets = charsets or _charsets(ui)
120 for ics in (util._encoding, util._fallbackencoding):
120 for ics in (util._encoding, util._fallbackencoding):
121 try:
121 try:
122 u = s.decode(ics)
122 u = s.decode(ics)
123 except UnicodeDecodeError:
123 except UnicodeDecodeError:
124 continue
124 continue
125 for ocs in sendcharsets:
125 for ocs in sendcharsets:
126 try:
126 try:
127 return u.encode(ocs), ocs
127 return u.encode(ocs), ocs
128 except UnicodeEncodeError:
128 except UnicodeEncodeError:
129 pass
129 pass
130 except LookupError:
130 except LookupError:
131 ui.warn(_('ignoring invalid sendcharset: %s\n') % cs)
131 ui.warn(_('ignoring invalid sendcharset: %s\n') % ocs)
132 # if ascii, or all conversion attempts fail, send (broken) ascii
132 # if ascii, or all conversion attempts fail, send (broken) ascii
133 return s, 'us-ascii'
133 return s, 'us-ascii'
134
134
135 def headencode(ui, s, charsets=None, display=False):
135 def headencode(ui, s, charsets=None, display=False):
136 '''Returns RFC-2047 compliant header from given string.'''
136 '''Returns RFC-2047 compliant header from given string.'''
137 if not display:
137 if not display:
138 # split into words?
138 # split into words?
139 s, cs = _encode(ui, s, charsets)
139 s, cs = _encode(ui, s, charsets)
140 return str(email.Header.Header(s, cs))
140 return str(email.Header.Header(s, cs))
141 return s
141 return s
142
142
143 def addressencode(ui, address, charsets=None, display=False):
143 def addressencode(ui, address, charsets=None, display=False):
144 '''Turns address into RFC-2047 compliant header.'''
144 '''Turns address into RFC-2047 compliant header.'''
145 if display or not address:
145 if display or not address:
146 return address or ''
146 return address or ''
147 name, addr = email.Utils.parseaddr(address)
147 name, addr = email.Utils.parseaddr(address)
148 name = headencode(ui, name, charsets)
148 name = headencode(ui, name, charsets)
149 try:
149 try:
150 acc, dom = addr.split('@')
150 acc, dom = addr.split('@')
151 acc = acc.encode('ascii')
151 acc = acc.encode('ascii')
152 dom = dom.encode('idna')
152 dom = dom.encode('idna')
153 addr = '%s@%s' % (acc, dom)
153 addr = '%s@%s' % (acc, dom)
154 except UnicodeDecodeError:
154 except UnicodeDecodeError:
155 raise util.Abort(_('invalid email address: %s') % addr)
155 raise util.Abort(_('invalid email address: %s') % addr)
156 except ValueError:
156 except ValueError:
157 try:
157 try:
158 # too strict?
158 # too strict?
159 addr = addr.encode('ascii')
159 addr = addr.encode('ascii')
160 except UnicodeDecodeError:
160 except UnicodeDecodeError:
161 raise util.Abort(_('invalid local address: %s') % addr)
161 raise util.Abort(_('invalid local address: %s') % addr)
162 return email.Utils.formataddr((name, addr))
162 return email.Utils.formataddr((name, addr))
163
163
164 def mimeencode(ui, s, charsets=None, display=False):
164 def mimeencode(ui, s, charsets=None, display=False):
165 '''creates mime text object, encodes it if needed, and sets
165 '''creates mime text object, encodes it if needed, and sets
166 charset and transfer-encoding accordingly.'''
166 charset and transfer-encoding accordingly.'''
167 cs = 'us-ascii'
167 cs = 'us-ascii'
168 if not display:
168 if not display:
169 s, cs = _encode(ui, s, charsets)
169 s, cs = _encode(ui, s, charsets)
170 return email.MIMEText.MIMEText(s, 'plain', cs)
170 return email.MIMEText.MIMEText(s, 'plain', cs)
General Comments 0
You need to be logged in to leave comments. Login now