213 Commits

Author SHA1 Message Date
Rodolfo García Peñas (kix) 8689995549 handle exceptions for IMAP ID command to improve error reporting
This commit adds a try-except block around the IMAP ID command to catch any exceptions that may occur during its execution. If an exception is raised, it will log a warning message with the details of the error, improving the error reporting and allowing users to understand what went wrong without crashing the application.
2026-05-21 17:47:21 +02:00
Rodolfo García Peñas (kix) 56b8cbca01 imapserver: remove outdated comment regarding tryTLS flag in authentication methods 2026-05-17 11:06:03 +02:00
Rodolfo García Peñas (kix) 3ca32e98fc imapserver: remove outdated comment regarding STARTTLS capabilities 2026-05-17 09:06:57 +02:00
Rodolfo García Peñas (kix) 46505c53ef imapserver: fix STARTTLS-stripping attack vulnerability
When `starttls = yes` is configured and the server does not advertise
STARTTLS in its capability list, the previous code silently returned
without attempting TLS, leaving the connection in cleartext.

RFC 2595 section 9 explicitly documents this attack vector:

  "A man-in-the-middle attacker can remove STARTTLS from the
   capability list or generate a failure response to the STARTTLS
   command."

Silently skipping STARTTLS in that case makes offlineimap completely
vulnerable to such capability-stripping attacks: the attacker wins by
simply removing the STARTTLS keyword from the server greeting.

Fix: when STARTTLS is not advertised but is configured, emit a warning
and attempt STARTTLS anyway.  If the server genuinely does not support
it, imaplib will raise an error and the existing error-handling path
raises OfflineImapError, aborting the connection.  If the attempt
succeeds, TLS is established normally.

The resulting behaviour is:
- Server advertises STARTTLS → unchanged, works as before.
- Server does NOT advertise STARTTLS (possible MITM strip):
  - Warning is logged to alert the user.
  - STARTTLS is attempted regardless.
  - If the server rejects it: OfflineImapError, connection aborted.
  - If the server accepts it: TLS is established, sync continues.

Reported by: hartwork
References: RFC 2595 §9
2026-05-16 10:30:39 +02:00
Rodolfo García Peñas (kix) 4bb9eb2e9d Merge branch 'pr-244' into testing 2026-05-15 10:24:09 +02:00
Rodolfo García Peñas (kix) 1c4f9b4f05 Enhance STARTTLS handling with fallback for non-standard IMAP servers
- Implement tolerance for non-standard IMAP servers that do not provide reliable CAPABILITY after STARTTLS.
- Introduce configuration option 'allow_nonstandard_capabilities' to enable fallback using pre-TLS capabilities.
2026-05-13 18:35:54 +02:00
Noa Torstensvik 450accc6f8 Reevaluate oauth2_access_token_eval every time
Before this, authentication will fail after the access token has
expired, and if this is handled by an external program using
oauth2_access_token_eval, offlineimap3 will be able to renew the token
by itself. We should allow oauth2_access_token_eval to run in this case.

One might imagine that authentication needs to be reperformed for
example because of bad internet, and that the token will therefore be
refreshed unnecessarily. I believe most users of
oauth2_access_token_eval will use caching and that this will not be a
big problem.

Signed-off-by: Noa Torstensvik <noa@torstensvik.se>
2026-05-09 14:20:52 +02:00
Rodolfo García Peñas (kix) d16afe503d Merge branch 'pr-241' into testing 2026-04-25 11:24:02 +02:00
Michael Hohmuth d4ccbbb049 Prevent deadlock in IMAPServer.close when using 'maxsyncaccounts' and 'maxconnections'.
We introduce a separate closing flag that's checked in
acquireconnection, decoupling the connectionlock from the semaphore
such that they can be acquired separately.

Signed-off-by: Michael Hohmuth <hohmuth@sax.de>
2026-04-24 14:27:20 +02:00
Rodolfo García Peñas (kix) 854f3891aa Robustly handle dead sockets during authentication by retrying the connection
This commit addresses issues where certain IMAP servers (like Protonmail Bridge)
abruptly close the socket during the authentication phase.

Following a simplified approach:
1. Updated __authn_helper to initiate STARTTLS unconditionally at the
   beginning of the loop.
2. Modified acquireconnection to catch OfflineImapError. If the socket is
   detected as dead during authentication, it now logs a warning and retries
   the entire connection process from scratch (up to 3 attempts).
3. This ensures each retry starts with a fresh connection and all configured
   authentication methods available, avoiding permanent list modification.
2026-04-20 09:43:37 +02:00
Rodolfo García Peñas (kix) 915dd0a510 Revert "fix: handle dead sockets during authentication by retrying remaining methods via AuthMethodSocketDeadError"
This reverts commit 38da540f99.
2026-04-20 09:14:30 +02:00
Rodolfo García Peñas (kix) 38da540f99 fix: handle dead sockets during authentication by retrying remaining methods via AuthMethodSocketDeadError
Certain IMAP servers (like Protonmail Bridge) suffer from bugs where they hang and drop the connection when receiving specific authentication commands (such as `AUTHENTICATE PLAIN` immediately after STARTTLS). Previously, if the socket died during an authentication attempt, offlineimap would abort the entire synchronization process immediately.

This commit introduces `AuthMethodSocketDeadError` to handle this scenario gracefully. When an authentication mechanism kills the socket, it is temporarily removed from the list of available mechanisms for that session. Offlineimap then reconnects and retries the remaining authentication mechanisms (e.g., falling back from PLAIN to LOGIN), allowing synchronization to continue successfully without entering an infinite loop.
2026-04-19 10:42:09 +02:00
Rodolfo García Peñas (kix) 177145e7cf Fix TLS stripping vulnerability when STARTTLS is requested (Issue #222)
ç
When a user sets `starttls = yes`, offlineimap expects to negotiate a secure TLS tunnel. However, if a MITM attacker intercepts the connection and removes the `STARTTLS` capability from the server's greeting, the `if 'STARTTLS' in imapobj.capabilities` check fails silently. This causes offlineimap to skip TLS negotiation and proceed to send credentials in plaintext.

This commit introduces a strict check in `__start_tls()`: if the user requested STARTTLS but the server does not advertise it, offlineimap will immediately abort with a clear error message, protecting the user's password from being exposed. To connect insecurely, the user must explicitly set `starttls = no`.
2026-04-19 10:37:19 +02:00
Rodolfo García Peñas (kix) 1680e0c623 Merge branch 'pr-239' into testing 2026-04-19 10:04:35 +02:00
Derek Schrock 4d300783c7 Use correct IMAP folder name with imapobj.select
Like other imapobj.select translate it vi
imaputil.foldername_to_imapname
2026-04-05 19:34:12 -04:00
Rodolfo García Peñas (kix) 9324259deb Check socket health before returning pooled connection
This patch adds a health check for pooled IMAP connections in the acquireconnection() method. After acquiring a connection from the pool, it sends a NOOP command to verify that the connection is still alive. If the NOOP command fails, it logs the failure, cleans up the stale connection, and recursively calls acquireconnection() to get a new one. This helps ensure that clients receive healthy connections from the pool and reduces the likelihood of encountering errors due to stale connections.
2026-04-04 20:03:17 +02:00
Rodolfo García Peñas (kix) 59663f77f2 Check if the socket is alive
This patch adds a helper function `_is_socket_alive` that checks if the underlying socket connection of an IMAP object is still usable. It uses `select` to check for readability and errors on the socket, and also checks if the socket has been closed by the peer. This function is called before attempting authentication methods to avoid long timeouts if the socket is already dead (e.g., after a failed STARTTLS). If the socket is found to be broken, it logs a debug message and aborts further authentication attempts.
2026-04-04 19:35:10 +02:00
Rodolfo García Peñas (kix) 4b97c358a8 Do not shutdown the socket in start_tls
This commit removes the redundant shutdown() calls in start_tls() and acquireconnection() that were added to work around a TLS connection failure. The shutdown() calls were causing spurious warnings in the cleanup chain when the socket was already closed. The TLS connection failure should now be handled by the exception raised in start_tls() without needing to shutdown the socket again.

This avoid problems with the socket being closed twice and generating warnings, while still properly handling the TLS connection failure. The exception raised in start_tls() will indicate the failure without needing to shutdown the socket again.
2026-04-04 17:37:19 +02:00
Rodolfo García Peñas (kix) ec7a4a95ae Solve imaplib2 hangs on STARTTLS
This patch works around imaplib2's unconditional CAPABILITY call after the TLS handshake, which causes a 60-second hang with servers like Protonmail Bridge that do not respond to CAPABILITY at that stage. The workaround temporarily substitutes a no-op for imaplib2's _get_capabilities method during the handshake, allowing it to set _tls_established=True without triggering the problematic CAPABILITY call. The patch also guards against a redundant second shutdown() call on the same socket, which can raise [Errno 9] Bad file descriptor and generate spurious warnings in the cleanup chain.
2026-04-04 13:45:34 +02:00
serge-sans-paille 9c352d7d9c Always pass an exception as first argument of ui.error
In various cases we were passing string messages, which is incompatible
with how ui.error handles its argument, leading to bugs like #233

Fix #233

Signed-off-by: serge-sans-paille <sergesanspaille@free.fr>
2026-04-01 08:05:12 +02:00
Rodolfo García Peñas (kix) 38114ed24f Merge pull request #194 from lilydjwg/master
Fix SyntaxWarning & DeprecatedWarning
2024-08-15 15:28:27 +02:00
lilydjwg 5519fe90cd fix a couple of DeprecationWarning from threading module
Signed-off-by: lilydjwg <lilydjwg@gmail.com>
2024-04-28 17:07:51 +08:00
Magarnicle 82cfed2290 Update imapserver.py
Handle releaseconnection getting called on a connection that is not in assignedconnections
2024-01-31 10:57:48 +10:00
Kirill A. Korinsky d99b2f72da Introduced support of system keyring
Here a simple way that allows to migrate password to system keyrgin, and
use it to run offlineimap.

Signed-off-by: Kirill A. Korinsky <kirill@korins.ky>
2023-11-18 21:09:34 +00:00
lilydjwg 206322b151 remove a stray expression 2022-04-06 18:18:24 +08:00
Rodolfo García Peñas (kix) 253f97a3e9 Merge pull request #86 from dopefishh/communicate-syncmode-to-synchooks
Communicate syncmode to synchooks
2022-03-25 11:43:10 +01:00
Nicolas Sebrecht f418754087 don't fail to report error when exception args are not as expected
Also, quit the "elif" logic because each case is raising an exception.

Fixes #97
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2021-10-31 00:03:02 +02:00
Rodolfo García Peñas (kix) ea4b0438fe Support for IMAPv4 ID extension
This patch enables the ID extension of IMAPv4.

The patch sends the client name and the client version to the server.

Usually, the server doesn't require it, but in some cases the server
drop de connection if the ID is not send.

#Close #71
2021-10-12 11:58:47 +02:00
Rodolfo García Peñas (kix) 31149d1b1f Revert "disable verification of ssl certs via CA if fingerpint is set"
This reverts commit 871b1175f1.
2021-10-12 10:30:00 +02:00
Mart Lubbers 7748de52fb Communicate syncmode to synchooks
This patch sets the environment variable OFFLINEIMAPSYNCMODE to either
full, quick or idle depending on the context of the pre- and
postsynchook.

Adding the context as an argument was considered but this would break
existing configurations and it makes calling a program directly more
cumbersome. Some programs (e.g. imapfilter) may not know what to do with
this extra argument.

Signed-off-by: Mart Lubbers <mart@martlubbers.net>
2021-09-07 09:43:16 +02:00
Rodolfo García Peñas (kix) 48f2df4267 Handle connection refused messages
This patch handle the connection refused messages. One of these messages
is related to returned zeros.
2021-08-07 20:33:06 +02:00
Rodolfo García Peñas (kix) 721d579a32 Merge pull request #66 from duritong/fix-fingerprint-verification
disable verification of ssl certs via CA if fingerpint is set
2021-05-09 11:28:17 +02:00
mh 871b1175f1 disable verification of ssl certs via CA if fingerpint is set
Up to a4863b2 offlineimap did not include a default CA bundle. And
folks who set cert_fingerprint (because they might connect to a
host using a self-signed cert or an onion service without the onion
address in the SANs) were able to validate their certificates.

Since a4863b2 you always have a `sslcacertfile` configured (since
it always falls back to the os one) and thus the old way didn't
work anymore.

If a use defines a `cert_fingerprint` there is not much use to
validate the cert through the CA chain, since the fingerprint
is the stronges verification you can get. Therefor we can disable
verfication when `cert_fingerprint` is set.

This enables users to fetch emails again from onion services or
hosts using self-signed certifcates, but doesn't question nor
change any other behavior.

Fixes #41
2021-04-29 14:34:06 +02:00
Sudip Mukherjee 3afd3395bd BUG: Exception with debug logs
When ui is set to 'Curses Blinkenlights' and debug logs are enabled,
we get an exception with 'embedded null character'.

Remove the NULL from the log, keeping the log message same as before.

Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
2021-02-20 00:20:36 +00:00
Guido Günther d19024ef20 imapserver: GSSAPI: make sure reply is all bytes
The current code mixed string and bytes leading to:

ERROR: Exceptions occurred during the run!
ERROR: While attempting to sync account 'honk.sigxcpu.org'
  sequence item 1: expected str instance, int found

Traceback:
  File "/usr/share/offlineimap3/offlineimap/accounts.py", line 298, in syncrunner
    self.__sync()
  File "/usr/share/offlineimap3/offlineimap/accounts.py", line 374, in __sync
    remoterepos.getfolders()
  File "/usr/share/offlineimap3/offlineimap/repository/IMAP.py", line 648, in getfolders
    imapobj = self.imapserver.acquireconnection()
  File "/usr/share/offlineimap3/offlineimap/imapserver.py", line 592, in acquireconnection
    self.__authn_helper(imapobj)
  File "/usr/share/offlineimap3/offlineimap/imapserver.py", line 449, in __authn_helper
    if func(imapobj):
  File "/usr/share/offlineimap3/offlineimap/imapserver.py", line 362, in __authn_gssapi
    imapobj.authenticate('GSSAPI', self.__gsshandler)
  File "/usr/lib/python3/dist-packages/imaplib2.py", line 691, in authenticate
    typ, dat = self._simple_command('AUTHENTICATE', mechanism.upper())
  File "/usr/lib/python3/dist-packages/imaplib2.py", line 1684, in _simple_command
    return self._command_complete(self._command(name, *args), kw)
  File "/usr/lib/python3/dist-packages/imaplib2.py", line 1404, in _command
    literal = literator(data, rqb)
  File "/usr/lib/python3/dist-packages/imaplib2.py", line 2247, in process
    ret = self.mech(self.decode(data))
  File "/usr/share/offlineimap3/offlineimap/imapserver.py", line 318, in __gsshandler
    reply = ''.join(reply)

Closes: #46
Signed-off-by: Guido Günther <agx@sigxcpu.org>
2021-02-03 20:34:48 +01:00
Thomas De Schampheleire 58d34df29f imapserver: fix exception handling in xoauth2handler
In case of an exception in XOAUTH2 code refresh (HTTP Error 400: Bad
Request), following exception occurs:

ERROR: While attempting to sync account 'xxx'
  __init__() missing 3 required positional arguments: 'msg', 'hdrs', and 'fp'

Traceback:
  File ".../offlineimap3/offlineimap/accounts.py", line 298, in syncrunner
    self.__sync()
  File ".../offlineimap3/offlineimap/accounts.py", line 374, in __sync
    remoterepos.getfolders()
  File ".../offlineimap3/offlineimap/repository/IMAP.py", line 648, in getfolders
    imapobj = self.imapserver.acquireconnection()
  File ".../offlineimap3/offlineimap/imapserver.py", line 591, in acquireconnection
    self.__authn_helper(imapobj)
  File ".../offlineimap3/offlineimap/imapserver.py", line 448, in __authn_helper
    if func(imapobj):
  File ".../offlineimap3/offlineimap/imapserver.py", line 382, in __authn_xoauth2
    imapobj.authenticate('XOAUTH2', self.__xoauth2handler)
  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 682, in authenticate
    typ, dat = self._simple_command('AUTHENTICATE', mechanism.upper())
  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 1675, in _simple_command
    return self._command_complete(self._command(name, *args), kw)
  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 1395, in _command
    literal = literator(data, rqb)
  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 2238, in process
    ret = self.mech(self.decode(data))
  File ".../offlineimap3/offlineimap/imapserver.py", line 257, in __xoauth2handler
    raise type(e)(msg, exc_info()[2])

The exception 'e' is of type HTTPError, which does not have the same kind of
constructor as normal Python exceptions.

Instead, print the constructed message and just raise the existing
exception.

With that change, the same condition triggers another problem further on:

ERROR: While attempting to sync account 'xxx'
  tuple index out of range

Traceback:
  File ".../offlineimap3/offlineimap/accounts.py", line 298, in syncrunner
    self.__sync()
  File ".../offlineimap3/offlineimap/accounts.py", line 374, in __sync
    remoterepos.getfolders()
  File ".../offlineimap3/offlineimap/repository/IMAP.py", line 648, in getfolders
    imapobj = self.imapserver.acquireconnection()
  File ".../offlineimap3/offlineimap/imapserver.py", line 664, in acquireconnection
    elif isinstance(e, socket.error) and e.args[0] == errno.ECONNREFUSED:

because e.args is empty.
2021-01-04 10:26:01 +01:00
Rodolfo García Peñas (kix) ca0a2651a3 Added Debug for imaplib2
When I ported offlineimap from Python 2 to Python 3 I removed the Debug for
IMAP (imaplib2).
The reason was offlineimap was setting the Debug directly in imaplib2,
not using the proper way (using the IMAP4 argument). Because we are
removing the virtual_imaplib2, I removed this option.

I removed this line in offlineimap/init.py:303:

---8<---
 300        dtype = dtype.strip()
 301        self.ui.add_debug(dtype)
 302        if dtype.lower() == u'imap':
-303          imaplib.Debug = 5
 304
 305     if options.runonce:
 306         # Must kill the possible default option.
---8<---

With this patch, the debug level 5 is restored in imaplib if the user
set the -d ALL or -d imap in offlineimap.
2020-11-08 21:30:22 +01:00
Rodolfo García Peñas (kix) c130e84a84 BUG: Support for server capabilities
Server capabilities are returned as list of bytes. We need convert them
to list of strings.

This patch do it. Probably you must recreate your cache after this patch.
2020-11-07 15:52:06 +01:00
Rodolfo García Peñas (kix) cbbeebbf44 imapserver.py removed extra parenthesis
We can remove this parenthesis.
2020-10-31 15:39:28 +01:00
Rodolfo García Peñas (kix) 352133dcc0 call to setDaemon uses Bool argument
The argument 1 should be True in these files.
2020-10-31 15:35:54 +01:00
Rodolfo García Peñas (kix) 78aeed20a9 imapserver call to OfflineImapError with right arguments
Call to OfflineImapError has the arguments:

- message
- severity
- error code

The None argument ir wrong here, we can remove it and then we have three arguments not four.
2020-10-31 15:29:24 +01:00
Rodolfo García Peñas (kix) 26efc62ba7 imapserver import style
This patch removes the unused import socket line. (warning)

Split the line with multiple imports in different lines. (warning)
2020-10-31 15:24:40 +01:00
Rodolfo García Peñas (kix) a60ca038ce Updated cram-md5 authentication
This patch updates the cram-md5 auth. We include two steps:

- Convert the password variable from string to bytes. This change is
  because in Python2 strings and bytes are the same, but not in Python3
- Updates the call to hmac.new, now the digestmod argument is mandatory.
  I used hashlib.md5, because we need md5 hash.

Closes #19

    Signed-off-by: Rodolfo García Peñas (kix) <kix@kix.es>
2020-10-31 00:09:38 +01:00
Thomas De Schampheleire 78807b55b4 imapserver.py: fix __xoauth2handler in Python 3
Error when using the XOAUTH2 token refresh logic:

  ("POST data should be bytes, an iterable of bytes, or a file object. It cannot be of type str. (configuration is: {....}", <traceback object at 0x7fc6e69f2b40>)
     ['  File ".../offlineimap3/offlineimap/accounts.py", line 298, in syncrunner
        self.__sync()
    ', '  File ".../offlineimap3/offlineimap/accounts.py", line 374, in __sync
        remoterepos.getfolders()
    ', '  File ".../offlineimap3/offlineimap/repository/IMAP.py", line 446, in getfolders
        imapobj = self.imapserver.acquireconnection()
    ', '  File ".../offlineimap3/offlineimap/imapserver.py", line 579, in acquireconnection
        self.__authn_helper(imapobj)
    ', '  File ".../offlineimap3/offlineimap/imapserver.py", line 443, in __authn_helper
        if func(imapobj):
    ', '  File ".../offlineimap3/offlineimap/imapserver.py", line 377, in __authn_xoauth2
        imapobj.authenticate(\'XOAUTH2\', self.__xoauth2handler)
    ', '  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 681, in authenticate
        typ, dat = self._simple_command(\'AUTHENTICATE\', mechanism.upper())
    ', '  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 1674, in _simple_command
        return self._command_complete(self._command(name, *args), kw)
    ', '  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 1394, in _command
        literal = literator(data, rqb)
    ', '  File ".../offlineimap3/venv/lib/python3.7/site-packages/imaplib2.py", line 2237, in process
        ret = self.mech(self.decode(data))
    ', '  File ".../offlineimap3/offlineimap/imapserver.py", line 253, in __xoauth2handler
        raise type(e)(msg, exc_info()[2])
    ']

Fix by encoding the data passed to urllib.

Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
2020-09-29 10:04:07 +02:00
Rodolfo García Peñas (kix) 4e1558adc3 six: changed offlineimap/imapserver.py
This patch removes the library six, compatible with python2.

I need change these re-raise calls.

Signed-off-by: Rodolfo García Peñas (kix) <kix@kix.es>
2020-09-03 21:36:03 +02:00
Rodolfo García Peñas (kix) 7947aea7a9 imapserver call LIST using empty quotes
The LIST command needs empty quotes, like

LIST ""

This is the right argument.
2020-09-01 18:21:27 +02:00
Rodolfo García Peñas (kix) 54fbfc67b4 imapserver.py updated docstring
Added argument.
2020-08-30 18:37:09 +02:00
Rodolfo García Peñas (kix) b118b80897 imapserver.py Removed unused variables
These variables are not used.
2020-08-30 18:35:01 +02:00
Rodolfo García Peñas (kix) b860e93072 imapserver.py removed unreacheable code
This code is not reacheable.
2020-08-30 18:32:42 +02:00
Rodolfo García Peñas (kix) 26668d29b0 imapserver.py removed extra characters
these characters could be removed.
2020-08-30 18:31:30 +02:00