aioimaplib
aioimaplib copied to clipboard
STARTTLS command
Here's a monkey patch I'm using in my project to get STARTTLS working. It's confirmed to work with two different IMAP servers.
from aioimaplib import aioimaplib
# Monkey patching aioimaplib to support starttls
async def protocol_starttls(self, host, ssl_context=None):
if 'STARTTLS' not in self.capabilities:
aioimaplib.Abort('server does not have STARTTLS capability')
if hasattr(self, '_tls_established') and self._tls_established:
aioimaplib.Abort('TLS session already established')
response = await self.execute(aioimaplib.Command(
'STARTTLS', self.new_tag(), loop=self.loop))
if response.result != 'OK':
return response
if ssl_context is None:
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
sock = self.transport.get_extra_info('socket')
sock.setblocking(True)
sock = ssl_context.wrap_socket(sock, server_hostname=host)
# XXX: This is kind of a hack. It works since the transport interface
# totally encapsulates this, but in the future it might break if people
# change the internals of the base transport.
sock.setblocking(False)
self.transport._sock = sock
self._tls_established = True
await self.capability()
return response
async def imap_starttls(self):
return (await asyncio.wait_for(
self.protocol.starttls(self.host), self.timeout))
aioimaplib.IMAP4ClientProtocol.starttls = protocol_starttls
aioimaplib.IMAP4.starttls = imap_starttls
Unfortunately, I don't have time to do a formal pull request, but if someone looks into doing this in the future, hopefully this serves as a starting point. Note that this code is using python3.6 instead of python3.4.
aiosmtplib has a slightly different implementation for STARTTLS based on set_protocol (see https://github.com/cole/aiosmtplib/blob/master/src/aiosmtplib/protocol.py). But when I tried implementing that, I kept getting wrong SSL version errors, so I ended up going the wrap_socket route.
There is any progress on this? It would be very useful in my use case
Trying to use the proposed patch I'm receiving:
aioimaplib.aioimaplib.Abort: command STARTTLS illegal in state STARTED
hello @dadokkio I must admit that I didn't have a look at this issue.
You see this error because you must before sending STARTTLS call
await imap_client.wait_hello_from_server()
Then you'll be in NONAUTH state that is required to send STARTTLS so you can call the above patch.
Hi, thanks for the reply. I tried to add the wait_hello_from_server but then I'm receiving this error:
Traceback (most recent call last):
File "manage.py", line 31, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 328, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 369, in execute
output = self.handle(*args, **options)
File "/app/shop/management/commands/monitor.py", line 101, in handle
asyncio.run(_check_inbox())
File "/usr/local/lib/python3.8/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "/app/shop/management/commands/monitor.py", line 73, in _check_inbox
yield from imap_client.starttls()
File "/app/shop/management/commands/monitor.py", line 46, in imap_starttls
return await asyncio.wait_for(self.protocol.starttls(self.host), self.timeout)
File "/usr/local/lib/python3.8/asyncio/tasks.py", line 483, in wait_for
return fut.result()
File "/app/shop/management/commands/monitor.py", line 34, in protocol_starttls
sock.setblocking(True)
File "/usr/local/lib/python3.8/asyncio/trsock.py", line 197, in setblocking
raise ValueError(
ValueError: setblocking(): transport sockets cannot be blocking
The idea is to use the integrate the tool with django managments command, and the value of sock at the error is:
<asyncio.TransportSocket fd=10, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('xxx.xxx.xxx.xxx', 41514), raddr=('xxx.xxx.xxx.xxx', 143)>
In case you're still having issues with this, feel free to take my code as an example: https://github.com/alkim0/mbsync-watcher/blob/master/mbsync_watcher/main.py.