1 # Copyright 1998-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 # Since python ebuilds remove the 'email' module when USE=build
5 # is enabled, use a local import so that
6 # portage.proxy.lazyimport._preload_portage_submodules()
7 # can load this module even though the 'email' module is missing.
8 # The elog mail modules won't work, but at least an ImportError
9 # won't cause portage to crash during stage builds. Since the
10 # 'smtlib' module imports the 'email' module, that's imported
17 from portage import os
18 from portage import _encodings
19 from portage import _unicode_decode, _unicode_encode
20 from portage.localization import _
23 if sys.hexversion >= 0x3000000:
26 def _force_ascii_if_necessary(s):
27 # Force ascii encoding in order to avoid UnicodeEncodeError
28 # from smtplib.sendmail with python3 (bug #291331).
29 s = _unicode_encode(s,
30 encoding='ascii', errors='backslashreplace')
31 s = _unicode_decode(s,
32 encoding='ascii', errors='replace')
37 def _force_ascii_if_necessary(s):
40 def TextMessage(_text):
41 from email.mime.text import MIMEText
42 mimetext = MIMEText(_text)
43 if sys.hexversion >= 0x3000000:
44 mimetext.set_charset("UTF-8")
47 def create_message(sender, recipient, subject, body, attachments=None):
49 from email.header import Header
50 from email.mime.base import MIMEBase as BaseMessage
51 from email.mime.multipart import MIMEMultipart as MultipartMessage
53 if sys.hexversion < 0x3000000:
54 sender = _unicode_encode(sender,
55 encoding=_encodings['content'], errors='strict')
56 recipient = _unicode_encode(recipient,
57 encoding=_encodings['content'], errors='strict')
58 subject = _unicode_encode(subject,
59 encoding=_encodings['content'], errors='backslashreplace')
60 body = _unicode_encode(body,
61 encoding=_encodings['content'], errors='backslashreplace')
63 if attachments == None:
64 mymessage = TextMessage(body)
66 mymessage = MultipartMessage()
67 mymessage.attach(TextMessage(body))
69 if isinstance(x, BaseMessage):
71 elif isinstance(x, basestring):
72 if sys.hexversion < 0x3000000:
73 x = _unicode_encode(x,
74 encoding=_encodings['content'],
75 errors='backslashreplace')
76 mymessage.attach(TextMessage(x))
78 raise portage.exception.PortageException(_("Can't handle type of attachment: %s") % type(x))
80 mymessage.set_unixfrom(sender)
81 mymessage["To"] = recipient
82 mymessage["From"] = sender
84 # Use Header as a workaround so that long subject lines are wrapped
85 # correctly by <=python-2.6 (gentoo bug #263370, python issue #1974).
86 # Also, need to force ascii for python3, in order to avoid
87 # UnicodeEncodeError with non-ascii characters:
88 # File "/usr/lib/python3.1/email/header.py", line 189, in __init__
89 # self.append(s, charset, errors)
90 # File "/usr/lib/python3.1/email/header.py", line 262, in append
91 # input_bytes = s.encode(input_charset, errors)
92 #UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-9: ordinal not in range(128)
93 mymessage["Subject"] = Header(_force_ascii_if_necessary(subject))
94 mymessage["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S %z")
98 def send_mail(mysettings, message):
102 mymailhost = "localhost"
106 myrecipient = "root@localhost"
108 # Syntax for PORTAGE_ELOG_MAILURI (if defined):
109 # address [[user:passwd@]mailserver[:port]]
110 # where address: recipient address
111 # user: username for smtp auth (defaults to none)
112 # passwd: password for smtp auth (defaults to none)
113 # mailserver: smtp server that should be used to deliver the mail (defaults to localhost)
114 # alternatively this can also be the absolute path to a sendmail binary if you don't want to use smtp
115 # port: port to use on the given smtp server (defaults to 25, values > 100000 indicate that starttls should be used on (port-100000))
116 if " " in mysettings.get("PORTAGE_ELOG_MAILURI", ""):
117 myrecipient, mymailuri = mysettings["PORTAGE_ELOG_MAILURI"].split()
119 myauthdata, myconndata = mymailuri.rsplit("@", 1)
121 mymailuser,mymailpasswd = myauthdata.split(":")
123 print(_("!!! invalid SMTP AUTH configuration, trying unauthenticated ..."))
125 myconndata = mymailuri
126 if ":" in myconndata:
127 mymailhost,mymailport = myconndata.split(":")
129 mymailhost = myconndata
131 myrecipient = mysettings.get("PORTAGE_ELOG_MAILURI", "")
133 myfrom = message.get("From")
135 if sys.hexversion < 0x3000000:
136 myrecipient = _unicode_encode(myrecipient,
137 encoding=_encodings['content'], errors='strict')
138 mymailhost = _unicode_encode(mymailhost,
139 encoding=_encodings['content'], errors='strict')
140 mymailport = _unicode_encode(mymailport,
141 encoding=_encodings['content'], errors='strict')
142 myfrom = _unicode_encode(myfrom,
143 encoding=_encodings['content'], errors='strict')
144 mymailuser = _unicode_encode(mymailuser,
145 encoding=_encodings['content'], errors='strict')
146 mymailpasswd = _unicode_encode(mymailpasswd,
147 encoding=_encodings['content'], errors='strict')
149 # user wants to use a sendmail binary instead of smtp
150 if mymailhost[0] == os.sep and os.path.exists(mymailhost):
151 fd = os.popen(mymailhost+" -f "+myfrom+" "+myrecipient, "w")
152 fd.write(_force_ascii_if_necessary(message.as_string()))
153 if fd.close() != None:
154 sys.stderr.write(_("!!! %s returned with a non-zero exit code. This generally indicates an error.\n") % mymailhost)
157 if int(mymailport) > 100000:
158 myconn = smtplib.SMTP(mymailhost, int(mymailport) - 100000)
160 if not myconn.has_extn("STARTTLS"):
161 raise portage.exception.PortageException(_("!!! TLS support requested for logmail but not supported by server"))
165 myconn = smtplib.SMTP(mymailhost, mymailport)
166 if mymailuser != "" and mymailpasswd != "":
167 myconn.login(mymailuser, mymailpasswd)
169 message_str = _force_ascii_if_necessary(message.as_string())
170 myconn.sendmail(myfrom, myrecipient, message_str)
172 except smtplib.SMTPException as e:
173 raise portage.exception.PortageException(_("!!! An error occurred while trying to send logmail:\n")+str(e))
174 except socket.error as e:
175 raise portage.exception.PortageException(_("!!! A network error occurred while trying to send logmail:\n%s\nSure you configured PORTAGE_ELOG_MAILURI correctly?") % str(e))