net-imap 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/docs/styles.css +0 -12
- data/lib/net/imap/data_encoding.rb +14 -2
- data/lib/net/imap/fetch_data.rb +518 -0
- data/lib/net/imap/response_data.rb +26 -207
- data/lib/net/imap/response_parser/parser_utils.rb +1 -1
- data/lib/net/imap/response_parser.rb +477 -243
- data/lib/net/imap/sequence_set.rb +67 -0
- data/lib/net/imap.rb +82 -17
- data/rakelib/benchmarks.rake +4 -11
- metadata +5 -3
@@ -54,6 +54,7 @@ module Net
|
|
54
54
|
T_STAR = :STAR # atom special; list wildcard
|
55
55
|
T_PERCENT = :PERCENT # atom special; list wildcard
|
56
56
|
T_LITERAL = :LITERAL # starts with atom special
|
57
|
+
T_LITERAL8 = :LITERAL8 # starts with atom char "~"
|
57
58
|
T_CRLF = :CRLF # atom special; text special; quoted special
|
58
59
|
T_TEXT = :TEXT # any char except CRLF
|
59
60
|
T_EOF = :EOF # end of response string
|
@@ -197,6 +198,7 @@ module Net
|
|
197
198
|
# ; revisions of this specification.
|
198
199
|
# flag-keyword = "$MDNSent" / "$Forwarded" / "$Junk" /
|
199
200
|
# "$NotJunk" / "$Phishing" / atom
|
201
|
+
#
|
200
202
|
# flag-perm = flag / "\*"
|
201
203
|
#
|
202
204
|
# Not checking for max one mbx-list-sflag in the parser.
|
@@ -219,19 +221,15 @@ module Net
|
|
219
221
|
MBX_FLAG = FLAG_EXTENSION
|
220
222
|
|
221
223
|
# flag-list = "(" [flag *(SP flag)] ")"
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
|
230
|
-
#
|
231
|
-
# mbx-list-oflag *(SP mbx-list-oflag)
|
232
|
-
FLAG_LIST = /\G\((#{FLAG }(?:#{SP}#{FLAG })*|)\)/ni
|
233
|
-
FLAG_PERM_LIST = /\G\((#{FLAG_PERM}(?:#{SP}#{FLAG_PERM})*|)\)/ni
|
234
|
-
MBX_LIST_FLAGS = /\G\((#{MBX_FLAG }(?:#{SP}#{MBX_FLAG })*|)\)/ni
|
224
|
+
# resp-text-code =/ "PERMANENTFLAGS" SP
|
225
|
+
# "(" [flag-perm *(SP flag-perm)] ")"
|
226
|
+
# mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag
|
227
|
+
# *(SP mbx-list-oflag) /
|
228
|
+
# mbx-list-oflag *(SP mbx-list-oflag)
|
229
|
+
# (Not checking for max one mbx-list-sflag in the parser.)
|
230
|
+
FLAG_LIST = /\G\((#{FLAG }(?:#{SP}#{FLAG })*|)\)/ni
|
231
|
+
FLAG_PERM_LIST = /\G\((#{FLAG_PERM}(?:#{SP}#{FLAG_PERM})*|)\)/ni
|
232
|
+
MBX_LIST_FLAGS = /\G (#{MBX_FLAG }(?:#{SP}#{MBX_FLAG })*) /nix
|
235
233
|
|
236
234
|
# RFC3501:
|
237
235
|
# QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> /
|
@@ -266,6 +264,56 @@ module Net
|
|
266
264
|
# ; Is a valid RFC 3501 "atom".
|
267
265
|
TAGGED_EXT_LABEL = /#{TAGGED_LABEL_FCHAR}#{TAGGED_LABEL_CHAR}*/n
|
268
266
|
|
267
|
+
# nz-number = digit-nz *DIGIT
|
268
|
+
# ; Non-zero unsigned 32-bit integer
|
269
|
+
# ; (0 < n < 4,294,967,296)
|
270
|
+
NZ_NUMBER = /[1-9]\d*/n
|
271
|
+
|
272
|
+
# seq-number = nz-number / "*"
|
273
|
+
# ; message sequence number (COPY, FETCH, STORE
|
274
|
+
# ; commands) or unique identifier (UID COPY,
|
275
|
+
# ; UID FETCH, UID STORE commands).
|
276
|
+
# ; * represents the largest number in use. In
|
277
|
+
# ; the case of message sequence numbers, it is
|
278
|
+
# ; the number of messages in a non-empty mailbox.
|
279
|
+
# ; In the case of unique identifiers, it is the
|
280
|
+
# ; unique identifier of the last message in the
|
281
|
+
# ; mailbox or, if the mailbox is empty, the
|
282
|
+
# ; mailbox's current UIDNEXT value.
|
283
|
+
# ; The server should respond with a tagged BAD
|
284
|
+
# ; response to a command that uses a message
|
285
|
+
# ; sequence number greater than the number of
|
286
|
+
# ; messages in the selected mailbox. This
|
287
|
+
# ; includes "*" if the selected mailbox is empty.
|
288
|
+
SEQ_NUMBER = /#{NZ_NUMBER}|\*/n
|
289
|
+
|
290
|
+
# seq-range = seq-number ":" seq-number
|
291
|
+
# ; two seq-number values and all values between
|
292
|
+
# ; these two regardless of order.
|
293
|
+
# ; Example: 2:4 and 4:2 are equivalent and
|
294
|
+
# ; indicate values 2, 3, and 4.
|
295
|
+
# ; Example: a unique identifier sequence range of
|
296
|
+
# ; 3291:* includes the UID of the last message in
|
297
|
+
# ; the mailbox, even if that value is less than
|
298
|
+
# ; 3291.
|
299
|
+
SEQ_RANGE = /#{SEQ_NUMBER}:#{SEQ_NUMBER}/n
|
300
|
+
|
301
|
+
# sequence-set = (seq-number / seq-range) ["," sequence-set]
|
302
|
+
# ; set of seq-number values, regardless of order.
|
303
|
+
# ; Servers MAY coalesce overlaps and/or execute
|
304
|
+
# ; the sequence in any order.
|
305
|
+
# ; Example: a message sequence number set of
|
306
|
+
# ; 2,4:7,9,12:* for a mailbox with 15 messages is
|
307
|
+
# ; equivalent to 2,4,5,6,7,9,12,13,14,15
|
308
|
+
# ; Example: a message sequence number set of
|
309
|
+
# ; *:4,5:7 for a mailbox with 10 messages is
|
310
|
+
# ; equivalent to 10,9,8,7,6,5,4,5,6,7 and MAY
|
311
|
+
# ; be reordered and overlap coalesced to be
|
312
|
+
# ; 4,5,6,7,8,9,10.
|
313
|
+
SEQUENCE_SET_ITEM = /#{SEQ_NUMBER}|#{SEQ_RANGE}/n
|
314
|
+
SEQUENCE_SET = /#{SEQUENCE_SET_ITEM}(?:,#{SEQUENCE_SET_ITEM})*/n
|
315
|
+
SEQUENCE_SET_STR = /\A#{SEQUENCE_SET}\z/n
|
316
|
+
|
269
317
|
# RFC3501:
|
270
318
|
# literal = "{" number "}" CRLF *CHAR8
|
271
319
|
# ; Number represents the number of CHAR8s
|
@@ -279,6 +327,16 @@ module Net
|
|
279
327
|
# ; sent from server to the client.
|
280
328
|
LITERAL = /\{(\d+)\}\r\n/n
|
281
329
|
|
330
|
+
# RFC3516 (BINARY):
|
331
|
+
# literal8 = "~{" number "}" CRLF *OCTET
|
332
|
+
# ; <number> represents the number of OCTETs
|
333
|
+
# ; in the response string.
|
334
|
+
# RFC9051:
|
335
|
+
# literal8 = "~{" number64 "}" CRLF *OCTET
|
336
|
+
# ; <number64> represents the number of OCTETs
|
337
|
+
# ; in the response string.
|
338
|
+
LITERAL8 = /~\{(\d+)\}\r\n/n
|
339
|
+
|
282
340
|
module_function
|
283
341
|
|
284
342
|
def unescape_quoted!(quoted)
|
@@ -298,27 +356,28 @@ module Net
|
|
298
356
|
# the default, used in most places
|
299
357
|
BEG_REGEXP = /\G(?:\
|
300
358
|
(?# 1: SPACE )( )|\
|
301
|
-
(?# 2:
|
359
|
+
(?# 2: LITERAL8)#{Patterns::LITERAL8}|\
|
360
|
+
(?# 3: ATOM prefixed with a compatible subtype)\
|
302
361
|
((?:\
|
303
|
-
(?#
|
304
|
-
(?#
|
305
|
-
(?#
|
306
|
-
(?#
|
362
|
+
(?# 4: NIL )(NIL)|\
|
363
|
+
(?# 5: NUMBER )(\d+)|\
|
364
|
+
(?# 6: PLUS )(\+))\
|
365
|
+
(?# 7: ATOM remaining after prefix )(#{Patterns::ATOMISH})?\
|
307
366
|
(?# This enables greedy alternation without lookahead, in linear time.)\
|
308
367
|
)|\
|
309
368
|
(?# Also need to check for ATOM without a subtype prefix.)\
|
310
|
-
(?#
|
311
|
-
(?#
|
312
|
-
(?#
|
313
|
-
(?#
|
314
|
-
(?#
|
315
|
-
(?#
|
316
|
-
(?#
|
317
|
-
(?#
|
318
|
-
(?#
|
319
|
-
(?#
|
320
|
-
(?#
|
321
|
-
(?#
|
369
|
+
(?# 8: ATOM )(#{Patterns::ATOMISH})|\
|
370
|
+
(?# 9: QUOTED )#{Patterns::QUOTED_rev2}|\
|
371
|
+
(?# 10: LPAR )(\()|\
|
372
|
+
(?# 11: RPAR )(\))|\
|
373
|
+
(?# 12: BSLASH )(\\)|\
|
374
|
+
(?# 13: STAR )(\*)|\
|
375
|
+
(?# 14: LBRA )(\[)|\
|
376
|
+
(?# 15: RBRA )(\])|\
|
377
|
+
(?# 16: LITERAL )#{Patterns::LITERAL}|\
|
378
|
+
(?# 17: PERCENT )(%)|\
|
379
|
+
(?# 18: CRLF )(\r\n)|\
|
380
|
+
(?# 19: EOF )(\z))/ni
|
322
381
|
|
323
382
|
# envelope, body(structure), namespaces
|
324
383
|
DATA_REGEXP = /\G(?:\
|
@@ -359,6 +418,9 @@ module Net
|
|
359
418
|
# string = quoted / literal
|
360
419
|
def_token_matchers :string, T_QUOTED, T_LITERAL
|
361
420
|
|
421
|
+
# used by nstring8 = nstring / literal8
|
422
|
+
def_token_matchers :string8, T_QUOTED, T_LITERAL, T_LITERAL8
|
423
|
+
|
362
424
|
# use where string represents "LABEL" values
|
363
425
|
def_token_matchers :case_insensitive__string,
|
364
426
|
T_QUOTED, T_LITERAL,
|
@@ -390,6 +452,24 @@ module Net
|
|
390
452
|
# ATOM-CHAR = <any CHAR except atom-specials>
|
391
453
|
ATOM_TOKENS = [T_ATOM, T_NUMBER, T_NIL, T_LBRA, T_PLUS]
|
392
454
|
|
455
|
+
SEQUENCE_SET_TOKENS = [T_ATOM, T_NUMBER, T_STAR]
|
456
|
+
|
457
|
+
# sequence-set = (seq-number / seq-range) ["," sequence-set]
|
458
|
+
# sequence-set =/ seq-last-command
|
459
|
+
# ; Allow for "result of the last command"
|
460
|
+
# ; indicator.
|
461
|
+
# seq-last-command = "$"
|
462
|
+
#
|
463
|
+
# *note*: doesn't match seq-last-command
|
464
|
+
def sequence_set
|
465
|
+
str = combine_adjacent(*SEQUENCE_SET_TOKENS)
|
466
|
+
if Patterns::SEQUENCE_SET_STR.match?(str)
|
467
|
+
SequenceSet.new(str)
|
468
|
+
else
|
469
|
+
parse_error("unexpected atom %p, expected sequence-set", str)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
393
473
|
# ASTRING-CHAR = ATOM-CHAR / resp-specials
|
394
474
|
# resp-specials = "]"
|
395
475
|
ASTRING_CHARS_TOKENS = [*ATOM_TOKENS, T_RBRA].freeze
|
@@ -460,6 +540,10 @@ module Net
|
|
460
540
|
NIL? ? nil : string
|
461
541
|
end
|
462
542
|
|
543
|
+
def nstring8
|
544
|
+
NIL? ? nil : string8
|
545
|
+
end
|
546
|
+
|
463
547
|
def nquoted
|
464
548
|
NIL? ? nil : quoted
|
465
549
|
end
|
@@ -469,6 +553,60 @@ module Net
|
|
469
553
|
NIL? ? nil : case_insensitive__string
|
470
554
|
end
|
471
555
|
|
556
|
+
# tagged-ext-comp = astring /
|
557
|
+
# tagged-ext-comp *(SP tagged-ext-comp) /
|
558
|
+
# "(" tagged-ext-comp ")"
|
559
|
+
# ; Extensions that follow this general
|
560
|
+
# ; syntax should use nstring instead of
|
561
|
+
# ; astring when appropriate in the context
|
562
|
+
# ; of the extension.
|
563
|
+
# ; Note that a message set or a "number"
|
564
|
+
# ; can always be represented as an "atom".
|
565
|
+
# ; A URL should be represented as
|
566
|
+
# ; a "quoted" string.
|
567
|
+
def tagged_ext_comp
|
568
|
+
vals = []
|
569
|
+
while true
|
570
|
+
vals << case lookahead!(*ASTRING_TOKENS, T_LPAR).symbol
|
571
|
+
when T_LPAR then lpar; ary = tagged_ext_comp; rpar; ary
|
572
|
+
when T_NUMBER then number
|
573
|
+
else astring
|
574
|
+
end
|
575
|
+
SP? or break
|
576
|
+
end
|
577
|
+
vals
|
578
|
+
end
|
579
|
+
|
580
|
+
# tagged-ext-simple is a subset of atom
|
581
|
+
# TODO: recognize sequence-set in the lexer
|
582
|
+
#
|
583
|
+
# tagged-ext-simple = sequence-set / number / number64
|
584
|
+
def tagged_ext_simple
|
585
|
+
number? || sequence_set
|
586
|
+
end
|
587
|
+
|
588
|
+
# tagged-ext-val = tagged-ext-simple /
|
589
|
+
# "(" [tagged-ext-comp] ")"
|
590
|
+
def tagged_ext_val
|
591
|
+
if lpar?
|
592
|
+
_ = peek_rpar? ? [] : tagged_ext_comp
|
593
|
+
rpar
|
594
|
+
_
|
595
|
+
else
|
596
|
+
tagged_ext_simple
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
# mailbox = "INBOX" / astring
|
601
|
+
# ; INBOX is case-insensitive. All case variants of
|
602
|
+
# ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX
|
603
|
+
# ; not as an astring. An astring which consists of
|
604
|
+
# ; the case-insensitive sequence "I" "N" "B" "O" "X"
|
605
|
+
# ; is considered to be INBOX and not an astring.
|
606
|
+
# ; Refer to section 5.1 for further
|
607
|
+
# ; semantic details of mailbox names.
|
608
|
+
alias mailbox astring
|
609
|
+
|
472
610
|
# valid number ranges are not enforced by parser
|
473
611
|
# number64 = 1*DIGIT
|
474
612
|
# ; Unsigned 63-bit integer
|
@@ -494,6 +632,12 @@ module Net
|
|
494
632
|
# ; Strictly ascending
|
495
633
|
alias uniqueid nz_number
|
496
634
|
|
635
|
+
# valid number ranges are not enforced by parser
|
636
|
+
#
|
637
|
+
# a 64-bit unsigned integer and is the decimal equivalent for the ID hex
|
638
|
+
# string used in the web interface and the Gmail API.
|
639
|
+
alias x_gm_id number
|
640
|
+
|
497
641
|
# [RFC3501 & RFC9051:]
|
498
642
|
# response = *(continue-req / response-data) response-done
|
499
643
|
#
|
@@ -630,34 +774,47 @@ module Net
|
|
630
774
|
|
631
775
|
# RFC3501 & RFC9051:
|
632
776
|
# response-tagged = tag SP resp-cond-state CRLF
|
633
|
-
#
|
634
|
-
# resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
|
635
|
-
# ; Status condition
|
636
|
-
#
|
637
|
-
# tag = 1*<any ASTRING-CHAR except "+">
|
638
777
|
def response_tagged
|
639
|
-
tag
|
640
|
-
name = resp_cond_state__name; SP!
|
641
|
-
TaggedResponse.new(tag, name, resp_text, @str)
|
778
|
+
TaggedResponse.new(tag, *(SP!; resp_cond_state), @str)
|
642
779
|
end
|
643
780
|
|
644
781
|
# RFC3501 & RFC9051:
|
645
782
|
# resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
|
783
|
+
#
|
784
|
+
# NOTE: In the spirit of RFC9051 Appx E 23 (and to workaround existing
|
785
|
+
# servers), we don't require a final SP and instead parse this as:
|
786
|
+
#
|
787
|
+
# resp-cond-state = ("OK" / "NO" / "BAD") [SP resp-text]
|
788
|
+
def resp_cond_state
|
789
|
+
[resp_cond_state__name, SP? ? resp_text : ResponseText::EMPTY]
|
790
|
+
end
|
791
|
+
|
646
792
|
def resp_cond_state__untagged
|
647
|
-
|
648
|
-
UntaggedResponse.new(name, resp_text, @str)
|
793
|
+
UntaggedResponse.new(*resp_cond_state, @str)
|
649
794
|
end
|
650
795
|
|
651
796
|
# resp-cond-auth = ("OK" / "PREAUTH") SP resp-text
|
797
|
+
#
|
798
|
+
# NOTE: In the spirit of RFC9051 Appx E 23 (and to workaround existing
|
799
|
+
# servers), we don't require a final SP and instead parse this as:
|
800
|
+
#
|
801
|
+
# resp-cond-auth = ("OK" / "PREAUTH") [SP resp-text]
|
652
802
|
def resp_cond_auth
|
653
|
-
|
654
|
-
|
803
|
+
UntaggedResponse.new(resp_cond_auth__name,
|
804
|
+
SP? ? resp_text : ResponseText::EMPTY,
|
805
|
+
@str)
|
655
806
|
end
|
656
807
|
|
657
808
|
# resp-cond-bye = "BYE" SP resp-text
|
809
|
+
#
|
810
|
+
# NOTE: In the spirit of RFC9051 Appx E 23 (and to workaround existing
|
811
|
+
# servers), we don't require a final SP and instead parse this as:
|
812
|
+
#
|
813
|
+
# resp-cond-bye = "BYE" [SP resp-text]
|
658
814
|
def resp_cond_bye
|
659
|
-
|
660
|
-
|
815
|
+
UntaggedResponse.new(label(BYE),
|
816
|
+
SP? ? resp_text : ResponseText::EMPTY,
|
817
|
+
@str)
|
661
818
|
end
|
662
819
|
|
663
820
|
# message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
|
@@ -740,10 +897,17 @@ module Net
|
|
740
897
|
when "ENVELOPE" then envelope
|
741
898
|
when "INTERNALDATE" then date_time
|
742
899
|
when "RFC822.SIZE" then number64
|
900
|
+
when /\ABINARY\[/ni then nstring8 # BINARY, IMAP4rev2
|
901
|
+
when /\ABINARY\.SIZE\[/ni then number # BINARY, IMAP4rev2
|
743
902
|
when "RFC822" then nstring # not in rev2
|
744
903
|
when "RFC822.HEADER" then nstring # not in rev2
|
745
904
|
when "RFC822.TEXT" then nstring # not in rev2
|
746
905
|
when "MODSEQ" then parens__modseq # CONDSTORE
|
906
|
+
when "EMAILID" then parens__objectid # OBJECTID
|
907
|
+
when "THREADID" then nparens__objectid # OBJECTID
|
908
|
+
when "X-GM-MSGID" then x_gm_id # GMail
|
909
|
+
when "X-GM-THRID" then x_gm_id # GMail
|
910
|
+
when "X-GM-LABELS" then x_gm_labels # GMail
|
747
911
|
else parse_error("unknown attribute `%s' for {%d}", name, n)
|
748
912
|
end
|
749
913
|
attr[name] = val
|
@@ -762,46 +926,69 @@ module Net
|
|
762
926
|
lbra? and rbra
|
763
927
|
when "BODY"
|
764
928
|
peek_lbra? and name << section and
|
765
|
-
peek_str?("<") and name <<
|
929
|
+
peek_str?("<") and name << gt__number__lt # partial
|
930
|
+
when "BINARY", "BINARY.SIZE"
|
931
|
+
name << section_binary
|
932
|
+
# see https://www.rfc-editor.org/errata/eid7246 and the note above
|
933
|
+
peek_str?("<") and name << gt__number__lt # partial
|
766
934
|
end
|
767
935
|
name
|
768
936
|
end
|
769
937
|
|
938
|
+
# this represents the partial size for BODY or BINARY
|
939
|
+
alias gt__number__lt atom
|
940
|
+
|
941
|
+
# RFC3501 & RFC9051:
|
942
|
+
# envelope = "(" env-date SP env-subject SP env-from SP
|
943
|
+
# env-sender SP env-reply-to SP env-to SP env-cc SP
|
944
|
+
# env-bcc SP env-in-reply-to SP env-message-id ")"
|
770
945
|
def envelope
|
771
946
|
@lex_state = EXPR_DATA
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
reply_to = address_list
|
787
|
-
match(T_SPACE)
|
788
|
-
to = address_list
|
789
|
-
match(T_SPACE)
|
790
|
-
cc = address_list
|
791
|
-
match(T_SPACE)
|
792
|
-
bcc = address_list
|
793
|
-
match(T_SPACE)
|
794
|
-
in_reply_to = nstring
|
795
|
-
match(T_SPACE)
|
796
|
-
message_id = nstring
|
797
|
-
match(T_RPAR)
|
798
|
-
result = Envelope.new(date, subject, from, sender, reply_to,
|
799
|
-
to, cc, bcc, in_reply_to, message_id)
|
800
|
-
end
|
947
|
+
lpar; date = env_date
|
948
|
+
SP!; subject = env_subject
|
949
|
+
SP!; from = env_from
|
950
|
+
SP!; sender = env_sender
|
951
|
+
SP!; reply_to = env_reply_to
|
952
|
+
SP!; to = env_to
|
953
|
+
SP!; cc = env_cc
|
954
|
+
SP!; bcc = env_bcc
|
955
|
+
SP!; in_reply_to = env_in_reply_to
|
956
|
+
SP!; message_id = env_message_id
|
957
|
+
rpar
|
958
|
+
Envelope.new(date, subject, from, sender, reply_to,
|
959
|
+
to, cc, bcc, in_reply_to, message_id)
|
960
|
+
ensure
|
801
961
|
@lex_state = EXPR_BEG
|
802
|
-
return result
|
803
962
|
end
|
804
963
|
|
964
|
+
# env-date = nstring
|
965
|
+
# env-subject = nstring
|
966
|
+
# env-in-reply-to = nstring
|
967
|
+
# env-message-id = nstring
|
968
|
+
alias env_date nstring
|
969
|
+
alias env_subject nstring
|
970
|
+
alias env_in_reply_to nstring
|
971
|
+
alias env_message_id nstring
|
972
|
+
|
973
|
+
# env-from = "(" 1*address ")" / nil
|
974
|
+
# env-sender = "(" 1*address ")" / nil
|
975
|
+
# env-reply-to = "(" 1*address ")" / nil
|
976
|
+
# env-to = "(" 1*address ")" / nil
|
977
|
+
# env-cc = "(" 1*address ")" / nil
|
978
|
+
# env-bcc = "(" 1*address ")" / nil
|
979
|
+
def nlist__address
|
980
|
+
return if NIL?
|
981
|
+
lpar; list = [address]; list << address until rpar?
|
982
|
+
list
|
983
|
+
end
|
984
|
+
|
985
|
+
alias env_from nlist__address
|
986
|
+
alias env_sender nlist__address
|
987
|
+
alias env_reply_to nlist__address
|
988
|
+
alias env_to nlist__address
|
989
|
+
alias env_cc nlist__address
|
990
|
+
alias env_bcc nlist__address
|
991
|
+
|
805
992
|
# date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
|
806
993
|
# SP time SP zone DQUOTE
|
807
994
|
alias date_time quoted
|
@@ -1070,6 +1257,13 @@ module Net
|
|
1070
1257
|
str << rbra
|
1071
1258
|
end
|
1072
1259
|
|
1260
|
+
# section-binary = "[" [section-part] "]"
|
1261
|
+
def section_binary
|
1262
|
+
str = +lbra
|
1263
|
+
str << section_part unless peek_rbra?
|
1264
|
+
str << rbra
|
1265
|
+
end
|
1266
|
+
|
1073
1267
|
# section-spec = section-msgtext / (section-part ["." section-text])
|
1074
1268
|
# section-msgtext = "HEADER" /
|
1075
1269
|
# "HEADER.FIELDS" [".NOT"] SP header-list /
|
@@ -1100,6 +1294,11 @@ module Net
|
|
1100
1294
|
str << rpar
|
1101
1295
|
end
|
1102
1296
|
|
1297
|
+
# section-part = nz-number *("." nz-number)
|
1298
|
+
# ; body part reference.
|
1299
|
+
# ; Allows for accessing nested body parts.
|
1300
|
+
alias section_part atom
|
1301
|
+
|
1103
1302
|
# RFC3501 & RFC9051:
|
1104
1303
|
# header-fld-name = astring
|
1105
1304
|
#
|
@@ -1148,18 +1347,17 @@ module Net
|
|
1148
1347
|
alias mailbox_data__lsub mailbox_data__list
|
1149
1348
|
alias mailbox_data__xlist mailbox_data__list
|
1150
1349
|
|
1350
|
+
# mailbox-list = "(" [mbx-list-flags] ")" SP
|
1351
|
+
# (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
|
1352
|
+
# [SP mbox-list-extended]
|
1353
|
+
# ; This is the list information pointed to by the ABNF
|
1354
|
+
# ; item "mailbox-data", which is defined above
|
1151
1355
|
def mailbox_list
|
1152
|
-
attr
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
else
|
1158
|
-
delim = token.value
|
1159
|
-
end
|
1160
|
-
match(T_SPACE)
|
1161
|
-
name = astring
|
1162
|
-
return MailboxList.new(attr, delim, name)
|
1356
|
+
lpar; attr = peek_rpar? ? [] : mbx_list_flags; rpar
|
1357
|
+
SP!; delim = nquoted
|
1358
|
+
SP!; name = mailbox
|
1359
|
+
# TODO: mbox-list-extended
|
1360
|
+
MailboxList.new(attr, delim, name)
|
1163
1361
|
end
|
1164
1362
|
|
1165
1363
|
def getquota_response
|
@@ -1285,93 +1483,131 @@ module Net
|
|
1285
1483
|
end
|
1286
1484
|
alias sort_data mailbox_data__search
|
1287
1485
|
|
1486
|
+
# RFC5256: THREAD
|
1487
|
+
# thread-data = "THREAD" [SP 1*thread-list]
|
1288
1488
|
def thread_data
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
if token.symbol == T_SPACE
|
1294
|
-
threads = []
|
1295
|
-
|
1296
|
-
while true
|
1297
|
-
shift_token
|
1298
|
-
token = lookahead
|
1299
|
-
|
1300
|
-
case token.symbol
|
1301
|
-
when T_LPAR
|
1302
|
-
threads << thread_branch(token)
|
1303
|
-
when T_CRLF
|
1304
|
-
break
|
1305
|
-
end
|
1306
|
-
end
|
1307
|
-
else
|
1308
|
-
# no member
|
1309
|
-
threads = []
|
1489
|
+
name = label("THREAD")
|
1490
|
+
threads = []
|
1491
|
+
if SP?
|
1492
|
+
threads << thread_list while lookahead_thread_list?
|
1310
1493
|
end
|
1311
|
-
|
1312
|
-
return UntaggedResponse.new(name, threads, @str)
|
1494
|
+
UntaggedResponse.new(name, threads, @str)
|
1313
1495
|
end
|
1314
1496
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
lastmember = nil
|
1318
|
-
|
1319
|
-
while true
|
1320
|
-
shift_token # ignore first T_LPAR
|
1321
|
-
token = lookahead
|
1497
|
+
alias lookahead_thread_list? lookahead_lpar?
|
1498
|
+
alias lookahead_thread_nested? lookahead_thread_list?
|
1322
1499
|
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
when T_LPAR
|
1336
|
-
if rootmember.nil?
|
1337
|
-
# dummy member
|
1338
|
-
lastmember = rootmember = ThreadMember.new(nil, [])
|
1339
|
-
end
|
1500
|
+
# RFC5256: THREAD
|
1501
|
+
# thread-list = "(" (thread-members / thread-nested) ")"
|
1502
|
+
def thread_list
|
1503
|
+
lpar
|
1504
|
+
thread = if lookahead_thread_nested?
|
1505
|
+
ThreadMember.new(nil, thread_nested)
|
1506
|
+
else
|
1507
|
+
thread_members
|
1508
|
+
end
|
1509
|
+
rpar
|
1510
|
+
thread
|
1511
|
+
end
|
1340
1512
|
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1513
|
+
# RFC5256: THREAD
|
1514
|
+
# thread-members = nz-number *(SP nz-number) [SP thread-nested]
|
1515
|
+
def thread_members
|
1516
|
+
members = []
|
1517
|
+
members << nz_number # thread root
|
1518
|
+
while SP?
|
1519
|
+
case lookahead!(T_NUMBER, T_LPAR).symbol
|
1520
|
+
when T_NUMBER then members << nz_number
|
1521
|
+
else nested = thread_nested; break
|
1344
1522
|
end
|
1345
1523
|
end
|
1524
|
+
members.reverse.inject(nested || []) {|subthreads, number|
|
1525
|
+
[ThreadMember.new(number, subthreads)]
|
1526
|
+
}.first
|
1527
|
+
end
|
1346
1528
|
|
1347
|
-
|
1529
|
+
# RFC5256: THREAD
|
1530
|
+
# thread-nested = 2*thread-list
|
1531
|
+
def thread_nested
|
1532
|
+
nested = [thread_list, thread_list]
|
1533
|
+
while lookahead_thread_list? do nested << thread_list end
|
1534
|
+
nested
|
1348
1535
|
end
|
1349
1536
|
|
1537
|
+
# mailbox-data =/ "STATUS" SP mailbox SP "(" [status-att-list] ")"
|
1350
1538
|
def mailbox_data__status
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1539
|
+
resp_name = label("STATUS"); SP!
|
1540
|
+
mbox_name = mailbox; SP!
|
1541
|
+
lpar; attr = status_att_list; rpar
|
1542
|
+
UntaggedResponse.new(resp_name, StatusData.new(mbox_name, attr), @str)
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
# RFC3501
|
1546
|
+
# status-att-list = status-att SP number *(SP status-att SP number)
|
1547
|
+
# RFC4466, RFC9051, and RFC3501 Errata
|
1548
|
+
# status-att-list = status-att-val *(SP status-att-val)
|
1549
|
+
def status_att_list
|
1550
|
+
attrs = [status_att_val]
|
1551
|
+
while SP? do attrs << status_att_val end
|
1552
|
+
attrs.to_h
|
1553
|
+
end
|
1554
|
+
|
1555
|
+
# RFC3501 Errata:
|
1556
|
+
# status-att-val = ("MESSAGES" SP number) / ("RECENT" SP number) /
|
1557
|
+
# ("UIDNEXT" SP nz-number) / ("UIDVALIDITY" SP nz-number) /
|
1558
|
+
# ("UNSEEN" SP number)
|
1559
|
+
# RFC4466:
|
1560
|
+
# status-att-val = ("MESSAGES" SP number) /
|
1561
|
+
# ("RECENT" SP number) /
|
1562
|
+
# ("UIDNEXT" SP nz-number) /
|
1563
|
+
# ("UIDVALIDITY" SP nz-number) /
|
1564
|
+
# ("UNSEEN" SP number)
|
1565
|
+
# ;; Extensions to the STATUS responses
|
1566
|
+
# ;; should extend this production.
|
1567
|
+
# ;; Extensions should use the generic
|
1568
|
+
# ;; syntax defined by tagged-ext.
|
1569
|
+
# RFC9051:
|
1570
|
+
# status-att-val = ("MESSAGES" SP number) /
|
1571
|
+
# ("UIDNEXT" SP nz-number) /
|
1572
|
+
# ("UIDVALIDITY" SP nz-number) /
|
1573
|
+
# ("UNSEEN" SP number) /
|
1574
|
+
# ("DELETED" SP number) /
|
1575
|
+
# ("SIZE" SP number64)
|
1576
|
+
# ; Extensions to the STATUS responses
|
1577
|
+
# ; should extend this production.
|
1578
|
+
# ; Extensions should use the generic
|
1579
|
+
# ; syntax defined by tagged-ext.
|
1580
|
+
# RFC7162:
|
1581
|
+
# status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer
|
1582
|
+
# ;; Extends non-terminal defined in [RFC4466].
|
1583
|
+
# ;; Value 0 denotes that the mailbox doesn't
|
1584
|
+
# ;; support persistent mod-sequences
|
1585
|
+
# ;; as described in Section 3.1.2.2.
|
1586
|
+
# RFC7889:
|
1587
|
+
# status-att-val =/ "APPENDLIMIT" SP (number / nil)
|
1588
|
+
# ;; status-att-val is defined in RFC 4466
|
1589
|
+
# RFC8438:
|
1590
|
+
# status-att-val =/ "SIZE" SP number64
|
1591
|
+
# RFC8474:
|
1592
|
+
# status-att-val =/ "MAILBOXID" SP "(" objectid ")"
|
1593
|
+
# ; follows tagged-ext production from [RFC4466]
|
1594
|
+
def status_att_val
|
1595
|
+
key = tagged_ext_label
|
1596
|
+
SP!
|
1597
|
+
val =
|
1598
|
+
case key
|
1599
|
+
when "MESSAGES" then number # RFC3501, RFC9051
|
1600
|
+
when "UNSEEN" then number # RFC3501, RFC9051
|
1601
|
+
when "DELETED" then number # RFC3501, RFC9051
|
1602
|
+
when "UIDNEXT" then nz_number # RFC3501, RFC9051
|
1603
|
+
when "UIDVALIDITY" then nz_number # RFC3501, RFC9051
|
1604
|
+
when "RECENT" then number # RFC3501 (obsolete)
|
1605
|
+
when "SIZE" then number64 # RFC8483, RFC9051
|
1606
|
+
when "MAILBOXID" then parens__objectid # RFC8474
|
1607
|
+
else
|
1608
|
+
number? || ExtensionData.new(tagged_ext_val)
|
1366
1609
|
end
|
1367
|
-
|
1368
|
-
key = token.value.upcase
|
1369
|
-
match(T_SPACE)
|
1370
|
-
val = number
|
1371
|
-
attr[key] = val
|
1372
|
-
end
|
1373
|
-
data = StatusData.new(mailbox, attr)
|
1374
|
-
return UntaggedResponse.new(name, data, @str)
|
1610
|
+
[key, val]
|
1375
1611
|
end
|
1376
1612
|
|
1377
1613
|
# The presence of "IMAP4rev1" or "IMAP4rev2" is unenforced here.
|
@@ -1573,6 +1809,9 @@ module Net
|
|
1573
1809
|
# resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value /
|
1574
1810
|
# "NOMODSEQ" /
|
1575
1811
|
# "MODIFIED" SP sequence-set
|
1812
|
+
#
|
1813
|
+
# RFC8474: OBJECTID
|
1814
|
+
# resp-text-code =/ "MAILBOXID" SP "(" objectid ")"
|
1576
1815
|
def resp_text_code
|
1577
1816
|
name = resp_text_code__name
|
1578
1817
|
data =
|
@@ -1592,6 +1831,7 @@ module Net
|
|
1592
1831
|
"LIMIT", "OVERQUOTA", "ALREADYEXISTS", "NONEXISTENT", "CLOSED",
|
1593
1832
|
"NOTSAVED", "UIDNOTSTICKY", "UNKNOWN-CTE", "HASCHILDREN"
|
1594
1833
|
when "NOMODSEQ" # CONDSTORE
|
1834
|
+
when "MAILBOXID" then SP!; parens__objectid # RFC8474: OBJECTID
|
1595
1835
|
else
|
1596
1836
|
SP? and text_chars_except_rbra
|
1597
1837
|
end
|
@@ -1638,61 +1878,40 @@ module Net
|
|
1638
1878
|
UIDPlusData.new(validity, src_uids, dst_uids)
|
1639
1879
|
end
|
1640
1880
|
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
when T_SPACE
|
1656
|
-
shift_token
|
1657
|
-
end
|
1658
|
-
result.push(address)
|
1659
|
-
end
|
1660
|
-
return result
|
1661
|
-
end
|
1662
|
-
end
|
1663
|
-
|
1664
|
-
ADDRESS_REGEXP = /\G\
|
1665
|
-
(?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
|
1666
|
-
(?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
|
1667
|
-
(?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
|
1668
|
-
(?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\
|
1669
|
-
\)/ni
|
1670
|
-
|
1881
|
+
ADDRESS_REGEXP = /\G
|
1882
|
+
\( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME
|
1883
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 2: ROUTE
|
1884
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 3: MAILBOX
|
1885
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 4: HOST
|
1886
|
+
\)
|
1887
|
+
/nix
|
1888
|
+
|
1889
|
+
# address = "(" addr-name SP addr-adl SP addr-mailbox SP
|
1890
|
+
# addr-host ")"
|
1891
|
+
# addr-adl = nstring
|
1892
|
+
# addr-host = nstring
|
1893
|
+
# addr-mailbox = nstring
|
1894
|
+
# addr-name = nstring
|
1671
1895
|
def address
|
1672
|
-
match(
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
end
|
1683
|
-
else
|
1684
|
-
name = nstring
|
1685
|
-
match(T_SPACE)
|
1686
|
-
route = nstring
|
1687
|
-
match(T_SPACE)
|
1688
|
-
mailbox = nstring
|
1689
|
-
match(T_SPACE)
|
1690
|
-
host = nstring
|
1691
|
-
match(T_RPAR)
|
1896
|
+
if (match = accept_re(ADDRESS_REGEXP))
|
1897
|
+
# note that "NIL" isn't captured by the regexp
|
1898
|
+
name, route, mailbox, host = match.captures
|
1899
|
+
.map { Patterns.unescape_quoted _1 }
|
1900
|
+
else # address may include literals
|
1901
|
+
lpar; name = addr_name
|
1902
|
+
SP!; route = addr_adl
|
1903
|
+
SP!; mailbox = addr_mailbox
|
1904
|
+
SP!; host = addr_host
|
1905
|
+
rpar
|
1692
1906
|
end
|
1693
|
-
|
1907
|
+
Address.new(name, route, mailbox, host)
|
1694
1908
|
end
|
1695
1909
|
|
1910
|
+
alias addr_adl nstring
|
1911
|
+
alias addr_host nstring
|
1912
|
+
alias addr_mailbox nstring
|
1913
|
+
alias addr_name nstring
|
1914
|
+
|
1696
1915
|
# flag-list = "(" [flag *(SP flag)] ")"
|
1697
1916
|
def flag_list
|
1698
1917
|
match_re(Patterns::FLAG_LIST, "flag-list")[1]
|
@@ -1707,22 +1926,23 @@ module Net
|
|
1707
1926
|
.map! { _1.start_with?("\\") ? _1[1..].capitalize.to_sym : _1 }
|
1708
1927
|
end
|
1709
1928
|
|
1710
|
-
#
|
1711
|
-
|
1712
|
-
# mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag
|
1713
|
-
# *(SP mbx-list-oflag) /
|
1714
|
-
# mbx-list-oflag *(SP mbx-list-oflag)
|
1715
|
-
# mbx-list-oflag = "\Noinferiors" / child-mbox-flag /
|
1716
|
-
# "\Subscribed" / "\Remote" / flag-extension
|
1717
|
-
# ; Other flags; multiple from this list are
|
1718
|
-
# ; possible per LIST response, but each flag
|
1719
|
-
# ; can only appear once per LIST response
|
1720
|
-
# mbx-list-sflag = "\NonExistent" / "\Noselect" / "\Marked" /
|
1721
|
-
# "\Unmarked"
|
1722
|
-
# ; Selectability flags; only one per LIST response
|
1723
|
-
def parens__mbx_list_flags
|
1929
|
+
# See Patterns::MBX_LIST_FLAGS
|
1930
|
+
def mbx_list_flags
|
1724
1931
|
match_re(Patterns::MBX_LIST_FLAGS, "mbx-list-flags")[1]
|
1725
|
-
.split(nil).map! { _1.capitalize.to_sym }
|
1932
|
+
.split(nil).map! { _1[1..].capitalize.to_sym }
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
# See https://developers.google.com/gmail/imap/imap-extensions
|
1936
|
+
def x_gm_label; accept(T_BSLASH) ? atom.capitalize.to_sym : astring end
|
1937
|
+
|
1938
|
+
# See https://developers.google.com/gmail/imap/imap-extensions
|
1939
|
+
def x_gm_labels
|
1940
|
+
lpar; return [] if rpar?
|
1941
|
+
labels = []
|
1942
|
+
labels << x_gm_label
|
1943
|
+
labels << x_gm_label while SP?
|
1944
|
+
rpar
|
1945
|
+
labels
|
1726
1946
|
end
|
1727
1947
|
|
1728
1948
|
# See https://www.rfc-editor.org/errata/rfc3501
|
@@ -1744,6 +1964,15 @@ module Net
|
|
1744
1964
|
|
1745
1965
|
def parens__modseq; lpar; _ = permsg_modsequence; rpar; _ end
|
1746
1966
|
|
1967
|
+
# RFC8474:
|
1968
|
+
# objectid = 1*255(ALPHA / DIGIT / "_" / "-")
|
1969
|
+
# ; characters in object identifiers are case
|
1970
|
+
# ; significant
|
1971
|
+
alias objectid atom
|
1972
|
+
|
1973
|
+
def parens__objectid; lpar; _ = objectid; rpar; _ end
|
1974
|
+
def nparens__objectid; NIL? ? nil : parens__objectid end
|
1975
|
+
|
1747
1976
|
# RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
|
1748
1977
|
# uid-set = (uniqueid / uid-range) *("," uid-set)
|
1749
1978
|
# uid-range = (uniqueid ":" uniqueid)
|
@@ -1789,42 +2018,47 @@ module Net
|
|
1789
2018
|
@pos = $~.end(0)
|
1790
2019
|
if $1
|
1791
2020
|
return Token.new(T_SPACE, $+)
|
1792
|
-
elsif $2
|
2021
|
+
elsif $2
|
2022
|
+
len = $+.to_i
|
2023
|
+
val = @str[@pos, len]
|
2024
|
+
@pos += len
|
2025
|
+
return Token.new(T_LITERAL8, val)
|
2026
|
+
elsif $3 && $7
|
1793
2027
|
# greedily match ATOM, prefixed with NUMBER, NIL, or PLUS.
|
1794
|
-
return Token.new(T_ATOM, $
|
1795
|
-
elsif $3
|
1796
|
-
return Token.new(T_NIL, $+)
|
2028
|
+
return Token.new(T_ATOM, $3)
|
1797
2029
|
elsif $4
|
1798
|
-
return Token.new(
|
2030
|
+
return Token.new(T_NIL, $+)
|
1799
2031
|
elsif $5
|
2032
|
+
return Token.new(T_NUMBER, $+)
|
2033
|
+
elsif $6
|
1800
2034
|
return Token.new(T_PLUS, $+)
|
1801
|
-
elsif $
|
2035
|
+
elsif $8
|
1802
2036
|
# match ATOM, without a NUMBER, NIL, or PLUS prefix
|
1803
2037
|
return Token.new(T_ATOM, $+)
|
1804
|
-
elsif $8
|
1805
|
-
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
1806
2038
|
elsif $9
|
1807
|
-
return Token.new(
|
2039
|
+
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
1808
2040
|
elsif $10
|
1809
|
-
return Token.new(
|
2041
|
+
return Token.new(T_LPAR, $+)
|
1810
2042
|
elsif $11
|
1811
|
-
return Token.new(
|
2043
|
+
return Token.new(T_RPAR, $+)
|
1812
2044
|
elsif $12
|
1813
|
-
return Token.new(
|
2045
|
+
return Token.new(T_BSLASH, $+)
|
1814
2046
|
elsif $13
|
1815
|
-
return Token.new(
|
2047
|
+
return Token.new(T_STAR, $+)
|
1816
2048
|
elsif $14
|
1817
|
-
return Token.new(
|
2049
|
+
return Token.new(T_LBRA, $+)
|
1818
2050
|
elsif $15
|
2051
|
+
return Token.new(T_RBRA, $+)
|
2052
|
+
elsif $16
|
1819
2053
|
len = $+.to_i
|
1820
2054
|
val = @str[@pos, len]
|
1821
2055
|
@pos += len
|
1822
2056
|
return Token.new(T_LITERAL, val)
|
1823
|
-
elsif $16
|
1824
|
-
return Token.new(T_PERCENT, $+)
|
1825
2057
|
elsif $17
|
1826
|
-
return Token.new(
|
2058
|
+
return Token.new(T_PERCENT, $+)
|
1827
2059
|
elsif $18
|
2060
|
+
return Token.new(T_CRLF, $+)
|
2061
|
+
elsif $19
|
1828
2062
|
return Token.new(T_EOF, $+)
|
1829
2063
|
else
|
1830
2064
|
parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid")
|