diff --git a/CHANGELOG.md b/CHANGELOG.md index 559a2d5d..49fc78fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Improvement: catch lack of support of PERIOD in vobject <= 0.9.9 * Fix: sharing: backmap of REPORT/PROPPATCH hrefs is now URL-encode-aware (edit of a shared collection failed when the principal contains '@') * Workaround: remove trailing spaces on TZID and TZNAME appended by buggy Microsoft clients +* Extension: [auth] imap_append_domain option (optional) ## 3.7.5 * Add: [sharing] conversion_bday_summary_template (customize summary) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index c76f70e2..7bfa9e72 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -1389,6 +1389,14 @@ One of: Default: `tls` +##### imap_append_domain + +_(>= 3.7.6)_ + +Append `@` + domain to login before trying IMAP connection + +Default: (unset) + ##### oauth2_token_endpoint _(>= 3.5.0)_ diff --git a/config b/config index 904986a6..2bd39383 100644 --- a/config +++ b/config @@ -161,6 +161,10 @@ # Value: tls | starttls | none #imap_security = tls +# Append '@' + domain to login before trying IMAP connection (optional) +# Value: domain.tld +#imap_append_domain = + # OAuth2 token endpoint URL #oauth2_token_endpoint = diff --git a/radicale/auth/imap.py b/radicale/auth/imap.py index f0d52b47..e9a1dfd4 100644 --- a/radicale/auth/imap.py +++ b/radicale/auth/imap.py @@ -48,6 +48,11 @@ class Auth(auth.BaseAuth): logger.info("auth imap port (autoselected): %d", self._port) else: logger.info("auth imap port: %d", self._port) + self._append_domain = self.configuration.get("auth", "imap_append_domain") + if self._append_domain is None or len(self._append_domain) == 0: + logger.info("auth imap append domain not used") + else: + logger.info("auth imap append domain: %s", self._append_domain) def _login(self, login, password) -> str: try: @@ -63,21 +68,25 @@ class Auth(auth.BaseAuth): connection = imaplib.IMAP4(host=self._host, port=self._port) if self._security == "starttls": connection.starttls(ssl.create_default_context()) + if self._append_domain is None or len(self._append_domain) == 0: + imaplogin = login + else: + imaplogin = login + "@" + self._append_domain try: if "AUTH=PLAIN" in connection.capabilities: - logger.debug("IMAP authentication PLAIN selected for user %r via %s:%d (security: %s)", login, self._host, self._port, self._security) + logger.debug("IMAP authentication PLAIN selected for user %r via %s:%d (security: %s)", imaplogin, self._host, self._port, self._security) connection.authenticate( "PLAIN", - lambda _: "{0}\x00{0}\x00{1}".format(login, password).encode(), + lambda _: "{0}\x00{0}\x00{1}".format(imaplogin, password).encode(), ) elif "AUTH=LOGIN" in connection.capabilities: - logger.debug("IMAP authentication LOGIN selected for user %r via %s:%d (security: %s)", login, self._host, self._port, self._security) - connection.login(login, password) + logger.debug("IMAP authentication LOGIN selected for user %r via %s:%d (security: %s)", imaplogin, self._host, self._port, self._security) + connection.login(imaplogin, password) else: logger.error("IMAP server is neither supporting AUTH=PLAIN or AUTH=LOGIN: %s:%d (security: %s)", self._host, self._port, self._security) return "" except imaplib.IMAP4.error as e: - logger.warning("IMAP authentication failed for user %r: %s", login, e, exc_info=False) + logger.warning("IMAP authentication failed for user %r: %s", imaplogin, e, exc_info=False) return "" connection.logout() return login diff --git a/radicale/config.py b/radicale/config.py index cf390a2d..9554e4ff 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -440,6 +440,10 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([ "value": "tls", "help": "Secure the IMAP connection: *tls*|starttls|none", "type": imap_security}), + ("imap_append_domain", { + "value": "", + "help": "Append '@' + domain to login before trying IMAP connection", + "type": str}), ("oauth2_token_endpoint", { "value": "", "help": "OAuth2 token endpoint URL",