Skip to content

Unmatched bracket in quoted flag causes "IndexError: string index out of range" #223

@torohill

Description

@torohill

This issue was caused by a Gmail label that included an unmatched (, but assuming the flags returned by Gmail are valid syntax then any unmatched ( in a quoted flag would trigger the bug.

I boiled the problem down to the following two test cases that trigger the error; one is the simplest case and one is based on the real world flags from Gmail. The real world test case comes from the actual flags returned by Gmail, which I extracted by adding some temporary logging code.

diff --git a/test/tests/test_00_imaputil.py b/test/tests/test_00_imaputil.py
index c5dbc5d..31748e9 100644
--- a/test/tests/test_00_imaputil.py
+++ b/test/tests/test_00_imaputil.py
@@ -64,6 +64,14 @@ class TestInternalFunctions(unittest.TestCase):
         res = imaputil.imapsplit('"mo\\" o" sdfsdf')
         self.assertEqual(res, ['"mo\\" o"', 'sdfsdf'])

+        # Simplest test case that triggers error
+        res = imaputil.imapsplit('("(")')
+        self.assertEqual(res, ['("(")'])
+
+        # Test case based on real world flags from Gmail
+        res = imaputil.imapsplit('X-GM-LABELS ("Foo (Bar") UID 100 FLAGS (\\Seen)')
+        self.assertEqual(res, ['X-GM-LABELS', '("Foo (Bar")', 'UID', '100', 'FLAGS', '(\\Seen)'])
+
     def test_02_flagsplit(self):
         """Test imaputil.flagsplit()"""
         res = imaputil.flagsplit('(\\Draft \\Deleted)') 

System/distribution:

Distributor ID: Ubuntu
Description:    Ubuntu 24.04.2 LTS
Release:        24.04
Codename:       noble

Offlineimap cloned from master, but offlineimap.py -V reports:

offlineimap v8.0.0, imaplib2 v3.05, Python v3.12.3, OpenSSL 3.0.13 30 Jan 2024

Config file:

[general]
accounts = example
metadata = ./metadata

[Account example]
localrepository = example-local
remoterepository = example-remote
synclabels = yes
# This header is where labels go.  Usually you will be fine
# with default value (X-Keywords), but in case you want it
# different, here we go:
labelsheader = X-Keywords

[Repository example-remote]
#This is the remote repository
type = Gmail
remotepass = password
remoteuser = [email protected]
readonly = True
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
# see known issues at https://www.offlineimap.org/doc/offlineimap.html
singlethreadperfolder = yes

[Repository example-local]
#This is the 'local' repository
type = GmailMaildir
localfolders = ./mail/

And here's the trace for the original error:

 ERROR: ERROR in syncfolder for example folder [Gmail]/All Mail: Traceback (most recent call last):                                                                                                                                                                                                                          
  File "/usr/share/offlineimap3/offlineimap/accounts.py", line 656, in syncfolder                                                                                                                                                                                                                                             
    remotefolder.cachemessagelist()                                                                                                                                                                                                                                                                                           
  File "/usr/share/offlineimap3/offlineimap/folder/Gmail.py", line 157, in cachemessagelist                                                                                                                                                                                                                                   
    options = imaputil.flags2hash(messagestr)                                                                                                                                                                                                                                                                                 
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                                                                                                 
  File "/usr/share/offlineimap3/offlineimap/imaputil.py", line 98, in flags2hash                                                                                                                                                                                                                                              
    return __options2hash(flagsplit(flags))                                                                                                                                                                                                                                                                                   
                          ^^^^^^^^^^^^^^^^                                                                                                                                                                                                                                                                                    
  File "/usr/share/offlineimap3/offlineimap/imaputil.py", line 74, in flagsplit                                                                                                                                                                                                                                               
    return imapsplit(s[1:-1])                                                                                                                                                                                                                                                                                                 
           ^^^^^^^^^^^^^^^^^^                                                                                                                                                                                                                                                                                                 
  File "/usr/share/offlineimap3/offlineimap/imaputil.py", line 122, in imapsplit                                                                                                                                                                                                                                              
    if workstr[rpareni] == ')':  # end of a group                                                                                                                                                                                                                                                                             
       ~~~~~~~^^^^^^^^^                                                                                                                                                                                                                                                                                                       
IndexError: string index out of range                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                                              
  string index out of range

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions