@@ -58,6 +58,21 @@ def parse(str)
58
58
T_TEXT = :TEXT # any char except CRLF
59
59
T_EOF = :EOF # end of response string
60
60
61
+ module ResponseConditions
62
+ OK = "OK"
63
+ NO = "NO"
64
+ BAD = "BAD"
65
+ BYE = "BYE"
66
+ PREAUTH = "PREAUTH"
67
+
68
+ RESP_COND_STATES = [ OK , NO , BAD ] . freeze
69
+ RESP_DATA_CONDS = [ OK , NO , BAD , BYE , ] . freeze
70
+ AUTH_CONDS = [ OK , PREAUTH ] . freeze
71
+ GREETING_CONDS = [ OK , BYE , PREAUTH ] . freeze
72
+ RESP_CONDS = [ OK , NO , BAD , BYE , PREAUTH ] . freeze
73
+ end
74
+ include ResponseConditions
75
+
61
76
module Patterns
62
77
63
78
module CharClassSubtraction
@@ -320,10 +335,13 @@ def unescape_quoted(quoted)
320
335
321
336
ASTRING_TOKENS = [ T_QUOTED , *ASTRING_CHARS_TOKENS , T_LITERAL ] . freeze
322
337
323
- # atom = 1*ATOM-CHAR
324
- #
325
- # TODO: match atom entirely by regexp (in the "lexer")
326
- def atom ; -combine_adjacent ( *ATOM_TOKENS ) end
338
+ # tag = 1*<any ASTRING-CHAR except "+">
339
+ TAG_TOKENS = ( ASTRING_CHARS_TOKENS - [ T_PLUS ] ) . freeze
340
+
341
+ # TODO: handle atom, astring_chars, and tag entirely inside the lexer
342
+ def atom ; combine_adjacent ( *ATOM_TOKENS ) end
343
+ def astring_chars ; combine_adjacent ( *ASTRING_CHARS_TOKENS ) end
344
+ def tag ; combine_adjacent ( *TAG_TOKENS ) end
327
345
328
346
# the #accept version of #atom
329
347
def atom? ; -combine_adjacent ( *ATOM_TOKENS ) if lookahead? ( *ATOM_TOKENS ) end
@@ -336,11 +354,6 @@ def case_insensitive__atom?
336
354
-combine_adjacent ( *ATOM_TOKENS ) . upcase if lookahead? ( *ATOM_TOKENS )
337
355
end
338
356
339
- # TODO: handle astring_chars entirely inside the lexer
340
- def astring_chars
341
- combine_adjacent ( *ASTRING_CHARS_TOKENS )
342
- end
343
-
344
357
# astring = 1*ASTRING-CHAR / string
345
358
def astring
346
359
lookahead? ( *ASTRING_CHARS_TOKENS ) ? astring_chars : string
@@ -357,6 +370,17 @@ def label(word)
357
370
parse_error ( "unexpected atom %p, expected %p instead" , val , word )
358
371
end
359
372
373
+ # expects "OK" or "NO" or "BAD" and raises InvalidResponseError on failure
374
+ def resp_cond_state__name
375
+ if RESP_COND_STATES . include? ( actual = tagged_ext_label )
376
+ actual
377
+ else
378
+ raise InvalidResponseError , "bad response type %p, expected %s" % [
379
+ actual , RESP_COND_STATES . join ( " or " )
380
+ ]
381
+ end
382
+ end
383
+
360
384
# nstring = string / nil
361
385
def nstring
362
386
NIL? ? nil : string
@@ -452,13 +476,18 @@ def response_untagged
452
476
end
453
477
end
454
478
479
+ # RFC3501 & RFC9051:
480
+ # response-tagged = tag SP resp-cond-state CRLF
481
+ #
482
+ # resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
483
+ # ; Status condition
484
+ #
485
+ # tag = 1*<any ASTRING-CHAR except "+">
455
486
def response_tagged
456
- tag = astring_chars
457
- match ( T_SPACE )
458
- token = match ( T_ATOM )
459
- name = token . value . upcase
460
- match ( T_SPACE )
461
- return TaggedResponse . new ( tag , name , resp_text , @str )
487
+ tag = tag ( ) ; SP!
488
+ name = resp_cond_state__name ; SP!
489
+ data = resp_text
490
+ TaggedResponse . new ( tag , name , data , @str )
462
491
end
463
492
464
493
def response_cond
0 commit comments