Skip to content

Commit 9a27a56

Browse files
committed
Refine SMTP HELO/EHLO handling; update readme
1 parent c040879 commit 9a27a56

File tree

2 files changed

+19
-19
lines changed

2 files changed

+19
-19
lines changed

README.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ Transparently add OAuth 2.0 support to IMAP/POP/SMTP client applications, script
44
<div align="center">
55
<br><strong>Email OAuth 2.0 Proxy is sponsored by</strong><br><br>
66
<a href="https://auth-email.com/?ref=emailproxy">
7-
<picture>
8-
<source width="300" media="(prefers-color-scheme: dark)" srcset="https://auth-email.com/static/img/logo-full-dark.svg">
9-
<source width="300" media="(prefers-color-scheme: light)" srcset="https://auth-email.com/static/img/logo-full-light.svg">
10-
<img width="300" src="https://auth-email.com/static/img/logo-full.png" alt="Auth-Email.com logo">
11-
</picture><br>
12-
<b>Email OAuth made simple</b><br>
13-
<sup>Use any app, client or device to access your OAuth mail accounts with ease.</sup>
7+
<img width="300" fetchpriority="high" src="https://auth-email.com/static/img/logo-providers.svg" alt="Auth-Email.com logo and supported mail providers"><br>
8+
<b>Auth-Email.com – email OAuth made easy</b><br>
9+
<sup>Send and receive from <i>any</i> account with <i>every</i> email client, app or device.</sup>
1410
</a><br><br>
1511
</div>
1612

@@ -24,7 +20,7 @@ It can be used with any email provider that supports OAuth 2.0 authentication, i
2420

2521
### Example use-cases<a id="example-use-cases"></a>
2622
- You need to use an Office 365 email account, but don't get on with Outlook.
27-
The email client you like doesn't support OAuth 2.0, which became mandatory [in January 2023](https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-deprecation-in-exchange-online-september/ba-p/3609437) ([September 2024 for personal Hotmail/Outlook accounts](https://support.microsoft.com/en-us/office/modern-authentication-methods-now-needed-to-continue-syncing-outlook-email-in-non-microsoft-email-apps-c5d65390-9676-4763-b41f-d7986499a90d); [September 2025 for O365 SMTP](https://techcommunity.microsoft.com/t5/exchange-team-blog/exchange-online-to-retire-basic-auth-for-client-submission-smtp/ba-p/4114750)).
23+
The email client you like doesn't support OAuth 2.0, which became mandatory for IMAP/POP [in January 2023](https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-deprecation-in-exchange-online-september/ba-p/3609437) ([September 2024 for personal Hotmail/Outlook accounts](https://support.microsoft.com/en-us/office/modern-authentication-methods-now-needed-to-continue-syncing-outlook-email-in-non-microsoft-email-apps-c5d65390-9676-4763-b41f-d7986499a90d); [April 2026 for O365 SMTP](https://techcommunity.microsoft.com/t5/exchange-team-blog/exchange-online-to-retire-basic-auth-for-client-submission-smtp/ba-p/4114750)).
2824
- You used to use Gmail via IMAP/POP/SMTP with your raw account credentials (i.e., your real password), but cannot do this now that Google has disabled this method, and don't want to use an [App Password](https://support.google.com/accounts/answer/185833) (or cannot enable this option).
2925
- You have an account already set up in an email client, and you need to switch it to OAuth 2.0 authentication.
3026
You can edit the server details, but the client forces you to delete and re-add the account to enable OAuth 2.0, and you don't want to do this.
@@ -254,7 +250,7 @@ See the [documentation and examples](https://github.com/simonrob/email-oauth2-pr
254250
## Potential improvements (pull requests welcome)<a id="potential-improvements-pull-requests-welcome"></a>
255251
- Full feature parity on different platforms (e.g., live menu updating; monitoring network status; clickable notifications)
256252
- Switch to asyncio? (with Python 3.12, [PEP 594](https://peps.python.org/pep-0594/) removed the asyncore package that the proxy is built upon – currently mitigated by the use of [pyasyncore](https://pypi.org/project/pyasyncore/))
257-
- Remote STARTTLS for IMAP/POP?
253+
- Local and/or remote STARTTLS for IMAP/POP?
258254

259255

260256
## Related projects and alternatives<a id="related-projects-and-alternatives"></a>

emailproxy.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
__author__ = 'Simon Robinson'
77
__copyright__ = 'Copyright (c) 2025 Simon Robinson'
88
__license__ = 'Apache 2.0'
9-
__package_version__ = '2025.10.2' # for pyproject.toml usage only - needs to be ast.literal_eval() compatible
9+
__package_version__ = '2025.10.4' # for pyproject.toml usage only - needs to be ast.literal_eval() compatible
1010
__version__ = '-'.join('%02d' % int(part) for part in __package_version__.split('.')) # ISO 8601 (YYYY-MM-DD)
1111

1212
import abc
@@ -2314,11 +2314,6 @@ def process_data(self, byte_data):
23142314
flags=re.IGNORECASE)
23152315
updated_response = re.sub(r'250([ -])STARTTLS(?:\r\n)?', r'', updated_response, flags=re.IGNORECASE)
23162316
self.ehlo_response += '%s\r\n' % updated_response if updated_response else ''
2317-
if ehlo_end and self.ehlo.lower().startswith(b'ehlo'):
2318-
if 'AUTH PLAIN LOGIN' not in self.ehlo_response:
2319-
self.ehlo_response += '250-AUTH PLAIN LOGIN\r\n'
2320-
if self.custom_configuration['local_starttls'] and not self.client_connection.ssl_connection:
2321-
self.ehlo_response += '250-STARTTLS\r\n' # we always remove STARTTLS; re-add if permitted
23222317

23232318
if ehlo_end:
23242319
self.client_connection.connection_state = SMTPOAuth2ClientConnection.STATE.PENDING
@@ -2327,10 +2322,19 @@ def process_data(self, byte_data):
23272322
self.starttls_state = self.STARTTLS.NEGOTIATING
23282323

23292324
elif self.starttls_state is self.STARTTLS.COMPLETE:
2330-
# we replay the original EHLO response to the client after server STARTTLS completes
2331-
split_response = self.ehlo_response.split('\r\n')
2332-
split_response[-1] = split_response[-1].replace('250-', '250 ') # fix last item if modified
2333-
super().process_data('\r\n'.join(split_response).encode('utf-8'))
2325+
# we modify and replay the original EHLO response to the client after server STARTTLS completes
2326+
self.ehlo_response = self.ehlo_response.rstrip('\r\n')
2327+
if self.ehlo.lower().startswith(b'ehlo'):
2328+
if 'AUTH PLAIN LOGIN' not in self.ehlo_response:
2329+
self.ehlo_response += '\r\n250-AUTH PLAIN LOGIN'
2330+
if self.custom_configuration['local_starttls'] and not self.client_connection.ssl_connection:
2331+
self.ehlo_response += '\r\n250-STARTTLS' # we always remove STARTTLS; re-add if permitted
2332+
2333+
# correct the last line where needed (250- vs 250 )
2334+
self.ehlo_response = self.ehlo_response.replace('\r\n250 ', '\r\n250-')
2335+
self.ehlo_response = '250 '.join(self.ehlo_response.rsplit('250-', 1))
2336+
2337+
super().process_data(b'%s\r\n' % self.ehlo_response.encode('utf-8'))
23342338
self.ehlo = None # only clear on completion - we need to use for any repeat calls
23352339
self.ehlo_response = ''
23362340

0 commit comments

Comments
 (0)