@@ -210,6 +210,14 @@ module RFC3629
210
210
TEXT_rev1 = /#{ TEXT_CHAR } +/
211
211
TEXT_rev2 = /#{ Regexp . union TEXT_CHAR , UTF8_2 , UTF8_3 , UTF8_4 } +/
212
212
213
+ # tagged-label-fchar = ALPHA / "-" / "_" / "."
214
+ TAGGED_LABEL_FCHAR = /[a-zA-Z\- _.]/n
215
+ # tagged-label-char = tagged-label-fchar / DIGIT / ":"
216
+ TAGGED_LABEL_CHAR = /[a-zA-Z\- _.0-9:]*/n
217
+ # tagged-ext-label = tagged-label-fchar *tagged-label-char
218
+ # ; Is a valid RFC 3501 "atom".
219
+ TAGGED_EXT_LABEL = /#{ TAGGED_LABEL_FCHAR } #{ TAGGED_LABEL_CHAR } */n
220
+
213
221
# RFC3501:
214
222
# literal = "{" number "}" CRLF *CHAR8
215
223
# ; Number represents the number of CHAR8s
@@ -407,6 +415,13 @@ def case_insensitive__nstring
407
415
alias number64 number
408
416
alias number64? number?
409
417
418
+ # valid number ranges are not enforced by parser
419
+ # nz-number = digit-nz *DIGIT
420
+ # ; Non-zero unsigned 32-bit integer
421
+ # ; (0 < n < 4,294,967,296)
422
+ alias nz_number number
423
+ alias nz_number? number?
424
+
410
425
# [RFC3501 & RFC9051:]
411
426
# response = *(continue-req / response-data) response-done
412
427
#
@@ -440,36 +455,64 @@ def continue_req
440
455
ContinuationRequest . new ( SP? ? resp_text : ResponseText ::EMPTY , @str )
441
456
end
442
457
458
+ RE_RESPONSE_TYPE = /\G (?:\d + )?(?<type>#{ Patterns ::TAGGED_EXT_LABEL } )/n
459
+
460
+ # [RFC3501:]
461
+ # response-data = "*" SP (resp-cond-state / resp-cond-bye /
462
+ # mailbox-data / message-data / capability-data) CRLF
463
+ # [RFC4466:]
464
+ # response-data = "*" SP response-payload CRLF
465
+ # response-payload = resp-cond-state / resp-cond-bye /
466
+ # mailbox-data / message-data / capability-data
467
+ # RFC5161 (ENABLE capability):
468
+ # response-data =/ "*" SP enable-data CRLF
469
+ # RFC5255 (LANGUAGE capability)
470
+ # response-payload =/ language-data
471
+ # RFC5255 (I18NLEVEL=1 and I18NLEVEL=2 capabilities)
472
+ # response-payload =/ comparator-data
473
+ # [RFC9051:]
474
+ # response-data = "*" SP (resp-cond-state / resp-cond-bye /
475
+ # mailbox-data / message-data / capability-data /
476
+ # enable-data) CRLF
477
+ #
478
+ # [merging in greeting and response-fatal:]
479
+ # greeting = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
480
+ # response-fatal = "*" SP resp-cond-bye CRLF
481
+ # response-data =/ "*" SP (resp-cond-auth / resp-cond-bye) CRLF
482
+ # [removing duplicates, this is simply]
483
+ # response-payload =/ resp-cond-auth
484
+ #
485
+ # TODO: remove resp-cond-auth and handle greeting separately
443
486
def response_data
444
487
STAR! ; SP!
445
- token = lookahead! ( T_NUMBER , T_ATOM )
446
- case token . symbol
447
- when T_NUMBER then numeric_response
448
- when T_ATOM
449
- case token . value . upcase
450
- when "OK" then response_cond # RFC3501, RFC9051
451
- when "BAD " then response_cond # RFC3501, RFC9051
452
- when "NO " then response_cond # RFC3501, RFC9051
453
- when "PREAUTH " then response_cond # RFC3501, RFC9051
454
- when "BYE " then response_cond # RFC3501, RFC9051
455
- when "FLAGS " then flags_response # RFC3501, RFC9051
456
- when "ID " then id_response # RFC2971
457
- when "LIST " then list_response # RFC3501 , RFC9051
458
- when "LSUB " then list_response # RFC3501 (obsolete)
459
- when "XLIST " then list_response # deprecated
460
- when "NAMESPACE " then namespace_response # RFC2342, RFC9051
461
- when "QUOTA " then getquota_response # RFC2087, RFC9208
462
- when "QUOTAROOT " then getquotaroot_response # RFC2087, RFC9208
463
- when "ACL " then getacl_response # RFC4314
464
- when "SEARCH " then search_response # RFC3501 (obsolete)
465
- when "SORT" then search_response # RFC5256, RFC7162
466
- when "THREAD " then thread_response # RFC5256
467
- when "STATUS " then status_response # RFC3501, RFC9051
468
- when "CAPABILITY" then capability_data__untagged # RFC3501, RFC9051
469
- when "ENABLED " then enable_data # RFC5161 , RFC9051
470
- when "NOOP " then ignored_response
471
- else unparsed_response
472
- end
488
+ m = peek_re ( RE_RESPONSE_TYPE ) or parse_error ( "unparsable response" )
489
+ case m [ "type" ] . upcase
490
+ when "OK" then response_cond # RFC3501, RFC9051
491
+ when "BAD" then response_cond # RFC3501, RFC9051
492
+ when "NO" then response_cond # RFC3501, RFC9051
493
+ when "PREAUTH" then response_cond # RFC3501, RFC9051
494
+ when "BYE " then response_cond # RFC3501, RFC9051
495
+ when "FLAGS " then flags_response # RFC3501, RFC9051
496
+ when "ID " then id_response # RFC2971
497
+ when "LIST " then list_response # RFC3501, RFC9051
498
+ when "LSUB " then list_response # RFC3501 (obsolete)
499
+ when "XLIST " then list_response # deprecated
500
+ when "NAMESPACE " then namespace_response # RFC2342 , RFC9051
501
+ when "QUOTA " then getquota_response # RFC2087, RFC9208
502
+ when "QUOTAROOT " then getquotaroot_response # RFC2087, RFC9208
503
+ when "ACL " then getacl_response # RFC4314
504
+ when "SEARCH " then search_response # RFC3501 (obsolete)
505
+ when "SORT " then search_response # RFC5256, RFC7162
506
+ when "THREAD " then thread_response # RFC5256
507
+ when "STATUS " then status_response # RFC3501, RFC9051
508
+ when "CAPABILITY" then capability_data__untagged # RFC3501, RFC9051
509
+ when "ENABLED " then enable_data # RFC5161, RFC9051
510
+ when "FETCH " then message_data__fetch # RFC3501, RFC9051
511
+ when "EXPUNGE" then message_data__expunge # RFC3501, RFC9051
512
+ when "EXISTS " then mailbox_data__exists # RFC3501 , RFC9051
513
+ when "RECENT " then mailbox_data__recent # RFC3501 (obsolete)
514
+ when "NOOP" then ignored_response
515
+ else unparsed_response
473
516
end
474
517
end
475
518
@@ -510,26 +553,24 @@ def response_cond
510
553
return UntaggedResponse . new ( name , resp_text , @str )
511
554
end
512
555
513
- def numeric_response
514
- n = number
515
- match ( T_SPACE )
516
- token = match ( T_ATOM )
517
- name = token . value . upcase
518
- case name
519
- when "EXISTS" , "RECENT" , "EXPUNGE"
520
- return UntaggedResponse . new ( name , n , @str )
521
- when "FETCH"
522
- shift_token
523
- match ( T_SPACE )
524
- data = FetchData . new ( n , msg_att ( n ) )
525
- return UntaggedResponse . new ( name , data , @str )
526
- else
527
- klass = name == "NOOP" ? IgnoredResponse : UntaggedResponse
528
- SP? ; txt = remaining_unparsed
529
- klass . new ( name , UnparsedData . new ( n , txt ) , @str )
530
- end
556
+ # message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
557
+ def message_data__fetch
558
+ seq = nz_number ; SP!
559
+ name = label "FETCH" ; SP!
560
+ data = FetchData . new ( seq , msg_att ( seq ) )
561
+ UntaggedResponse . new ( name , data , @str )
531
562
end
532
563
564
+ def response_data__simple_numeric
565
+ data = nz_number ; SP!
566
+ name = tagged_ext_label
567
+ UntaggedResponse . new ( name , data , @str )
568
+ end
569
+
570
+ alias message_data__expunge response_data__simple_numeric
571
+ alias mailbox_data__exists response_data__simple_numeric
572
+ alias mailbox_data__recent response_data__simple_numeric
573
+
533
574
def msg_att ( n )
534
575
match ( T_LPAR )
535
576
attr = { }
0 commit comments