Skip to content

Commit 070641c

Browse files
committed
Merge branch 'main' into build
2 parents c352ce0 + 9a27a56 commit 070641c

File tree

5 files changed

+33
-32
lines changed

5 files changed

+33
-32
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
output: ${{ steps.latest_tag.outputs.tag }}
1616

1717
steps:
18-
- uses: actions/checkout@v4
18+
- uses: actions/checkout@v5
1919
- id: latest_tag
2020
run: |
2121
git fetch -a
@@ -31,15 +31,15 @@ jobs:
3131
os: [macos-latest, windows-latest]
3232

3333
steps:
34-
- uses: actions/checkout@v4
35-
- uses: actions/setup-python@v5
34+
- uses: actions/checkout@v5
35+
- uses: actions/setup-python@v6
3636
with:
3737
python-version: '3.x'
3838

3939
# install requirements
4040
# - note: `imageio` is required here to convert our PNG icon to a format nuitka can handle (nuitka itself is a separate action)
4141
# - note: `certifi` is required here because pyinstaller no longer bundles certificates (pyinstaller #7229)
42-
# - note: `boto3`, `requests`, `google-auth` and corresponding pyinstaller hidden imports / nuitka included
42+
# - note: `boto3`, `requests`, `google-auth` and corresponding pyinstaller hidden imports / nuitka included
4343
# packages are so that advanced proxy features work in pre-built binaries
4444
# - note: `--hidden-import timeago.locales.en_short` is required for GUI mode until `timeago` #40 is fixed
4545
- run: |
@@ -77,7 +77,7 @@ jobs:
7777
filename: emailproxy-${{ needs.get_tag.outputs.output }}_pyinstaller-${{ runner.os }}.zip
7878

7979
# append the pyinstaller zip to the latest release
80-
- uses: xresloader/[email protected].0
80+
- uses: xresloader/[email protected].1
8181
with:
8282
file: dist/*.zip
8383
update_latest_release: true
@@ -89,6 +89,7 @@ jobs:
8989
move emailproxy.py _.py
9090
echo '# nuitka-project-if: {OS} in ("Windows"):' >> _.txt
9191
echo '# nuitka-project: --windows-icon-from-ico=icon.png' >> _.txt
92+
echo '# nuitka-project: --windows-console-mode=disable' >> _.txt
9293
echo '# nuitka-project-if: {OS} == "Darwin":' >> _.txt
9394
echo '# nuitka-project: --macos-app-icon=icon.png' >> _.txt
9495
move _.txt emailproxy.py
@@ -108,7 +109,7 @@ jobs:
108109
google
109110
jwt
110111
nofollow-import-to: '*.tests'
111-
112+
112113
# replace previous build files (-f vs. -fo behaviour differences mean we can't combine); add macOS quarantine tips
113114
- if: runner.os == 'macOS'
114115
run: |
@@ -130,7 +131,7 @@ jobs:
130131
filename: emailproxy-${{ needs.get_tag.outputs.output }}_nuitka-${{ runner.os }}.zip
131132

132133
# append the nuitka zip to the latest release
133-
- uses: xresloader/[email protected].0
134+
- uses: xresloader/[email protected].1
134135
with:
135136
file: dist/*.zip
136137
update_latest_release: true

.github/workflows/pypi.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
runs-on: ubuntu-latest
1414

1515
steps:
16-
- uses: actions/checkout@v4
17-
- uses: actions/setup-python@v5
16+
- uses: actions/checkout@v5
17+
- uses: actions/setup-python@v6
1818
with:
1919
python-version: '3.x'
2020

@@ -57,7 +57,7 @@ jobs:
5757
./dist/*.whl
5858
5959
# append the built packages to the latest release
60-
- uses: xresloader/upload-to-github-release@v1
60+
- uses: xresloader/upload-to-github-release@v1.6.1
6161
env:
6262
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6363
with:

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.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ documentation = Accounts are specified using your email address as the section h
139139

140140
- Office 365 shared mailboxes are supported: add an account entry here using the email address of the shared
141141
mailbox as the account name. When asked to authenticate, log in as the user that access has been delegated to.
142-
Note that Office 365 no-longer supports the `[email protected]/delegated.mailbox` username syntax.
142+
Note that Office 365 no-longer supports the `[email protected]\delegated.mailbox` username syntax.
143143

144144
- It is possible to create Office 365 / Outlook OAuth 2.0 clients that do not require a secret to be sent. If this
145145
is the case for your setup, delete the `client_secret` line from your account's configuration entry (do not leave

emailproxy.py

Lines changed: 16 additions & 12 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.6.25' # 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
@@ -2163,8 +2163,8 @@ def process_data(self, byte_data):
21632163
updated_response = re.sub('( AUTH=%s)+' % capability, ' AUTH=PLAIN', str_response, flags=re.IGNORECASE)
21642164
if not re.search(' AUTH=PLAIN', updated_response, re.IGNORECASE):
21652165
# cannot just replace e.g., one 'CAPABILITY ' match because IMAP4 must be first if present (RFC 1730)
2166-
updated_response = re.sub('(CAPABILITY)( IMAP%s)?' % capability, r'\1\2 AUTH=PLAIN', updated_response,
2167-
count=1, flags=re.IGNORECASE)
2166+
updated_response = re.sub('(CAPABILITY)((?: IMAP%s)*)' % capability, r'\1\2 AUTH=PLAIN',
2167+
updated_response, count=1, flags=re.IGNORECASE)
21682168
updated_response = updated_response.replace(' AUTH=PLAIN', '', updated_response.count(' AUTH=PLAIN') - 1)
21692169
if not re.search(' SASL-IR', updated_response, re.IGNORECASE):
21702170
updated_response = updated_response.replace(' AUTH=PLAIN', ' AUTH=PLAIN SASL-IR')
@@ -2313,11 +2313,6 @@ def process_data(self, byte_data):
23132313
updated_response = re.sub(r'250([ -])AUTH(?: [A-Z\d_-]{1,20})+', r'250\1AUTH PLAIN LOGIN', str_data,
23142314
flags=re.IGNORECASE)
23152315
updated_response = re.sub(r'250([ -])STARTTLS(?:\r\n)?', r'', updated_response, flags=re.IGNORECASE)
2316-
if ehlo_end and self.ehlo.lower().startswith(b'ehlo'):
2317-
if 'AUTH PLAIN LOGIN' not in self.ehlo_response:
2318-
self.ehlo_response += '250-AUTH PLAIN LOGIN\r\n'
2319-
if self.custom_configuration['local_starttls'] and not self.client_connection.ssl_connection:
2320-
self.ehlo_response += '250-STARTTLS\r\n' # we always remove STARTTLS; re-add if permitted
23212316
self.ehlo_response += '%s\r\n' % updated_response if updated_response else ''
23222317

23232318
if ehlo_end:
@@ -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)