We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SMTP: Exception Handling Resolution Order
In `frappe/frappe/email/smtp.py` In `SMTPServer.sess`:
`		try:`
`				....`
`		except `_socket.error` as e:     `
`			....      `
`		except smtplib.SMTPAuthenticationError as e:     `
`			....   `
`		except smtplib.SMTPException:    `
`			....   `
`

Where:
`_socket.error` is `OSError` Which is defined: `class OSError(Exception):`
`class SMTPException(OSError):`
`class SMTPResponseException(SMTPException):`
`class SMTPAuthenticationError(SMTPResponseException):`

From the python documentation:

> A class in an except clause is compatible with an exception if it is the same class or a base class thereof (but not the other way around — an except clause listing a derived class is not compatible with a base class).

So the way the except clauses are ordered now will always be handled by the `except` clause with `_socket.error` no matter what the error is.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `frappe/email/smtp.py`
Content:
```
1 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2 # MIT License. See license.txt
3 
4 from __future__ import unicode_literals
5 from six import reraise as raise_
6 import frappe
7 import smtplib
8 import email.utils
9 import _socket, sys
10 from frappe import _
11 from frappe.utils import cint, cstr, parse_addr
12 
13 def send(email, append_to=None, retry=1):
14 	"""Deprecated: Send the message or add it to Outbox Email"""
15 	def _send(retry):
16 		try:
17 			smtpserver = SMTPServer(append_to=append_to)
18 
19 			# validate is called in as_string
20 			email_body = email.as_string()
21 
22 			smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body)
23 		except smtplib.SMTPSenderRefused:
24 			frappe.throw(_("Invalid login or password"), title='Email Failed')
25 			raise
26 		except smtplib.SMTPRecipientsRefused:
27 			frappe.msgprint(_("Invalid recipient address"), title='Email Failed')
28 			raise
29 		except (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError):
30 			if not retry:
31 				raise
32 			else:
33 				retry = retry - 1
34 				_send(retry)
35 
36 	_send(retry)
37 
38 def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sender=None):
39 	"""Returns outgoing email account based on `append_to` or the default
40 		outgoing account. If default outgoing account is not found, it will
41 		try getting settings from `site_config.json`."""
42 
43 	sender_email_id = None
44 	if sender:
45 		sender_email_id = parse_addr(sender)[1]
46 
47 	if not getattr(frappe.local, "outgoing_email_account", None):
48 		frappe.local.outgoing_email_account = {}
49 
50 	if not (frappe.local.outgoing_email_account.get(append_to)
51 		or frappe.local.outgoing_email_account.get(sender_email_id)
52 		or frappe.local.outgoing_email_account.get("default")):
53 		email_account = None
54 
55 		if append_to:
56 			# append_to is only valid when enable_incoming is checked
57 
58 			# in case of multiple Email Accounts with same append_to
59 			# narrow it down based on email_id
60 			email_account = _get_email_account({
61 				"enable_outgoing": 1,
62 				"enable_incoming": 1,
63 				"append_to": append_to,
64 				"email_id": sender_email_id
65 			})
66 
67 			# else find the first Email Account with append_to
68 			if not email_account:
69 				email_account = _get_email_account({
70 					"enable_outgoing": 1,
71 					"enable_incoming": 1,
72 					"append_to": append_to
73 				})
74 
75 		if not email_account and sender_email_id:
76 			# check if the sender has email account with enable_outgoing
77 			email_account = _get_email_account({"enable_outgoing": 1, "email_id": sender_email_id})
78 
79 		if not email_account:
80 			# sender don't have the outging email account
81 			sender_email_id = None
82 			email_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set)
83 
84 		if not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
85 			frappe.throw(_("Please setup default Email Account from Setup > Email > Email Account"),
86 				frappe.OutgoingEmailError)
87 
88 		if email_account:
89 			if email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):
90 				raise_exception = True
91 				if email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication:
92 					raise_exception = False
93 				email_account.password = email_account.get_password(raise_exception=raise_exception)
94 			email_account.default_sender = email.utils.formataddr((email_account.name, email_account.get("email_id")))
95 
96 		frappe.local.outgoing_email_account[append_to or sender_email_id or "default"] = email_account
97 
98 	return frappe.local.outgoing_email_account.get(append_to) \
99 		or frappe.local.outgoing_email_account.get(sender_email_id) \
100 		or frappe.local.outgoing_email_account.get("default")
101 
102 def get_default_outgoing_email_account(raise_exception_not_set=True):
103 	'''conf should be like:
104 		{
105 		 "mail_server": "smtp.example.com",
106 		 "mail_port": 587,
107 		 "use_tls": 1,
108 		 "mail_login": "emails@example.com",
109 		 "mail_password": "Super.Secret.Password",
110 		 "auto_email_id": "emails@example.com",
111 		 "email_sender_name": "Example Notifications",
112 		 "always_use_account_email_id_as_sender": 0,
113 		 "always_use_account_name_as_sender_name": 0
114 		}
115 	'''
116 	email_account = _get_email_account({"enable_outgoing": 1, "default_outgoing": 1})
117 	if email_account:
118 		email_account.password = email_account.get_password(raise_exception=False)
119 
120 	if not email_account and frappe.conf.get("mail_server"):
121 		# from site_config.json
122 		email_account = frappe.new_doc("Email Account")
123 		email_account.update({
124 			"smtp_server": frappe.conf.get("mail_server"),
125 			"smtp_port": frappe.conf.get("mail_port"),
126 
127 			# legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing
128 			"use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0),
129 			"login_id": frappe.conf.get("mail_login"),
130 			"email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or 'notifications@example.com',
131 			"password": frappe.conf.get("mail_password"),
132 			"always_use_account_email_id_as_sender": frappe.conf.get("always_use_account_email_id_as_sender", 0),
133 			"always_use_account_name_as_sender_name": frappe.conf.get("always_use_account_name_as_sender_name", 0)
134 		})
135 		email_account.from_site_config = True
136 		email_account.name = frappe.conf.get("email_sender_name") or "Frappe"
137 
138 	if not email_account and not raise_exception_not_set:
139 		return None
140 
141 	if frappe.are_emails_muted():
142 		# create a stub
143 		email_account = frappe.new_doc("Email Account")
144 		email_account.update({
145 			"email_id": "notifications@example.com"
146 		})
147 
148 	return email_account
149 
150 def _get_email_account(filters):
151 	name = frappe.db.get_value("Email Account", filters)
152 	return frappe.get_doc("Email Account", name) if name else None
153 
154 class SMTPServer:
155 	def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, append_to=None):
156 		# get defaults from mail settings
157 
158 		self._sess = None
159 		self.email_account = None
160 		self.server = None
161 		if server:
162 			self.server = server
163 			self.port = port
164 			self.use_tls = cint(use_tls)
165 			self.login = login
166 			self.password = password
167 
168 		else:
169 			self.setup_email_account(append_to)
170 
171 	def setup_email_account(self, append_to=None, sender=None):
172 		self.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender)
173 		if self.email_account:
174 			self.server = self.email_account.smtp_server
175 			self.login = (getattr(self.email_account, "login_id", None) or self.email_account.email_id)
176 			if not self.email_account.no_smtp_authentication:
177 				if self.email_account.ascii_encode_password:
178 					self.password = frappe.safe_encode(self.email_account.password, 'ascii')
179 				else:
180 					self.password = self.email_account.password
181 			else:
182 				self.password = None
183 			self.port = self.email_account.smtp_port
184 			self.use_tls = self.email_account.use_tls
185 			self.sender = self.email_account.email_id
186 			self.always_use_account_email_id_as_sender = cint(self.email_account.get("always_use_account_email_id_as_sender"))
187 			self.always_use_account_name_as_sender_name = cint(self.email_account.get("always_use_account_name_as_sender_name"))
188 
189 	@property
190 	def sess(self):
191 		"""get session"""
192 		if self._sess:
193 			return self._sess
194 
195 		# check if email server specified
196 		if not getattr(self, 'server'):
197 			err_msg = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account')
198 			frappe.msgprint(err_msg)
199 			raise frappe.OutgoingEmailError(err_msg)
200 
201 		try:
202 			if self.use_tls and not self.port:
203 				self.port = 587
204 
205 			self._sess = smtplib.SMTP(cstr(self.server or ""),
206 				cint(self.port) or None)
207 
208 			if not self._sess:
209 				err_msg = _('Could not connect to outgoing email server')
210 				frappe.msgprint(err_msg)
211 				raise frappe.OutgoingEmailError(err_msg)
212 
213 			if self.use_tls:
214 				self._sess.ehlo()
215 				self._sess.starttls()
216 				self._sess.ehlo()
217 
218 			if self.login and self.password:
219 				ret = self._sess.login(str(self.login or ""), str(self.password or ""))
220 
221 				# check if logged correctly
222 				if ret[0]!=235:
223 					frappe.msgprint(ret[1])
224 					raise frappe.OutgoingEmailError(ret[1])
225 
226 			return self._sess
227 
228 		except _socket.error as e:
229 			# Invalid mail server -- due to refusing connection
230 			frappe.msgprint(_('Invalid Outgoing Mail Server or Port'))
231 			traceback = sys.exc_info()[2]
232 			raise_(frappe.ValidationError, e, traceback)
233 
234 		except smtplib.SMTPAuthenticationError as e:
235 			frappe.msgprint(_("Invalid login or password"))
236 			traceback = sys.exc_info()[2]
237 			raise_(frappe.ValidationError, e, traceback)
238 
239 		except smtplib.SMTPException:
240 			frappe.msgprint(_('Unable to send emails at this time'))
241 			raise
242
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py
--- a/frappe/email/smtp.py
+++ b/frappe/email/smtp.py
@@ -225,12 +225,6 @@
 
 			return self._sess
 
-		except _socket.error as e:
-			# Invalid mail server -- due to refusing connection
-			frappe.msgprint(_('Invalid Outgoing Mail Server or Port'))
-			traceback = sys.exc_info()[2]
-			raise_(frappe.ValidationError, e, traceback)
-
 		except smtplib.SMTPAuthenticationError as e:
 			frappe.msgprint(_("Invalid login or password"))
 			traceback = sys.exc_info()[2]
@@ -239,3 +233,9 @@
 		except smtplib.SMTPException:
 			frappe.msgprint(_('Unable to send emails at this time'))
 			raise
+
+		except _socket.error as e:
+			# Invalid mail server -- due to refusing connection
+			frappe.msgprint(_('Invalid Outgoing Mail Server or Port'))
+			traceback = sys.exc_info()[2]
+			raise_(frappe.ValidationError, e, traceback)

{"golden_diff": "diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py\n--- a/frappe/email/smtp.py\n+++ b/frappe/email/smtp.py\n@@ -225,12 +225,6 @@\n \n \t\t\treturn self._sess\n \n-\t\texcept _socket.error as e:\n-\t\t\t# Invalid mail server -- due to refusing connection\n-\t\t\tfrappe.msgprint(_('Invalid Outgoing Mail Server or Port'))\n-\t\t\ttraceback = sys.exc_info()[2]\n-\t\t\traise_(frappe.ValidationError, e, traceback)\n-\n \t\texcept smtplib.SMTPAuthenticationError as e:\n \t\t\tfrappe.msgprint(_(\"Invalid login or password\"))\n \t\t\ttraceback = sys.exc_info()[2]\n@@ -239,3 +233,9 @@\n \t\texcept smtplib.SMTPException:\n \t\t\tfrappe.msgprint(_('Unable to send emails at this time'))\n \t\t\traise\n+\n+\t\texcept _socket.error as e:\n+\t\t\t# Invalid mail server -- due to refusing connection\n+\t\t\tfrappe.msgprint(_('Invalid Outgoing Mail Server or Port'))\n+\t\t\ttraceback = sys.exc_info()[2]\n+\t\t\traise_(frappe.ValidationError, e, traceback)\n", "issue": "SMTP: Exception Handling Resolution Order\nIn `frappe/frappe/email/smtp.py` In `SMTPServer.sess`:\r\n`\t\ttry:`\r\n`\t\t\t\t....`\r\n`\t\texcept `_socket.error` as e:     `\r\n`\t\t\t....      `\r\n`\t\texcept smtplib.SMTPAuthenticationError as e:     `\r\n`\t\t\t....   `\r\n`\t\texcept smtplib.SMTPException:    `\r\n`\t\t\t....   `\r\n`\r\n\r\nWhere:\r\n`_socket.error` is `OSError` Which is defined: `class OSError(Exception):`\r\n`class SMTPException(OSError):`\r\n`class SMTPResponseException(SMTPException):`\r\n`class SMTPAuthenticationError(SMTPResponseException):`\r\n\r\nFrom the python documentation:\r\n\r\n> A class in an except clause is compatible with an exception if it is the same class or a base class thereof (but not the other way around \u2014 an except clause listing a derived class is not compatible with a base class).\r\n\r\nSo the way the except clauses are ordered now will always be handled by the `except` clause with `_socket.error` no matter what the error is.\n", "before_files": [{"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# MIT License. See license.txt\n\nfrom __future__ import unicode_literals\nfrom six import reraise as raise_\nimport frappe\nimport smtplib\nimport email.utils\nimport _socket, sys\nfrom frappe import _\nfrom frappe.utils import cint, cstr, parse_addr\n\ndef send(email, append_to=None, retry=1):\n\t\"\"\"Deprecated: Send the message or add it to Outbox Email\"\"\"\n\tdef _send(retry):\n\t\ttry:\n\t\t\tsmtpserver = SMTPServer(append_to=append_to)\n\n\t\t\t# validate is called in as_string\n\t\t\temail_body = email.as_string()\n\n\t\t\tsmtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body)\n\t\texcept smtplib.SMTPSenderRefused:\n\t\t\tfrappe.throw(_(\"Invalid login or password\"), title='Email Failed')\n\t\t\traise\n\t\texcept smtplib.SMTPRecipientsRefused:\n\t\t\tfrappe.msgprint(_(\"Invalid recipient address\"), title='Email Failed')\n\t\t\traise\n\t\texcept (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError):\n\t\t\tif not retry:\n\t\t\t\traise\n\t\t\telse:\n\t\t\t\tretry = retry - 1\n\t\t\t\t_send(retry)\n\n\t_send(retry)\n\ndef get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sender=None):\n\t\"\"\"Returns outgoing email account based on `append_to` or the default\n\t\toutgoing account. If default outgoing account is not found, it will\n\t\ttry getting settings from `site_config.json`.\"\"\"\n\n\tsender_email_id = None\n\tif sender:\n\t\tsender_email_id = parse_addr(sender)[1]\n\n\tif not getattr(frappe.local, \"outgoing_email_account\", None):\n\t\tfrappe.local.outgoing_email_account = {}\n\n\tif not (frappe.local.outgoing_email_account.get(append_to)\n\t\tor frappe.local.outgoing_email_account.get(sender_email_id)\n\t\tor frappe.local.outgoing_email_account.get(\"default\")):\n\t\temail_account = None\n\n\t\tif append_to:\n\t\t\t# append_to is only valid when enable_incoming is checked\n\n\t\t\t# in case of multiple Email Accounts with same append_to\n\t\t\t# narrow it down based on email_id\n\t\t\temail_account = _get_email_account({\n\t\t\t\t\"enable_outgoing\": 1,\n\t\t\t\t\"enable_incoming\": 1,\n\t\t\t\t\"append_to\": append_to,\n\t\t\t\t\"email_id\": sender_email_id\n\t\t\t})\n\n\t\t\t# else find the first Email Account with append_to\n\t\t\tif not email_account:\n\t\t\t\temail_account = _get_email_account({\n\t\t\t\t\t\"enable_outgoing\": 1,\n\t\t\t\t\t\"enable_incoming\": 1,\n\t\t\t\t\t\"append_to\": append_to\n\t\t\t\t})\n\n\t\tif not email_account and sender_email_id:\n\t\t\t# check if the sender has email account with enable_outgoing\n\t\t\temail_account = _get_email_account({\"enable_outgoing\": 1, \"email_id\": sender_email_id})\n\n\t\tif not email_account:\n\t\t\t# sender don't have the outging email account\n\t\t\tsender_email_id = None\n\t\t\temail_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set)\n\n\t\tif not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')):\n\t\t\tfrappe.throw(_(\"Please setup default Email Account from Setup > Email > Email Account\"),\n\t\t\t\tfrappe.OutgoingEmailError)\n\n\t\tif email_account:\n\t\t\tif email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):\n\t\t\t\traise_exception = True\n\t\t\t\tif email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication:\n\t\t\t\t\traise_exception = False\n\t\t\t\temail_account.password = email_account.get_password(raise_exception=raise_exception)\n\t\t\temail_account.default_sender = email.utils.formataddr((email_account.name, email_account.get(\"email_id\")))\n\n\t\tfrappe.local.outgoing_email_account[append_to or sender_email_id or \"default\"] = email_account\n\n\treturn frappe.local.outgoing_email_account.get(append_to) \\\n\t\tor frappe.local.outgoing_email_account.get(sender_email_id) \\\n\t\tor frappe.local.outgoing_email_account.get(\"default\")\n\ndef get_default_outgoing_email_account(raise_exception_not_set=True):\n\t'''conf should be like:\n\t\t{\n\t\t \"mail_server\": \"smtp.example.com\",\n\t\t \"mail_port\": 587,\n\t\t \"use_tls\": 1,\n\t\t \"mail_login\": \"emails@example.com\",\n\t\t \"mail_password\": \"Super.Secret.Password\",\n\t\t \"auto_email_id\": \"emails@example.com\",\n\t\t \"email_sender_name\": \"Example Notifications\",\n\t\t \"always_use_account_email_id_as_sender\": 0,\n\t\t \"always_use_account_name_as_sender_name\": 0\n\t\t}\n\t'''\n\temail_account = _get_email_account({\"enable_outgoing\": 1, \"default_outgoing\": 1})\n\tif email_account:\n\t\temail_account.password = email_account.get_password(raise_exception=False)\n\n\tif not email_account and frappe.conf.get(\"mail_server\"):\n\t\t# from site_config.json\n\t\temail_account = frappe.new_doc(\"Email Account\")\n\t\temail_account.update({\n\t\t\t\"smtp_server\": frappe.conf.get(\"mail_server\"),\n\t\t\t\"smtp_port\": frappe.conf.get(\"mail_port\"),\n\n\t\t\t# legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing\n\t\t\t\"use_tls\": cint(frappe.conf.get(\"use_tls\") or 0) or cint(frappe.conf.get(\"use_ssl\") or 0),\n\t\t\t\"login_id\": frappe.conf.get(\"mail_login\"),\n\t\t\t\"email_id\": frappe.conf.get(\"auto_email_id\") or frappe.conf.get(\"mail_login\") or 'notifications@example.com',\n\t\t\t\"password\": frappe.conf.get(\"mail_password\"),\n\t\t\t\"always_use_account_email_id_as_sender\": frappe.conf.get(\"always_use_account_email_id_as_sender\", 0),\n\t\t\t\"always_use_account_name_as_sender_name\": frappe.conf.get(\"always_use_account_name_as_sender_name\", 0)\n\t\t})\n\t\temail_account.from_site_config = True\n\t\temail_account.name = frappe.conf.get(\"email_sender_name\") or \"Frappe\"\n\n\tif not email_account and not raise_exception_not_set:\n\t\treturn None\n\n\tif frappe.are_emails_muted():\n\t\t# create a stub\n\t\temail_account = frappe.new_doc(\"Email Account\")\n\t\temail_account.update({\n\t\t\t\"email_id\": \"notifications@example.com\"\n\t\t})\n\n\treturn email_account\n\ndef _get_email_account(filters):\n\tname = frappe.db.get_value(\"Email Account\", filters)\n\treturn frappe.get_doc(\"Email Account\", name) if name else None\n\nclass SMTPServer:\n\tdef __init__(self, login=None, password=None, server=None, port=None, use_tls=None, append_to=None):\n\t\t# get defaults from mail settings\n\n\t\tself._sess = None\n\t\tself.email_account = None\n\t\tself.server = None\n\t\tif server:\n\t\t\tself.server = server\n\t\t\tself.port = port\n\t\t\tself.use_tls = cint(use_tls)\n\t\t\tself.login = login\n\t\t\tself.password = password\n\n\t\telse:\n\t\t\tself.setup_email_account(append_to)\n\n\tdef setup_email_account(self, append_to=None, sender=None):\n\t\tself.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender)\n\t\tif self.email_account:\n\t\t\tself.server = self.email_account.smtp_server\n\t\t\tself.login = (getattr(self.email_account, \"login_id\", None) or self.email_account.email_id)\n\t\t\tif not self.email_account.no_smtp_authentication:\n\t\t\t\tif self.email_account.ascii_encode_password:\n\t\t\t\t\tself.password = frappe.safe_encode(self.email_account.password, 'ascii')\n\t\t\t\telse:\n\t\t\t\t\tself.password = self.email_account.password\n\t\t\telse:\n\t\t\t\tself.password = None\n\t\t\tself.port = self.email_account.smtp_port\n\t\t\tself.use_tls = self.email_account.use_tls\n\t\t\tself.sender = self.email_account.email_id\n\t\t\tself.always_use_account_email_id_as_sender = cint(self.email_account.get(\"always_use_account_email_id_as_sender\"))\n\t\t\tself.always_use_account_name_as_sender_name = cint(self.email_account.get(\"always_use_account_name_as_sender_name\"))\n\n\t@property\n\tdef sess(self):\n\t\t\"\"\"get session\"\"\"\n\t\tif self._sess:\n\t\t\treturn self._sess\n\n\t\t# check if email server specified\n\t\tif not getattr(self, 'server'):\n\t\t\terr_msg = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account')\n\t\t\tfrappe.msgprint(err_msg)\n\t\t\traise frappe.OutgoingEmailError(err_msg)\n\n\t\ttry:\n\t\t\tif self.use_tls and not self.port:\n\t\t\t\tself.port = 587\n\n\t\t\tself._sess = smtplib.SMTP(cstr(self.server or \"\"),\n\t\t\t\tcint(self.port) or None)\n\n\t\t\tif not self._sess:\n\t\t\t\terr_msg = _('Could not connect to outgoing email server')\n\t\t\t\tfrappe.msgprint(err_msg)\n\t\t\t\traise frappe.OutgoingEmailError(err_msg)\n\n\t\t\tif self.use_tls:\n\t\t\t\tself._sess.ehlo()\n\t\t\t\tself._sess.starttls()\n\t\t\t\tself._sess.ehlo()\n\n\t\t\tif self.login and self.password:\n\t\t\t\tret = self._sess.login(str(self.login or \"\"), str(self.password or \"\"))\n\n\t\t\t\t# check if logged correctly\n\t\t\t\tif ret[0]!=235:\n\t\t\t\t\tfrappe.msgprint(ret[1])\n\t\t\t\t\traise frappe.OutgoingEmailError(ret[1])\n\n\t\t\treturn self._sess\n\n\t\texcept _socket.error as e:\n\t\t\t# Invalid mail server -- due to refusing connection\n\t\t\tfrappe.msgprint(_('Invalid Outgoing Mail Server or Port'))\n\t\t\ttraceback = sys.exc_info()[2]\n\t\t\traise_(frappe.ValidationError, e, traceback)\n\n\t\texcept smtplib.SMTPAuthenticationError as e:\n\t\t\tfrappe.msgprint(_(\"Invalid login or password\"))\n\t\t\ttraceback = sys.exc_info()[2]\n\t\t\traise_(frappe.ValidationError, e, traceback)\n\n\t\texcept smtplib.SMTPException:\n\t\t\tfrappe.msgprint(_('Unable to send emails at this time'))\n\t\t\traise\n", "path": "frappe/email/smtp.py"}], "after_files": [{"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# MIT License. See license.txt\n\nfrom __future__ import unicode_literals\nfrom six import reraise as raise_\nimport frappe\nimport smtplib\nimport email.utils\nimport _socket, sys\nfrom frappe import _\nfrom frappe.utils import cint, cstr, parse_addr\n\ndef send(email, append_to=None, retry=1):\n\t\"\"\"Deprecated: Send the message or add it to Outbox Email\"\"\"\n\tdef _send(retry):\n\t\ttry:\n\t\t\tsmtpserver = SMTPServer(append_to=append_to)\n\n\t\t\t# validate is called in as_string\n\t\t\temail_body = email.as_string()\n\n\t\t\tsmtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body)\n\t\texcept smtplib.SMTPSenderRefused:\n\t\t\tfrappe.throw(_(\"Invalid login or password\"), title='Email Failed')\n\t\t\traise\n\t\texcept smtplib.SMTPRecipientsRefused:\n\t\t\tfrappe.msgprint(_(\"Invalid recipient address\"), title='Email Failed')\n\t\t\traise\n\t\texcept (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError):\n\t\t\tif not retry:\n\t\t\t\traise\n\t\t\telse:\n\t\t\t\tretry = retry - 1\n\t\t\t\t_send(retry)\n\n\t_send(retry)\n\ndef get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sender=None):\n\t\"\"\"Returns outgoing email account based on `append_to` or the default\n\t\toutgoing account. If default outgoing account is not found, it will\n\t\ttry getting settings from `site_config.json`.\"\"\"\n\n\tsender_email_id = None\n\tif sender:\n\t\tsender_email_id = parse_addr(sender)[1]\n\n\tif not getattr(frappe.local, \"outgoing_email_account\", None):\n\t\tfrappe.local.outgoing_email_account = {}\n\n\tif not (frappe.local.outgoing_email_account.get(append_to)\n\t\tor frappe.local.outgoing_email_account.get(sender_email_id)\n\t\tor frappe.local.outgoing_email_account.get(\"default\")):\n\t\temail_account = None\n\n\t\tif append_to:\n\t\t\t# append_to is only valid when enable_incoming is checked\n\n\t\t\t# in case of multiple Email Accounts with same append_to\n\t\t\t# narrow it down based on email_id\n\t\t\temail_account = _get_email_account({\n\t\t\t\t\"enable_outgoing\": 1,\n\t\t\t\t\"enable_incoming\": 1,\n\t\t\t\t\"append_to\": append_to,\n\t\t\t\t\"email_id\": sender_email_id\n\t\t\t})\n\n\t\t\t# else find the first Email Account with append_to\n\t\t\tif not email_account:\n\t\t\t\temail_account = _get_email_account({\n\t\t\t\t\t\"enable_outgoing\": 1,\n\t\t\t\t\t\"enable_incoming\": 1,\n\t\t\t\t\t\"append_to\": append_to\n\t\t\t\t})\n\n\t\tif not email_account and sender_email_id:\n\t\t\t# check if the sender has email account with enable_outgoing\n\t\t\temail_account = _get_email_account({\"enable_outgoing\": 1, \"email_id\": sender_email_id})\n\n\t\tif not email_account:\n\t\t\t# sender don't have the outging email account\n\t\t\tsender_email_id = None\n\t\t\temail_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set)\n\n\t\tif not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')):\n\t\t\tfrappe.throw(_(\"Please setup default Email Account from Setup > Email > Email Account\"),\n\t\t\t\tfrappe.OutgoingEmailError)\n\n\t\tif email_account:\n\t\t\tif email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):\n\t\t\t\traise_exception = True\n\t\t\t\tif email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication:\n\t\t\t\t\traise_exception = False\n\t\t\t\temail_account.password = email_account.get_password(raise_exception=raise_exception)\n\t\t\temail_account.default_sender = email.utils.formataddr((email_account.name, email_account.get(\"email_id\")))\n\n\t\tfrappe.local.outgoing_email_account[append_to or sender_email_id or \"default\"] = email_account\n\n\treturn frappe.local.outgoing_email_account.get(append_to) \\\n\t\tor frappe.local.outgoing_email_account.get(sender_email_id) \\\n\t\tor frappe.local.outgoing_email_account.get(\"default\")\n\ndef get_default_outgoing_email_account(raise_exception_not_set=True):\n\t'''conf should be like:\n\t\t{\n\t\t \"mail_server\": \"smtp.example.com\",\n\t\t \"mail_port\": 587,\n\t\t \"use_tls\": 1,\n\t\t \"mail_login\": \"emails@example.com\",\n\t\t \"mail_password\": \"Super.Secret.Password\",\n\t\t \"auto_email_id\": \"emails@example.com\",\n\t\t \"email_sender_name\": \"Example Notifications\",\n\t\t \"always_use_account_email_id_as_sender\": 0,\n\t\t \"always_use_account_name_as_sender_name\": 0\n\t\t}\n\t'''\n\temail_account = _get_email_account({\"enable_outgoing\": 1, \"default_outgoing\": 1})\n\tif email_account:\n\t\temail_account.password = email_account.get_password(raise_exception=False)\n\n\tif not email_account and frappe.conf.get(\"mail_server\"):\n\t\t# from site_config.json\n\t\temail_account = frappe.new_doc(\"Email Account\")\n\t\temail_account.update({\n\t\t\t\"smtp_server\": frappe.conf.get(\"mail_server\"),\n\t\t\t\"smtp_port\": frappe.conf.get(\"mail_port\"),\n\n\t\t\t# legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing\n\t\t\t\"use_tls\": cint(frappe.conf.get(\"use_tls\") or 0) or cint(frappe.conf.get(\"use_ssl\") or 0),\n\t\t\t\"login_id\": frappe.conf.get(\"mail_login\"),\n\t\t\t\"email_id\": frappe.conf.get(\"auto_email_id\") or frappe.conf.get(\"mail_login\") or 'notifications@example.com',\n\t\t\t\"password\": frappe.conf.get(\"mail_password\"),\n\t\t\t\"always_use_account_email_id_as_sender\": frappe.conf.get(\"always_use_account_email_id_as_sender\", 0),\n\t\t\t\"always_use_account_name_as_sender_name\": frappe.conf.get(\"always_use_account_name_as_sender_name\", 0)\n\t\t})\n\t\temail_account.from_site_config = True\n\t\temail_account.name = frappe.conf.get(\"email_sender_name\") or \"Frappe\"\n\n\tif not email_account and not raise_exception_not_set:\n\t\treturn None\n\n\tif frappe.are_emails_muted():\n\t\t# create a stub\n\t\temail_account = frappe.new_doc(\"Email Account\")\n\t\temail_account.update({\n\t\t\t\"email_id\": \"notifications@example.com\"\n\t\t})\n\n\treturn email_account\n\ndef _get_email_account(filters):\n\tname = frappe.db.get_value(\"Email Account\", filters)\n\treturn frappe.get_doc(\"Email Account\", name) if name else None\n\nclass SMTPServer:\n\tdef __init__(self, login=None, password=None, server=None, port=None, use_tls=None, append_to=None):\n\t\t# get defaults from mail settings\n\n\t\tself._sess = None\n\t\tself.email_account = None\n\t\tself.server = None\n\t\tif server:\n\t\t\tself.server = server\n\t\t\tself.port = port\n\t\t\tself.use_tls = cint(use_tls)\n\t\t\tself.login = login\n\t\t\tself.password = password\n\n\t\telse:\n\t\t\tself.setup_email_account(append_to)\n\n\tdef setup_email_account(self, append_to=None, sender=None):\n\t\tself.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender)\n\t\tif self.email_account:\n\t\t\tself.server = self.email_account.smtp_server\n\t\t\tself.login = (getattr(self.email_account, \"login_id\", None) or self.email_account.email_id)\n\t\t\tif not self.email_account.no_smtp_authentication:\n\t\t\t\tif self.email_account.ascii_encode_password:\n\t\t\t\t\tself.password = frappe.safe_encode(self.email_account.password, 'ascii')\n\t\t\t\telse:\n\t\t\t\t\tself.password = self.email_account.password\n\t\t\telse:\n\t\t\t\tself.password = None\n\t\t\tself.port = self.email_account.smtp_port\n\t\t\tself.use_tls = self.email_account.use_tls\n\t\t\tself.sender = self.email_account.email_id\n\t\t\tself.always_use_account_email_id_as_sender = cint(self.email_account.get(\"always_use_account_email_id_as_sender\"))\n\t\t\tself.always_use_account_name_as_sender_name = cint(self.email_account.get(\"always_use_account_name_as_sender_name\"))\n\n\t@property\n\tdef sess(self):\n\t\t\"\"\"get session\"\"\"\n\t\tif self._sess:\n\t\t\treturn self._sess\n\n\t\t# check if email server specified\n\t\tif not getattr(self, 'server'):\n\t\t\terr_msg = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account')\n\t\t\tfrappe.msgprint(err_msg)\n\t\t\traise frappe.OutgoingEmailError(err_msg)\n\n\t\ttry:\n\t\t\tif self.use_tls and not self.port:\n\t\t\t\tself.port = 587\n\n\t\t\tself._sess = smtplib.SMTP(cstr(self.server or \"\"),\n\t\t\t\tcint(self.port) or None)\n\n\t\t\tif not self._sess:\n\t\t\t\terr_msg = _('Could not connect to outgoing email server')\n\t\t\t\tfrappe.msgprint(err_msg)\n\t\t\t\traise frappe.OutgoingEmailError(err_msg)\n\n\t\t\tif self.use_tls:\n\t\t\t\tself._sess.ehlo()\n\t\t\t\tself._sess.starttls()\n\t\t\t\tself._sess.ehlo()\n\n\t\t\tif self.login and self.password:\n\t\t\t\tret = self._sess.login(str(self.login or \"\"), str(self.password or \"\"))\n\n\t\t\t\t# check if logged correctly\n\t\t\t\tif ret[0]!=235:\n\t\t\t\t\tfrappe.msgprint(ret[1])\n\t\t\t\t\traise frappe.OutgoingEmailError(ret[1])\n\n\t\t\treturn self._sess\n\n\t\texcept smtplib.SMTPAuthenticationError as e:\n\t\t\tfrappe.msgprint(_(\"Invalid login or password\"))\n\t\t\ttraceback = sys.exc_info()[2]\n\t\t\traise_(frappe.ValidationError, e, traceback)\n\n\t\texcept smtplib.SMTPException:\n\t\t\tfrappe.msgprint(_('Unable to send emails at this time'))\n\t\t\traise\n\n\t\texcept _socket.error as e:\n\t\t\t# Invalid mail server -- due to refusing connection\n\t\t\tfrappe.msgprint(_('Invalid Outgoing Mail Server or Port'))\n\t\t\ttraceback = sys.exc_info()[2]\n\t\t\traise_(frappe.ValidationError, e, traceback)\n", "path": "frappe/email/smtp.py"}]}