239 Commits

Author SHA1 Message Date
Rodolfo García Peñas (kix) 1f04bbd8bf folder/IMAP, repository/IMAP: make encoding conditional on utf_8_support
When utf_8_support is False (the default, standard RFC 3501 mode),
folder names received from the server are in Modified UTF-7 and must
be kept in that encoding internally.  When utf_8_support is True,
names are decoded to UTF-8 for internal use.

Previous code decoded unconditionally in IMAPFolder.__init__, which
would corrupt non-ASCII names received as Modified UTF-7 when
utf_8_support is False.  The inverse problem existed in
getfullIMAPname() and the three encode_mailbox_name() call sites in
IMAPRepository: they always converted UTF-8 → Modified UTF-7 before
sending to the server, which is wrong when utf_8_support is False
(names are already in Modified UTF-7 and must not be double-encoded).

Fix by applying the same conditional pattern consistently:

  if account.utf_8_support:
      name = imaputil.utf8_IMAP(name)   # UTF-8 → Modified UTF-7
  return imaputil.foldername_to_imapname(name)

This is applied in:
  - IMAPFolder.__init__       (decode on receive)
  - IMAPFolder.getfullIMAPname (encode before SELECT)
  - IMAPRepository.getfolders  (folderincludes SELECT)
  - IMAPRepository.deletefolder
  - IMAPRepository.makefolder_single

encode_mailbox_name() (which always assumed UTF-8 input) is removed
as it is no longer used anywhere.

Based on patch by Etienne Buira <etienne.buira@free.fr>
2026-05-17 11:22:35 +02:00
Rodolfo García Peñas (kix) 61291de09d Merge branch 'pr-247' into testing 2026-05-16 10:32:57 +02:00
Andreas Schacker a81e267ce5 Fix typos 2026-05-15 22:27:23 +02:00
Rodolfo García Peñas (kix) 15cadd7545 Refactor IMAP folder handling to use encode_mailbox_name for folder name encoding
This commit refactors the IMAP folder handling in the offlineimap repository to use the `encode_mailbox_name` function for encoding folder names. This change ensures that folder names are properly encoded when interacting with the IMAP server, improving compatibility and reliability when dealing with folder names that may contain special characters or non-ASCII characters. The `encode_mailbox_name` function combines UTF-8 encoding and quoting as needed, providing a consistent way to handle folder names across the codebase.
2026-05-15 09:51:11 +02:00
Rodolfo García Peñas (kix) 0185b11255 Fix UnicodeEncodeError on emails with malformed bytes
When fetching emails with defects or malformed bytes (e.g., spam or broken
MIME boundaries), the Python 3 `email.parser` handles un-decodable bytes
by safely substituting them with the Unicode Replacement Character `\ufffd`
(using the `errors='replace'` handler by default).

However, a problem arises during the `_fetch_from_imap` sync process when
OfflineIMAP3 tests if the message can be serialized back to bytes via
`as_bytes()`. If the malformed text part was originally declared as
`us-ascii` (or left unspecified, defaulting to ASCII), Python attempts to
encode the `\ufffd` string back to bytes using the ASCII codec. Since `\ufffd`
is out of the ASCII range, this triggers a `UnicodeEncodeError`, causing
OfflineIMAP3 to raise an `OfflineImapError` and entirely skip syncing the
message.

This commit fixes the issue by catching the `UnicodeEncodeError` when
`as_bytes()` fails. It then walks through the message parts, identifies
the text payloads that cannot be encoded with their current charset, and
dynamically forces their charset to `utf-8`. This allows Python to safely
encode the `\ufffd` character (automatically applying base64/quoted-printable
transfer encoding if needed), successfully serializing the message so it
can be synced without crashing.

Closes #240
Closes #229
Closes #224
Closes #160
2026-04-18 23:10:29 +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) 47f74c4408 Handle bare exceptions thrown while getting message ID
This patch removes the bare exception, as proposed by Florian Snow.
See PR170.

Closes #119
2024-08-24 15:47:58 +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 489ff3bdb1 fix regexes containing invalid escape sequences
Signed-off-by: lilydjwg <lilydjwg@gmail.com>
2024-04-28 17:07:51 +08:00
Robo Shimmer c252df5590 ignore broken message-id - catch the exception 2024-01-15 22:42:18 +01:00
alessio-pascolini 210c23bdee Update IMAP.py
Return before decoding items of imapdata, None cannot be decoded.
2022-03-08 10:22:11 +01:00
Joseph Ishac 9e0fb59bdf Adding missing import of NoBoundaryInMultipartDefect 2021-06-07 22:18:31 -04:00
Joseph Ishac 5b976fc5da Making error handling when parsing messages consistent 2021-06-07 21:57:54 -04:00
Joseph Ishac b4f100c92c Merge branch to add extra debugging to imap parsing into encoding_edge_cases branch 2021-06-07 14:07:43 -04:00
Joseph Ishac b0aad911ab Adding some extra error handling when parsing from imaplib 2021-06-07 14:05:07 -04:00
Joseph Ishac 6a45eef3b5 Fixed a minor bug discovered if server is unreachable when debugging 2021-04-12 22:58:58 -04:00
Joseph Ishac 259bf83607 Fixing up the rest of the parsing and IMAP functions, and GMAIL classes as well. Also adding is_debugging() to the UI to allow us to quickly determine if we should build some of the expensive debug objects 2021-02-23 16:17:54 -05:00
Joseph Ishac 3166723baa Removing obsolete emailutil.py. The date functionality was pulled into folder/Base.py which now handles messages as binary objects 2021-02-21 23:20:39 -05:00
Joseph Ishac 49b6c30ace Cleaning up some more use of the old message string "content" to use "msg" email object instead. 2021-02-19 17:00:15 -05:00
Joseph Ishac 1d2478bcb6 Series of *UNTESTED* changes that should move the internal structure of
a message from a string to an email object that is part of the built-in
email library.  The allows for emails to be processed as bytes and
re-encoded properly if they are not UTF-8 or ascii encoded.  Currently
these changes cover the Base, IMAP, and Maildir classes but not the
specialized GMAIL class yet.
2021-02-09 14:58:30 -05:00
Rodolfo García Peñas (kix) 0fe1caa6a5 Avoid crash in search if no results
I am incluiding this check to avoid crash if the array is empty
and we are trying to read the first element.
2020-11-08 22:07:14 +01:00
Rodolfo García Peñas (kix) a311faf183 IMAP search now works fine
This patch converts the search results from bytes to strings

I add a bit comment about it here:

    In Py2, with IMAP, imaplib2 returned a list of one element string.
      ['1, 2, 3, ...'] -> in Py3 is [b'1 2 3,...']
    In Py2, with Davmail, imaplib2 returned a list of strings.
      ['1', '2', '3', ...] -> in Py3 should be [b'1', b'2', b'3',...]

    In my tests with Py3, I get a list with one element: [b'1 2 3 ...']
    Then I convert the values to string and I get ['1 2 3 ...']

    With Davmail, it should be [b'1', b'2', b'3',...]
    When I convert the values to string, I get ['1', '2', '3',...]
2020-11-08 15:47:51 +01:00
Rodolfo García Peñas (kix) 5ccb89a412 BUG: Read response as string from APPENDUID
We need read the response from APPENUID and convert it to string.
This patch do it.
2020-11-07 16:48:09 +01:00
Rodolfo García Peñas (kix) 3f86218e55 IMAP.py split long lines
This patch split long lines (>=80 chars)
2020-11-07 15:25:27 +01:00
Rodolfo García Peñas (kix) 442c88d838 imaplib expect bytes in the append
imaplib2 is doing this code for strings:

        if isinstance(message, str):
            message = bytes(message, 'ASCII')

But our message is already encoded using 'utf-8'.
Then, we can set the message as bytes, encoded using 'utf-8'
in offlineimap and imaplib2 won't change our message.

This patch solves this problem:

WARNING:OfflineImap:
Traceback:
  File "/home/kix/src/offlineimap3/offlineimap/folder/Base.py", line 1127, in syncmessagesto
    action(dstfolder, statusfolder)
  File "/home/kix/src/offlineimap3/offlineimap/folder/Base.py", line 955, in __syncmessagesto_copy
    self.copymessageto(uid, dstfolder, statusfolder, register=0)
  File "/home/kix/src/offlineimap3/offlineimap/folder/Base.py", line 855, in copymessageto
    new_uid = dstfolder.savemessage(uid, message, flags, rtime)
  File "/home/kix/src/offlineimap3/offlineimap/folder/IMAP.py", line 668, in savemessage
    (typ, dat) = imapobj.append(self.getfullIMAPname(),
  File "/usr/lib/python3/dist-packages/imaplib2.py", line 660, in append
    message = bytes(message, 'ASCII')
2020-10-25 20:36:07 +01:00
Thomas De Schampheleire 33e0efa163 IMAP: replace non-UTF-8 characters rather than aborting
Emails received may not be UTF-8. Following error was observed on a specific
mail:

Traceback (most recent call last):
  File "/home/tdescham/repo/offlineimap3/offlineimap/threadutil.py", line 146, in run
    Thread.run(self)
  File "/usr/lib/python3.7/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/home/tdescham/repo/offlineimap3/offlineimap/folder/Base.py", line 850, in copymessageto
    message = self.getmessage(uid)
  File "/home/tdescham/repo/offlineimap3/offlineimap/folder/IMAP.py", line 327, in getmessage
    data = self._fetch_from_imap(str(uid), self.retrycount)
  File "/home/tdescham/repo/offlineimap3/offlineimap/folder/IMAP.py", line 844, in _fetch_from_imap
    ndata1 = data[0][1].decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0 in position 10177: invalid start byte

This completely aborted offlineimap3, blocking further mail reception.

Instead, use the 'replace' error strategy in Python:

    Replace with a suitable replacement character; Python will use the
    official U+FFFD REPLACEMENT CHARACTER for the built-in Unicode codecs on
    decoding and ‘?’ on encoding.
    https://docs.python.org/2/library/codecs.html#codec-base-classes
2020-10-21 16:29:13 +02:00
Thomas De Schampheleire 820e5c855f IMAP: Python 3 bytes fix on first download of account
ERROR: ERROR in syncfolder for gmail folder INBOX: Traceback (most recent call last):
  File ".../offlineimap3/offlineimap/accounts.py", line 634, in syncfolder
    cachemessagelists_upto_date(maxage)
  File ".../offlineimap3/offlineimap/accounts.py", line 526, in cachemessagelists_upto_date
    min_date=time.gmtime(time.mktime(date) + 24 * 60 * 60))
  File ".../offlineimap3/offlineimap/folder/IMAP.py", line 277, in cachemessagelist
    imapobj, min_date=min_date, min_uid=min_uid)
  File ".../offlineimap3/offlineimap/folder/IMAP.py", line 259, in _msgs_to_fetch
    search_result = search(search_cond)
  File ".../offlineimap3/offlineimap/folder/IMAP.py", line 222, in search
    if ' ' in res_data[0] or res_data[0] == '':
TypeError: a bytes-like object is required, not 'str'
2020-10-21 14:28:23 +02:00
Rodolfo García Peñas (kix) 49c85d732d Using isinstance instead type
This patch uses isinstance, like Thomas pointed in their last commit.
2020-10-12 12:52:04 +02:00
Thomas De Schampheleire 423785725b IMAP.py: server responses are in bytes, not string
Following error is seen when parsing server responses for sent mail:

2020-10-12 08:19:11 WARNING: Can't parse FETCH response, we awaited string: b' UID 26855)'
2020-10-12 08:19:11 WARNING: savemessage: Searching mails for new Message-ID failed. Could not determine new UID on Sent.

The comparison with 'type("")' means comparing with 'string' type in Python
3, but the left-hand side is a bytes object.

In case a tuple was received (first case in the code), the input is already
decoded from bytes to strings, but in case a single input was received it
was not.

Note that the comparison with 'type("")' is a bit odd, a more logical way
seems to be:
    if isinstance(item, bytes)

Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
2020-10-12 09:01:51 +02:00
Rodolfo García Peñas (kix) 01c621d86c Allow folder names with atom specials
This patch allows using folders with atom-specials like
"(", ")", spaces,...

We need quotes the folder name if it includes this special
characters.

Closes #4
2020-10-11 23:01:08 +02:00
Rodolfo García Peñas (kix) cefac73af4 six: changed offlineimap/folder/IMAP.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:01 +02:00
Rodolfo García Peñas (kix) df4b9174d7 IMAP.py __savemessage_fetchheaders decode bytes
This patch changes the function __savemessage_fetchheaders to decode the
bytes retunred by imaplib2.

We need a list of headers, with string values, but imapli2 is providing
a list with bytes. This change convert the values to str.

Signed-off-by: Rodolfo García Peñas (kix) <kix@kix.es>
2020-09-03 21:35:52 +02:00
Rodolfo García Peñas (kix) 8e63f58b22 folder/IMAP.py matching uids is a list
matchinguids variable is a list of UIDs, separated by spaces. You can
check it some lines later, using the split command.

We need decode the bytes value returned by imaplib2 and convert it to
sting.

Signed-off-by: Rodolfo García Peñas (kix) <kix@kix.es>
2020-09-03 21:35:48 +02:00
Rodolfo García Peñas (kix) 41c2ced1d5 IMAP.py _msgs_to_fetch decode bytes
imaplib2 returns the type as string, like "OK" but
returns imapdata as list of bytes, like [b'0'] so we need decode it
to use the existing code
2020-09-01 19:07:52 +02:00
Rodolfo García Peñas (kix) 1ff54e7a7c IMAP.py Get the server response right
Now, the server response is in a list of strings. We need the second
string, so we need read the [1].

Previously, was a list of tuples, so, we used [0][1].
2020-09-01 18:21:27 +02:00
Rodolfo García Peñas (kix) ea8c0c6f3e __generate_randomheader uses now string
This patch converts the string to bytes to use crc32.

We can remore the fffffff because in python3 is always positive value.
In other patch.
2020-09-01 17:40:12 +02:00
Rodolfo García Peñas (kix) 7980f7ff1a IMAP.py calls Internaldate2epoch with bytes argument
The function Internaldate2epoch needs a bytes argument,
not an string, we need encode it:

imaplibutil.Internaldate2epoch(messagestr.encode('utf-8'))
2020-09-01 17:40:12 +02:00
Rodolfo García Peñas (kix) d011702b5b removed virtual_imaplib2
Now we use the system imaplib2. I am using Debian.
2020-08-31 16:24:26 +02:00
Rodolfo García Peñas (kix) dc8872377c folder/IMAP.py Removed unused variables
These variables are not used.
2020-08-30 13:30:39 +02:00
Rodolfo García Peñas (kix) c3fe45ff2b folder/IMAP.py added docstrings arguments 2020-08-30 13:28:36 +02:00
Rodolfo García Peñas (kix) 88230bda47 folder/IMAP.py removed extra chars
This patch removes some backslash and (, )
2020-08-30 13:26:20 +02:00
Rodolfo García Peñas (kix) 7b082f0fe9 offlineimap/folder files singleton-comparison
This patch change these errors in the 'folder' folder

C0121: Comparison to None should be 'expr is None' (singleton-comparison)
C0121: Comparison to None should be 'expr is not None' (singleton-comparison)
2020-08-30 11:15:00 +02:00
Rodolfo García Peñas (kix) 7d62441dc2 Changed import order
These changes close the lintian warning C0411.
2020-08-29 21:10:16 +02:00
Rodolfo García Peñas (kix) da1788db53 Reformat offlineimap/folder/IMAP.py
Add some spaces, remove lines,... now format is better (lintian).
2020-08-29 19:43:09 +02:00
Rodolfo García Peñas (kix) 18d2e972ac Mail now is typle of bytes convert to str
This patch saves the tuple and discard the bytes/str (now bytes).

Then, convert the tuple elements to str (from bytes).
2020-08-29 19:00:28 +02:00
Rodolfo García Peñas (kix) e61e77cf40 Convert bytes to string in IMAP folder
Convert the IMAP reply to string.
2020-08-28 17:25:06 +02:00
Rodolfo García Peñas (kix) 1379c32c5a IMAP do not use single quotes on fetch
The IMAP command is:

C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])

not

C: A654 FETCH '2:4' (FLAGS BODY[HEADER.FIELDS (DATE FROM)])

The single quotes must be removed.
2020-08-28 17:18:29 +02:00
Nicolas Sebrecht e802f5fbd5 folder: IMAP: improve search logging
Log when exception occured during search command, too.

Github-ref: https://github.com/OfflineIMAP/offlineimap/issues/512
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2017-12-22 15:13:51 +01:00
Nicolas Sebrecht 9805d3e7af no UIDPLUS: improve logging on failures
When there is not UIDPLUS we have to figure the UID by our means. When this
process fails, we don't know if the email was successfully uploaded. This patch
provides better logs to explain what happened.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2017-11-12 22:40:06 +01:00
Urs Liska ef3299b7ce Remove some unnecessary whitespace (in existing code)
Addresses https://github.com/OfflineIMAP/offlineimap/pull/498#discussion_r141672756

Signed-off-by: Urs Liska <git@ursliska.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2017-10-02 21:09:43 +02:00