From dcbbce54eb010fcd9bf10af68a5fd2b21fed060c Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 01/11] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Speed=20up=20`resp-t?= =?UTF-8?q?ext-code`=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert resp_text_code case statement to use strings. This leads to significant performance improvements, in my benchmarks. --- lib/net/imap/response_parser.rb | 58 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index 08a0e920..5b0b9ec3 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1473,44 +1473,48 @@ def resp_text end end - # See https://www.rfc-editor.org/errata/rfc3501 + # RFC3501 (See https://www.rfc-editor.org/errata/rfc3501): + # resp-text-code = "ALERT" / + # "BADCHARSET" [SP "(" charset *(SP charset) ")" ] / + # capability-data / "PARSE" / + # "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" / + # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + # "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / + # "UNSEEN" SP nz-number / + # atom [SP 1*] + # capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1" + # *(SP capability) # - # resp-text-code = "ALERT" / - # "BADCHARSET" [SP "(" charset *(SP charset) ")" ] / - # capability-data / "PARSE" / - # "PERMANENTFLAGS" SP "(" - # [flag-perm *(SP flag-perm)] ")" / - # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / - # "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / - # "UNSEEN" SP nz-number / - # atom [SP 1*] + # RFC4315 (UIDPLUS), RFC9051 (IMAP4rev2): + # resp-code-apnd = "APPENDUID" SP nz-number SP append-uid + # resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set + # resp-text-code =/ resp-code-apnd / resp-code-copy / "UIDNOTSTICKY" # - # +UIDPLUS+ ABNF:: https://www.rfc-editor.org/rfc/rfc4315.html#section-4 - # resp-text-code =/ resp-code-apnd / resp-code-copy / "UIDNOTSTICKY" + # RFC7162 (CONDSTORE): + # resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value / + # "NOMODSEQ" / + # "MODIFIED" SP sequence-set def resp_text_code - token = match(T_ATOM) - name = token.value.upcase + name = resp_text_code__name case name - when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n + when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", "NOMODSEQ" result = ResponseCode.new(name, nil) - when /\A(?:BADCHARSET)\z/n + when "BADCHARSET" result = ResponseCode.new(name, charset_list) - when /\A(?:CAPABILITY)\z/ni + when "CAPABILITY" result = ResponseCode.new(name, capability__list) - when /\A(?:PERMANENTFLAGS)\z/n - match(T_SPACE) + when "PERMANENTFLAGS" + SP! result = ResponseCode.new(name, flag_list) - when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n - match(T_SPACE) + when "UIDVALIDITY", "UIDNEXT", "UNSEEN" + SP! result = ResponseCode.new(name, number) - when /\A(?:APPENDUID)\z/n + when "APPENDUID" result = ResponseCode.new(name, resp_code_apnd__data) - when /\A(?:COPYUID)\z/n + when "COPYUID" result = ResponseCode.new(name, resp_code_copy__data) else - token = lookahead - if token.symbol == T_SPACE - shift_token + if SP? result = ResponseCode.new(name, text_chars_except_rbra) else result = ResponseCode.new(name, nil) @@ -1519,6 +1523,8 @@ def resp_text_code return result end + alias resp_text_code__name case_insensitive__atom + # 1* def text_chars_except_rbra match_re(CTEXT_REGEXP, '1*')[0] From c0e9c379c5da15bdd79f312892504b0a577218a9 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Fri, 27 Oct 2023 23:56:16 -0400 Subject: [PATCH 02/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20resp-text?= =?UTF-8?q?-code:=20remove=20redundant=20lvar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/response_parser.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index 5b0b9ec3..3489d16d 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1498,29 +1498,28 @@ def resp_text_code name = resp_text_code__name case name when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", "NOMODSEQ" - result = ResponseCode.new(name, nil) + ResponseCode.new(name, nil) when "BADCHARSET" - result = ResponseCode.new(name, charset_list) + ResponseCode.new(name, charset_list) when "CAPABILITY" - result = ResponseCode.new(name, capability__list) + ResponseCode.new(name, capability__list) when "PERMANENTFLAGS" SP! - result = ResponseCode.new(name, flag_list) + ResponseCode.new(name, flag_list) when "UIDVALIDITY", "UIDNEXT", "UNSEEN" SP! - result = ResponseCode.new(name, number) + ResponseCode.new(name, number) when "APPENDUID" - result = ResponseCode.new(name, resp_code_apnd__data) + ResponseCode.new(name, resp_code_apnd__data) when "COPYUID" - result = ResponseCode.new(name, resp_code_copy__data) + ResponseCode.new(name, resp_code_copy__data) else if SP? - result = ResponseCode.new(name, text_chars_except_rbra) + ResponseCode.new(name, text_chars_except_rbra) else - result = ResponseCode.new(name, nil) + ResponseCode.new(name, nil) end end - return result end alias resp_text_code__name case_insensitive__atom From 321a859019bfcae64847dae75cfc79f1d6525831 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sat, 28 Oct 2023 14:00:46 -0400 Subject: [PATCH 03/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20ResponseCode?= =?UTF-8?q?=20after=20resp-text-code=20case=20stmt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a simple refactoring to extract the common code --- lib/net/imap/response_parser.rb | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index 3489d16d..d3627ff6 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1498,28 +1498,25 @@ def resp_text_code name = resp_text_code__name case name when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", "NOMODSEQ" - ResponseCode.new(name, nil) + data = nil when "BADCHARSET" - ResponseCode.new(name, charset_list) + data = charset_list when "CAPABILITY" - ResponseCode.new(name, capability__list) + data = capability__list when "PERMANENTFLAGS" SP! - ResponseCode.new(name, flag_list) + data = flag_list when "UIDVALIDITY", "UIDNEXT", "UNSEEN" SP! - ResponseCode.new(name, number) + data = number when "APPENDUID" - ResponseCode.new(name, resp_code_apnd__data) + data = resp_code_apnd__data when "COPYUID" - ResponseCode.new(name, resp_code_copy__data) + data = resp_code_copy__data else - if SP? - ResponseCode.new(name, text_chars_except_rbra) - else - ResponseCode.new(name, nil) - end + data = SP? && text_chars_except_rbra end + ResponseCode.new(name, data) end alias resp_text_code__name case_insensitive__atom From e366c3ad3e623ad19396f7774321e1e45d6e7fa2 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Fri, 27 Oct 2023 23:56:16 -0400 Subject: [PATCH 04/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20and=20for?= =?UTF-8?q?mat=20`resp-text-code`=20case=20stmt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move assignment outside the case stmt and reformat to one-line cases. --- lib/net/imap/response_parser.rb | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index d3627ff6..ad33d76a 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1496,26 +1496,21 @@ def resp_text # "MODIFIED" SP sequence-set def resp_text_code name = resp_text_code__name - case name - when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", "NOMODSEQ" - data = nil - when "BADCHARSET" - data = charset_list - when "CAPABILITY" - data = capability__list - when "PERMANENTFLAGS" - SP! - data = flag_list - when "UIDVALIDITY", "UIDNEXT", "UNSEEN" - SP! - data = number - when "APPENDUID" - data = resp_code_apnd__data - when "COPYUID" - data = resp_code_copy__data - else - data = SP? && text_chars_except_rbra - end + data = + case name + when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" + when "NOMODSEQ" # CONDSTORE + when "BADCHARSET" then charset_list + when "CAPABILITY" then capability__list + when "PERMANENTFLAGS" then SP!; flag_list + when "UIDVALIDITY" then SP!; number + when "UIDNEXT" then SP!; number + when "UNSEEN" then SP!; number + when "APPENDUID" then resp_code_apnd__data + when "COPYUID" then resp_code_copy__data + else + SP? and text_chars_except_rbra + end ResponseCode.new(name, data) end From 509a71c7587cac174eb0127da488ba8c8c61dbb7 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 05/11] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Reorder=20`resp-text?= =?UTF-8?q?-code`=20case=20stmt=20for=20speed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the codes that represent error conditions or are uncommon to the end. Any "better" ordering requires more statistics on how common each case is executed in common scenarios. --- lib/net/imap/response_parser.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index ad33d76a..31023c00 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1498,9 +1498,6 @@ def resp_text_code name = resp_text_code__name data = case name - when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" - when "NOMODSEQ" # CONDSTORE - when "BADCHARSET" then charset_list when "CAPABILITY" then capability__list when "PERMANENTFLAGS" then SP!; flag_list when "UIDVALIDITY" then SP!; number @@ -1508,6 +1505,9 @@ def resp_text_code when "UNSEEN" then SP!; number when "APPENDUID" then resp_code_apnd__data when "COPYUID" then resp_code_copy__data + when "BADCHARSET" then charset_list + when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" + when "NOMODSEQ" # CONDSTORE else SP? and text_chars_except_rbra end From e2d3adc4593fc08db484fee124e82eab917a0909 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 06/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20`CAPABILI?= =?UTF-8?q?TY`=20response=20code=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/response_parser.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index 31023c00..e3db2007 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1346,11 +1346,13 @@ def enable_data end # As a workaround for buggy servers, allow a trailing SP: - # *(SP capapility) [SP] + # *(SP capability) [SP] def capability__list - data = []; while _ = SP? && capability? do data << _ end; data + list = []; while SP? && (capa = capability?) do list << capa end; list end + alias resp_code__capability capability__list + # capability = ("AUTH=" auth-type) / atom # ; New capabilities MUST begin with "X" or be # ; registered with IANA as standard or @@ -1498,7 +1500,7 @@ def resp_text_code name = resp_text_code__name data = case name - when "CAPABILITY" then capability__list + when "CAPABILITY" then resp_code__capability when "PERMANENTFLAGS" then SP!; flag_list when "UIDVALIDITY" then SP!; number when "UIDNEXT" then SP!; number From 8be322d22483cce7d45e973124bbfbaa9c8151c2 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 07/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20`PERMANEN?= =?UTF-8?q?TFLAGS`=20response=20code=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/response_parser.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index e3db2007..c1729d3b 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1501,7 +1501,7 @@ def resp_text_code data = case name when "CAPABILITY" then resp_code__capability - when "PERMANENTFLAGS" then SP!; flag_list + when "PERMANENTFLAGS" then SP? ? flag_perm__list : [] when "UIDVALIDITY" then SP!; number when "UIDNEXT" then SP!; number when "UNSEEN" then SP!; number @@ -1638,6 +1638,8 @@ def flag_list end end + # TODO: not quite correct. flag-perm != flag + alias flag_perm__list flag_list # See https://www.rfc-editor.org/errata/rfc3501 # From e9f4a33b63e1ffd7f16c1e691663b92015530eda Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 08/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20IMAP4rev1?= =?UTF-8?q?=20resp=20codes=20with=20nz-number=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This matches the ABNF better but it's effectively identical—we don't validate that numbers are within range. --- lib/net/imap/response_parser.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index c1729d3b..dbc44058 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1502,9 +1502,9 @@ def resp_text_code case name when "CAPABILITY" then resp_code__capability when "PERMANENTFLAGS" then SP? ? flag_perm__list : [] - when "UIDVALIDITY" then SP!; number - when "UIDNEXT" then SP!; number - when "UNSEEN" then SP!; number + when "UIDNEXT" then SP!; nz_number + when "UIDVALIDITY" then SP!; nz_number + when "UNSEEN" then SP!; nz_number # rev1 only when "APPENDUID" then resp_code_apnd__data when "COPYUID" then resp_code_copy__data when "BADCHARSET" then charset_list From 113d3f7d9bd8c2eb021545f233f591e4d19ed917 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sat, 28 Oct 2023 15:11:47 -0400 Subject: [PATCH 09/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20`UIDPLUS`?= =?UTF-8?q?=20resp=20code=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/response_parser.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index dbc44058..94362326 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1505,8 +1505,8 @@ def resp_text_code when "UIDNEXT" then SP!; nz_number when "UIDVALIDITY" then SP!; nz_number when "UNSEEN" then SP!; nz_number # rev1 only - when "APPENDUID" then resp_code_apnd__data - when "COPYUID" then resp_code_copy__data + when "APPENDUID" then SP!; resp_code_apnd__data # rev2, UIDPLUS + when "COPYUID" then SP!; resp_code_copy__data # rev2, UIDPLUS when "BADCHARSET" then charset_list when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" when "NOMODSEQ" # CONDSTORE @@ -1549,8 +1549,8 @@ def charset_list # match uid_set even if that returns a single-member array. # def resp_code_apnd__data - match(T_SPACE); validity = number - match(T_SPACE); dst_uids = uid_set # uniqueid ⊂ uid-set + validity = number; SP! + dst_uids = uid_set # uniqueid ⊂ uid-set UIDPlusData.new(validity, nil, dst_uids) end @@ -1558,9 +1558,9 @@ def resp_code_apnd__data # # resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set def resp_code_copy__data - match(T_SPACE); validity = number - match(T_SPACE); src_uids = uid_set - match(T_SPACE); dst_uids = uid_set + validity = number; SP! + src_uids = uid_set; SP! + dst_uids = uid_set UIDPlusData.new(validity, src_uids, dst_uids) end From 6a840b5dbd43d93227d6971e8a9293045e14d5c6 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 10/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20`BADCHARS?= =?UTF-8?q?ET`=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/response_parser.rb | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index 94362326..d2c6218d 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1507,7 +1507,7 @@ def resp_text_code when "UNSEEN" then SP!; nz_number # rev1 only when "APPENDUID" then SP!; resp_code_apnd__data # rev2, UIDPLUS when "COPYUID" then SP!; resp_code_copy__data # rev2, UIDPLUS - when "BADCHARSET" then charset_list + when "BADCHARSET" then SP? ? charset__list : [] when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" when "NOMODSEQ" # CONDSTORE else @@ -1523,17 +1523,9 @@ def text_chars_except_rbra match_re(CTEXT_REGEXP, '1*')[0] end - def charset_list - result = [] - if accept(T_SPACE) - match(T_LPAR) - result << charset - while accept(T_SPACE) - result << charset - end - match(T_RPAR) - end - result + # "(" charset *(SP charset) ")" + def charset__list + lpar; list = [charset]; while SP? do list << charset end; rpar; list end # already matched: "APPENDUID" @@ -1644,13 +1636,7 @@ def flag_list # See https://www.rfc-editor.org/errata/rfc3501 # # charset = atom / quoted - def charset - if token = accept(T_QUOTED) - token.value - else - atom - end - end + def charset; quoted? || atom end # RFC7162: # mod-sequence-value = 1*DIGIT From 18911248d08a661af663561c19d2a2ca2cb6daa8 Mon Sep 17 00:00:00 2001 From: nicholas evans Date: Sun, 22 Jan 2023 17:29:31 -0500 Subject: [PATCH 11/11] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Explicitly=20match?= =?UTF-8?q?=20RFC5530=20and=20RFC9051=20resp=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit None of these response codes have any code data, so it isn't really necessary to add explicit support. This could have a (very small) performance impact, by short-circuiting any further code data parsing for matching codes. --- lib/net/imap/response_parser.rb | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/net/imap/response_parser.rb b/lib/net/imap/response_parser.rb index d2c6218d..a9c267ed 100644 --- a/lib/net/imap/response_parser.rb +++ b/lib/net/imap/response_parser.rb @@ -1487,6 +1487,35 @@ def resp_text # capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1" # *(SP capability) # + # RFC5530: + # resp-text-code =/ "UNAVAILABLE" / "AUTHENTICATIONFAILED" / + # "AUTHORIZATIONFAILED" / "EXPIRED" / + # "PRIVACYREQUIRED" / "CONTACTADMIN" / "NOPERM" / + # "INUSE" / "EXPUNGEISSUED" / "CORRUPTION" / + # "SERVERBUG" / "CLIENTBUG" / "CANNOT" / + # "LIMIT" / "OVERQUOTA" / "ALREADYEXISTS" / + # "NONEXISTENT" + # RFC9051: + # resp-text-code = "ALERT" / + # "BADCHARSET" [SP "(" charset *(SP charset) ")" ] / + # capability-data / "PARSE" / + # "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" / + # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + # "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / + # resp-code-apnd / resp-code-copy / "UIDNOTSTICKY" / + # "UNAVAILABLE" / "AUTHENTICATIONFAILED" / + # "AUTHORIZATIONFAILED" / "EXPIRED" / + # "PRIVACYREQUIRED" / "CONTACTADMIN" / "NOPERM" / + # "INUSE" / "EXPUNGEISSUED" / "CORRUPTION" / + # "SERVERBUG" / "CLIENTBUG" / "CANNOT" / + # "LIMIT" / "OVERQUOTA" / "ALREADYEXISTS" / + # "NONEXISTENT" / "NOTSAVED" / "HASCHILDREN" / + # "CLOSED" / + # "UNKNOWN-CTE" / + # atom [SP 1*] + # capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev2" + # *(SP capability) + # # RFC4315 (UIDPLUS), RFC9051 (IMAP4rev2): # resp-code-apnd = "APPENDUID" SP nz-number SP append-uid # resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set @@ -1508,7 +1537,12 @@ def resp_text_code when "APPENDUID" then SP!; resp_code_apnd__data # rev2, UIDPLUS when "COPYUID" then SP!; resp_code_copy__data # rev2, UIDPLUS when "BADCHARSET" then SP? ? charset__list : [] - when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE" + when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", + "UNAVAILABLE", "AUTHENTICATIONFAILED", "AUTHORIZATIONFAILED", + "EXPIRED", "PRIVACYREQUIRED", "CONTACTADMIN", "NOPERM", "INUSE", + "EXPUNGEISSUED", "CORRUPTION", "SERVERBUG", "CLIENTBUG", "CANNOT", + "LIMIT", "OVERQUOTA", "ALREADYEXISTS", "NONEXISTENT", "CLOSED", + "NOTSAVED", "UIDNOTSTICKY", "UNKNOWN-CTE", "HASCHILDREN" when "NOMODSEQ" # CONDSTORE else SP? and text_chars_except_rbra