This commit is contained in:
Shamil 2025-12-08 06:12:10 +02:00 committed by GitHub
commit 90418f1106
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 13 deletions

View file

@ -166,14 +166,15 @@ class IMAP4:
Errors raise the exception class <instance>.error("<reason>"). Errors raise the exception class <instance>.error("<reason>").
IMAP4 server errors raise <instance>.abort("<reason>"), IMAP4 server errors raise <instance>.abort("<reason>"),
which is a sub-class of 'error'. Mailbox status changes which is a sub-class of 'error'.
from READ-WRITE to READ-ONLY raise the exception class
<instance>.readonly("<reason>"), which is a sub-class of 'abort'. When the server returns a READ-ONLY response code, the is_readonly
attribute is set to True. Applications should check this attribute
to determine mailbox access level.
"error" exceptions imply a program error. "error" exceptions imply a program error.
"abort" exceptions imply the connection should be reset, and "abort" exceptions imply the connection should be reset, and
the command re-tried. the command re-tried.
"readonly" exceptions imply the command should be re-tried.
Note: to use this module, you must read the RFCs pertaining to the Note: to use this module, you must read the RFCs pertaining to the
IMAP4 protocol, as the semantics of the arguments to each IMAP4 IMAP4 protocol, as the semantics of the arguments to each IMAP4
@ -860,12 +861,8 @@ def select(self, mailbox='INBOX', readonly=False):
self.state = 'AUTH' # Might have been 'SELECTED' self.state = 'AUTH' # Might have been 'SELECTED'
return typ, dat return typ, dat
self.state = 'SELECTED' self.state = 'SELECTED'
if 'READ-ONLY' in self.untagged_responses \ if 'READ-ONLY' in self.untagged_responses:
and not readonly: self.is_readonly = True
if __debug__:
if self.debug >= 1:
self._dump_ur(self.untagged_responses)
raise self.readonly('%s is not writable' % mailbox)
return typ, self.untagged_responses.get('EXISTS', [None]) return typ, self.untagged_responses.get('EXISTS', [None])
@ -1094,9 +1091,8 @@ def _command(self, name, *args):
if typ in self.untagged_responses: if typ in self.untagged_responses:
del self.untagged_responses[typ] del self.untagged_responses[typ]
if 'READ-ONLY' in self.untagged_responses \ if 'READ-ONLY' in self.untagged_responses:
and not self.is_readonly: self.is_readonly = True
raise self.readonly('mailbox status changed to READ-ONLY')
tag = self._new_tag() tag = self._new_tag()
name = bytes(name, self._encoding) name = bytes(name, self._encoding)

View file

@ -657,6 +657,21 @@ def test_unselect(self):
self.assertEqual(data[0], b'Returned to authenticated state. (Success)') self.assertEqual(data[0], b'Returned to authenticated state. (Success)')
self.assertEqual(client.state, 'AUTH') self.assertEqual(client.state, 'AUTH')
def test_select_readonly_mailbox(self):
class ReadOnlyHandler(SimpleIMAPHandler):
def cmd_SELECT(self, tag, args):
self.server.is_selected = True
self._send_line(b'* 1 EXISTS')
self._send_line(b'* OK [READ-ONLY] Mailbox is read-only')
self._send_tagged(tag, 'OK', '[READ-ONLY] SELECT completed')
client, _ = self._setup(ReadOnlyHandler)
client.login('user', 'pass')
self.assertFalse(client.is_readonly)
typ, data = client.select('INBOX')
self.assertEqual(typ, 'OK')
self.assertTrue(client.is_readonly)
self.assertEqual(client.state, 'SELECTED')
# property tests # property tests
def test_file_property_should_not_be_accessed(self): def test_file_property_should_not_be_accessed(self):

View file

@ -0,0 +1,3 @@
Fix :mod:`imaplib` to handle mailboxes with ACL rights like ``lrs`` correctly
by setting ``is_readonly`` flag instead of raising exception. Patched by Shamil
Abdulaev.