net-imap 0.4.4 → 0.4.7
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 +491 -270
- data/lib/net/imap/sequence_set.rb +67 -0
- data/lib/net/imap.rb +82 -17
- data/net-imap.gemspec +1 -0
- data/rakelib/benchmarks.rake +4 -11
- metadata +6 -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,75 @@ 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 (quirky_SP?; 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
|
+
|
992
|
+
# Used when servers erroneously send an extra SP.
|
993
|
+
#
|
994
|
+
# As of 2023-11-28, Outlook.com (still) sends SP
|
995
|
+
# between +address+ in <tt>env-*</tt> lists.
|
996
|
+
alias quirky_SP? SP?
|
997
|
+
|
805
998
|
# date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
|
806
999
|
# SP time SP zone DQUOTE
|
807
1000
|
alias date_time quoted
|
@@ -1070,6 +1263,13 @@ module Net
|
|
1070
1263
|
str << rbra
|
1071
1264
|
end
|
1072
1265
|
|
1266
|
+
# section-binary = "[" [section-part] "]"
|
1267
|
+
def section_binary
|
1268
|
+
str = +lbra
|
1269
|
+
str << section_part unless peek_rbra?
|
1270
|
+
str << rbra
|
1271
|
+
end
|
1272
|
+
|
1073
1273
|
# section-spec = section-msgtext / (section-part ["." section-text])
|
1074
1274
|
# section-msgtext = "HEADER" /
|
1075
1275
|
# "HEADER.FIELDS" [".NOT"] SP header-list /
|
@@ -1100,6 +1300,11 @@ module Net
|
|
1100
1300
|
str << rpar
|
1101
1301
|
end
|
1102
1302
|
|
1303
|
+
# section-part = nz-number *("." nz-number)
|
1304
|
+
# ; body part reference.
|
1305
|
+
# ; Allows for accessing nested body parts.
|
1306
|
+
alias section_part atom
|
1307
|
+
|
1103
1308
|
# RFC3501 & RFC9051:
|
1104
1309
|
# header-fld-name = astring
|
1105
1310
|
#
|
@@ -1148,18 +1353,17 @@ module Net
|
|
1148
1353
|
alias mailbox_data__lsub mailbox_data__list
|
1149
1354
|
alias mailbox_data__xlist mailbox_data__list
|
1150
1355
|
|
1356
|
+
# mailbox-list = "(" [mbx-list-flags] ")" SP
|
1357
|
+
# (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
|
1358
|
+
# [SP mbox-list-extended]
|
1359
|
+
# ; This is the list information pointed to by the ABNF
|
1360
|
+
# ; item "mailbox-data", which is defined above
|
1151
1361
|
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)
|
1362
|
+
lpar; attr = peek_rpar? ? [] : mbx_list_flags; rpar
|
1363
|
+
SP!; delim = nquoted
|
1364
|
+
SP!; name = mailbox
|
1365
|
+
# TODO: mbox-list-extended
|
1366
|
+
MailboxList.new(attr, delim, name)
|
1163
1367
|
end
|
1164
1368
|
|
1165
1369
|
def getquota_response
|
@@ -1254,124 +1458,143 @@ module Net
|
|
1254
1458
|
# mailbox-data = obsolete-search-response / ...
|
1255
1459
|
# obsolete-search-response = "SEARCH" *(SP nz-number)
|
1256
1460
|
def mailbox_data__search
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
if
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
token = lookahead
|
1265
|
-
case token.symbol
|
1266
|
-
when T_CRLF
|
1267
|
-
break
|
1268
|
-
when T_SPACE
|
1269
|
-
shift_token
|
1270
|
-
when T_NUMBER
|
1271
|
-
data.push(number)
|
1272
|
-
when T_LPAR
|
1273
|
-
# TODO: include the MODSEQ value in a response
|
1274
|
-
shift_token
|
1275
|
-
match(T_ATOM)
|
1276
|
-
match(T_SPACE)
|
1277
|
-
match(T_NUMBER)
|
1278
|
-
match(T_RPAR)
|
1279
|
-
end
|
1280
|
-
end
|
1281
|
-
else
|
1282
|
-
data = []
|
1461
|
+
name = label_in("SEARCH", "SORT")
|
1462
|
+
data = []
|
1463
|
+
while _ = SP? && nz_number? do data << _ end
|
1464
|
+
if lpar?
|
1465
|
+
label("MODSEQ"); SP!
|
1466
|
+
mod_sequence_value
|
1467
|
+
rpar
|
1283
1468
|
end
|
1284
|
-
|
1469
|
+
UntaggedResponse.new(name, data, @str)
|
1285
1470
|
end
|
1286
1471
|
alias sort_data mailbox_data__search
|
1287
1472
|
|
1473
|
+
# RFC5256: THREAD
|
1474
|
+
# thread-data = "THREAD" [SP 1*thread-list]
|
1288
1475
|
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 = []
|
1476
|
+
name = label("THREAD")
|
1477
|
+
threads = []
|
1478
|
+
if SP?
|
1479
|
+
threads << thread_list while lookahead_thread_list?
|
1310
1480
|
end
|
1311
|
-
|
1312
|
-
return UntaggedResponse.new(name, threads, @str)
|
1481
|
+
UntaggedResponse.new(name, threads, @str)
|
1313
1482
|
end
|
1314
1483
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
lastmember = nil
|
1484
|
+
alias lookahead_thread_list? lookahead_lpar?
|
1485
|
+
alias lookahead_thread_nested? lookahead_thread_list?
|
1318
1486
|
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
end
|
1332
|
-
lastmember = newmember
|
1333
|
-
when T_SPACE
|
1334
|
-
# do nothing
|
1335
|
-
when T_LPAR
|
1336
|
-
if rootmember.nil?
|
1337
|
-
# dummy member
|
1338
|
-
lastmember = rootmember = ThreadMember.new(nil, [])
|
1339
|
-
end
|
1487
|
+
# RFC5256: THREAD
|
1488
|
+
# thread-list = "(" (thread-members / thread-nested) ")"
|
1489
|
+
def thread_list
|
1490
|
+
lpar
|
1491
|
+
thread = if lookahead_thread_nested?
|
1492
|
+
ThreadMember.new(nil, thread_nested)
|
1493
|
+
else
|
1494
|
+
thread_members
|
1495
|
+
end
|
1496
|
+
rpar
|
1497
|
+
thread
|
1498
|
+
end
|
1340
1499
|
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1500
|
+
# RFC5256: THREAD
|
1501
|
+
# thread-members = nz-number *(SP nz-number) [SP thread-nested]
|
1502
|
+
def thread_members
|
1503
|
+
members = []
|
1504
|
+
members << nz_number # thread root
|
1505
|
+
while SP?
|
1506
|
+
case lookahead!(T_NUMBER, T_LPAR).symbol
|
1507
|
+
when T_NUMBER then members << nz_number
|
1508
|
+
else nested = thread_nested; break
|
1344
1509
|
end
|
1345
1510
|
end
|
1511
|
+
members.reverse.inject(nested || []) {|subthreads, number|
|
1512
|
+
[ThreadMember.new(number, subthreads)]
|
1513
|
+
}.first
|
1514
|
+
end
|
1346
1515
|
|
1347
|
-
|
1516
|
+
# RFC5256: THREAD
|
1517
|
+
# thread-nested = 2*thread-list
|
1518
|
+
def thread_nested
|
1519
|
+
nested = [thread_list, thread_list]
|
1520
|
+
while lookahead_thread_list? do nested << thread_list end
|
1521
|
+
nested
|
1348
1522
|
end
|
1349
1523
|
|
1524
|
+
# mailbox-data =/ "STATUS" SP mailbox SP "(" [status-att-list] ")"
|
1350
1525
|
def mailbox_data__status
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1526
|
+
resp_name = label("STATUS"); SP!
|
1527
|
+
mbox_name = mailbox; SP!
|
1528
|
+
lpar; attr = status_att_list; rpar
|
1529
|
+
UntaggedResponse.new(resp_name, StatusData.new(mbox_name, attr), @str)
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
# RFC3501
|
1533
|
+
# status-att-list = status-att SP number *(SP status-att SP number)
|
1534
|
+
# RFC4466, RFC9051, and RFC3501 Errata
|
1535
|
+
# status-att-list = status-att-val *(SP status-att-val)
|
1536
|
+
def status_att_list
|
1537
|
+
attrs = [status_att_val]
|
1538
|
+
while SP? do attrs << status_att_val end
|
1539
|
+
attrs.to_h
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
# RFC3501 Errata:
|
1543
|
+
# status-att-val = ("MESSAGES" SP number) / ("RECENT" SP number) /
|
1544
|
+
# ("UIDNEXT" SP nz-number) / ("UIDVALIDITY" SP nz-number) /
|
1545
|
+
# ("UNSEEN" SP number)
|
1546
|
+
# RFC4466:
|
1547
|
+
# status-att-val = ("MESSAGES" SP number) /
|
1548
|
+
# ("RECENT" SP number) /
|
1549
|
+
# ("UIDNEXT" SP nz-number) /
|
1550
|
+
# ("UIDVALIDITY" SP nz-number) /
|
1551
|
+
# ("UNSEEN" SP number)
|
1552
|
+
# ;; Extensions to the STATUS responses
|
1553
|
+
# ;; should extend this production.
|
1554
|
+
# ;; Extensions should use the generic
|
1555
|
+
# ;; syntax defined by tagged-ext.
|
1556
|
+
# RFC9051:
|
1557
|
+
# status-att-val = ("MESSAGES" SP number) /
|
1558
|
+
# ("UIDNEXT" SP nz-number) /
|
1559
|
+
# ("UIDVALIDITY" SP nz-number) /
|
1560
|
+
# ("UNSEEN" SP number) /
|
1561
|
+
# ("DELETED" SP number) /
|
1562
|
+
# ("SIZE" SP number64)
|
1563
|
+
# ; Extensions to the STATUS responses
|
1564
|
+
# ; should extend this production.
|
1565
|
+
# ; Extensions should use the generic
|
1566
|
+
# ; syntax defined by tagged-ext.
|
1567
|
+
# RFC7162:
|
1568
|
+
# status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer
|
1569
|
+
# ;; Extends non-terminal defined in [RFC4466].
|
1570
|
+
# ;; Value 0 denotes that the mailbox doesn't
|
1571
|
+
# ;; support persistent mod-sequences
|
1572
|
+
# ;; as described in Section 3.1.2.2.
|
1573
|
+
# RFC7889:
|
1574
|
+
# status-att-val =/ "APPENDLIMIT" SP (number / nil)
|
1575
|
+
# ;; status-att-val is defined in RFC 4466
|
1576
|
+
# RFC8438:
|
1577
|
+
# status-att-val =/ "SIZE" SP number64
|
1578
|
+
# RFC8474:
|
1579
|
+
# status-att-val =/ "MAILBOXID" SP "(" objectid ")"
|
1580
|
+
# ; follows tagged-ext production from [RFC4466]
|
1581
|
+
def status_att_val
|
1582
|
+
key = tagged_ext_label
|
1583
|
+
SP!
|
1584
|
+
val =
|
1585
|
+
case key
|
1586
|
+
when "MESSAGES" then number # RFC3501, RFC9051
|
1587
|
+
when "UNSEEN" then number # RFC3501, RFC9051
|
1588
|
+
when "DELETED" then number # RFC3501, RFC9051
|
1589
|
+
when "UIDNEXT" then nz_number # RFC3501, RFC9051
|
1590
|
+
when "UIDVALIDITY" then nz_number # RFC3501, RFC9051
|
1591
|
+
when "RECENT" then number # RFC3501 (obsolete)
|
1592
|
+
when "SIZE" then number64 # RFC8483, RFC9051
|
1593
|
+
when "MAILBOXID" then parens__objectid # RFC8474
|
1594
|
+
else
|
1595
|
+
number? || ExtensionData.new(tagged_ext_val)
|
1366
1596
|
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)
|
1597
|
+
[key, val]
|
1375
1598
|
end
|
1376
1599
|
|
1377
1600
|
# The presence of "IMAP4rev1" or "IMAP4rev2" is unenforced here.
|
@@ -1573,6 +1796,9 @@ module Net
|
|
1573
1796
|
# resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value /
|
1574
1797
|
# "NOMODSEQ" /
|
1575
1798
|
# "MODIFIED" SP sequence-set
|
1799
|
+
#
|
1800
|
+
# RFC8474: OBJECTID
|
1801
|
+
# resp-text-code =/ "MAILBOXID" SP "(" objectid ")"
|
1576
1802
|
def resp_text_code
|
1577
1803
|
name = resp_text_code__name
|
1578
1804
|
data =
|
@@ -1592,6 +1818,7 @@ module Net
|
|
1592
1818
|
"LIMIT", "OVERQUOTA", "ALREADYEXISTS", "NONEXISTENT", "CLOSED",
|
1593
1819
|
"NOTSAVED", "UIDNOTSTICKY", "UNKNOWN-CTE", "HASCHILDREN"
|
1594
1820
|
when "NOMODSEQ" # CONDSTORE
|
1821
|
+
when "MAILBOXID" then SP!; parens__objectid # RFC8474: OBJECTID
|
1595
1822
|
else
|
1596
1823
|
SP? and text_chars_except_rbra
|
1597
1824
|
end
|
@@ -1638,61 +1865,40 @@ module Net
|
|
1638
1865
|
UIDPlusData.new(validity, src_uids, dst_uids)
|
1639
1866
|
end
|
1640
1867
|
|
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
|
-
|
1868
|
+
ADDRESS_REGEXP = /\G
|
1869
|
+
\( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME
|
1870
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 2: ROUTE
|
1871
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 3: MAILBOX
|
1872
|
+
\s (?: NIL | #{Patterns::QUOTED_rev2} ) # 4: HOST
|
1873
|
+
\)
|
1874
|
+
/nix
|
1875
|
+
|
1876
|
+
# address = "(" addr-name SP addr-adl SP addr-mailbox SP
|
1877
|
+
# addr-host ")"
|
1878
|
+
# addr-adl = nstring
|
1879
|
+
# addr-host = nstring
|
1880
|
+
# addr-mailbox = nstring
|
1881
|
+
# addr-name = nstring
|
1671
1882
|
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)
|
1883
|
+
if (match = accept_re(ADDRESS_REGEXP))
|
1884
|
+
# note that "NIL" isn't captured by the regexp
|
1885
|
+
name, route, mailbox, host = match.captures
|
1886
|
+
.map { Patterns.unescape_quoted _1 }
|
1887
|
+
else # address may include literals
|
1888
|
+
lpar; name = addr_name
|
1889
|
+
SP!; route = addr_adl
|
1890
|
+
SP!; mailbox = addr_mailbox
|
1891
|
+
SP!; host = addr_host
|
1892
|
+
rpar
|
1692
1893
|
end
|
1693
|
-
|
1894
|
+
Address.new(name, route, mailbox, host)
|
1694
1895
|
end
|
1695
1896
|
|
1897
|
+
alias addr_adl nstring
|
1898
|
+
alias addr_host nstring
|
1899
|
+
alias addr_mailbox nstring
|
1900
|
+
alias addr_name nstring
|
1901
|
+
|
1696
1902
|
# flag-list = "(" [flag *(SP flag)] ")"
|
1697
1903
|
def flag_list
|
1698
1904
|
match_re(Patterns::FLAG_LIST, "flag-list")[1]
|
@@ -1707,22 +1913,23 @@ module Net
|
|
1707
1913
|
.map! { _1.start_with?("\\") ? _1[1..].capitalize.to_sym : _1 }
|
1708
1914
|
end
|
1709
1915
|
|
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
|
1916
|
+
# See Patterns::MBX_LIST_FLAGS
|
1917
|
+
def mbx_list_flags
|
1724
1918
|
match_re(Patterns::MBX_LIST_FLAGS, "mbx-list-flags")[1]
|
1725
|
-
.split(nil).map! { _1.capitalize.to_sym }
|
1919
|
+
.split(nil).map! { _1[1..].capitalize.to_sym }
|
1920
|
+
end
|
1921
|
+
|
1922
|
+
# See https://developers.google.com/gmail/imap/imap-extensions
|
1923
|
+
def x_gm_label; accept(T_BSLASH) ? atom.capitalize.to_sym : astring end
|
1924
|
+
|
1925
|
+
# See https://developers.google.com/gmail/imap/imap-extensions
|
1926
|
+
def x_gm_labels
|
1927
|
+
lpar; return [] if rpar?
|
1928
|
+
labels = []
|
1929
|
+
labels << x_gm_label
|
1930
|
+
labels << x_gm_label while SP?
|
1931
|
+
rpar
|
1932
|
+
labels
|
1726
1933
|
end
|
1727
1934
|
|
1728
1935
|
# See https://www.rfc-editor.org/errata/rfc3501
|
@@ -1744,6 +1951,15 @@ module Net
|
|
1744
1951
|
|
1745
1952
|
def parens__modseq; lpar; _ = permsg_modsequence; rpar; _ end
|
1746
1953
|
|
1954
|
+
# RFC8474:
|
1955
|
+
# objectid = 1*255(ALPHA / DIGIT / "_" / "-")
|
1956
|
+
# ; characters in object identifiers are case
|
1957
|
+
# ; significant
|
1958
|
+
alias objectid atom
|
1959
|
+
|
1960
|
+
def parens__objectid; lpar; _ = objectid; rpar; _ end
|
1961
|
+
def nparens__objectid; NIL? ? nil : parens__objectid end
|
1962
|
+
|
1747
1963
|
# RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
|
1748
1964
|
# uid-set = (uniqueid / uid-range) *("," uid-set)
|
1749
1965
|
# uid-range = (uniqueid ":" uniqueid)
|
@@ -1789,42 +2005,47 @@ module Net
|
|
1789
2005
|
@pos = $~.end(0)
|
1790
2006
|
if $1
|
1791
2007
|
return Token.new(T_SPACE, $+)
|
1792
|
-
elsif $2
|
2008
|
+
elsif $2
|
2009
|
+
len = $+.to_i
|
2010
|
+
val = @str[@pos, len]
|
2011
|
+
@pos += len
|
2012
|
+
return Token.new(T_LITERAL8, val)
|
2013
|
+
elsif $3 && $7
|
1793
2014
|
# greedily match ATOM, prefixed with NUMBER, NIL, or PLUS.
|
1794
|
-
return Token.new(T_ATOM, $
|
1795
|
-
elsif $3
|
1796
|
-
return Token.new(T_NIL, $+)
|
2015
|
+
return Token.new(T_ATOM, $3)
|
1797
2016
|
elsif $4
|
1798
|
-
return Token.new(
|
2017
|
+
return Token.new(T_NIL, $+)
|
1799
2018
|
elsif $5
|
2019
|
+
return Token.new(T_NUMBER, $+)
|
2020
|
+
elsif $6
|
1800
2021
|
return Token.new(T_PLUS, $+)
|
1801
|
-
elsif $
|
2022
|
+
elsif $8
|
1802
2023
|
# match ATOM, without a NUMBER, NIL, or PLUS prefix
|
1803
2024
|
return Token.new(T_ATOM, $+)
|
1804
|
-
elsif $8
|
1805
|
-
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
1806
2025
|
elsif $9
|
1807
|
-
return Token.new(
|
2026
|
+
return Token.new(T_QUOTED, Patterns.unescape_quoted($+))
|
1808
2027
|
elsif $10
|
1809
|
-
return Token.new(
|
2028
|
+
return Token.new(T_LPAR, $+)
|
1810
2029
|
elsif $11
|
1811
|
-
return Token.new(
|
2030
|
+
return Token.new(T_RPAR, $+)
|
1812
2031
|
elsif $12
|
1813
|
-
return Token.new(
|
2032
|
+
return Token.new(T_BSLASH, $+)
|
1814
2033
|
elsif $13
|
1815
|
-
return Token.new(
|
2034
|
+
return Token.new(T_STAR, $+)
|
1816
2035
|
elsif $14
|
1817
|
-
return Token.new(
|
2036
|
+
return Token.new(T_LBRA, $+)
|
1818
2037
|
elsif $15
|
2038
|
+
return Token.new(T_RBRA, $+)
|
2039
|
+
elsif $16
|
1819
2040
|
len = $+.to_i
|
1820
2041
|
val = @str[@pos, len]
|
1821
2042
|
@pos += len
|
1822
2043
|
return Token.new(T_LITERAL, val)
|
1823
|
-
elsif $16
|
1824
|
-
return Token.new(T_PERCENT, $+)
|
1825
2044
|
elsif $17
|
1826
|
-
return Token.new(
|
2045
|
+
return Token.new(T_PERCENT, $+)
|
1827
2046
|
elsif $18
|
2047
|
+
return Token.new(T_CRLF, $+)
|
2048
|
+
elsif $19
|
1828
2049
|
return Token.new(T_EOF, $+)
|
1829
2050
|
else
|
1830
2051
|
parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid")
|