net-imap 0.4.2 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Net
4
4
  class IMAP < Protocol
5
+ autoload :FetchData, "#{__dir__}/fetch_data"
6
+ autoload :SequenceSet, "#{__dir__}/sequence_set"
5
7
 
6
8
  # Net::IMAP::ContinuationRequest represents command continuation requests.
7
9
  #
@@ -55,17 +57,71 @@ module Net
55
57
 
56
58
  # Net::IMAP::IgnoredResponse represents intentionally ignored responses.
57
59
  #
58
- # This includes untagged response "NOOP" sent by eg. Zimbra to avoid some
59
- # clients to close the connection.
60
+ # This includes untagged response "NOOP" sent by eg. Zimbra to avoid
61
+ # some clients to close the connection.
60
62
  #
61
63
  # It matches no IMAP standard.
64
+ class IgnoredResponse < UntaggedResponse
65
+ end
66
+
67
+ # **Note:** This represents an intentionally _unstable_ API. Where
68
+ # instances of this class are returned, future releases may return a
69
+ # different (incompatible) object <em>without deprecation or warning</em>.
62
70
  #
63
- class IgnoredResponse < Struct.new(:raw_data)
71
+ # Net::IMAP::UnparsedData represents data for unknown response types or
72
+ # unknown extensions to response types without a well-defined extension
73
+ # grammar.
74
+ #
75
+ # See also: UnparsedNumericResponseData, ExtensionData, IgnoredResponse
76
+ class UnparsedData < Struct.new(:unparsed_data)
64
77
  ##
65
- # method: raw_data
66
- # :call-seq: raw_data -> string
78
+ # method: unparsed_data
79
+ # :call-seq: unparsed_data -> string
67
80
  #
68
- # The raw response data.
81
+ # The unparsed data
82
+ end
83
+
84
+ # **Note:** This represents an intentionally _unstable_ API. Where
85
+ # instances of this class are returned, future releases may return a
86
+ # different (incompatible) object <em>without deprecation or warning</em>.
87
+ #
88
+ # Net::IMAP::UnparsedNumericResponseData represents data for unhandled
89
+ # response types with a numeric prefix. See the documentation for #number.
90
+ #
91
+ # See also: UnparsedData, ExtensionData, IgnoredResponse
92
+ class UnparsedNumericResponseData < Struct.new(:number, :unparsed_data)
93
+ ##
94
+ # method: number
95
+ # :call-seq: number -> integer
96
+ #
97
+ # Returns a numeric response data prefix, when available.
98
+ #
99
+ # Many response types are prefixed with a non-negative #number. For
100
+ # message data, #number may represent a sequence number or a UID. For
101
+ # mailbox data, #number may represent a message count.
102
+
103
+ ##
104
+ # method: unparsed_data
105
+ # :call-seq: unparsed_data -> string
106
+ #
107
+ # The unparsed data, not including #number or UntaggedResponse#name.
108
+ end
109
+
110
+ # **Note:** This represents an intentionally _unstable_ API. Where
111
+ # instances of this class are returned, future releases may return a
112
+ # different (incompatible) object <em>without deprecation or warning</em>.
113
+ #
114
+ # Net::IMAP::ExtensionData represents data that is parsable according to the
115
+ # forward-compatible extension syntax in RFC3501, RFC4466, or RFC9051, but
116
+ # isn't directly known or understood by Net::IMAP yet.
117
+ #
118
+ # See also: UnparsedData, UnparsedNumericResponseData, IgnoredResponse
119
+ class ExtensionData < Struct.new(:data)
120
+ ##
121
+ # method: data
122
+ # :call-seq: data -> string
123
+ #
124
+ # The parsed extension data.
69
125
  end
70
126
 
71
127
  # Net::IMAP::TaggedResponse represents tagged responses.
@@ -108,6 +164,9 @@ module Net
108
164
  # UntaggedResponse#data when the response type is a "condition" ("OK", "NO",
109
165
  # "BAD", "PREAUTH", or "BYE").
110
166
  class ResponseText < Struct.new(:code, :text)
167
+ # Used to avoid an allocation when ResponseText is empty
168
+ EMPTY = new(nil, "").freeze
169
+
111
170
  ##
112
171
  # method: code
113
172
  # :call-seq: code -> ResponseCode or nil
@@ -197,6 +256,10 @@ module Net
197
256
  # * +ALREADYEXISTS+
198
257
  # * +NONEXISTENT+
199
258
  #
259
+ # Other supported \IMAP extension response codes:
260
+ # * +OBJECTID+ {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-7]
261
+ # * +MAILBOXID+, #data will be a string
262
+ #
200
263
  class ResponseCode < Struct.new(:name, :data)
201
264
  ##
202
265
  # method: name
@@ -448,210 +511,6 @@ module Net
448
511
  # "UIDVALIDITY", "UNSEEN". Each value is a number.
449
512
  end
450
513
 
451
- # Net::IMAP::FetchData represents the contents of a FETCH response.
452
- #
453
- # Net::IMAP#fetch and Net::IMAP#uid_fetch both return an array of
454
- # FetchData objects.
455
- #
456
- # === Fetch attributes
457
- #
458
- #--
459
- # TODO: merge branch with accessor methods for each type of attr. Then
460
- # move nearly all of the +attr+ documentation onto the appropriate
461
- # accessor methods.
462
- #++
463
- #
464
- # Each key of the #attr hash is the data item name for the fetched value.
465
- # Each data item represents a message attribute, part of one, or an
466
- # interpretation of one. #seqno is not a message attribute. Most message
467
- # attributes are static and must never change for a given <tt>[server,
468
- # account, mailbox, UIDVALIDITY, UID]</tt> tuple. A few message attributes
469
- # can be dynamically changed, e.g. using the {STORE
470
- # command}[rdoc-ref:Net::IMAP#store].
471
- #
472
- # See {[IMAP4rev1] §7.4.2}[https://www.rfc-editor.org/rfc/rfc3501.html#section-7.4.2]
473
- # and {[IMAP4rev2] §7.5.2}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.5.2]
474
- # for full description of the standard fetch response data items, and
475
- # Net::IMAP@Message+envelope+and+body+structure for other relevant RFCs.
476
- #
477
- # ==== Static fetch data items
478
- #
479
- # The static data items
480
- # defined by [IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501.html]] are:
481
- #
482
- # [<tt>"UID"</tt>]
483
- # A number expressing the unique identifier of the message.
484
- #
485
- # [<tt>"BODY[]"</tt>, <tt>"BODY[]<#{offset}>"</tt>]
486
- # The [RFC5322[https://tools.ietf.org/html/rfc5322]] expression of the
487
- # entire message, as a string.
488
- #
489
- # If +offset+ is specified, this returned string is a substring of the
490
- # entire contents, starting at that origin octet. This means that
491
- # <tt>BODY[]<0></tt> MAY be truncated, but <tt>BODY[]</tt> is NEVER
492
- # truncated.
493
- #
494
- # <em>Messages can be parsed using the "mail" gem.</em>
495
- #
496
- # [Note]
497
- # When fetching <tt>BODY.PEEK[#{specifier}]</tt>, the data will be
498
- # returned in <tt>BODY[#{specifier}]</tt>, without the +PEEK+. This is
499
- # true for all of the <tt>BODY[...]</tt> attribute forms.
500
- #
501
- # [<tt>"BODY[HEADER]"</tt>, <tt>"BODY[HEADER]<#{offset}>"</tt>]
502
- # The [RFC5322[https://tools.ietf.org/html/rfc5322]] header of the
503
- # message.
504
- #
505
- # <em>Message headers can be parsed using the "mail" gem.</em>
506
- #
507
- # [<tt>"BODY[HEADER.FIELDS (#{fields.join(" ")})]"</tt>,]
508
- # [<tt>"BODY[HEADER.FIELDS (#{fields.join(" ")})]<#{offset}>"</tt>]
509
- # When field names are given, the subset contains only the header fields
510
- # that matches one of the names in the list. The field names are based
511
- # on what was requested, not on what was returned.
512
- #
513
- # [<tt>"BODY[HEADER.FIELDS.NOT (#{fields.join(" ")})]"</tt>,]
514
- # [<tt>"BODY[HEADER.FIELDS.NOT (#{fields.join(" ")})]<#{offset}>"</tt>]
515
- # When the <tt>HEADER.FIELDS.NOT</tt> is used, the subset is all of the
516
- # fields that <em>do not</em> match any names in the list.
517
- #
518
- # [<tt>"BODY[TEXT]"</tt>, <tt>"BODY[TEXT]<#{offset}>"</tt>]
519
- # The text body of the message, omitting
520
- # the [RFC5322[https://tools.ietf.org/html/rfc5322]] header.
521
- #
522
- # [<tt>"BODY[#{part}]"</tt>, <tt>"BODY[#{part}]<#{offset}>"</tt>]
523
- # The text of a particular body section, if it was fetched.
524
- #
525
- # Multiple part specifiers will be joined with <tt>"."</tt>. Numeric
526
- # part specifiers refer to the MIME part number, counting up from +1+.
527
- # Messages that don't use MIME, or MIME messages that are not multipart
528
- # and don't hold an encapsulated message, only have a part +1+.
529
- #
530
- # 8-bit textual data is permitted if
531
- # a [CHARSET[https://tools.ietf.org/html/rfc2978]] identifier is part of
532
- # the body parameter parenthesized list for this section. See
533
- # BodyTypeBasic.
534
- #
535
- # MESSAGE/RFC822 or MESSAGE/GLOBAL message, or a subset of the header, if
536
- # it was fetched.
537
- #
538
- # [<tt>"BODY[#{part}.HEADER]"</tt>,]
539
- # [<tt>"BODY[#{part}.HEADER]<#{offset}>"</tt>,]
540
- # [<tt>"BODY[#{part}.HEADER.FIELDS.NOT (#{fields.join(" ")})]"</tt>,]
541
- # [<tt>"BODY[#{part}.HEADER.FIELDS.NOT (#{fields.join(" ")})]<#{offset}>"</tt>,]
542
- # [<tt>"BODY[#{part}.TEXT]"</tt>,]
543
- # [<tt>"BODY[#{part}.TEXT]<#{offset}>"</tt>,]
544
- # [<tt>"BODY[#{part}.MIME]"</tt>,]
545
- # [<tt>"BODY[#{part}.MIME]<#{offset}>"</tt>]
546
- # +HEADER+, <tt>HEADER.FIELDS</tt>, <tt>HEADER.FIELDS.NOT</tt>, and
547
- # <tt>TEXT</tt> can be prefixed by numeric part specifiers, if it refers
548
- # to a part of type <tt>message/rfc822</tt> or <tt>message/global</tt>.
549
- #
550
- # +MIME+ refers to the [MIME-IMB[https://tools.ietf.org/html/rfc2045]]
551
- # header for this part.
552
- #
553
- # [<tt>"BODY"</tt>]
554
- # A form of +BODYSTRUCTURE+, without any extension data.
555
- #
556
- # [<tt>"BODYSTRUCTURE"</tt>]
557
- # Returns a BodyStructure object that describes
558
- # the [MIME-IMB[https://tools.ietf.org/html/rfc2045]] body structure of
559
- # a message, if it was fetched.
560
- #
561
- # [<tt>"ENVELOPE"</tt>]
562
- # An Envelope object that describes the envelope structure of a message.
563
- # See the documentation for Envelope for a description of the envelope
564
- # structure attributes.
565
- #
566
- # [<tt>"INTERNALDATE"</tt>]
567
- # The internal date and time of the message on the server. This is not
568
- # the date and time in
569
- # the [RFC5322[https://tools.ietf.org/html/rfc5322]] header, but rather
570
- # a date and time which reflects when the message was received.
571
- #
572
- # [<tt>"RFC822.SIZE"</tt>]
573
- # A number expressing the [RFC5322[https://tools.ietf.org/html/rfc5322]]
574
- # size of the message.
575
- #
576
- # [Note]
577
- # \IMAP was originally developed for the older RFC-822 standard, and
578
- # as a consequence several fetch items in \IMAP incorporate "RFC822"
579
- # in their name. With the exception of +RFC822.SIZE+, there are more
580
- # modern replacements; for example, the modern version of
581
- # +RFC822.HEADER+ is <tt>BODY.PEEK[HEADER]</tt>. In all cases,
582
- # "RFC822" should be interpreted as a reference to the
583
- # updated [RFC5322[https://tools.ietf.org/html/rfc5322]] standard.
584
- #
585
- # [<tt>"RFC822"</tt>]
586
- # Semantically equivalent to <tt>BODY[]</tt>.
587
- # [<tt>"RFC822.HEADER"</tt>]
588
- # Semantically equivalent to <tt>BODY[HEADER]</tt>.
589
- # [<tt>"RFC822.TEXT"</tt>]
590
- # Semantically equivalent to <tt>BODY[TEXT]</tt>.
591
- #
592
- # [Note:]
593
- # >>>
594
- # Additional static fields are defined in \IMAP extensions and
595
- # [IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html]], but
596
- # Net::IMAP can't parse them yet.
597
- #
598
- #--
599
- # <tt>"BINARY[#{section_binary}]<#{offset}>"</tt>:: TODO...
600
- # <tt>"BINARY.SIZE[#{sectionbinary}]"</tt>:: TODO...
601
- # <tt>"EMAILID"</tt>:: TODO...
602
- # <tt>"THREADID"</tt>:: TODO...
603
- # <tt>"SAVEDATE"</tt>:: TODO...
604
- #++
605
- #
606
- # ==== Dynamic message attributes
607
- # The only dynamic item defined
608
- # by [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]] is:
609
- # [<tt>"FLAGS"</tt>]
610
- # An array of flags that are set for this message. System flags are
611
- # symbols that have been capitalized by String#capitalize. Keyword
612
- # flags are strings and their case is not changed.
613
- #
614
- # \IMAP extensions define new dynamic fields, e.g.:
615
- #
616
- # [<tt>"MODSEQ"</tt>]
617
- # The modification sequence number associated with this IMAP message.
618
- #
619
- # Requires the [CONDSTORE[https://tools.ietf.org/html/rfc7162]]
620
- # server {capability}[rdoc-ref:Net::IMAP#capability].
621
- #
622
- # [Note:]
623
- # >>>
624
- # Additional dynamic fields are defined in \IMAP extensions, but
625
- # Net::IMAP can't parse them yet.
626
- #
627
- #--
628
- # <tt>"ANNOTATE"</tt>:: TODO...
629
- # <tt>"PREVIEW"</tt>:: TODO...
630
- #++
631
- #
632
- class FetchData < Struct.new(:seqno, :attr)
633
- ##
634
- # method: seqno
635
- # :call-seq: seqno -> Integer
636
- #
637
- # The message sequence number.
638
- #
639
- # [Note]
640
- # This is never the unique identifier (UID), not even for the
641
- # Net::IMAP#uid_fetch result. If it was returned, the UID is available
642
- # from <tt>attr["UID"]</tt>.
643
-
644
- ##
645
- # method: attr
646
- # :call-seq: attr -> hash
647
- #
648
- # A hash. Each key is specifies a message attribute, and the value is the
649
- # corresponding data item.
650
- #
651
- # See rdoc-ref:FetchData@Fetch+attributes for descriptions of possible
652
- # values.
653
- end
654
-
655
514
  # Net::IMAP::Envelope represents envelope structures of messages.
656
515
  #
657
516
  # [Note]
@@ -665,6 +524,7 @@ module Net
665
524
  # for full description of the envelope fields, and
666
525
  # Net::IMAP@Message+envelope+and+body+structure for other relevant RFCs.
667
526
  #
527
+ # Returned by FetchData#envelope
668
528
  class Envelope < Struct.new(:date, :subject, :from, :sender, :reply_to,
669
529
  :to, :cc, :bcc, :in_reply_to, :message_id)
670
530
  ##
@@ -1083,7 +943,6 @@ module Net
1083
943
  # * description[rdoc-ref:BodyTypeBasic#description]
1084
944
  # * encoding[rdoc-ref:BodyTypeBasic#encoding]
1085
945
  # * size[rdoc-ref:BodyTypeBasic#size]
1086
- #
1087
946
  class BodyTypeMessage < Struct.new(:media_type, :subtype,
1088
947
  :param, :content_id,
1089
948
  :description, :encoding, :size,
@@ -8,13 +8,14 @@ module Net
8
8
  # (internal API, subject to change)
9
9
  module ParserUtils # :nodoc:
10
10
 
11
- module Generator
11
+ module Generator # :nodoc:
12
12
 
13
13
  LOOKAHEAD = "(@token ||= next_token)"
14
14
  SHIFT_TOKEN = "(@token = nil)"
15
15
 
16
16
  # we can skip lexer for single character matches, as a shortcut
17
17
  def def_char_matchers(name, char, token)
18
+ byte = char.ord
18
19
  match_name = name.match(/\A[A-Z]/) ? "#{name}!" : name
19
20
  char = char.dump
20
21
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
@@ -27,7 +28,7 @@ module Net
27
28
 
28
29
  # use token or string peek
29
30
  def peek_#{name}?
30
- @token ? @token.symbol == #{token} : @str[@pos] == #{char}
31
+ @token ? @token.symbol == #{token} : @str.getbyte(@pos) == #{byte}
31
32
  end
32
33
 
33
34
  # like accept(token_symbols); returns token or nil
@@ -35,7 +36,7 @@ module Net
35
36
  if @token&.symbol == #{token}
36
37
  #{SHIFT_TOKEN}
37
38
  #{char}
38
- elsif !@token && @str[@pos] == #{char}
39
+ elsif !@token && @str.getbyte(@pos) == #{byte}
39
40
  @pos += 1
40
41
  #{char}
41
42
  end
@@ -46,7 +47,7 @@ module Net
46
47
  if @token&.symbol == #{token}
47
48
  #{SHIFT_TOKEN}
48
49
  #{char}
49
- elsif !@token && @str[@pos] == #{char}
50
+ elsif !@token && @str.getbyte(@pos) == #{byte}
50
51
  @pos += 1
51
52
  #{char}
52
53
  else
@@ -109,7 +110,6 @@ module Net
109
110
  end
110
111
  end
111
112
  RUBY
112
-
113
113
  end
114
114
 
115
115
  end
@@ -170,6 +170,16 @@ module Net
170
170
  @token ||= next_token
171
171
  end
172
172
 
173
+ # like match, without consuming the token
174
+ def lookahead!(*args)
175
+ if args.include?((@token ||= next_token)&.symbol)
176
+ @token
177
+ else
178
+ parse_error('unexpected token %s (expected %s)',
179
+ @token&.symbol, args.join(" or "))
180
+ end
181
+ end
182
+
173
183
  def peek_str?(str)
174
184
  assert_no_lookahead if Net::IMAP.debug
175
185
  @str[@pos, str.length] == str